From 6705fc3bdc9b6d05f4132f289ea8719755d4340f Mon Sep 17 00:00:00 2001 From: Josh Suereth Date: Thu, 6 Feb 2014 11:58:50 -0500 Subject: [PATCH] Adding automation for releasing *and* directly release to bintray. * Create helpers to ensure appropriate bintray setup. * Add mechanism to acknowledge (via REST) that a release is complete and good * Create release script to automatically tag/test/push packages and remember all the steps to actually ensure a bintray release is coherent, complete and basically working on linux. Windows can suffer as usual until we have more infrastructure. --- build.sbt | 6 +++- project/bintray.scala | 49 +++++++++++++++++++++++++++++++ project/plugins.sbt | 3 ++ project/release.scala | 68 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 project/bintray.scala create mode 100644 project/release.scala diff --git a/build.sbt b/build.sbt index 60985bcab..d019d9eca 100644 --- a/build.sbt +++ b/build.sbt @@ -9,6 +9,8 @@ sbtVersion in Global := { scalaVersion in Global := "2.9.2" +crossScalaVersions := Seq("2.9.2", "2.10.2") + name := "sbt-native-packager" organization := "com.typesafe.sbt" @@ -30,7 +32,7 @@ ghpages.settings git.remoteRepo := "git@github.com:sbt/sbt-native-packager.git" -publishTo := Some(Resolver.url("sbt-plugin-releases", new URL("http://scalasbt.artifactoryonline.com/scalasbt/sbt-plugin-releases/"))(Resolver.ivyStylePatterns)) +Bintray.settings publishMavenStyle := false @@ -38,6 +40,8 @@ scriptedSettings scriptedLaunchOpts <+= version apply { v => "-Dproject.version="+v } +Release.settings + diff --git a/project/bintray.scala b/project/bintray.scala new file mode 100644 index 000000000..59fc5abb7 --- /dev/null +++ b/project/bintray.scala @@ -0,0 +1,49 @@ +import sbt._ +import sbt.Keys._ + +object Bintray { + val bintrayPublishAllStaged = TaskKey[Unit]("bintray-publish-all-staged", "Publish all staged artifacts on bintray.") + val checkBintrayCredentials = TaskKey[Unit]("bintray-check-credentials", "Checks to see if bintray credentials are configured.") + val bintrayPluginId = "sbt-plugin-releases" + val bintrayPluginUrl = "https://api.bintray.com/content/sbt/sbt-plugin-releases/" + val bintrayPluginLayout = "[module]/[revision]/"+ Resolver.localBasePattern + + def bintrayCreds(creds: Seq[sbt.Credentials]): (String, String) = { + val matching = + for { + c <- creds + if c.isInstanceOf[sbt.DirectCredentials] + val cred = c.asInstanceOf[sbt.DirectCredentials] + if cred.host == "api.bintray.com" + } yield cred.userName -> cred.passwd + + matching.headOption getOrElse sys.error("Unable to find bintray credentials (api.bintray.com)") + } + + def publishContent(pkg: String, repo: String, version: String, creds: Seq[sbt.Credentials]): Unit = { + val subject = "sbt" // Sbt org - TODO - don't hardcode + val uri = s"https://bintray.com/api/v1/content/$subject/$repo/$pkg/$version/publish" + + val (u,p) = bintrayCreds(creds) + import dispatch.classic._ + // TODO - Log the output + Http(url(uri).POST.as(u,p).>|) + } + + def settings: Seq[Setting[_]] = + Seq( + publishTo := { + val resolver = Resolver.url("bintray-"+bintrayPluginId, new URL(bintrayPluginUrl))(Patterns(false, bintrayPluginLayout)) + Some(resolver) + }, + checkBintrayCredentials := { + val creds = credentials.value + val (user, _) = bintrayCreds(creds) + streams.value.log.info(s"Using $user for bintray login.") + }, + bintrayPublishAllStaged := { + val creds = credentials.value + publishContent(projectID.value.name, bintrayPluginId, version.value, creds) + } + ) +} \ No newline at end of file diff --git a/project/plugins.sbt b/project/plugins.sbt index 647bfb0d2..df1aff044 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -14,3 +14,6 @@ libraryDependencies <+= (sbtVersion) { sv => // Scripted plugin needs to declare this as a dependency libraryDependencies += "jline" % "jline" % "2.11" + +// For our bintray publishing +libraryDependencies += "net.databinder" %% "dispatch-http" % "0.8.10" diff --git a/project/release.scala b/project/release.scala new file mode 100644 index 000000000..8d3038e0f --- /dev/null +++ b/project/release.scala @@ -0,0 +1,68 @@ +import sbt._ +import Keys._ + +import complete.DefaultParsers._ +import complete.Parser + +object Release { + + val versionNumberParser: Parser[String] = { + val classifier: Parser[String] = ("-" ~ ID) map { + case (dash, id) => dash + id + } + val version: Parser[String] = (Digit ~ chars(".0123456789").* ~ classifier) map { + case ((first, rest), rest2) => ((first +: rest).mkString + rest2) + } + val complete = (chars("v") ~ token(version, "")) map { + case (v, num) => v + num + } + complete + } + + def releaseParser(state: State): Parser[String] = + Space ~> versionNumberParser + + + val releaseHelp = Help("release", + "release " -> "Runs the release script for a given version number", + """|release + | + |Runs our release script. This will: + |1. Run all the tests (unit + scripted) for the current OS. + |2. Tag the git repo with the given tag (v). + |3. Reload the build with the new version number from the git tag. + |4. publish all the artifacts to bintray.""".stripMargin + ) + + def scriptedForPlatform: String = { + // TODO - Implement. Instead of only running tests we can, we should + // probably ping some service to see if all platform tests have + // succeeded. + "scripted universal/* debian/* rpm/*" + } + + def releaseAction(state: State, tag: String): State = { + // TODO - Ensure we're releasing on JDK 6, so we're binary compatible. + // First check to ensure we have a sane publishing environment... + "bintrayCheckCredentials" :: + "+ test" :: + // Workaround for 0.12.4 scripted issue + "set scalaVersion in Global := \"2.10.2\"" :: + scriptedForPlatform :: + // TODO - Signed tags, possibly using pgp keys? + ("git tag " + tag) :: + "reload" :: + // TODO - once we figure out bintray + pubishSigned, switch to signed + // releases. + "+ publishSigned" :: + "bintrayPublishAllStaged" :: + ("git push origin " + tag) :: + state + } + + val releaseCommand = + Command("release", releaseHelp)(releaseParser)(releaseAction) + + def settings: Seq[Setting[_]]= + Seq(commands += releaseCommand) +} \ No newline at end of file