Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add branch change tasks to allow for gitflow releasing #191

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions src/main/scala/ReleaseExtra.scala
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,16 @@ object ReleaseStateTransformations {

}

lazy val inquireBranches: ReleaseStep = { st: State =>
val releaseBranch = st.get(commandLineReleaseBranch).flatten.getOrElse(SimpleReader.readLine("Release branch : ") match {
case Some(input) => input.trim
case None => sys.error("No branch provided!")
})
val nextBranch = st.get(commandLineNextBranch).flatten.getOrElse(vcs(st).currentBranch)

st.put(branches, (releaseBranch, nextBranch))
}


lazy val runClean : ReleaseStep = ReleaseStep(
action = { st: State =>
Expand Down Expand Up @@ -114,6 +124,23 @@ object ReleaseStateTransformations {
), st)
}

lazy val setReleaseBranch: ReleaseStep = setBranch(_._1, newBranch = true)
lazy val setNextBranch: ReleaseStep = setBranch(_._2)
private[sbtrelease] def setBranch(selectBranch: Branches => String, newBranch: Boolean = false): ReleaseStep = { st: State =>
val vs = st.get(branches).getOrElse(sys.error("No branches are set! Was this release part executed before inquireBranches?"))
val selected = selectBranch(vs)

st.log.info(s"Checking out $selected")
val vc = vcs(st)
val processLogger: ProcessLogger = if (vc.isInstanceOf[Git]) {
// Git outputs to standard error, so use a logger that redirects stderr to info
vc.stdErrorToStdOut(st.log)
} else st.log
(if (newBranch) vc.newBranch(selected) else vc.setBranch(selected)) !! processLogger

st
}

private def vcs(st: State): Vcs = {
st.extract.get(releaseVcs).getOrElse(sys.error("Aborting release. Working directory is not a repository of a recognized VCS."))
}
Expand Down Expand Up @@ -331,12 +358,21 @@ object ExtraReleaseCommands {
private lazy val inquireVersionsCommandKey = "release-inquire-versions"
lazy val inquireVersionsCommand = Command.command(inquireVersionsCommandKey)(inquireVersions)

private lazy val inquireBranchesCommandKey = "release-branches-versions"
lazy val inquireBranchesCommand = Command.command(inquireBranchesCommandKey)(inquireBranches)

private lazy val setReleaseVersionCommandKey = "release-set-release-version"
lazy val setReleaseVersionCommand = Command.command(setReleaseVersionCommandKey)(setReleaseVersion)

private lazy val setNextVersionCommandKey = "release-set-next-version"
lazy val setNextVersionCommand = Command.command(setNextVersionCommandKey)(setNextVersion)

private lazy val setReleaseBranchCommandKey = "release-set-release-branch"
lazy val setReleaseBranchCommand = Command.command(setReleaseBranchCommandKey)(setReleaseBranch)

private lazy val setNextBranchCommandKey = "release-set-next-branch"
lazy val setNextBranchCommand = Command.command(setNextBranchCommandKey)(setNextBranch)

private lazy val commitReleaseVersionCommandKey = "release-commit-release-version"
lazy val commitReleaseVersionCommand = Command.command(commitReleaseVersionCommandKey)(commitReleaseVersion)

Expand Down
13 changes: 12 additions & 1 deletion src/main/scala/ReleasePlugin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,11 @@ object ReleasePlugin extends AutoPlugin {
object ReleaseKeys {

val versions = AttributeKey[Versions]("releaseVersions")
val branches = AttributeKey[Branches]("releaseBranches")
val commandLineReleaseVersion = AttributeKey[Option[String]]("release-input-release-version")
val commandLineNextVersion = AttributeKey[Option[String]]("release-input-next-version")
val commandLineReleaseBranch = AttributeKey[Option[String]]("release-input-release-branch")
val commandLineNextBranch = AttributeKey[Option[String]]("release-input-next-branch")
val useDefaults = AttributeKey[Boolean]("releaseUseDefaults")
val skipTests = AttributeKey[Boolean]("releaseSkipTests")
val cross = AttributeKey[Boolean]("releaseCross")
Expand All @@ -136,17 +139,23 @@ object ReleasePlugin extends AutoPlugin {
(Space ~> token("release-version") ~> Space ~> token(StringBasic, "<release version>")) map ParseResult.ReleaseVersion
private[this] val NextVersion: Parser[ParseResult] =
(Space ~> token("next-version") ~> Space ~> token(StringBasic, "<next version>")) map ParseResult.NextVersion
private[this] val ReleaseBranch: Parser[ParseResult] =
(Space ~> token("release-branch") ~> Space ~> token(StringBasic, "<release branch>")) map ParseResult.ReleaseBranch
private[this] val NextBranch: Parser[ParseResult] =
(Space ~> token("next-branch") ~> Space ~> token(StringBasic, "<next branch>")) map ParseResult.NextBranch

private[this] sealed abstract class ParseResult extends Product with Serializable
private[this] object ParseResult {
final case class ReleaseVersion(value: String) extends ParseResult
final case class NextVersion(value: String) extends ParseResult
final case class ReleaseBranch(value: String) extends ParseResult
final case class NextBranch(value: String) extends ParseResult
case object WithDefaults extends ParseResult
case object SkipTests extends ParseResult
case object CrossBuild extends ParseResult
}

private[this] val releaseParser: Parser[Seq[ParseResult]] = (ReleaseVersion | NextVersion | WithDefaults | SkipTests | CrossBuild).*
private[this] val releaseParser: Parser[Seq[ParseResult]] = (ReleaseVersion | NextVersion | ReleaseBranch | NextBranch | WithDefaults | SkipTests | CrossBuild).*

val releaseCommand: Command = Command(releaseCommandKey)(_ => releaseParser) { (st, args) =>
val extracted = Project.extract(st)
Expand All @@ -160,6 +169,8 @@ object ReleasePlugin extends AutoPlugin {
.put(cross, crossEnabled)
.put(commandLineReleaseVersion, args.collectFirst{case ParseResult.ReleaseVersion(value) => value})
.put(commandLineNextVersion, args.collectFirst{case ParseResult.NextVersion(value) => value})
.put(commandLineReleaseBranch, args.collectFirst{case ParseResult.ReleaseBranch(value) => value})
.put(commandLineNextBranch, args.collectFirst{case ParseResult.NextBranch(value) => value})

val initialChecks = releaseParts.map(_.check)

Expand Down
16 changes: 16 additions & 0 deletions src/main/scala/Vcs.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ trait Vcs {
def isBehindRemote: Boolean
def pushChanges: ProcessBuilder
def currentBranch: String
def setBranch(branch: String): ProcessBuilder
def newBranch(branch: String): ProcessBuilder
def hasUntrackedFiles: Boolean
def hasModifiedFiles: Boolean

Expand Down Expand Up @@ -106,6 +108,11 @@ class Mercurial(val baseDir: File) extends Vcs with GitLike {

def currentBranch = (cmd("branch") !!) trim

// FIXME: Need help here
def setBranch(branch: String) = cmd("branch", branch)

def newBranch(branch: String) = setBranch(branch)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's been too long since I've used hg to know what the right things are here. I don't think this is right though, I think branches get committed, so you would need to commit after calling the branch command. As with the svn support, happy to merge if these are changed to throw an exception saying this hasn't been implemented for hg yet.

// FIXME: This is utterly bogus, but I cannot find a good way...
def checkRemote(remote: String) = cmd("id", "-n")

Expand Down Expand Up @@ -134,6 +141,10 @@ class Git(val baseDir: File) extends Vcs with GitLike {

def currentBranch = (cmd("symbolic-ref", "HEAD") !!).trim.stripPrefix("refs/heads/")

def setBranch(branch: String) = cmd("checkout", branch)

def newBranch(branch: String) = cmd("checkout", "-b", branch)

def currentHash = revParse("HEAD")

private def revParse(name: String) = (cmd("rev-parse", name) !!) trim
Expand Down Expand Up @@ -201,6 +212,11 @@ class Subversion(val baseDir: File) extends Vcs {

override def currentBranch: String = workingDirSvnUrl.substring(workingDirSvnUrl.lastIndexOf("/") + 1)

// FIXME: Need help here
def setBranch(branch: String) = cmd("checkout", branch)

def newBranch(branch: String) = cmd("checkout", "-b", branch)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is definitely not the right behaviour for svn, I think svn branching requires doing an svn copy from trunk to /branches/<branch-name>. I'll be happy to merge if, rather than doing the wrong thing, these throw an exception to say this operation is not yet supported by svn.

override def pushChanges: ProcessBuilder = commit("push changes", false)

override def isBehindRemote: Boolean = false
Expand Down
1 change: 1 addition & 0 deletions src/main/scala/package.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package object sbtrelease {
type Versions = (String, String)
type Branches = (String, String)

def versionFormatError = sys.error("Version format is not compatible with " + Version.VersionR.pattern.toString)
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ val parser = Space ~> StringBasic

checkContentsOfVersionSbt := {
val expected = parser.parsed
val versionFile = ((baseDirectory).value) / "version.sbt"
assert(IO.read(versionFile).contains(expected), s"does not contains ${expected} in ${versionFile}")
val versionFile = ((baseDirectory).value) / "version.sbt"
assert(IO.read(versionFile).contains(expected), s"does not contains ${expected} in ${versionFile}")
}


2 changes: 2 additions & 0 deletions src/sbt-test/sbt-release/gitflow/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
target
global/
23 changes: 23 additions & 0 deletions src/sbt-test/sbt-release/gitflow/build.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import ReleaseTransformations._
import sbt.complete.DefaultParsers._

releaseProcess := Seq(
checkSnapshotDependencies,
inquireVersions,
inquireBranches,
setReleaseVersion,
commitReleaseVersion,
setReleaseBranch,
setNextBranch,
setNextVersion,
commitNextVersion
)

val checkContentsOfVersionSbt = inputKey[Unit]("Check that the contents of version.sbt is as expected")
val parser = Space ~> StringBasic

checkContentsOfVersionSbt := {
val expected = parser.parsed
val versionFile = ((baseDirectory).value) / "version.sbt"
assert(IO.read(versionFile).contains(expected), s"does not contains ${expected} in ${versionFile}")
}
7 changes: 7 additions & 0 deletions src/sbt-test/sbt-release/gitflow/project/build.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
val pluginVersion = System.getProperty("plugin.version")
if(pluginVersion == null)
throw new RuntimeException("""|The system property 'plugin.version' is not defined.
|Specify this property using the scriptedLaunchOpts -D.""".stripMargin)
else addSbtPlugin("com.github.gseitz" % "sbt-release" % pluginVersion)
}
10 changes: 10 additions & 0 deletions src/sbt-test/sbt-release/gitflow/test
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
$ exec git init .
$ exec git add .
$ exec git commit -m init

> 'release release-version 0.7.0 next-version 1.0.0-SNAPSHOT release-branch release-test next-branch master'
> checkContentsOfVersionSbt 1.0.0-SNAPSHOT
$ exec git checkout release-test
> checkContentsOfVersionSbt 0.7.0

-> release with-defaults
1 change: 1 addition & 0 deletions src/sbt-test/sbt-release/gitflow/version.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
version in ThisBuild := "0.1.0-SNAPSHOT"