[2.0.x] a quick tip: how to upload zip output from 'play dist' to Nexus/Artifactory

1,608 views
Skip to first unread message

Mark Baker

unread,
Sep 25, 2012, 7:44:22 AM9/25/12
to play-fr...@googlegroups.com
I just figured this out and wanted to share in case anyone encounters the same issue as my Googling for a solution wasn't very successful.

One of the benefits of moving to sbt in Play 2 (in my opinion) is being able to do 'play dist' or 'sbt dist' and get a nice zip file of your play app in a completely standalone form. 
As others have noted this makes it easy enough to then scp to a server, unzip and run.

We are doing that, but wanted to store prebuilt versions in a Repository so we could easily move between integration/production versions without doing a rebuild, or roll back to older versions if there was an issue. So using Artifactory, Nexus or another Maven Repository for this seems like a good idea as sbt already knows how to publish to them.

The only problem was that by default sbt only wants to upload the .jar of the play app without its dependencies.

To be fair the sbt docs do describe how to change this, but its not immediately obvious how to adapt it to Play, so let me share the solution. 

In our Build.scala file we have the following lines now:

  lazy val publishSettings = Seq(

    // disable publishing the usual jars
    publishArtifact in Compile := false,

    // don't use the compiled jar, use the dist output
    artifact in (Compile, dist) ~= { (art: Artifact) => art.copy(`type` = "zip", extension = "zip")  },

    // disable using the Scala version in output paths and artifacts 
    // (this is optional, but it makes for a cleaner name in the repository)
    crossPaths := false,

    // publish to Artefactory/Nexus
    organization := "com.your-org-name-here",
    publishMavenStyle := true,

    publishTo <<= version { (v: String) =>
      if (v.trim.endsWith("SNAPSHOT"))
        Some("snapshots" at repo + "libs-snapshot-local")
      else
        Some("releases"  at repo + "libs-release-local")
    }
  ) ++ addArtifact(artifact in (Compile, dist), dist)


  val main = PlayProject(appName, appVersion, appDependencies,
    mainLang = SCALA,
    settings = Defaults.defaultSettings ++ publishSettings)


The fiddly line to work out was artifact in (Compile, dist) ~= { (art: Artifact) => art.copy(`type` = "zip", extension = "zip")  } which creates the needed Artifact from the 'dist' task as a zip file (otherwise it is published as a jar file)

Hopefully that will help anyone else trying to do this in the future ;)

Cheers,

Mark
 

Xavier Jodoin

unread,
Jan 21, 2013, 2:28:33 PM1/21/13
to play-fr...@googlegroups.com
It doesn't work anymore with play 2.1 ?

ror] java.lang.AssertionError: assertion failed: Internal task engine error: nothing running.  This usually indicates a cycle in tasks.
[error]   Calling tasks (internal task engine state):
[error] Task((task-definition-key: ScopedKey(Scope(Select(ProjectRef(file:/home/xjodoin/git/univers-server/,univers-server)),Global,Global,Global),play-package-everything))) -> Calling

Mark Baker

unread,
Jan 21, 2013, 2:32:34 PM1/21/13
to play-fr...@googlegroups.com
I haven't tested with 2.1 yet, so that could well be the case. I'm not likely to move to 2.1 in the next 3 month tho :(

So someone else may have to fix this. 

---------------
Sent from my electronical gizmo
--
 
 

Travis Dixon

unread,
Sep 9, 2013, 2:16:55 AM9/9/13
to play-fr...@googlegroups.com
Looking at this as well now, getting the same error:
[error] java.lang.AssertionError: assertion failed: Internal task engine error: nothing running.  This usually indicates a cycle in tasks.
[error]   Calling tasks (internal task engine state):
[error] Task((task-definition-key: ScopedKey(Scope(Select(ProjectRef(file:/Users/thetrav/Projects/auspost/provider-portal/play/,provider-portal)),Global,Global,Global),play-package-everything))) -> Calling
[error] Use 'last' for the full log.

error sounds like somewhere it's looping between publish,  play-package-everything and our new task... maybe play-package-everything somehow causes the publish task to be called?

Mark Baker

unread,
Sep 9, 2013, 5:26:29 AM9/9/13
to play-fr...@googlegroups.com
Hi Travis,

We have this working now with Play 2.1 - thanks for reminding me to update this thread!

The main fix to avoid a loop was to define a publish-dist task and use that - to update the code snippet I published originally:

  val publishDist = TaskKey[sbt.File]("publish-dist", "publish the dist artifact")

  lazy val publishSettings = sbtrelease.ReleasePlugin.releaseSettings ++ Seq(

    // Don't publish the compiled jar, the docs, or the source
    publishArtifact in (Compile, packageDoc) := false,
    publishArtifact in (Compile, packageSrc) := false,

    publish <<= (publish) dependsOn play.Project.dist,
    artifact in publishDist ~= {
      (art: Artifact) => art.copy(`type` = "zip", extension = "zip")
    },

    // disable using the Scala version in output paths and artifacts
    crossPaths := false,

    // publish to Artefactory
    organization := "com.your-organization.name", // FILL IN HERE
    publishMavenStyle := true,
    pomIncludeRepository := {
      x => false
    },

    publishDist <<= (distDirectory, normalizedName, version) map { (distDir, id, version) =>
      val packageName = "%s-%s" format(id, version)
      distDir / (packageName + ".zip")
    },

    publishTo <<= version {
      (v: String) =>
        val repo = "http://your-artifactory-server:8081/artifactory/" // FILL IN HERE
        if (v.trim.endsWith("SNAPSHOT"))
          Some("snapshots" at repo + "libs-snapshot-local")
        else
          Some("releases" at repo + "libs-release-local")
    }
  // add the artifact from dist (that we created above)
  ) ++ addArtifact(artifact in publishDist, publishDist)


  val main = play.Project(appName,
    dependencies = appDependencies,
    settings = Defaults.defaultSettings ++ publishSettings
    ).settings(
      version <<= version in ThisBuild 
    )
  

Hope that helps!

Cheers,

Mark

Mark Baker

unread,
Sep 9, 2013, 5:28:40 AM9/9/13
to play-fr...@googlegroups.com
This with with sbt-release plugin 0.7 by the way, so our plugins.sbt file includes this line:

addSbtPlugin("com.github.gseitz" % "sbt-release" % "0.7")

Travis Dixon

unread,
Sep 9, 2013, 8:19:14 PM9/9/13
to play-fr...@googlegroups.com
Thanks Mark,

That certainly gives me something to dig into, but doesn't appear to work as a straight drop in fix.

Running "play publish-dist" goes through the publishTo code (println confirms this for me) but there's no attempt to publish to my repo, nor any logging to indicate that something has gone wrong.

Am I perhaps supposed to run some other task that causes publish-dist to be invoked?

Travis Dixon

unread,
Sep 9, 2013, 9:12:24 PM9/9/13
to play-fr...@googlegroups.com
All sorted, the key was to run:
play release

Thanks a ton for responding to this thread

Mark Baker

unread,
Sep 10, 2013, 2:50:23 AM9/10/13
to play-fr...@googlegroups.com
Glad to hear it is working Travis!
--
You received this message because you are subscribed to a topic in the Google Groups "play-framework" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/play-framework/Td389FbdgCY/unsubscribe.
To unsubscribe from this group and all its topics, send an email to play-framewor...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.


--

Mark Baker

unread,
Oct 4, 2013, 6:53:54 AM10/4/13
to play-fr...@googlegroups.com
So a quick update - predictably this is not working 'as is' with Play 2.2 due to changing over to the sbt-native-packager for doing 'dist'.
I'll fix it up and post my results here when I do.

Ho hum, just once I'd like to upgrade to a new version of Play that doesn't break existing things :/



On Tuesday, 10 September 2013 07:50:23 UTC+1, Mark Baker wrote:
Glad to hear it is working Travis!

On Tuesday, September 10, 2013, Travis Dixon wrote:
All sorted, the key was to run:
play release

Thanks a ton for responding to this thread

--
You received this message because you are subscribed to a topic in the Google Groups "play-framework" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/play-framework/Td389FbdgCY/unsubscribe.
To unsubscribe from this group and all its topics, send an email to play-framework+unsubscribe@googlegroups.com.

For more options, visit https://groups.google.com/groups/opt_out.

Travis Dixon

unread,
Oct 9, 2013, 11:15:08 PM10/9/13
to play-fr...@googlegroups.com
Hi Mark,

Did you make any progress on this one?  I've just run into it myself, and I'm flailing around trying to find where they moved the "dist" command to

Travis Dixon

unread,
Oct 10, 2013, 12:13:20 AM10/10/13
to play-fr...@googlegroups.com
com.typesafe.sbt.SbtNativePackager.NativePackagerKeys.dist

now I'm hunting the distDirectory setting or equivalent


To unsubscribe from this group and all its topics, send an email to play-framewor...@googlegroups.com.

Mark Baker

unread,
Oct 10, 2013, 2:38:57 AM10/10/13
to play-fr...@googlegroups.com
Hi Travis,

No progress yet I'm afraid - I had to park the work due to lack of time, but I will get back to it at some point. 

I suspect the dist task will be in the native packager plugin but I feel like I will need to download some source to figure it out. 
To unsubscribe from this group and all its topics, send an email to play-framewor...@googlegroups.com.

For more options, visit https://groups.google.com/groups/opt_out.

Travis Dixon

unread,
Oct 10, 2013, 8:13:29 PM10/10/13
to play-fr...@googlegroups.com
I ended up abandoning nexus publishing from sbt, too many features of sbt-release were not working for me.

As I said above: com.typesafe.sbt.SbtNativePackager.NativePackagerKeys.dist is the new dist key
the "distDirectory" is now the "target" key, but that's not enough to get it working either.

I ended up getting jenkins to run the dist task, bash to increment the version numbers, maven to do the nexus publish.

I am not happy about the maven requirement :(

Guy John

unread,
Oct 15, 2013, 11:30:15 AM10/15/13
to play-fr...@googlegroups.com
Hey Travis

I work with Mark, and I've pulled this task onto my backlog to have a look at (more fool me)

I'm had some success, but there's a bit of a way to go. the problem with using the target key instead of distDirectory is that the zip isn't published to /target/myproject.zip but to /target/universa/proj.zip, and there doesn't seem to be key that points directly to it.


by updating the publishDist key in the publishSettings, I can get the zip file published, but at the moment I'm hard coding the "universal" name

   publishDist <<= (target, normalizedName, version) map { (targetDir, id, version) =>
     val packageName = "%s-%s" format(id, version)
     targetDir / "universal" / (packageName + ".zip")
   },

I feel like there should be some way to get that string without hard coding, but my sbt-fu isn't strong enough.

I also updated the "publish dependsOn" line to use the com.typesafe.sbt.SbtNativePackager.NativePackagerKeys.dist key. These two changes have got me as far as publishing the zip file and the pom to artifactory. One more useful thing is that we can now use publishArtifact in (Compile, packageBin) := false to stop sbt also publishing the jar file, and this no longer seems to stop the jar file being included in the zip.

The new problem, however, is that dist no longer seems to create the start script in the zip for me. The play docs say that this should be in the /bin folder, but that doesn't even seem to exist. I'm trying to work out if there is a key i need to change, but no joy so far.





Guy John

unread,
Oct 15, 2013, 11:34:51 AM10/15/13
to play-fr...@googlegroups.com
in the interests of completeness, here's the full publish settings as described above

  lazy val dist = com.typesafe.sbt.SbtNativePackager.NativePackagerKeys.dist

  val publishDist = TaskKey[sbt.File]("publish-dist", "publish the dist artifact")

  lazy val publishSettings = sbtrelease.ReleasePlugin.releaseSettings ++ Seq(

    publishArtifact in (Compile, packageDoc) := false,
    publishArtifact in (Compile, packageSrc) := false,
    publishArtifact in (Compile, packageBin) := false,

    publish <<= (publish) dependsOn dist,
    publishLocal <<= (publishLocal) dependsOn dist,

    artifact in publishDist ~= {
      (art: Artifact) => art.copy(`type` = "zip", extension = "zip")
    },

    // disable using the Scala version in output paths and artifacts
    crossPaths := false,

    // publish to Artefactory
    organization := "com.your-organisation.name",
    publishMavenStyle := true,
    pomIncludeRepository := {
      x => false
    },

    publishDist <<= (target, normalizedName, version) map { (targetDir, id, version) =>
      val packageName = "%s-%s" format(id, version)
      targetDir / "universal" / (packageName + ".zip")
    },

    publishTo <<= version {
      (v: String) =>
        val repo = "http://your-artifactory-server:8081/artifactory/" // FILL IN HERE
        if (v.trim.endsWith("SNAPSHOT"))
          Some("snapshots" at repo + "libs-snapshot-local")
        else
          Some("releases" at repo + "libs-release-local")
    }

  ) ++ addArtifact(artifact in publishDist, publishDist)

Guy John

unread,
Oct 18, 2013, 6:49:04 AM10/18/13
to play-fr...@googlegroups.com
Ok, think I've cracked all the relevant parts now. Instead of hard coding the "universal" name, I just pull in the target from the Universal scope. This needs to be imported from com.typesafe.sbt.SbtNativePackager along with the NativePackagerKeys
Also seems that I'd missed the change to using playScalaSettings which fixed up the rest of my problems.

  lazy val dist = com.typesafe.sbt.SbtNativePackager.NativePackagerKeys.dist

  val publishDist = TaskKey[sbt.File]("publish-dist", "publish the dist artifact")

  lazy val publishSettings = sbtrelease.ReleasePlugin.releaseSettings ++ Seq(

    publishArtifact in (Compile, packageDoc) := false,
    publishArtifact in (Compile, packageSrc) := false,
    publishArtifact in (Compile, packageBin) := false,

    publish <<= (publish) dependsOn dist,
    publishLocal <<= (publishLocal) dependsOn dist,

    artifact in publishDist ~= {
      (art: Artifact) => art.copy(`type` = "zip", extension = "zip")
    },

    // disable using the Scala version in output paths and artifacts
    crossPaths := false,

    // publish to Artefactory
    organization := "com.your-organisation.name",
    publishMavenStyle := true,
    pomIncludeRepository := {
      x => false
    },

    publishDist <<= (target in Universal, normalizedName, version) map { (targetDir, id, version) =>
      val packageName = "%s-%s" format(id, version)
      targetDir / (packageName + ".zip")
    },

    publishTo <<= version {
      (v: String) =>
        val repo = "http://your-artifactory-server:8081/artifactory/" // FILL IN HERE
        if (v.trim.endsWith("SNAPSHOT"))
          Some("snapshots" at repo + "libs-snapshot-local")
        else
          Some("releases" at repo + "libs-release-local")
    }

  ) ++ addArtifact(artifact in publishDist, publishDist)
  val main = play.Project(appName,
    dependencies = appDependencies,
    settings = playScalaSettings ++ publishSettings

Diogo Longo

unread,
Dec 13, 2013, 3:09:57 PM12/13/13
to play-fr...@googlegroups.com
Hi Guy,

today after config my project to publish zip file, I came across an exception:

java.lang.ExceptionInInitializerError
    at java
.lang.Class.forName0(Native Method)
    at java
.lang.Class.forName(Class.java:270)
    at sbt
.ModuleUtilities$.getObject(ModuleUtilities.scala:13)
......
Caused by: java.lang.NullPointerException
    at sbt
.Scoped$ScopingSetting$class.in(Structure.scala:103)
    at sbt
.SettingKey.in(Structure.scala:32)
    at
ApplicationBuild$.publishSettings$lzycompute(Build.scala:80)
    at
ApplicationBuild$.publishSettings(Build.scala:71)
    at
ApplicationBuild$$anonfun$1.apply(Build.scala:53)


after some time I changed the code to:
lazy val publishDist = TaskKey[sbt.File]("publish-dist", "publish the dist artifact")

and works.

Diogo Longo

unread,
Dec 16, 2013, 1:06:42 PM12/16/13
to play-fr...@googlegroups.com
How to publish tar.gz file instead zip?

Harry

unread,
Aug 19, 2014, 4:56:54 AM8/19/14
to play-fr...@googlegroups.com
This quite helpful.. I am  getting two errors
error: not found: value sbtrelease
error: not found: value appName
Reply all
Reply to author
Forward
0 new messages