From 897bd27a0d3bec2dd40bb74df9f66c4ab018fc89 Mon Sep 17 00:00:00 2001 From: Jake Schwartz Date: Sun, 19 Feb 2017 20:27:46 -0800 Subject: [PATCH 01/10] Add branch change tasks to allow for gitflow releasing - Add inquireBranches, setReleaseBranch and setNextBranch which mimic the version setting/changing commands - Add gitflow test case --- src/main/scala/ReleaseExtra.scala | 35 +++++++++++++++++++ src/main/scala/ReleasePlugin.scala | 1 + src/main/scala/Vcs.scala | 16 +++++++++ src/main/scala/package.scala | 1 + src/sbt-test/sbt-release/gitflow/.gitignore | 2 ++ src/sbt-test/sbt-release/gitflow/build.sbt | 15 ++++++++ .../sbt-release/gitflow/project/build.sbt | 7 ++++ src/sbt-test/sbt-release/gitflow/test | 6 ++++ src/sbt-test/sbt-release/gitflow/version.sbt | 1 + 9 files changed, 84 insertions(+) create mode 100644 src/sbt-test/sbt-release/gitflow/.gitignore create mode 100644 src/sbt-test/sbt-release/gitflow/build.sbt create mode 100644 src/sbt-test/sbt-release/gitflow/project/build.sbt create mode 100644 src/sbt-test/sbt-release/gitflow/test create mode 100644 src/sbt-test/sbt-release/gitflow/version.sbt diff --git a/src/main/scala/ReleaseExtra.scala b/src/main/scala/ReleaseExtra.scala index 9673112..b6784b9 100644 --- a/src/main/scala/ReleaseExtra.scala +++ b/src/main/scala/ReleaseExtra.scala @@ -74,6 +74,15 @@ object ReleaseStateTransformations { } + lazy val inquireBranches: ReleaseStep = { st: State => + val releaseBranch = SimpleReader.readLine("Release branch : ") match { + case Some(input) => input.trim + case None => sys.error("No branch provided!") + } + + st.put(branches, (releaseBranch, vcs(st).currentBranch)) + } + lazy val runClean : ReleaseStep = ReleaseStep( action = { st: State => @@ -114,6 +123,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.")) } @@ -331,12 +357,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) diff --git a/src/main/scala/ReleasePlugin.scala b/src/main/scala/ReleasePlugin.scala index a714310..9f37669 100644 --- a/src/main/scala/ReleasePlugin.scala +++ b/src/main/scala/ReleasePlugin.scala @@ -117,6 +117,7 @@ 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 useDefaults = AttributeKey[Boolean]("releaseUseDefaults") diff --git a/src/main/scala/Vcs.scala b/src/main/scala/Vcs.scala index 48f5e11..5155850 100644 --- a/src/main/scala/Vcs.scala +++ b/src/main/scala/Vcs.scala @@ -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 @@ -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) + // FIXME: This is utterly bogus, but I cannot find a good way... def checkRemote(remote: String) = cmd("id", "-n") @@ -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 @@ -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) + override def pushChanges: ProcessBuilder = commit("push changes", false) override def isBehindRemote: Boolean = false diff --git a/src/main/scala/package.scala b/src/main/scala/package.scala index 267be3c..67b9c7a 100644 --- a/src/main/scala/package.scala +++ b/src/main/scala/package.scala @@ -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) } diff --git a/src/sbt-test/sbt-release/gitflow/.gitignore b/src/sbt-test/sbt-release/gitflow/.gitignore new file mode 100644 index 0000000..3a3aee2 --- /dev/null +++ b/src/sbt-test/sbt-release/gitflow/.gitignore @@ -0,0 +1,2 @@ +target +global/ diff --git a/src/sbt-test/sbt-release/gitflow/build.sbt b/src/sbt-test/sbt-release/gitflow/build.sbt new file mode 100644 index 0000000..c6821cb --- /dev/null +++ b/src/sbt-test/sbt-release/gitflow/build.sbt @@ -0,0 +1,15 @@ +import sbtrelease.ReleaseStateTransformations._ + +releaseProcess := Seq( + checkSnapshotDependencies, + inquireVersions, + inquireBranches, + setReleaseVersion, + commitReleaseVersion, + setReleaseBranch, + pushChanges, + setNextBranch, + setNextVersion, + commitNextVersion, + pushChanges +) \ No newline at end of file diff --git a/src/sbt-test/sbt-release/gitflow/project/build.sbt b/src/sbt-test/sbt-release/gitflow/project/build.sbt new file mode 100644 index 0000000..ebda578 --- /dev/null +++ b/src/sbt-test/sbt-release/gitflow/project/build.sbt @@ -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) +} diff --git a/src/sbt-test/sbt-release/gitflow/test b/src/sbt-test/sbt-release/gitflow/test new file mode 100644 index 0000000..a272053 --- /dev/null +++ b/src/sbt-test/sbt-release/gitflow/test @@ -0,0 +1,6 @@ +$ exec git init . +$ exec git add . +$ exec git commit -m init + +-> release +> release with-defaults diff --git a/src/sbt-test/sbt-release/gitflow/version.sbt b/src/sbt-test/sbt-release/gitflow/version.sbt new file mode 100644 index 0000000..57b0bcb --- /dev/null +++ b/src/sbt-test/sbt-release/gitflow/version.sbt @@ -0,0 +1 @@ +version in ThisBuild := "0.1.0-SNAPSHOT" From 02d9b841eff0c16a71546f9731a621e5312be423 Mon Sep 17 00:00:00 2001 From: Jake Schwartz Date: Mon, 20 Feb 2017 13:30:19 -0800 Subject: [PATCH 02/10] Added parens around branch command --- src/main/scala/ReleaseExtra.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/ReleaseExtra.scala b/src/main/scala/ReleaseExtra.scala index b6784b9..05e2d8f 100644 --- a/src/main/scala/ReleaseExtra.scala +++ b/src/main/scala/ReleaseExtra.scala @@ -135,7 +135,7 @@ object ReleaseStateTransformations { // 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 + (if (newBranch) vc.newBranch(selected) else vc.setBranch(selected)) !! processLogger st } From 811676125b69327918685e1c1d391d87eee909ca Mon Sep 17 00:00:00 2001 From: Jake Schwartz Date: Wed, 22 Feb 2017 23:29:19 -0800 Subject: [PATCH 03/10] Scripted test for gitflow and cli params for release and next branches --- src/main/scala/ReleaseExtra.scala | 7 ++++--- src/main/scala/ReleasePlugin.scala | 12 +++++++++++- .../command-line-version-numbers/build.sbt | 4 ++-- src/sbt-test/sbt-release/gitflow/build.sbt | 6 ++---- src/sbt-test/sbt-release/gitflow/test | 8 ++++++-- 5 files changed, 25 insertions(+), 12 deletions(-) diff --git a/src/main/scala/ReleaseExtra.scala b/src/main/scala/ReleaseExtra.scala index 05e2d8f..3b5acbd 100644 --- a/src/main/scala/ReleaseExtra.scala +++ b/src/main/scala/ReleaseExtra.scala @@ -75,12 +75,13 @@ object ReleaseStateTransformations { } lazy val inquireBranches: ReleaseStep = { st: State => - val releaseBranch = SimpleReader.readLine("Release branch : ") match { + 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, vcs(st).currentBranch)) + st.put(branches, (releaseBranch, nextBranch)) } diff --git a/src/main/scala/ReleasePlugin.scala b/src/main/scala/ReleasePlugin.scala index 9f37669..1f6aafb 100644 --- a/src/main/scala/ReleasePlugin.scala +++ b/src/main/scala/ReleasePlugin.scala @@ -120,6 +120,8 @@ object ReleasePlugin extends AutoPlugin { 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") @@ -137,17 +139,23 @@ object ReleasePlugin extends AutoPlugin { (Space ~> token("release-version") ~> Space ~> token(StringBasic, "")) map ParseResult.ReleaseVersion private[this] val NextVersion: Parser[ParseResult] = (Space ~> token("next-version") ~> Space ~> token(StringBasic, "")) map ParseResult.NextVersion + private[this] val ReleaseBranch: Parser[ParseResult] = + (Space ~> token("release-branch") ~> Space ~> token(StringBasic, "")) map ParseResult.ReleaseBranch + private[this] val NextBranch: Parser[ParseResult] = + (Space ~> token("next-branch") ~> Space ~> token(StringBasic, "")) 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) @@ -161,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) diff --git a/src/sbt-test/sbt-release/command-line-version-numbers/build.sbt b/src/sbt-test/sbt-release/command-line-version-numbers/build.sbt index 8abb62e..736a355 100644 --- a/src/sbt-test/sbt-release/command-line-version-numbers/build.sbt +++ b/src/sbt-test/sbt-release/command-line-version-numbers/build.sbt @@ -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}") } diff --git a/src/sbt-test/sbt-release/gitflow/build.sbt b/src/sbt-test/sbt-release/gitflow/build.sbt index c6821cb..adc0fe6 100644 --- a/src/sbt-test/sbt-release/gitflow/build.sbt +++ b/src/sbt-test/sbt-release/gitflow/build.sbt @@ -1,4 +1,4 @@ -import sbtrelease.ReleaseStateTransformations._ +import ReleaseTransformations._ releaseProcess := Seq( checkSnapshotDependencies, @@ -7,9 +7,7 @@ releaseProcess := Seq( setReleaseVersion, commitReleaseVersion, setReleaseBranch, - pushChanges, setNextBranch, setNextVersion, - commitNextVersion, - pushChanges + commitNextVersion ) \ No newline at end of file diff --git a/src/sbt-test/sbt-release/gitflow/test b/src/sbt-test/sbt-release/gitflow/test index a272053..af6a086 100644 --- a/src/sbt-test/sbt-release/gitflow/test +++ b/src/sbt-test/sbt-release/gitflow/test @@ -2,5 +2,9 @@ $ exec git init . $ exec git add . $ exec git commit -m init --> release -> release with-defaults +> '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 From c70b7db5a6e750db12ecea3d0a15aa589166b55a Mon Sep 17 00:00:00 2001 From: Jake Schwartz Date: Thu, 23 Feb 2017 05:39:27 -0800 Subject: [PATCH 04/10] Copied over test task to gitflow/build.sbt --- src/sbt-test/sbt-release/gitflow/build.sbt | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/sbt-test/sbt-release/gitflow/build.sbt b/src/sbt-test/sbt-release/gitflow/build.sbt index adc0fe6..3d7069c 100644 --- a/src/sbt-test/sbt-release/gitflow/build.sbt +++ b/src/sbt-test/sbt-release/gitflow/build.sbt @@ -1,4 +1,5 @@ import ReleaseTransformations._ +import sbt.complete.DefaultParsers._ releaseProcess := Seq( checkSnapshotDependencies, @@ -10,4 +11,13 @@ releaseProcess := Seq( setNextBranch, setNextVersion, commitNextVersion -) \ No newline at end of file +) + +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}") +} \ No newline at end of file From ad29693d446639087012dc9dff32e5ef05ec946a Mon Sep 17 00:00:00 2001 From: Jake Schwartz Date: Thu, 9 Mar 2017 12:58:41 -0800 Subject: [PATCH 05/10] Allow new branches to be created based on the current tracking remote and pushed with the -u flag --- src/main/scala/ReleaseExtra.scala | 30 +++++++++++--------------- src/main/scala/Vcs.scala | 35 +++++++++++++++---------------- 2 files changed, 29 insertions(+), 36 deletions(-) diff --git a/src/main/scala/ReleaseExtra.scala b/src/main/scala/ReleaseExtra.scala index 3b5acbd..89bfb6b 100644 --- a/src/main/scala/ReleaseExtra.scala +++ b/src/main/scala/ReleaseExtra.scala @@ -124,9 +124,9 @@ object ReleaseStateTransformations { ), st) } - lazy val setReleaseBranch: ReleaseStep = setBranch(_._1, newBranch = true) + lazy val setReleaseBranch: ReleaseStep = setBranch(_._1) lazy val setNextBranch: ReleaseStep = setBranch(_._2) - private[sbtrelease] def setBranch(selectBranch: Branches => String, newBranch: Boolean = false): ReleaseStep = { st: State => + private[sbtrelease] def setBranch(selectBranch: Branches => String): 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) @@ -136,7 +136,7 @@ object ReleaseStateTransformations { // 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 + vc.setBranch(selected) !! processLogger st } @@ -241,9 +241,6 @@ object ReleaseStateTransformations { lazy val pushChanges: ReleaseStep = ReleaseStep(pushChangesAction, checkUpstream) private[sbtrelease] lazy val checkUpstream = { st: State => - if (!vcs(st).hasUpstream) { - sys.error("No tracking branch is set up. Either configure a remote tracking branch, or remove the pushChanges release part.") - } val defaultChoice = extractDefault(st, "n") st.log.info("Checking remote [%s] ..." format vcs(st).trackingRemote) @@ -267,19 +264,16 @@ object ReleaseStateTransformations { val defaultChoice = extractDefault(st, "y") val vc = vcs(st) - if (vc.hasUpstream) { - defaultChoice orElse SimpleReader.readLine("Push changes to the remote repository (y/n)? [y] ") match { - case Yes() | Some("") => - 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 - vc.pushChanges !! processLogger - case _ => st.log.warn("Remember to push the changes yourself!") - } - } else { - st.log.info("Changes were NOT pushed, because no upstream branch is configured for the local branch [%s]" format vcs(st).currentBranch) + defaultChoice orElse SimpleReader.readLine("Push changes to the remote repository (y/n)? [y] ") match { + case Yes() | Some("") => + 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 + vc.pushChanges(!vc.hasUpstream) !! processLogger + case _ => st.log.warn("Remember to push the changes yourself!") } + st } diff --git a/src/main/scala/Vcs.scala b/src/main/scala/Vcs.scala index 5155850..d020566 100644 --- a/src/main/scala/Vcs.scala +++ b/src/main/scala/Vcs.scala @@ -19,10 +19,9 @@ trait Vcs { def hasUpstream: Boolean def trackingRemote: String def isBehindRemote: Boolean - def pushChanges: ProcessBuilder + def pushChanges(withUpstream: Boolean): ProcessBuilder def currentBranch: String def setBranch(branch: String): ProcessBuilder - def newBranch(branch: String): ProcessBuilder def hasUntrackedFiles: Boolean def hasModifiedFiles: Boolean @@ -104,14 +103,11 @@ class Mercurial(val baseDir: File) extends Vcs with GitLike { def isBehindRemote = cmd("incoming", "-b", ".", "-q") ! devnull == 0 - def pushChanges = cmd("push", "-b", ".") + def pushChanges(withUpstream: Boolean) = cmd("push", "-b", ".") def currentBranch = (cmd("branch") !!) trim - // FIXME: Need help here - def setBranch(branch: String) = cmd("branch", branch) - - def newBranch(branch: String) = setBranch(branch) + def setBranch(branch: String) = throw sys.error("Branch switching not currently supported in hg") // FIXME: This is utterly bogus, but I cannot find a good way... def checkRemote(remote: String) = cmd("id", "-n") @@ -141,9 +137,13 @@ 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 setBranch(branch: String) = { + if (trackingRemoteCmd ! devnull != 0) { + val currentRemote = currentRemote + cmd("checkout", "-b", branch) + cmd("config", "--add", "branch.%s.remote".format(branch), currentRemote) + } else cmd("checkout", branch) + } def currentHash = revParse("HEAD") @@ -171,11 +171,13 @@ class Git(val baseDir: File) extends Vcs with GitLike { def status = cmd("status", "--porcelain") - def pushChanges = pushCurrentBranch #&& pushTags + def pushChanges(setUpstream: Boolean) = pushCurrentBranch(setUpstream) #&& pushTags - private def pushCurrentBranch = { + private def pushCurrentBranch(setUpstream: Boolean) = { val localBranch = currentBranch - cmd("push", trackingRemote, "%s:%s" format (localBranch, trackingBranch)) + if (setUpstream) { + cmd ("push", "-u", trackingRemote, localBranch) + } else cmd("push", trackingRemote, "%s:%s" format (localBranch, trackingBranch)) } private def pushTags = cmd("push", "--tags", trackingRemote) @@ -212,12 +214,9 @@ 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) + def setBranch(branch: String) = throw sys.error("Branch switching not currently supported in svn") - override def pushChanges: ProcessBuilder = commit("push changes", false) + override def pushChanges(withUpstream: Boolean): ProcessBuilder = commit("push changes", false) override def isBehindRemote: Boolean = false From dc77de83e45b56e3f3cf7c8766411b80c70620ea Mon Sep 17 00:00:00 2001 From: Jake Schwartz Date: Thu, 9 Mar 2017 13:33:34 -0800 Subject: [PATCH 06/10] Fix compilation error --- src/main/scala/Vcs.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/Vcs.scala b/src/main/scala/Vcs.scala index d020566..9ee5ec2 100644 --- a/src/main/scala/Vcs.scala +++ b/src/main/scala/Vcs.scala @@ -139,7 +139,7 @@ class Git(val baseDir: File) extends Vcs with GitLike { def setBranch(branch: String) = { if (trackingRemoteCmd ! devnull != 0) { - val currentRemote = currentRemote + val currentRemote = trackingRemote cmd("checkout", "-b", branch) cmd("config", "--add", "branch.%s.remote".format(branch), currentRemote) } else cmd("checkout", branch) From ff80dec137c4a150ad5f6e2cb8750d81e85aad36 Mon Sep 17 00:00:00 2001 From: Jake Schwartz Date: Thu, 9 Mar 2017 13:55:45 -0800 Subject: [PATCH 07/10] Change logic for determining new branch --- src/main/scala/Vcs.scala | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/scala/Vcs.scala b/src/main/scala/Vcs.scala index 9ee5ec2..a23ec92 100644 --- a/src/main/scala/Vcs.scala +++ b/src/main/scala/Vcs.scala @@ -130,17 +130,17 @@ class Git(val baseDir: File) extends Vcs with GitLike { private lazy val trackingBranchCmd = cmd("config", "branch.%s.merge" format currentBranch) private def trackingBranch: String = (trackingBranchCmd !!).trim.stripPrefix("refs/heads/") - private lazy val trackingRemoteCmd: ProcessBuilder = cmd("config", "branch.%s.remote" format currentBranch) - def trackingRemote: String = (trackingRemoteCmd !!) trim + private def trackingRemoteCmd(branch: String): ProcessBuilder = cmd("config", "branch.%s.remote" format branch) + def trackingRemote: String = (trackingRemoteCmd(currentBranch) !!) trim - def hasUpstream = trackingRemoteCmd ! devnull == 0 && trackingBranchCmd ! devnull == 0 + def hasUpstream = trackingRemoteCmd(currentBranch) ! devnull == 0 && trackingBranchCmd ! devnull == 0 def currentBranch = (cmd("symbolic-ref", "HEAD") !!).trim.stripPrefix("refs/heads/") def setBranch(branch: String) = { - if (trackingRemoteCmd ! devnull != 0) { + if (trackingRemoteCmd(branch) ! devnull != 0) { val currentRemote = trackingRemote - cmd("checkout", "-b", branch) + cmd("checkout", "-b", branch).!! cmd("config", "--add", "branch.%s.remote".format(branch), currentRemote) } else cmd("checkout", branch) } From 610c71c1bcd8466ebd34910b654993224655bb3f Mon Sep 17 00:00:00 2001 From: Jake Schwartz Date: Thu, 9 Mar 2017 14:08:49 -0800 Subject: [PATCH 08/10] Documentation for gitflow --- README.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/README.md b/README.md index 81b4737..1bb2078 100644 --- a/README.md +++ b/README.md @@ -320,6 +320,36 @@ Now let's also add steps for [posterous-sbt](https://github.com/n8han/posterous- The `check` part of the release step is run at the start, to make sure we have everything set up to post the release notes later on. After publishing the actual build artifacts, we also publish the release notes. +#### GitFlow +[Gitflow](http://nvie.com/posts/a-successful-git-branching-model/) is a very popular Git branching method for creating +releases. This involves creating a new release branch from the develop branch. + + import ReleaseTransformations._ + import sbtrelease._ + + releaseCommitMessage := s"Version bump to ${version.value}" + + releaseUseGlobalVersion := false + + releaseNextVersion := { ver => Version(ver).map(_.bumpMinor.asSnapshot.string).getOrElse(versionFormatError) } + + releaseProcess := Seq[ReleaseStep]( + checkSnapshotDependencies, + inquireVersions, + inquireBranches, + setReleaseVersion, + commitReleaseVersion, + setReleaseBranch, + pushChanges, + setNextBranch, + setNextVersion, + commitNextVersion, + pushChanges + ) + +`inquireBranches` will ask for a release branch name where the release version will get pushed. It will then +switch back to the current branch before committing and pushing next version. + ## Credits Thank you, [Jason](https://github.com/retronym) and [Mark](https://github.com/harrah), for your feedback and ideas. From 51b85ec21e7c3b4e57d546400116a5335cea0183 Mon Sep 17 00:00:00 2001 From: Jake Schwartz Date: Thu, 9 Mar 2017 14:09:17 -0800 Subject: [PATCH 09/10] Documentation for gitflow --- README.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/README.md b/README.md index 1bb2078..5b4f08b 100644 --- a/README.md +++ b/README.md @@ -327,11 +327,7 @@ releases. This involves creating a new release branch from the develop branch. import ReleaseTransformations._ import sbtrelease._ - releaseCommitMessage := s"Version bump to ${version.value}" - - releaseUseGlobalVersion := false - - releaseNextVersion := { ver => Version(ver).map(_.bumpMinor.asSnapshot.string).getOrElse(versionFormatError) } + // ... releaseProcess := Seq[ReleaseStep]( checkSnapshotDependencies, From dbd3e27d605b480ed5564418744d5260c051c2d5 Mon Sep 17 00:00:00 2001 From: Jake Schwartz Date: Thu, 9 Mar 2017 20:24:05 -0800 Subject: [PATCH 10/10] Fix scripted test for gitflow --- src/sbt-test/sbt-release/gitflow/test | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sbt-test/sbt-release/gitflow/test b/src/sbt-test/sbt-release/gitflow/test index af6a086..b7078f1 100644 --- a/src/sbt-test/sbt-release/gitflow/test +++ b/src/sbt-test/sbt-release/gitflow/test @@ -1,4 +1,5 @@ $ exec git init . +$ exec git config --add branch.master.remote origin $ exec git add . $ exec git commit -m init