How do I change universal zip file name using sbt-native-packager - scala

I am using:
scala 2.10.3
sbt 13.2
with plugin:
addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "0.7.3")
I am using the universal:packgeBin to generate the universal zip file and publish to ivy repository.
I'd like to change the zip file name from project_id_scalaversion_buildVersion.zip to project_id_scalaversion_buildVersion_dist.zip. How would I do that?

This answer is based on version 1.0.3 that I have used, but it should apply to the latest version (1.1.5) as well.
You can name your package however you want. The only thing to do is to add the following setting to the configuration of your project:
Universal / packageName := s"${name.value}_${scalaVersion.value}_${version.value}_dist"

I think you cannot change the name of the generated artifact just for the universal:packageBin easily.
You can change the name of the generated artifact globally, by using artifactName.
artifactName := { (sv: ScalaVersion, module: ModuleID, artifact: Artifact) =>
artifact.name + module.revision + "_dist." + artifact.extension
}
This will however also modify also the name of the generated jar file, and perhaps some other names of the generated artifacts.
If you wanted to change the name only of the file generated by the universal:packageBin you could rename the file after it was generated. Sbt gives you utilities which make this rather easy.
Universal / packageBin := {
val originalFileName = (Universal / packageBin).value
val (base, ext) = originalFileName.baseAndExt
val newFileName = file(originalFileName.getParent) / (base + "_dist." + ext)
IO.move(originalFileName, newFileName)
newFileName
}
Now invoking the Universal/packageBin should execute your new task, which will rename the file after it's created.

Related

How do I copy a zip dependency to the target directory in an SBT build?

I am working on an SBT project that generates an RPM via the sat-native-packager. One of the items that I want to pull into the RPM is a ZIP file that was published from a separate project using the sat-pack plugin. This ZIP file contains a number of JAR files, along with multiple scripts for invoking them.
I have the following in my RPM project's build.sbt:
libraryDependencies += ("com.mycompany" %% "aputils" % "1.0.0-SNAPSHOT").artifacts(Artifact("aputils", "zip", "zip"))
// Task to download and unpack the utils bundle
lazy val unpackUtilsTask = taskKey[Unit]("Download the utils bundle to the target directory")
unpackUtilsTask := {
val log = streams.value.log
val report: UpdateReport = (update in Rpm).value
val filter = artifactFilter(extension = "zip")
val matches: Seq[File] = report.matching(filter)
matches.foreach{ f =>
log.info(s"Filter match: ${f}")
IO.copyFile(f, target.value)
}
}
When I run this task, it does not match any entries in the UpdateReport. Nothing is printed, and no files are copied to target/. If I modify the task to instead print all of the files in the UpdateReport:
report.allFiles.foreach(f => log.info(s"All files: $f))
I see a number of JAR files, but not my ZIP file. The JAR files turn out to be all of the JAR files that are contained in the ZIP file. I am not sure why the ZIP is being unpacked and its contents are being listed as dependencies like this. If I mark the dependency as notTransitive, then the contained JARs are not listed in the report, but the ZIP still isn't included either.
This project is using SBT 0.13.15. I would prefer not to update it to 1.x at this time, but will do so if I must.
I will need to unzip the ZIP file under target/ eventually so I can define one or more packageMapping entries to pull the files into the RPM, but that seems easy enough to do via sbt.IO, if I can first just get a reference to the original ZIP file that is pulled down from our Artifactory server.
This didn't get any responses after a couple days, but I'll post the answer that I was able to come up with after more trial and error.
I was on the right track by examining the UpdateReport, but I wasn't looking at the right data within it. I needed to drill down to find a ModuleReport, which would show me where the .zip file was being downloaded to on the build machine. Once I have that path, it is trivial to unpack it to target/ using IO.unzip(). Here is how my task ended up looking:
libraryDependencies += ("com.mycompany" %% "aputils" % "1.0.0-SNAPSHOT").artifacts(Artifact("aputils", "zip", "zip"))
// Task to unzip the utils ZIP file to the target directory so we can define a package mapping
lazy val unpackUtilsTask = taskKey[Unit]("Download the utils bundle to the target directory")
unpackUtilsTask := {
val log = streams.value.log
val cReport: ConfigurationReport = (update in Compile).value.configuration("compile").get
cReport.modules.foreach{ mReport =>
if (mReport.module.name.startsWith("aputils")) {
mReport.artifacts.foreach{ case (art, f) =>
log.info(s"Unpacking aputils bundle: ${f.getAbsolutePath}")
IO.unzip(f, target.value)
}
}
}
}
packageBin in Rpm := ((packageBin in Rpm).dependsOn(unpackUtilsTask)).value
The last line attaches the task to the task that builds the RPM, so it will be unzipped before the RPM is built, and we can define packageMappings to put the contents of the .zip file into the generated RPM.

Cannot get sbt-concat to bundle styles from sbt-sass or sbt-less

(Example project provided) I cannot get sbt-concat to work as designed to find and concatenate stylesheets that result from styles that may be produced from preprocessor tasks. In my production app, I'm trying to use it to bundle select minified output files from sbt-sass. It does not work within the complex setup of that project, so I created an example project to see if I could get it to work at all. It does not work in the example project either. Here is a test project build.sbt that tries to create several bundles, with just about every possibility I can think of, just to see if any of them work (public Github repo, which you should be able to clone and immediately replicate the problem):
import com.typesafe.sbt.web.Import.WebKeys._
import com.typesafe.sbt.web.pipeline.Pipeline
name := """sbt-concat-test"""
version := "1.0-SNAPSHOT"
lazy val root = (project in file(".")).enablePlugins(PlayScala, SbtWeb)
scalaVersion := "2.11.1"
libraryDependencies ++= Seq(
jdbc,
anorm,
cache,
ws
)
resolvers += Resolver.sonatypeRepo("releases")
includeFilter in (Assets, LessKeys.less) := "*.less"
excludeFilter in (Assets, LessKeys.less) := "_*.less"
val myPipelineTask = taskKey[Pipeline.Stage]("Some pipeline task")
myPipelineTask := { mappings => println(mappings); mappings }
pipelineStages := Seq(myPipelineTask, concat)
Concat.groups := Seq(
"style-group1.css" -> group(sourceDirectory.value ** "*.css"),
"style-group2.css" -> group(baseDirectory.value ** "*.css"),
"style-group3.css" -> group((sourceDirectory in Assets).value ** "*.css"),
"style-group4.css" -> group(target.value ** "*.css"),
"style-group5.css" -> group(Seq("core.css", "styles/core.css", "assets/styles/core.css", "app/assets/styles/core.css")),
"style-group6.css" -> group(Seq("lessStyle.css", "ui/lessStyle.css", "styles/ui/lessStyle.css", "assets/styles/ui/lessStyle.css", "app/assets/styles/ui/lessStyle.css")),
"style-group7.css" -> group(Seq("sassStyle.css", "ui/sassStyle.css", "styles/ui/sassStyle.css", "assets/styles/ui/sassStyle.css", "app/assets/styles/ui/sassStyle.css")),
"style-group8.css" -> group(Seq("**/*.css"))
)
I run ; clean; reload; stage from activator to test. I see asset source files copied over into the target folder, with the following results for the declared bundles:
style-group1.css does not exist
style-group2.css contains the contents of button.css and core.css
style-group3.css contains the contents of core.css and button.css
style-group4.css does not exist
style-group5.css contains only the contents of core.css
style-group6.css contains only the contents of compiled lessStyle.scss
style-group7.css contains only the contents of compiled sassStyle.scss
style-group8.css does not exist
I do not understand why the 2nd and 3rd cases do not pick up the preprocessor-produced css files, yet the tailor-made 6th and 7th cases do. Perhaps notably, the result of myPipelineTask shows PathMappings for all source files, as well as the derived css and sourcemaps from the Sass and Less tasks.
According to Typesafe support, the source of my woes is the fact that sbt-concat implements its PathFinder logic in such a way as to only pick up assets that are verbatim the same filename as in the source directory. The sequence of relative filenames works for files in the target directory, but has no pattern matching. This is rather unfortunate.
What does work is to construct a Seq of output files that will exist post-compilation by using a PathFinder on the source directory. So for .scss files, something like:
Concat.groups := {
// Determine the output names of the style files to bundle
// This is really roundabout because sbt-concat only offers 2 ways of
// specifying files, relative paths and using a PathFinder, and the
// latter approach restricts itself to source files instead of output files.
val sourceDir = (sourceDirectory in Assets).value
val scssFiles = (sourceDir ** "*.scss").getPaths
val scssRelativePaths = scssFiles.map(_.stripPrefix(sourceDir.getPath).stripPrefix("/"))
val outputRelativePaths = scssRelativePaths.map(_.stripSuffix(".scss") + ".min.css")
Seq("bundle.min.css" -> group(outputRelativePaths))
}
As a side note, another quirk of sbt-concat is that it doesn't put its new files in its own directory in web-assets:assetsTarget to separate them from artifacts of other pipeline stages. The Concat.parentDir is also unnecessary because you can simply anything you would place in that variable as a prefix for your bundle file name directly.

Publish zip created by sbt-native-packager

I am trying to publish to a repository the zip file generated by the sbt-native-packager plugin through universal:packageBin task.
I configured my project like this:
publishTo := Some("Repo" at "http://repo")
publishMavenStyle := true
packagerSettings
packageArchetype.java_application
I am struggling trying to create a new sbt task (named publishZip) using publish task and packageBin task to publish the zip file. How can I achieve this ?
Add the following line to your sbt build (around packagerSettings should be fine)
deploymentSettings
Depending on what you want to do you may not need to define the publishZip task you could run
sbt universal:publish which should only publish the zip
redefine publish so it depends on universal:publish which would publish all the projects artifacts
publish <<= publish.dependsOn(publish in config("universal"))
Then run sbt publish.
For completeness sake deploymentSettings (and packagerSettings) come from com.typesafe.sbt.SbtNativePackager which is useful to know if you use a scala build :)
The deploymentSettings worked however I wanted to refine the settings. It seems there were several issues going on. I and finally came up with the following solution:
//--use sbt-native-packager default java application
packageArchetype.java_application
//--a dummy task to hold the result of the universal:packageBin to stop the circular dependency issue
val packageZip = taskKey[File]("package-zip")
//--hard coded result of "universal:packageBin"
packageZip := (baseDirectory in Compile).value / "target" / "universal" / (name.value + "-" + version.value + ".zip")
//--label the zip artifact as a zip instead of the default jar
artifact in (Universal, packageZip) ~= { (art:Artifact) => art.copy(`type` = "zip", extension = "zip") }
//--add the artifact so it is included in the publishing tasks
addArtifact(artifact in (Universal, packageZip), packageZip in Universal)
//--make sure the zip gets made before the publishing commands for the added artifacts
publish := (publish dependsOn (packageBin in Universal)).value
publishM2 := (publishM2 dependsOn (packageBin in Universal)).value
publishLocal := (publishLocal dependsOn (packageBin in Universal)).value
The key pieces of this solution came from a comment from yanns and dgrandes

Change 'play dist' output file name via command-line

Is it possible to change play dist output file name apart from modifying appName on Build.scala?
I'm trying to automate the creation of two different zip files from the same project tree, e.g.: myapp-production-1.0-SNAPSHOT.zip and myapp-integration-1.0-SNAPSHOT.zip.
Thank you very much!
For Play Framework 2.4 changing the name of the output file worked like this in build.sbt:
packageName in Universal := "dist"
In Play Framework 2.2, you can add the following line to the build.sbt file:
name in Universal := "dist"
Your app will be packaged in target/universal/dist.zip
You can modify the output by changing the build.sbt file in your project directory.
name := "myname"
version := "1.0-SNAPSHOT"
Remember to recompile, might have to clean too.

Making stand-alone jar with Simple Build Tool

Is there a way to tell sbt to package all needed libraries (scala-library.jar) into the main package, so it is stand-alone? (static?)
Edit 2011:
Since then, retronym (which posted an answer in this page back in 2010), made this sbt-plugin "sbt-onejar", now in its new address on GitHub, with docs updated for SBT 0.12.
Packages your project using One-JARâ„¢
onejar-sbt is a simple-build-tool plugin for building a single executable JAR containing all your code and dependencies as nested JARs.
Currently One-JAR version 0.9.7 is used. This is included with the plugin, and need not be separately downloaded.
Original answer:
Directly, this is not possible without extending sbt (a custom action after the model of the "package" sbt action).
GitHub mentions an assembly task, custom made for jetty deployment. You could adapt it for your need though.
The code is pretty generic (from this post, and user Rio):
project / build / AssemblyProject.scala
import sbt._
trait AssemblyProject extends BasicScalaProject
{
def assemblyExclude(base: PathFinder) = base / "META-INF" ** "*"
def assemblyOutputPath = outputPath / assemblyJarName
def assemblyJarName = artifactID + "-assembly-" + version + ".jar"
def assemblyTemporaryPath = outputPath / "assembly-libs"
def assemblyClasspath = runClasspath
def assemblyExtraJars = mainDependencies.scalaJars
def assemblyPaths(tempDir: Path, classpath: PathFinder, extraJars: PathFinder, exclude: PathFinder => PathFinder) =
{
val (libs, directories) = classpath.get.toList.partition(ClasspathUtilities.isArchive)
for(jar <- extraJars.get ++ libs) FileUtilities.unzip(jar, tempDir, log).left.foreach(error)
val base = (Path.lazyPathFinder(tempDir :: directories) ##)
(descendents(base, "*") --- exclude(base)).get
}
lazy val assembly = assemblyTask(assemblyTemporaryPath, assemblyClasspath, assemblyExtraJars, assemblyExclude) dependsOn(compile)
def assemblyTask(tempDir: Path, classpath: PathFinder, extraJars: PathFinder, exclude: PathFinder => PathFinder) =
packageTask(Path.lazyPathFinder(assemblyPaths(tempDir, classpath, extraJars, exclude)), assemblyOutputPath, packageOptions)
}
It takes a bit of work, but you can also use Proguard from within SBT to create a standalone JAR.
I did this recently in the SBT build for Scalala.
Working off of what #retronym offered above, I built a simple example that builds a stand alone jar which includes the Scala libraries (i.e. scala-library.jar) using Proguard with sbt. Thanks, retronym.
The simplest example using sbt-assembly
Create directory project in your home project dir with file assembly.sbt including
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.11.2")
In file build.sbt
import AssemblyKeys._ // put this at the top of the file
assemblySettings
jarName += "Mmyjarnameall.jar"
libraryDependencies ++= Seq( "exmpleofmydependency % "mydep" % "0.1" )
mergeStrategy in assembly <<= (mergeStrategy in assembly) { (old) =>
{
case s if s.endsWith(".class") => MergeStrategy.last
case s if s.endsWith("pom.xml") => MergeStrategy.last
case s if s.endsWith("pom.properties") => MergeStrategy.last
case x => old(x)
}
}
The simplest method is just to create the jar from the command line. If you don't know how to do this I would highly recommend that you do so. Automation is useful, but its much better if you know what the automation is doing.
The easiest way to automate the creation of a runnable jar is to use a bash script or batch script in windows.
The simplest way in sbt is just to add the Scala libraries you need to the resource directories:
unmanagedResourceDirectories in Compile := Seq(file("/sdat/bins/ScalaCurrent/lib/scalaClasses"))
So in my environment ScalaCurrent is a link to the current Scala library. 2.11.4 at the time of writing. The key point is that I extract the Scala library but place it inside a ScalaClassses directory. Each extracted library needs to go into its top level directory.