-
-
Save viktorklang/a09aad920c1a4072cfe6 to your computer and use it in GitHub Desktop.
/* | |
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) | |
) | |
} |
Thanks @sschaef, I've updated Gistard (https://gist.github.com/viktorklang/a09aad920c1a4072cfe6/3cd3eb4131e6302d3a365d7919e810443012b331), so if you have enabled Gistard auto updating, you should get it when you do gistardReload
.
Problem: cleaning with auto-updating enabled deletes Gistard itself.
> clean
[info] Gistard: Deleting /Users/pgiarrusso/.sbt/0.13/plugins/Gistard.scala
Restarting sbt now triggers a build failure, because the auto-update config references the now-deleted Gistard.
That was with the following in /Users/pgiarrusso/.sbt/0.13/gistard.sbt
:
gistard.GistardPlugin.autoImport.GistardKeys.gistardDependencies += gistard.GistardPlugin.autoImport.gist("viktorklang", "a09aad920c1a4072cfe6", "", "Gistard.scala", new File(System.getProperty("user.home")) / ".sbt" / "0.13" / "plugins")
(This looks awkward, but autoimports don't work in ~/.sbt
— there was a ticket for that).
And with Gistard installed as:
cd ~/.sbt/0.13/plugins
wget https://gist.githubusercontent.com/viktorklang/a09aad920c1a4072cfe6/raw/Gistard.scala
Would that problem not show-up with a per-project installation?
@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
bug in https://gist.github.com/viktorklang/a09aad920c1a4072cfe6#file-gistard-scala-L191