Skip to content

Commit

Permalink
Merge pull request #8 from ohnosequences/pr/8
Browse files Browse the repository at this point in the history
Parametrize release task with the version tag
  • Loading branch information
laughedelic authored Oct 3, 2016
2 parents 521cc6e + 796182d commit f6d5b2f
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 99 deletions.
2 changes: 1 addition & 1 deletion Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ The main task is `githubRelease`, it creates the release and publishes the asses

There are some other tasks which work as intermediate checks:

* `ghreleaseCheckCredentials` — checks Github OAuth token and helps to set it if needed
* `ghreleaseGetCredentials` — checks Github OAuth token and helps to set it if needed
* `ghreleaseCheckRepo` — checks that the repository exists and is accessible
* `ghreleaseCheckReleaseBuilder` — checks that Github repo contains the tag and there is no release based on it yet

Expand Down
2 changes: 2 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ libraryDependencies += "org.kohsuke" % "github-api" % "1.77"

// libraryDependencies += "com.github.xuwei-k" %% "ghscala" % "0.2.14"
wartremoverErrors in (Compile, compile) --= Seq(Wart.Any, Wart.NonUnitStatements)

// enablePlugins(SbtGithubReleasePlugin)
5 changes: 2 additions & 3 deletions project/plugins.sbt
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
resolvers += "Era7 maven releases" at "https://s3-eu-west-1.amazonaws.com/releases.era7.com"

addSbtPlugin("ohnosequences" % "nice-sbt-settings" % "0.8.0-RC2-8-gc3118aa")

// resolvers += "Era7 maven snapshots" at "https://s3-eu-west-1.amazonaws.com/snapshots.era7.com"
// addSbtPlugin("ohnosequences" % "nice-sbt-settings" % "0.8.0-RC2+")
addSbtPlugin("ohnosequences" % "nice-sbt-settings" % "0.8.0-RC2-10-gf28fae6")

// addSbtPlugin("ohnosequences" % "sbt-github-release" % "0.1.2-SNAPSHOT")
111 changes: 75 additions & 36 deletions src/main/scala/GithubRelease.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,82 +8,121 @@ import scala.util.Try

case object GithubRelease {

type DefTask[X] = Def.Initialize[Task[X]]
type DefSetting[X] = Def.Initialize[Setting[X]]

case object keys {
lazy val ghreleaseNotes = settingKey[File]("File with the release notes for the current version")
type TagName = String

lazy val ghreleaseRepoOrg = settingKey[String]("Github repository organization")
lazy val ghreleaseRepoName = settingKey[String]("Github repository name")
lazy val ghreleaseTag = settingKey[String]("The name of the Git tag")
lazy val ghreleaseTitle = settingKey[String]("The title of the release")
lazy val ghreleaseCommitish = settingKey[String]("Specifies the commitish value that determines where the Git tag is created from")
lazy val ghreleaseMediaTypesMap = settingKey[File => String]("This function will determine media type for the assets")
lazy val ghreleaseIsPrerelease = settingKey[String => Boolean]("A function to determine release as a prerelease based on the tag name")
lazy val ghreleaseNotes = settingKey[TagName => String]("Release notes for the given tag")
lazy val ghreleaseTitle = settingKey[TagName => String]("The title of the release")
lazy val ghreleaseIsPrerelease = settingKey[TagName => Boolean]("A function to determine release as a prerelease based on the tag name")

lazy val ghreleaseAssets = taskKey[Seq[File]]("The artifact files to upload")

// TODO: remove this, make them tasks or parameters for the main task
// lazy val draft = settingKey[Boolean]("true to create a draft (unpublished) release, false to create a published one")

lazy val ghreleaseCheckCredentials = taskKey[GitHub]("Checks authentification and suggests to create a new oauth token if needed")
lazy val ghreleaseCheckRepo = taskKey[GHRepository]("Checks repo existence and returns it if it's fine")
lazy val ghreleaseCheckReleaseBuilder = taskKey[GHReleaseBuilder]("Checks remote tag and returns empty release builder if everything is fine")
lazy val ghreleaseGetCredentials = taskKey[GitHub]("Checks authentification and suggests to create a new oauth token if needed")
lazy val ghreleaseGetRepo = taskKey[GHRepository]("Checks repo existence and returns it if it's fine")

lazy val ghreleaseGetReleaseBuilder = inputKey[GHReleaseBuilder]("Checks remote tag and returns empty release builder if everything is fine")

lazy val githubRelease = taskKey[GHRelease]("Publishes a release of Github")
lazy val githubRelease = inputKey[GHRelease]("Publishes a release of Github")
}

case object defs {
import keys._

def getReleaseBuilder(tagName: String) = Def.task {
def ghreleaseMediaTypesMap: File => String = {
val typeMap = new javax.activation.MimetypesFileTypeMap()
// NOTE: github doesn't know about application/java-archive type (see https://developer.github.com/v3/repos/releases/#input-2)
typeMap.addMimeTypes("application/zip jar zip")
// and .pom is unlikely to be in the system's default MIME types map
typeMap.addMimeTypes("application/xml pom xml")

typeMap.getContentType
}

def ghreleaseGetCredentials: DefTask[GitHub] = Def.task {
val log = streams.value.log
val conf = file(System.getProperty("user.home")) / ".github"

while (!conf.exists || !GitHub.connect.isCredentialValid) {
log.warn("Your github credentials for sbt-github-release plugin are not set yet")
SimpleReader.readLine("Go to https://github.com/settings/applications \ncreate a new token and paste it here: ") match {
case Some(token) => {
try {
val gh = GitHub.connectUsingOAuth(token)
if (gh.isCredentialValid) {
IO.writeLines(conf, Seq("oauth = " + token))//, append = true)
log.info("Wrote OAuth token to " + conf)
}
} catch {
case e: Exception => log.error(e.toString)
}
}
case _ => sys.error("If you want to use sbt-github-release plugin, you should set credentials correctly")
}
}
log.info("Github credentials are ok")
GitHub.connect
}

def ghreleaseGetRepo: DefTask[GHRepository] = Def.task {
val log = streams.value.log
val github = ghreleaseGetCredentials.value
val repo = s"${ghreleaseRepoOrg.value}/${ghreleaseRepoName.value}"

val repository = Try { github.getRepository(repo) } getOrElse {
sys.error(s"Repository ${repo} doesn't exist or is not accessible.")
}
repository
}

def ghreleaseGetReleaseBuilder(tagName: String): DefTask[GHReleaseBuilder] = Def.task {
val log = streams.value.log
val repo = ghreleaseCheckRepo.value
val repo = ghreleaseGetRepo.value

val tagNames = repo.listTags.asSet.map(_.getName)
if (! tagNames.contains(tagName)) {
sys.error("Remote repository doesn't have [${tagName}] tag. You need to push it first.")
sys.error(s"Remote repository doesn't have [${tagName}] tag. You need to push it first.")
}

def releaseExists: Boolean =
repo.listReleases.asSet.map(_.getTagName).contains(tagName)

// if (!draft.value && releaseExists) {
if (releaseExists) {
sys.error("There is already a Github release based on [${tagName}] tag. You cannot release it twice.")
sys.error(s"There is already a Github release based on [${tagName}] tag. You cannot release it twice.")
// TODO: ask to overwrite (+ report if it is a draft)
}

repo.createRelease(tagName)
}

def githubRelease = Def.taskDyn {
if (isSnapshot.value) {
sys.error(s"Current version is '${version.value}'. You shouldn't publish snapshots, maybe you forgot to set the release version")
}

def githubRelease(tagName: String): DefTask[GHRelease] = Def.taskDyn {
val log = streams.value.log

val text = IO.read(ghreleaseNotes.value)
val notesPath = ghreleaseNotes.value.relativeTo(baseDirectory.value).getOrElse(ghreleaseNotes.value)
if (text.isEmpty) {
log.error(s"Release notes file [${notesPath}] is empty")
val notes = ghreleaseNotes.value(tagName)
if (notes.isEmpty) {
log.warn(s"Release notes are empty")
SimpleReader.readLine("Are you sure you want to continue without release notes (y/n)? [n] ") match {
case Some("n" | "N") => sys.error("Aborting release. Write release notes and try again")
case Some("n" | "N") => sys.error("Aborting release due to empty release notes")
case _ => // go on
}
} else log.info(s"Using release notes from the [${notesPath}] file")
}

val tagName = ghreleaseTag.value
val isPre = ghreleaseIsPrerelease.value(tagName)

Def.task {
val releaseBuilder = {
val rBuilder = getReleaseBuilder(tagName).value
.body(text)
.name(ghreleaseTitle.value)
.prerelease(isPre)
// .draft(draft.value)

if (ghreleaseCommitish.value.isEmpty) rBuilder
else rBuilder.commitish(ghreleaseCommitish.value)
}
val releaseBuilder = ghreleaseGetReleaseBuilder(tagName).value
.body(notes)
.name(ghreleaseTitle.value(tagName))
.prerelease(isPre)

val release = Try { releaseBuilder.create } getOrElse {
sys.error("Couldn't create release")
Expand All @@ -94,7 +133,7 @@ case object GithubRelease {
log.info(s"Github ${pre}release '${release.getName}' is published at\n ${release.getHtmlUrl}")

ghreleaseAssets.value foreach { asset =>
val mediaType = ghreleaseMediaTypesMap.value(asset)
val mediaType = keys.ghreleaseMediaTypesMap.value(asset)
val rel = asset.relativeTo(baseDirectory.value).getOrElse(asset)

release.uploadAsset(asset, mediaType)
Expand Down
93 changes: 34 additions & 59 deletions src/main/scala/SbtGithubReleasePlugin.scala
Original file line number Diff line number Diff line change
@@ -1,82 +1,57 @@
package ohnosequences.sbt

import sbt._, Keys._

import sbt._, Keys._, complete._, DefaultParsers._
import org.kohsuke.github._
import scala.collection.JavaConversions._
import scala.util.Try
import GithubRelease._, keys._

case object SbtGithubReleasePlugin extends AutoPlugin {

// This plugin will load automatically
override def trigger = allRequirements
override def requires = empty

val autoImport = GithubRelease.keys

import GithubRelease._, keys._

// Default settings
override lazy val projectSettings = Seq[Setting[_]](
ghreleaseNotes := baseDirectory.value / "notes" / (version.value+".markdown"),
ghreleaseNotes := { tagName =>
val ver = tagName.stripPrefix("v")
IO.read(baseDirectory.value / "notes" / s"${ver}.markdown")
},
ghreleaseRepoOrg := organization.value,
ghreleaseRepoName := name.value,
ghreleaseTag := "v"+version.value,
ghreleaseTitle := name.value +" "+ ghreleaseTag.value,
ghreleaseCommitish := "",
ghreleaseTitle := { tagName => s"${name.value} ${tagName}" },
// According to the Semantic Versioning Specification (rule 9)
// a version containing a hyphen is a pre-release version
ghreleaseIsPrerelease := { _.matches(""".*-.*""") },

ghreleaseMediaTypesMap := {
val typeMap = new javax.activation.MimetypesFileTypeMap()
// NOTE: github doesn't know about application/java-archive type (see https://developer.github.com/v3/repos/releases/#input-2)
typeMap.addMimeTypes("application/zip jar zip")
// and .pom is unlikely to be in the system's default MIME types map
typeMap.addMimeTypes("application/xml pom xml")

typeMap.getContentType
},

ghreleaseAssets := packagedArtifacts.value.values.toSeq,

ghreleaseCheckCredentials := {
val log = streams.value.log
val conf = file(System.getProperty("user.home")) / ".github"
while (!conf.exists || !GitHub.connect.isCredentialValid) {
log.warn("Your github credentials for sbt-github-release plugin are not set yet")
SimpleReader.readLine("Go to https://github.com/settings/applications \ncreate a new token and paste it here: ") match {
case Some(token) => {
try {
val gh = GitHub.connectUsingOAuth(token)
if (gh.isCredentialValid) {
IO.writeLines(conf, Seq("oauth = " + token))//, append = true)
log.info("Wrote OAuth token to " + conf)
}
} catch {
case e: Exception => log.error(e.toString)
}
}
case _ => sys.error("If you want to use sbt-github-release plugin, you should set credentials correctly")
}
}
log.info("Github credentials are ok")
GitHub.connect
},

ghreleaseCheckRepo := {
val log = streams.value.log
val github = ghreleaseCheckCredentials.value
val repo = s"${ghreleaseRepoOrg.value}/${ghreleaseRepoName.value}"
ghreleaseIsPrerelease := { _.matches(""".*-.*""") },
ghreleaseMediaTypesMap := defs.ghreleaseMediaTypesMap,
ghreleaseAssets := packagedArtifacts.value.values.toSeq,
ghreleaseGetCredentials := defs.ghreleaseGetCredentials.value,
ghreleaseGetRepo := defs.ghreleaseGetRepo.value,

ghreleaseGetReleaseBuilder := Def.inputTaskDyn {
defs.ghreleaseGetReleaseBuilder(tagNameArg.parsed)
}.evaluated,

githubRelease := Def.inputTaskDyn {
defs.githubRelease(tagNameArg.parsed)
}.evaluated
)

val repository = Try { github.getRepository(repo) } getOrElse {
sys.error(s"Repository ${repo} doesn't exist or is not accessible.")
}
repository
},
def tagNameArg: Def.Initialize[Parser[String]] = Def.setting {
val gitOut: Try[String] = Try {
sys.process.Process(Seq("git", "tag", "--list"), baseDirectory.value).!!
}

// ghreleaseCheckReleaseBuilder := getReleaseBuilder.value,
val suggestions: Try[Parser[String]] = gitOut.map { out =>
oneOf(
out.split('\n').map { tag => token(tag.trim) }
)
}

githubRelease := defs.githubRelease.value
)
val fallback = s"v${version.value}"

(Space ~> suggestions.getOrElse(StringBasic)) ?? fallback
}
}

0 comments on commit f6d5b2f

Please sign in to comment.