Last active
June 9, 2017 07:27
-
-
Save viktorklang/a09aad920c1a4072cfe6 to your computer and use it in GitHub Desktop.
Gistard — an sbt autoplugin for depending on Gists — such as Gistard itself
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Copyright 2015 Viktor Klang | |
Licensed under the Apache License, Version 2.0 (the "License"); | |
you may not use this file except in compliance with the License. | |
You may obtain a copy of the License at | |
http://www.apache.org/licenses/LICENSE-2.0 | |
Unless required by applicable law or agreed to in writing, software | |
distributed under the License is distributed on an "AS IS" BASIS, | |
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
See the License for the specific language governing permissions and | |
limitations under the License. | |
*/ | |
/** | |
* Documentation | |
* | |
* Install: | |
* | |
* `wget -P <sbt-project-base-dir>/project https://gist.githubusercontent.com/viktorklang/a09aad920c1a4072cfe6/raw/Gistard.scala` | |
* | |
* Or visit the above address with your web browser of choice and save the file in your `<sbt-project-basedir>/project` folder as `Gistard.scala` (important) | |
* | |
* | |
* Configuration: | |
* | |
* **It is recommended to configure Gistard to update itself using Gistard** so add this to your `build.sbt`: | |
* | |
* If you want to make sure you are always on the latest version, omit the revision as below: | |
* | |
* GistardKeys.gistardDependencies += gist("viktorklang", "a09aad920c1a4072cfe6", "", "Gistard.scala", baseDirectory.value / "project") | |
* | |
* If you want to update to a specific revision, use this instead: | |
* | |
* GistardKeys.gistardDependencies += gist("viktorklang", "a09aad920c1a4072cfe6", "<insert revision here>", "Gistard.scala", baseDirectory.value / "project") | |
* | |
* To see available revisions, have a look here: https://gist.github.com/viktorklang/a09aad920c1a4072cfe6/revisions | |
* | |
* | |
* `Gistard` has three tasks for maintaining the dependencies, `gistardUpdate` which downloads the gist dependencies if they are not already downloaded, | |
* `gistardClean` which deletes all gist dependencies, and `gistardReload` which downloads the gist dependencies and replaces the existing versions. | |
* | |
* `Gistard` by default hooks `gistardUpdate` into the `update` task and `gistardClean` into the `clean` task. | |
* | |
* | |
* Usage: | |
* | |
* So you've found a Gist that you want to automatically pull into your `sbt` project, fantastic! | |
* | |
* Example: | |
* | |
* You were browsing `viktorklang`s Gists when you saw `GistardVerify` (https://gist.github.com/viktorklang/3fdb2ec2709b79fdc7a4), | |
* and now you want to add that snippet to your `build.sbt` using `Gistard`. | |
* | |
* In the URL, the "viktorklang" part will be the value of the `owner` field of the Gist, | |
* and "3fdb2ec2709b79fdc7a4" is the `id` field value. | |
* You note that the file you want to depend on is named `GistardVerify.scala`, | |
* this will be the `fileName` field value. | |
* | |
* First you decide if you want to depend on a specific revision of that Gist, you can see them at: | |
* https://gist.github.com/viktorklang/3fdb2ec2709b79fdc7a4/revisions | |
* | |
* You decide that you want to have "the latest" revision (empty String for revision below), | |
* and that you want to place it in `main/scala/gistard/verify` so you add the following to your `build.sbt`: | |
* | |
* GistardKeys.gistardDependencies += gist(owner = "viktorklang", | |
* id = "3fdb2ec2709b79fdc7a4", | |
* revision = "", | |
* fileName = "GistardVerify.scala", | |
* localDirectory = scalaSource.value / "gistard" / "verify") | |
* | |
* Then you either restart `sbt` or run the `reload` command to see this change take effect and all that is left to do is to | |
* run the `update` task to have `Gistard` download the `GistardVerify.scala` file. | |
* | |
* | |
* | |
* | |
**/ | |
package gistard | |
import sbt._ | |
import sbt.Keys._ | |
import scala.collection.immutable | |
import java.net.URL | |
import java.io.{File => JFile} | |
/** | |
* Gistard is a GitHub Gist hosted sbt autoplugin that allows you to depend on GitHub Gist files as source dependencies. | |
* Gistard itself is a GitHub Gist and can use Gistard to update itself. | |
**/ | |
object Gistard { | |
/** | |
* Gist represents a specific revision of a specific file in a specific Gist for a specific owner. | |
* If the revision is empty, it will fetch the latest revision. | |
**/ | |
final case class Gist(owner: String, id: String, revision: String, fileName: String, localDirectory: File) { | |
if (fileName.isEmpty) throw new IllegalArgumentException("Gist file must be non-empty!") | |
if (id.isEmpty) throw new IllegalArgumentException("Gist ID must be non-empty!") | |
if (!revision.isEmpty && Hash.toHex(Hash.fromHex(revision)) != revision) | |
throw new IllegalArgumentException("Gist revision must be hexadecimal or empty!") | |
if (owner.isEmpty) throw new IllegalArgumentException("Gist owner must be non-empty!") | |
override def toString = "Gist[owner=%s,id=%s,revision=%s,fileName=%s,localDirectory=%s]".format(owner, id, revision, fileName, localDirectory) | |
} | |
/** | |
* APIVersion is the API for Github, | |
* it is possible, if needed, to provide your own implementation of this, | |
* see the `gistardAPIVersion` SettingKey. | |
**/ | |
trait APIVersion { | |
/** | |
* @return the URL to the contents of the given Gist | |
**/ | |
def contentURLFor(gist: Gist): URL | |
/** | |
* @return the URL to the summary of the given Gist | |
**/ | |
def summaryURLFor(gist: Gist): URL | |
/** | |
* Downloads all Gists given as the `gistDependencies` parameter, | |
* doesn't update already existing files unless `force` is set to true. | |
**/ | |
def update(gistDependencies: Set[Gist], force: Boolean, log: Logger): Seq[File] = | |
gistDependencies.flatMap(update(_, force, log))(scala.collection.breakOut) | |
/** | |
* Downloads all Gists given as the `gist` parameter, | |
* doesn't update already existing files unless `force` is set to true. | |
**/ | |
def update(gist: Gist, force: Boolean, log: Logger): Option[File] = { | |
val file = gist.localDirectory / gist.fileName | |
if (file.isDirectory) { | |
log.error("Gistard: Can't download %s since the file already exists but is a directory: %s".format(gist, file)) | |
None | |
} else if (force || !file.exists) { | |
log.info("Gistard: downloading %s as %s".format(gist, file)) | |
IO.withTemporaryFile("gistard", gist.fileName) { | |
temp => | |
IO.download(contentURLFor(gist), temp) | |
if (file.exists) IO.delete(file) | |
IO.move(temp, file) | |
Some(file) | |
} | |
} else { | |
log.info("Gistard: Using cached version of %s instead of downloading".format(file)) | |
Some(file) | |
} | |
} | |
/** | |
* Deletes all Gists given as the `gistDependencies` parameter | |
**/ | |
def clean(gistDependencies: Set[Gist], log: Logger): Set[File] = | |
gistDependencies.flatMap(clean(_, log)) | |
/** | |
* Deletes the Gist given as the `gist` parameter. | |
**/ | |
def clean(gist: Gist, log: Logger): Option[File] = { | |
val file = gist.localDirectory / gist.fileName | |
if (!file.exists) None | |
else if (!file.isFile) { | |
log.error("Gistard: Could not delete %s since it is a directory and not a file".format(file)) | |
None | |
} else if (!file.delete()) { | |
log.error("Gistard: Could not delete: %s".format(file)) | |
None | |
} else { | |
log.info("Gistard: Deleting %s".format(file)) | |
Some(file) | |
} | |
} | |
} | |
object GithubV3 extends APIVersion { | |
override def contentURLFor(gist: Gist): URL = | |
if (gist.revision.isEmpty) | |
new URL("https://gist.githubusercontent.com/%s/%s/raw/%s".format(gist.owner, gist.id, gist.fileName)) | |
else | |
new URL("https://gist.githubusercontent.com/%s/%s/raw/%s/%s".format(gist.owner, gist.id, gist.revision, gist.fileName)) | |
override def summaryURLFor(gist: Gist): URL = | |
if (gist.revision.isEmpty) | |
new URL("https://api.github.com/gists/%d".format(gist.id)) | |
else | |
new URL("https://api.github.com/gists/%d/%s".format(gist.id, gist.revision)) | |
} | |
} | |
object GistardPlugin extends AutoPlugin { | |
import Gistard._ | |
object autoImport { | |
object GistardKeys { | |
val gistardUpdate = TaskKey[Seq[JFile]]("gistard-update", "Downloads all Gist dependencies that aren't already downloaded") | |
val gistardClean = TaskKey[Set[JFile]]("gistard-clean", "Deletes all Gist Dependencies") | |
val gistardReload = TaskKey[Seq[JFile]]("gistard-reload", "Downloads all Gist dependencies") | |
val gistardDependencies = SettingKey[Set[Gist]]("gistard-dependencies", "All Gist Dependencies") | |
val gistardAPIVersion = SettingKey[APIVersion]("gistard-api-version", "What version of the Github API to use") | |
} | |
/** | |
* Auto-imported convenience method to create a new `Gist` from the given parameters. | |
**/ | |
def gist(owner: String, id: String, revision: String, fileName: String, localDirectory: File): Gist = | |
Gist(owner, id, revision, fileName, localDirectory) | |
} | |
import autoImport._ | |
import GistardKeys._ | |
override def requires = sbt.plugins.JvmPlugin | |
override def trigger = allRequirements | |
override lazy val projectSettings = Seq( | |
gistardAPIVersion := GithubV3, | |
gistardDependencies := Set(), | |
gistardUpdate := (gistardAPIVersion).value.update(gistardDependencies.value, false, streams.value.log), | |
gistardClean := (gistardAPIVersion).value.clean(gistardDependencies.value, streams.value.log), | |
gistardReload := (gistardAPIVersion).value.update(gistardDependencies.value, true, streams.value.log), | |
update <<= update dependsOn(gistardUpdate), | |
clean <<= clean dependsOn(gistardClean) | |
) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@Blaisorblade Omg, I just saw your comment, 1 year late. :(
I haven't seen that problem in a per-project installation, could most definitely be related to having it in ~/.sbt