Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Setting unique snapshot version when cross-building in SBT

Tags:

scala

sbt

When publish, if there is -SNAPSHOT in a version, I'd like to replace it with a unique timestamp. I've already done this and it works, except for cross-building with +.

It seems that the version setting I set with the timestamp gets cleared out whenever the Scala version is changed for the cross-build.

When I try to cross-build publish I'm using the following command:

sbt ";stamp-version ;+publish"

Here is the code for the command stamp-version:

object TimestampVersion {

  import Version.Snapshot

  lazy val versionSettings = Seq(commands += stampVersion)

  def stampVersion = Command.command("stamp-version") { state =>
    val extracted = Project.extract(state)
    extracted.append(List(version in ThisBuild ~= { ver =>
      val stmp = stamp(ver)
      Logging.info("Stamping version %s".format(stmp))
      stmp
    }), state)
  }

  def stamp(version: String): String = {
    if (version endsWith Snapshot) {
      // we use a dot here to not break rpm versioning rules
      (version stripSuffix Snapshot) + "." + timestamp(System.currentTimeMillis)
    } else {
      version
    }
  }

  def timestamp(time: Long): String = {
    // no delimiter between date & time in order to not break rpm versioning rules
    val sdf = new java.text.SimpleDateFormat("yyyyMMddHHmmss")
    sdf.setTimeZone(TimeZone.getTimeZone("UTC"))
    sdf.format(new Date(time))
  }
}

Is there a different way I can store my timestamped version that can be accessed by my cross-builds?

EDIT: To be clear, I want each cross-built version to have the same timestamp so that when I use it from somewhere else I can depend on like this: "x" %% "y" % "z"

Also, it would be best if the solution could work with SBT 0.12.x since I still have that dependency.

like image 810
myyk Avatar asked Jan 10 '14 20:01

myyk


People also ask

What is ThisBuild in sbt?

Typically, if a key has no associated value in a more-specific scope, sbt will try to get a value from a more general scope, such as the ThisBuild scope. This feature allows you to set a value once in a more general scope, allowing multiple more-specific scopes to inherit the value.

What is publishLocal?

The publishLocal action is used to publish your project to your Ivy local file repository, which is usually located at $HOME/. ivy2/local/ . You can then use this project from other projects on the same machine.

Which Java version does sbt use?

sbt is a build tool for Scala, Java, and more. It requires Java 1.8 or later.

What is crossScalaVersions?

SBT's crossScalaVersions allows projects to compile with multiple versions of Scala. However, crossScalaVersions is a hack that reuses projects by mutates settings.


2 Answers

I'm not sure if it's recommended approach in SBT 0.13.x, but the following has seemed to have worked fine for me.

If you need to "have the same timestamp" for all versions listed in crossScalaVersions, then you may want to leverage Generating files, i.e. IO.write and IO.read methods, to save a timestamp file with a unique snapshot version.

NOTE Do not set scalaVersion in build.sbt as it overwrites the one set by +. That was the problem in my initial answer.

Have the following task stampVersion in build.sbt (I leave migrating it to SBT < 0.13 as an exercise):

lazy val stampVersion = taskKey[File]("Generates timestamp file with unique snapshot version")

stampVersion := {
  val log = streams.value.log
  val stmp = System.currentTimeMillis
  val file = target.value / "timestamp"
  log.info(s"Stamping version $stmp saved in $file")
  IO.write(file, s"""$stmp""")
  file
}

When you run the task stampVersion, a file gets created in target/timestamp file.

With the following task, you can read its content.

lazy val getStampVersion = taskKey[String]("Retrieves unique snapshot version from timestamp file")

getStampVersion := {
  val log = streams.value.log
  val file = (target in Compile).value / "timestamp"
  val v = IO.read(file)
  log.info(s"Retrieving version from $file: $v [scalaVersion: ${scalaVersion.value}]")
  v
}

Use show getStampVersion to show the version saved in the file.

[sbt-0-13-1]> stampVersion
[info] Stamping version 1390606523705 saved in /Users/jacek/sandbox/so/sbt-0.13.1/target/timestamp
[success] Total time: 0 s, completed Jan 25, 2014 12:35:23 AM
[sbt-0-13-1]> show getStampVersion
[info] Retrieving version from /Users/jacek/sandbox/so/sbt-0.13.1/target/timestamp: 1390606523705 [scalaVersion: 2.10.3]
[info] 1390606523705
[success] Total time: 0 s, completed Jan 25, 2014 12:35:34 AM

In order to set version key to a timestamped version, I defined a command setVersionFromStampFile (as it changes the state of a project).

def setVersionFromStampFile = Command.command("setVersionFromStampFile") { state =>
  val e = Project extract state
  import e._
  val (newState, stampVersion) = runTask(getStampVersion, state)
  val scalaV = scalaVersion in currentRef get structure.data getOrElse Nil
  state.log.info(s"scalaVersion: $scalaV")
  val settings = Seq(
    version := stampVersion
  )
  append(settings ++ structure.settings, state)
}

commands += setVersionFromStampFile

With the command setVersionFromStampFile, whenever it gets run, version gets set appropriately.

[sbt-0-13-1]> show version
[info] 0.1-SNAPSHOT
[sbt-0-13-1]> setVersionFromStampFile
[info] Retrieving version from /Users/jacek/sandbox/so/sbt-0.13.1/target/timestamp: 1390606523705 [scalaVersion: 2.10.3]
[info] scalaVersion: 2.10.3
[info] Set current project to sbt-0-13-1 (in build file:/Users/jacek/sandbox/so/sbt-0.13.1/)
[sbt-0-13-1]> show version
[info] 1390606523705

In the build definition build.sbt, have the setting crossScalaVersions defined, e.g.

crossScalaVersions := Seq("2.9.3", "2.10.3")

Define a command alias setStampAsVersionAndShow in build.sbt to ease testing - you'll see the values of version and scalaVersion settings after executing the command setVersionFromStampFile:

addCommandAlias("setStampAsVersionAndShow",
                "; setVersionFromStampFile ; show version; show scalaVersion")

This lets you cross-execute a series of commands:

[sbt-0-13-1]> + setStampAsVersionAndShow
[info] Setting version to 2.9.3
[info] Set current project to sbt-0-13-1 (in build file:/Users/jacek/sandbox/so/sbt-0.13.1/)
[info] Retrieving version from /Users/jacek/sandbox/so/sbt-0.13.1/target/timestamp: 1390606523705 [scalaVersion: 2.9.3]
[info] scalaVersion: 2.9.3
[info] Set current project to sbt-0-13-1 (in build file:/Users/jacek/sandbox/so/sbt-0.13.1/)
[info] 1390606523705
[info] 2.9.3
[info] Setting version to 2.10.3
[info] Set current project to sbt-0-13-1 (in build file:/Users/jacek/sandbox/so/sbt-0.13.1/)
[info] Retrieving version from /Users/jacek/sandbox/so/sbt-0.13.1/target/timestamp: 1390606523705 [scalaVersion: 2.10.3]
[info] scalaVersion: 2.10.3
[info] Set current project to sbt-0-13-1 (in build file:/Users/jacek/sandbox/so/sbt-0.13.1/)
[info] 1390606523705
[info] 2.10.3
[info] Setting version to 2.10.3
[info] Set current project to sbt-0-13-1 (in build file:/Users/jacek/sandbox/so/sbt-0.13.1/)

Define publishTo setting and another command alias setStampAsVersionAndPublish in build.sbt:

publishTo := Some(Resolver.file("file", target.value / "xxx"))

addCommandAlias("setStampAsVersionAndPublish",
                "; setVersionFromStampFile ; show scalaVersion ; publish")

You should now be able to publish as you'd expect:

  1. Let's start afresh.

    [sbt-0-13-1]> clean
    [success] Total time: 0 s, completed Jan 25, 2014 12:50:22 AM
    
  2. Generate a version file.

    [sbt-0-13-1]> stampVersion
    [info] Stamping version 1390607428495 saved in /Users/jacek/sandbox/so/sbt-0.13.1/target/timestamp
    [success] Total time: 0 s, completed Jan 25, 2014 12:50:28 AM
    
  3. Check it out with publish without cross-building (no +).

    [sbt-0-13-1]> setStampAsVersionAndPublish
    [info] Retrieving version from /Users/jacek/sandbox/so/sbt-0.13.1/target/timestamp: 1390607428495 [scalaVersion: 2.10.3]
    [info] scalaVersion: 2.10.3
    [info] Set current project to sbt-0-13-1 (in build file:/Users/jacek/sandbox/so/sbt-0.13.1/)
    [info] 2.10.3
    [info] Packaging /Users/jacek/sandbox/so/sbt-0.13.1/target/scala-2.10/sbt-0-13-1_2.10-1390607428495-sources.jar ...
    [info] Done packaging.
    [info] Updating {file:/Users/jacek/sandbox/so/sbt-0.13.1/}sbt-0-13-1...
    [info] Wrote /Users/jacek/sandbox/so/sbt-0.13.1/target/scala-2.10/sbt-0-13-1_2.10-1390607428495.pom
    [info] Resolving org.fusesource.jansi#jansi;1.4 ...
    [info] Done updating.
    [info] :: delivering :: default#sbt-0-13-1_2.10;1390607428495 :: 1390607428495 :: release :: Sat Jan 25 00:50:33 CET 2014
    [info]  delivering ivy file to /Users/jacek/sandbox/so/sbt-0.13.1/target/scala-2.10/ivy-1390607428495.xml
    [info] Compiling 1 Scala source to /Users/jacek/sandbox/so/sbt-0.13.1/target/scala-2.10/classes...
    [info] Main Scala API documentation to /Users/jacek/sandbox/so/sbt-0.13.1/target/scala-2.10/api...
    model contains 2 documentable templates
    [info] Main Scala API documentation successful.
    [info] Packaging /Users/jacek/sandbox/so/sbt-0.13.1/target/scala-2.10/sbt-0-13-1_2.10-1390607428495-javadoc.jar ...
    [info] Done packaging.
    [info] Packaging /Users/jacek/sandbox/so/sbt-0.13.1/target/scala-2.10/sbt-0-13-1_2.10-1390607428495.jar ...
    [info] Done packaging.
    [info]  published sbt-0-13-1_2.10 to /Users/jacek/sandbox/so/sbt-0.13.1/target/xxx/default/sbt-0-13-1_2.10/1390607428495/sbt-0-13-1_2.10-1390607428495.pom
    [info]  published sbt-0-13-1_2.10 to /Users/jacek/sandbox/so/sbt-0.13.1/target/xxx/default/sbt-0-13-1_2.10/1390607428495/sbt-0-13-1_2.10-1390607428495.jar
    [info]  published sbt-0-13-1_2.10 to /Users/jacek/sandbox/so/sbt-0.13.1/target/xxx/default/sbt-0-13-1_2.10/1390607428495/sbt-0-13-1_2.10-1390607428495-sources.jar
    [info]  published sbt-0-13-1_2.10 to /Users/jacek/sandbox/so/sbt-0.13.1/target/xxx/default/sbt-0-13-1_2.10/1390607428495/sbt-0-13-1_2.10-1390607428495-javadoc.jar
    [success] Total time: 1 s, completed Jan 25, 2014 12:50:34 AM
    
  4. Give the final command a go - + enters the scene.

    [sbt-0-13-1]> + setStampAsVersionAndPublish
    [info] Setting version to 2.9.3
    [info] Set current project to sbt-0-13-1 (in build file:/Users/jacek/sandbox/so/sbt-0.13.1/)
    [info] Retrieving version from /Users/jacek/sandbox/so/sbt-0.13.1/target/timestamp: 1390607428495 [scalaVersion: 2.9.3]
    [info] scalaVersion: 2.9.3
    [info] Set current project to sbt-0-13-1 (in build file:/Users/jacek/sandbox/so/sbt-0.13.1/)
    [info] 2.9.3
    [info] Packaging /Users/jacek/sandbox/so/sbt-0.13.1/target/scala-2.9.3/sbt-0-13-1_2.9.3-1390607428495-sources.jar ...
    [info] Done packaging.
    [info] Updating {file:/Users/jacek/sandbox/so/sbt-0.13.1/}sbt-0-13-1...
    [info] Wrote /Users/jacek/sandbox/so/sbt-0.13.1/target/scala-2.9.3/sbt-0-13-1_2.9.3-1390607428495.pom
    [info] Resolving org.fusesource.jansi#jansi;1.4 ...
    [info] Done updating.
    [info] :: delivering :: default#sbt-0-13-1_2.9.3;1390607428495 :: 1390607428495 :: release :: Sat Jan 25 00:50:51 CET 2014
    [info] Compiling 1 Scala source to /Users/jacek/sandbox/so/sbt-0.13.1/target/scala-2.9.3/classes...
    [info]  delivering ivy file to /Users/jacek/sandbox/so/sbt-0.13.1/target/scala-2.9.3/ivy-1390607428495.xml
    [info] Main Scala API documentation to /Users/jacek/sandbox/so/sbt-0.13.1/target/scala-2.9.3/api...
    model contains 2 documentable templates
    [info] Main Scala API documentation successful.
    [info] Packaging /Users/jacek/sandbox/so/sbt-0.13.1/target/scala-2.9.3/sbt-0-13-1_2.9.3-1390607428495-javadoc.jar ...
    [info] Done packaging.
    [info] Packaging /Users/jacek/sandbox/so/sbt-0.13.1/target/scala-2.9.3/sbt-0-13-1_2.9.3-1390607428495.jar ...
    [info] Done packaging.
    [info]  published sbt-0-13-1_2.9.3 to /Users/jacek/sandbox/so/sbt-0.13.1/target/xxx/default/sbt-0-13-1_2.9.3/1390607428495/sbt-0-13-1_2.9.3-1390607428495.pom
    [info]  published sbt-0-13-1_2.9.3 to /Users/jacek/sandbox/so/sbt-0.13.1/target/xxx/default/sbt-0-13-1_2.9.3/1390607428495/sbt-0-13-1_2.9.3-1390607428495.jar
    [info]  published sbt-0-13-1_2.9.3 to /Users/jacek/sandbox/so/sbt-0.13.1/target/xxx/default/sbt-0-13-1_2.9.3/1390607428495/sbt-0-13-1_2.9.3-1390607428495-sources.jar
    [info]  published sbt-0-13-1_2.9.3 to /Users/jacek/sandbox/so/sbt-0.13.1/target/xxx/default/sbt-0-13-1_2.9.3/1390607428495/sbt-0-13-1_2.9.3-1390607428495-javadoc.jar
    [success] Total time: 4 s, completed Jan 25, 2014 12:50:54 AM
    [info] Setting version to 2.10.3
    [info] Set current project to sbt-0-13-1 (in build file:/Users/jacek/sandbox/so/sbt-0.13.1/)
    [info] Retrieving version from /Users/jacek/sandbox/so/sbt-0.13.1/target/timestamp: 1390607428495 [scalaVersion: 2.10.3]
    [info] scalaVersion: 2.10.3
    [info] Set current project to sbt-0-13-1 (in build file:/Users/jacek/sandbox/so/sbt-0.13.1/)
    [info] 2.10.3
    [info] Packaging /Users/jacek/sandbox/so/sbt-0.13.1/target/scala-2.10/sbt-0-13-1_2.10-1390607428495-sources.jar ...
    [info] Done packaging.
    [info] Updating {file:/Users/jacek/sandbox/so/sbt-0.13.1/}sbt-0-13-1...
    [info] Wrote /Users/jacek/sandbox/so/sbt-0.13.1/target/scala-2.10/sbt-0-13-1_2.10-1390607428495.pom
    [info] Resolving org.fusesource.jansi#jansi;1.4 ...
    [info] Done updating.
    [info] :: delivering :: default#sbt-0-13-1_2.10;1390607428495 :: 1390607428495 :: release :: Sat Jan 25 00:50:55 CET 2014
    [info]  delivering ivy file to /Users/jacek/sandbox/so/sbt-0.13.1/target/scala-2.10/ivy-1390607428495.xml
    [info] Compiling 1 Scala source to /Users/jacek/sandbox/so/sbt-0.13.1/target/scala-2.10/classes...
    [info] Main Scala API documentation to /Users/jacek/sandbox/so/sbt-0.13.1/target/scala-2.10/api...
    model contains 2 documentable templates
    [info] Main Scala API documentation successful.
    [info] Packaging /Users/jacek/sandbox/so/sbt-0.13.1/target/scala-2.10/sbt-0-13-1_2.10-1390607428495-javadoc.jar ...
    [info] Done packaging.
    [info] Packaging /Users/jacek/sandbox/so/sbt-0.13.1/target/scala-2.10/sbt-0-13-1_2.10-1390607428495.jar ...
    [info] Done packaging.
    [info]  published sbt-0-13-1_2.10 to /Users/jacek/sandbox/so/sbt-0.13.1/target/xxx/default/sbt-0-13-1_2.10/1390607428495/sbt-0-13-1_2.10-1390607428495.pom
    [info]  published sbt-0-13-1_2.10 to /Users/jacek/sandbox/so/sbt-0.13.1/target/xxx/default/sbt-0-13-1_2.10/1390607428495/sbt-0-13-1_2.10-1390607428495.jar
    [info]  published sbt-0-13-1_2.10 to /Users/jacek/sandbox/so/sbt-0.13.1/target/xxx/default/sbt-0-13-1_2.10/1390607428495/sbt-0-13-1_2.10-1390607428495-sources.jar
    [info]  published sbt-0-13-1_2.10 to /Users/jacek/sandbox/so/sbt-0.13.1/target/xxx/default/sbt-0-13-1_2.10/1390607428495/sbt-0-13-1_2.10-1390607428495-javadoc.jar
    [success] Total time: 2 s, completed Jan 25, 2014 12:50:56 AM
    [info] Setting version to 2.10.3
    [info] Set current project to sbt-0-13-1 (in build file:/Users/jacek/sandbox/so/sbt-0.13.1/)
    
  5. Display basic information about sbt and the build with about.

    [sbt-0-13-1]> about
    [info] This is sbt 0.13.1
    [info] The current project is {file:/Users/jacek/sandbox/so/sbt-0.13.1/}sbt-0-13-1 0.1-SNAPSHOT
    [info] The current project is built against Scala 2.10.3
    [info] Available Plugins: com.typesafe.sbt.SbtGit, com.typesafe.sbt.SbtProguard, growl.GrowlingTests, np.Plugin, com.timushev.sbt.updates.UpdatesPlugin
    [info] sbt, sbt plugins, and build definitions are using Scala 2.10.3
    
like image 142
Jacek Laskowski Avatar answered Oct 08 '22 19:10

Jacek Laskowski


We could set the DATETIME in bash like so

export DATETIME=`date +"%Y-%m-%d_%H-%M-%S"`

and then build our software like

sbt 'set version in ThisBuild := s"1.0.0-${sys.env.get("DATETIME").get}"' +compile +publish

This will take care that same timestamp is used for both compile versions.

like image 36
saad Avatar answered Oct 08 '22 20:10

saad