diff --git a/build.sbt b/build.sbt index f97ca528b..40a6f22cb 100644 --- a/build.sbt +++ b/build.sbt @@ -24,6 +24,8 @@ com.typesafe.sbt.SbtSite.SiteKeys.siteMappings <+= (baseDirectory) map { dir => site.sphinxSupport() +site.includeScaladoc() + ghpages.settings git.remoteRepo := "git@github.com:sbt/sbt-native-packager.git" diff --git a/project/build.properties b/project/build.properties index be6c454fb..64abd373f 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=0.13.5 +sbt.version=0.13.6 diff --git a/src/main/scala/com/typesafe/sbt/PackagerPlugin.scala b/src/main/scala/com/typesafe/sbt/PackagerPlugin.scala index 568e10d43..e2cbb8406 100644 --- a/src/main/scala/com/typesafe/sbt/PackagerPlugin.scala +++ b/src/main/scala/com/typesafe/sbt/PackagerPlugin.scala @@ -2,56 +2,139 @@ package com.typesafe.sbt import packager._ -import debian.Keys.genChanges -import Keys.{ packageName, packageZipTarball, packageXzTarball } +import debian.DebianPlugin.autoImport.genChanges +import universal.UniversalPlugin.autoImport.{ packageZipTarball, packageXzTarball } import sbt._ -import sbt.Keys.{ normalizedName, packageBin } - -object SbtNativePackager extends Plugin - with linux.LinuxPlugin - with debian.DebianPlugin - with rpm.RpmPlugin - with windows.WindowsPlugin - with docker.DockerPlugin - with universal.UniversalPlugin - with GenericPackageSettings { - - val NativePackagerKeys = packager.Keys - - val NativePackagerHelper = packager.MappingsHelper - - def packagerSettings = linuxSettings ++ - debianSettings ++ - rpmSettings ++ - windowsSettings ++ - dockerSettings ++ - universalSettings ++ - Seq( // Bad defaults that let us at least not explode users who don't care about native packagers - NativePackagerKeys.maintainer := "", - NativePackagerKeys.packageDescription := "", - NativePackagerKeys.packageSummary := "", - NativePackagerKeys.packageName <<= normalizedName, - NativePackagerKeys.executableScriptName <<= NativePackagerKeys.packageName - ) - - import SettingsHelper._ - def deploymentSettings = makeDeploymentSettings(Debian, packageBin in Debian, "deb") ++ - makeDeploymentSettings(Rpm, packageBin in Rpm, "rpm") ++ - makeDeploymentSettings(Windows, packageBin in Windows, "msi") ++ - makeDeploymentSettings(Universal, packageBin in Universal, "zip") ++ - addPackage(Universal, packageZipTarball in Universal, "tgz") ++ - makeDeploymentSettings(UniversalDocs, packageBin in UniversalDocs, "zip") ++ - addPackage(UniversalDocs, packageXzTarball in UniversalDocs, "txz") ++ - makeDeploymentSettings(Debian, genChanges in Debian, "changes") +import sbt.Keys.{ name, normalizedName, packageBin } + +/** + * == SBT Native Packager Plugin == + * + * This is the top level plugin for the sbt native packager. + * You don't have to enable this by yourself, instead we recommend + * using an archetype for this. + * + * Currently you can choose between + * + * + * + * == Configuration == + * + * The are a few settings you should set if you want to build package + * no matter what format. + * + * {{{ + * maintainer := "Your name " + * packageDescription := "A short description of your application" + * }}} + * + * For all other general settings take a look at [[com.typesafe.sbt.packager.NativePackagerKeys]] + * + * @example Enable the plugin in the `build.sbt` + * {{{ + * enablePlugins(SbtNativePackager) + * }}} + * + */ +object SbtNativePackager extends AutoPlugin { + + /* === Universal Configuration === */ + val Universal = universal.UniversalPlugin.autoImport.Universal + val UniversalDocs = universal.UniversalPlugin.autoImport.UniversalDocs + val UniversalSrc = universal.UniversalPlugin.autoImport.UniversalSrc + + /* === OS Configurations === */ + val Linux = linux.LinuxPlugin.autoImport.Linux + val Debian = debian.DebianPlugin.autoImport.Debian + val Rpm = rpm.RpmPlugin.autoImport.Rpm + val Windows = windows.WindowsPlugin.autoImport.Windows + val Docker = docker.DockerPlugin.autoImport.Docker + + /** + * imports all [[com.typesafe.sbt.packager.NativePackagerKeys]] and two objects: + * + * === NativePackagerKeys === + * + * This inclues ''all'' available keys provided by the sbt-native-packager. + * Used it if a setting/task key is not in scope. + * + * {{{ + * NativePackagerKeys.notAutomaticallyImported := "cool!" + * }}} + * + * === NativePackagerHelper === + * + * This object contains a set of helper methods for working with mappings. + * + */ + object autoImport extends packager.NativePackagerKeys { + + val NativePackagerKeys = packager.Keys + val NativePackagerHelper = packager.MappingsHelper + + import SettingsHelper._ + + def deploymentSettings = makeDeploymentSettings(Debian, packageBin in Debian, "deb") ++ + makeDeploymentSettings(Rpm, packageBin in Rpm, "rpm") ++ + makeDeploymentSettings(Windows, packageBin in Windows, "msi") ++ + makeDeploymentSettings(Universal, packageBin in Universal, "zip") ++ + addPackage(Universal, packageZipTarball in Universal, "tgz") ++ + makeDeploymentSettings(UniversalDocs, packageBin in UniversalDocs, "zip") ++ + addPackage(UniversalDocs, packageXzTarball in UniversalDocs, "txz") ++ + makeDeploymentSettings(Debian, genChanges in Debian, "changes") + } + + import autoImport._ + + override lazy val projectSettings = Seq( + // Bad defaults that let us at least not explode users who don't care about native packagers + maintainer := "", + packageDescription := name.value, + packageSummary := name.value, + packageName := normalizedName.value, + executableScriptName := normalizedName.value + + ) object packageArchetype { - private[this] def genericMappingSettings: Seq[Setting[_]] = packagerSettings ++ mapGenericFilesToLinux ++ mapGenericFilesToWindows + + /** + * == Recommended usage == + * + * {{{ + * enablePlugins(JavaAppPackaging) + * }}} + */ + @deprecated("Use enablePlugins(JavaAppPackaging)", "1.x") def java_application: Seq[Setting[_]] = - genericMappingSettings ++ archetypes.JavaAppPackaging.settings - def akka_application: Seq[Setting[_]] = - genericMappingSettings ++ archetypes.AkkaApp.settings - def java_server: Seq[Setting[_]] = - genericMappingSettings ++ archetypes.JavaServerAppPackaging.settings + projectSettings ++ + universal.UniversalPlugin.projectSettings ++ + linux.LinuxPlugin.projectSettings ++ + debian.DebianPlugin.projectSettings ++ + rpm.RpmPlugin.projectSettings ++ + docker.DockerPlugin.projectSettings ++ + windows.WindowsPlugin.projectSettings ++ + archetypes.JavaAppPackaging.projectSettings + + /** + * {{{ + * enablePlugins(AkkaAppPackaging) + * }}} + */ + @deprecated("Use enablePlugins(AkkaAppPackaging)", "1.x") + def akka_application: Seq[Setting[_]] = java_application ++ archetypes.AkkaAppPackaging.projectSettings + + /** + * {{{ + * enablePlugins(JavaServerAppPackaging) + * }}} + */ + @deprecated("Use enablePlugins(JavaServerAppPackaging)", "1.x") + def java_server: Seq[Setting[_]] = java_application ++ archetypes.JavaServerAppPackaging.projectSettings } // TODO - Add a few targets that detect the current OS and build a package for that OS. diff --git a/src/main/scala/com/typesafe/sbt/packager/GenericPackageSettings.scala b/src/main/scala/com/typesafe/sbt/packager/GenericPackageSettings.scala deleted file mode 100644 index b65f9778d..000000000 --- a/src/main/scala/com/typesafe/sbt/packager/GenericPackageSettings.scala +++ /dev/null @@ -1,144 +0,0 @@ -package com.typesafe.sbt -package packager - -import Keys._ -import sbt._ -import sbt.Keys.{ name, mappings, sourceDirectory, normalizedName } -import linux.LinuxSymlink -import linux.LinuxPackageMapping - -trait GenericPackageSettings - extends linux.LinuxPlugin - with debian.DebianPlugin - with rpm.RpmPlugin - with windows.WindowsPlugin - with universal.UniversalPlugin { - - import linux.LinuxPlugin.Users - - // This method wires a lot of hand-coded generalities about how to map directories - // into linux, and the conventions we expect. - // It is by no means 100% accurate, but should be ok for the simplest cases. - // For advanced users, use the underlying APIs. - // Right now, it's also pretty focused on command line scripts packages. - - /** - * Maps linux file format from the universal from the conventions: - * - * `/src/linux` files are mapped directly into linux packages. - * `` files are placed under `/usr/share/` - * `/bin` files are given symlinks in `/usr/bin` - * `/conf` directory is given a symlink to `/etc/` - * Files in `conf/` or `etc/` directories are automatically marked as configuration. - * `../man/...1` files are automatically compressed into .gz files. - * - */ - def mapGenericMappingsToLinux(mappings: Seq[(File, String)], user: String, group: String)(rename: String => String): Seq[LinuxPackageMapping] = { - val (directories, nondirectories) = mappings.partition(_._1.isDirectory) - val (binaries, nonbinaries) = nondirectories.partition(_._1.canExecute) - val (manPages, nonManPages) = nonbinaries partition { - case (file, name) => (name contains "man/") && (name endsWith ".1") - } - val compressedManPages = - for ((file, name) <- manPages) - yield file -> (name + ".gz") - val (configFiles, remaining) = nonManPages partition { - case (file, name) => (name contains "etc/") || (name contains "conf/") - } - def packageMappingWithRename(mappings: (File, String)*): LinuxPackageMapping = { - val renamed = - for ((file, name) <- mappings) - yield file -> rename(name) - packageMapping(renamed: _*) - } - - Seq( - packageMappingWithRename((binaries ++ directories): _*) withUser user withGroup group withPerms "0755", - packageMappingWithRename(compressedManPages: _*).gzipped withUser user withGroup group withPerms "0644", - packageMappingWithRename(configFiles: _*) withConfig () withUser user withGroup group withPerms "0644", - packageMappingWithRename(remaining: _*) withUser user withGroup group withPerms "0644") - } - - def mapGenericFilesToLinux: Seq[Setting[_]] = Seq( - - // First we look at the src/linux files - linuxPackageMappings <++= (sourceDirectory in Linux) map { dir => - mapGenericMappingsToLinux(MappingsHelper contentOf dir, Users.Root, Users.Root)(identity) - }, - // Now we look at the src/universal files. - linuxPackageMappings <++= (packageName in Linux, mappings in Universal, defaultLinuxInstallLocation) map { - (pkg, mappings, installLocation) => - // TODO - More windows filters... - def isWindowsFile(f: (File, String)): Boolean = - f._2 endsWith ".bat" - - mapGenericMappingsToLinux(mappings filterNot isWindowsFile, Users.Root, Users.Root) { name => - installLocation + "/" + pkg + "/" + name - } - }, - // Now we generate symlinks. - linuxPackageSymlinks <++= (packageName in Linux, mappings in Universal, defaultLinuxInstallLocation) map { (pkg, mappings, installLocation) => - for { - (file, name) <- mappings - if !file.isDirectory - if name startsWith "bin/" - if !(name endsWith ".bat") // IGNORE windows-y things. - } yield LinuxSymlink("/usr/" + name, installLocation + "/" + pkg + "/" + name) - }, - // Map configuration files - linuxPackageSymlinks <++= (packageName in Linux, mappings in Universal, defaultLinuxInstallLocation, defaultLinuxConfigLocation) - map { (pkg, mappings, installLocation, configLocation) => - val needsConfLink = - mappings exists { - case (file, name) => - (name startsWith "conf/") && !file.isDirectory - } - if (needsConfLink) Seq(LinuxSymlink( - link = configLocation + "/" + pkg, - destination = installLocation + "/" + pkg + "/conf")) - else Seq.empty - }) - - def mapGenericFilesToWindows: Seq[Setting[_]] = Seq( - mappings in Windows <<= mappings in Universal, - wixFeatures <<= (name in Windows, mappings in Windows) map makeWindowsFeatures) - // TODO select main script! Filter Config links! - def makeWindowsFeatures(name: String, mappings: Seq[(File, String)]): Seq[windows.WindowsFeature] = { - import windows._ - - val files = - for { - (file, name) <- mappings - if !file.isDirectory - } yield ComponentFile(name, editable = (name startsWith "conf")) - val corePackage = - WindowsFeature( - id = WixHelper.cleanStringForId(name + "_core").takeRight(38), // Must be no longer - title = name, - desc = "All core files.", - absent = "disallow", - components = files) - // TODO - Detect bat files to add paths... - val addBinToPath = - // TODO - we may have issues here... - WindowsFeature( - id = "AddBinToPath", - title = "Update Enviornment Variables", - desc = "Update PATH environment variables (requires restart).", - components = Seq(AddDirectoryToPath("bin"))) - val configLinks = for { - (file, name) <- mappings - if !file.isDirectory - if name startsWith "conf/" - } yield name.replaceAll("//", "/").stripSuffix("/").stripSuffix("/") - val menuLinks = - WindowsFeature( - id = "AddConfigLinks", - title = "Configuration start menu links", - desc = "Adds start menu shortcuts to edit configuration files.", - components = Seq(AddShortCuts(configLinks))) - // TODO - Add feature for shortcuts to binary scripts. - Seq(corePackage, addBinToPath, menuLinks) - } - -} \ No newline at end of file diff --git a/src/main/scala/com/typesafe/sbt/packager/Keys.scala b/src/main/scala/com/typesafe/sbt/packager/Keys.scala index 053ab5372..468774ad9 100644 --- a/src/main/scala/com/typesafe/sbt/packager/Keys.scala +++ b/src/main/scala/com/typesafe/sbt/packager/Keys.scala @@ -3,37 +3,46 @@ package packager import sbt._ -object Keys extends linux.Keys - with debian.DebianKeys - with rpm.RpmKeys - with windows.WindowsKeys - with docker.DockerKeys - with universal.UniversalKeys { +/** + * General purpose keys for the native packager + */ +trait NativePackagerKeys { - // These keys are used by the JavaApp/JavaServer archetypes. val packageName = SettingKey[String]("packageName", "Name of the created output package. Used for dirs/scripts.") - val executableScriptName = SettingKey[String]("executableScriptName", "Name of the executing script.") - val makeBashScript = TaskKey[Option[File]]("makeBashScript", "Creates or discovers the bash script used by this project.") - val bashScriptDefines = TaskKey[Seq[String]]("bashScriptDefines", "A list of definitions that should be written to the bash file template.") - val bashScriptExtraDefines = TaskKey[Seq[String]]("bashScriptExtraDefines", "A list of extra definitions that should be written to the bash file template.") - val bashScriptConfigLocation = TaskKey[Option[String]]("bashScriptConfigLocation", "The location where the bash script will load default argument configuration from.") - val batScriptExtraDefines = TaskKey[Seq[String]]("batScriptExtraDefines", "A list of extra definitions that should be written to the bat file template.") - val scriptClasspathOrdering = TaskKey[Seq[(File, String)]]("scriptClasspathOrdering", "The order of the classpath used at runtime for the bat/bash scripts.") - val projectDependencyArtifacts = TaskKey[Seq[Attributed[File]]]("projectDependencyArtifacts", "The set of exported artifacts from our dependent projects.") - val scriptClasspath = TaskKey[Seq[String]]("scriptClasspath", "A list of relative filenames (to the lib/ folder in the distribution) of what to include on the classpath.") - val makeBatScript = TaskKey[Option[File]]("makeBatScript", "Creates or discovers the bat script used by this project.") - val batScriptReplacements = TaskKey[Seq[(String, String)]]("batScriptReplacements", - """|Replacements of template parameters used in the windows bat script. - | Default supported templates: - | APP_ENV_NAME - the name of the application for defining _HOME variables - | APP_NAME - the name of the app - | APP_DEFINES - the defines to go into the app - | """.stripMargin) + val packageSummary = SettingKey[String]("package-summary", "Summary of the contents of a linux package.") + val packageDescription = SettingKey[String]("package-description", "The description of the package. Used when searching.") + val maintainer = SettingKey[String]("maintainer", "The name/email address of a maintainer for the native package.") - // TODO put these into the linux plugin - val defaultLinuxInstallLocation = SettingKey[String]("defaultLinuxInstallLocation", "The location where we will install generic linux packages.") - val defaultLinuxLogsLocation = SettingKey[String]("defaultLinuxLogsLocation", "The location where application logs will be stored.") - val defaultLinuxConfigLocation = SettingKey[String]("defaultLinuxConfigLocation", "The location where application config files will be stored") - val defaultLinuxStartScriptLocation = SettingKey[String]("defaultLinuxStartScriptLocation", "The location where start script for server application will be stored") + val executableScriptName = SettingKey[String]("executableScriptName", "Name of the executing script.") } + +/** + * This Keys object can be used for + *
    + *
  • non autoplugin builds
  • + *
  • import single keys, which are not inside the autoImport
  • + *
+ * + * == Non autoplugin builds == + * + * {{{ + * import com.typesafe.sbt.packager.Keys._ + * + * packageName := "" + * }}} + * + * == autoplugin builds == + * + * {{{ + * NativePackagerKeys.packageName := "" + * }}} + */ +object Keys extends NativePackagerKeys + with universal.UniversalKeys + with linux.LinuxKeys + with windows.WindowsKeys + with docker.DockerKeys + with debian.DebianKeys + with rpm.RpmKeys + with archetypes.JavaAppKeys \ No newline at end of file diff --git a/src/main/scala/com/typesafe/sbt/packager/Stager.scala b/src/main/scala/com/typesafe/sbt/packager/Stager.scala new file mode 100644 index 000000000..3c3114a60 --- /dev/null +++ b/src/main/scala/com/typesafe/sbt/packager/Stager.scala @@ -0,0 +1,43 @@ +package com.typesafe.sbt.packager + +import sbt._ +import sbt.Keys.TaskStreams +import java.io.File + +object Stager { + + /** + * create a cache and sync files if needed + * + * @param config - create a configuration specific cache directory + * @param cacheDirectory - e.g. streams.value.cacheDirectory + * @param stageDirectory - staging directory + * @param mappings - staging content + * + * @example {{{ + * + * }}} + */ + def stageFiles(config: String)(cacheDirectory: File, stageDirectory: File, mappings: Seq[(File, String)]): File = { + val cache = cacheDirectory / ("packager-mappings-" + config) + val copies = mappings map { + case (file, path) => file -> (stageDirectory / path) + } + Sync(cache, FileInfo.hash, FileInfo.exists)(copies) + // Now set scripts to executable using Java's lack of understanding of permissions. + // TODO - Config file user-readable permissions.... + for { + (from, to) <- copies + if from.canExecute + } to.setExecutable(true) + stageDirectory + } + + /** + * @see stageFiles + */ + def stage(config: String)(streams: TaskStreams, stageDirectory: File, mappings: Seq[(File, String)]): File = { + stageFiles(config)(streams.cacheDirectory, stageDirectory, mappings) + } + +} \ No newline at end of file diff --git a/src/main/scala/com/typesafe/sbt/packager/archetypes/AkkaApp.scala b/src/main/scala/com/typesafe/sbt/packager/archetypes/AkkaApp.scala index 30d62b01c..0578263ad 100644 --- a/src/main/scala/com/typesafe/sbt/packager/archetypes/AkkaApp.scala +++ b/src/main/scala/com/typesafe/sbt/packager/archetypes/AkkaApp.scala @@ -2,10 +2,49 @@ package com.typesafe.sbt package packager package archetypes +import sbt._ +import sbt.Keys.{ target, sourceDirectory } + +import packager.Keys.{ executableScriptName } +import SbtNativePackager.Universal + /** - * Created by max.cai on 2014-09-27. + * Provides a new default script for akka-micro-kernel applications. + * This plugin requires the [[com.typesafe.sbt.packager.archetypes.JavaAppPackaging]], + * which will be automatically enabled. + * + * @see [[http://doc.akka.io/docs/akka/snapshot/scala/microkernel.html]] + * @see [[https://github.com/sbt/sbt-native-packager/pull/363]] + * + * @example Enable this plugin in your `build.sbt` with + * + * {{{ + * enablePlugins(AkkaAppPackaging) + * }}} + * + * */ -object AkkaApp extends JavaApp { +object AkkaAppPackaging extends AutoPlugin with JavaAppStartScript { + + /** + * Name of the bash template if user wants to provide custom one + */ val bashTemplate = "akka-bash-template" + + /** + * Name of the bat template if user wants to provide custom one + */ val batTemplate = "akka-bat-template" + + override def requires = JavaAppPackaging + + override def projectSettings = settings + + import JavaAppPackaging.autoImport._ + + private def settings: Seq[Setting[_]] = Seq( + makeBashScript <<= (bashScriptDefines, target in Universal, executableScriptName, sourceDirectory) map makeUniversalBinScript(bashTemplate), + makeBatScript <<= (batScriptReplacements, target in Universal, executableScriptName, sourceDirectory) map makeUniversalBatScript(batTemplate) + ) + } diff --git a/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaApp.scala b/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaApp.scala index a4ca7c991..1a18f2724 100644 --- a/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaApp.scala +++ b/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaApp.scala @@ -2,32 +2,50 @@ package com.typesafe.sbt package packager package archetypes -import Keys._ import sbt._ -import sbt.Project.Initialize import sbt.Keys.{ mappings, target, name, mainClass, sourceDirectory } -import com.typesafe.sbt.packager.linux.{ LinuxFileMetaData, LinuxPackageMapping } -import SbtNativePackager._ +import packager.Keys.{ packageName, executableScriptName } +import linux.{ LinuxFileMetaData, LinuxPackageMapping } +import linux.LinuxPlugin.autoImport.{linuxPackageMappings, defaultLinuxInstallLocation} +import SbtNativePackager.{ Universal, Debian } /** + * == Java Application == + * * This class contains the default settings for creating and deploying an archetypical Java application. - * A Java application archetype is defined as a project that has a main method and is run by placing - * all of its JAR files on the classpath and calling that main method. + * A Java application archetype is defined as a project that has a main method and is run by placing + * all of its JAR files on the classpath and calling that main method. + * + * == Configuration == + * + * This plugin adds new settings to configure your packaged application. + * The keys are defined in [[com.typesafe.sbt.packager.archetypes.JavaAppKeys]] * - * This doesn't create the best of distributions, but it can simplify the distribution of code. + * @example Enable this plugin in your `build.sbt` with * - * **NOTE: EXPERIMENTAL** This currently only supports universal distributions. + * {{{ + * enablePlugins(JavaAppPackaging) + * }}} */ -object JavaAppPackaging extends JavaApp { +object JavaAppPackaging extends AutoPlugin with JavaAppStartScript { + + /** + * Name of the bash template if user wants to provide custom one + */ val bashTemplate = "bash-template" + + /** + * Name of the bat template if user wants to provide custom one + */ val batTemplate = "bat-template" -} -trait JavaApp { - val bashTemplate: String - val batTemplate: String + object autoImport extends JavaAppKeys - def settings: Seq[Setting[_]] = Seq( + import JavaAppPackaging.autoImport._ + + override def requires = debian.DebianPlugin && rpm.RpmPlugin && docker.DockerPlugin && windows.WindowsPlugin + + override def projectSettings = Seq( // Here we record the classpath as it's added to the mappings separately, so // we can use its order to generate the bash/bat scripts. scriptClasspathOrdering := Nil, @@ -52,7 +70,7 @@ trait JavaApp { hasMain getOrElse Nil }, // TODO - Overridable bash template. - makeBashScript <<= (bashScriptDefines, target in Universal, executableScriptName, sourceDirectory) map makeUniversalBinScript, + makeBashScript <<= (bashScriptDefines, target in Universal, executableScriptName, sourceDirectory) map makeUniversalBinScript(bashTemplate), batScriptExtraDefines := Nil, batScriptReplacements <<= (packageName, Keys.mainClass in Compile, scriptClasspath, batScriptExtraDefines) map { (name, mainClass, cp, extras) => mainClass map { mc => @@ -60,7 +78,7 @@ trait JavaApp { } getOrElse Nil }, - makeBatScript <<= (batScriptReplacements, target in Universal, executableScriptName, sourceDirectory) map makeUniversalBatScript, + makeBatScript <<= (batScriptReplacements, target in Universal, executableScriptName, sourceDirectory) map makeUniversalBatScript(batTemplate), mappings in Universal <++= (makeBashScript, executableScriptName) map { (script, name) => for { s <- script.toSeq @@ -79,7 +97,7 @@ trait JavaApp { LinuxPackageMapping(Seq(d -> (installLocation + "/" + name)), LinuxFileMetaData()) }) - def makeRelativeClasspathNames(mappings: Seq[(File, String)]): Seq[String] = + private def makeRelativeClasspathNames(mappings: Seq[(File, String)]): Seq[String] = for { (file, name) <- mappings } yield { @@ -89,41 +107,8 @@ trait JavaApp { else "../" + name } - def makeUniversalBinScript(defines: Seq[String], tmpDir: File, name: String, sourceDir: File): Option[File] = - if (defines.isEmpty) None - else { - val defaultTemplateLocation = sourceDir / "templates" / bashTemplate - val defaultTemplateSource = getClass.getResource(bashTemplate) - - val template = if (defaultTemplateLocation.exists) - defaultTemplateLocation.toURI.toURL - else defaultTemplateSource - - val scriptBits = JavaAppBashScript.generateScript(defines, template) - val script = tmpDir / "tmp" / "bin" / name - IO.write(script, scriptBits) - // TODO - Better control over this! - script.setExecutable(true) - Some(script) - } - - def makeUniversalBatScript(replacements: Seq[(String, String)], tmpDir: File, name: String, sourceDir: File): Option[File] = - if (replacements.isEmpty) None - else { - val defaultTemplateLocation = sourceDir / "templates" / batTemplate - val defaultTemplateSource = getClass.getResource(batTemplate) - val template = if (defaultTemplateLocation.exists) - defaultTemplateLocation.toURI.toURL - else defaultTemplateSource - - val scriptBits = JavaAppBatScript.generateScript(replacements, template) - val script = tmpDir / "tmp" / "bin" / (name + ".bat") - IO.write(script, scriptBits) - Some(script) - } - // Constructs a jar name from components...(ModuleID/Artifact) - def makeJarName(org: String, name: String, revision: String, artifactName: String, artifactClassifier: Option[String]): String = + private def makeJarName(org: String, name: String, revision: String, artifactName: String, artifactClassifier: Option[String]): String = (org + "." + name + "-" + Option(artifactName.replace(name, "")).filterNot(_.isEmpty).map(_ + "-").getOrElse("") + @@ -133,7 +118,7 @@ trait JavaApp { // Determines a nicer filename for an attributed jar file, using the // ivy metadata if available. - def getJarFullFilename(dep: Attributed[File]): String = { + private def getJarFullFilename(dep: Attributed[File]): String = { val filename: Option[String] = for { module <- dep.metadata.get(AttributeKey[ModuleID]("module-id")) artifact <- dep.metadata.get(AttributeKey[Artifact]("artifact")) @@ -142,17 +127,17 @@ trait JavaApp { } // Here we grab the dependencies... - def dependencyProjectRefs(build: sbt.BuildDependencies, thisProject: ProjectRef): Seq[ProjectRef] = + private def dependencyProjectRefs(build: sbt.BuildDependencies, thisProject: ProjectRef): Seq[ProjectRef] = build.classpathTransitive.get(thisProject).getOrElse(Nil) - def filterArtifacts(artifacts: Seq[(Artifact, File)], config: Option[String]): Seq[(Artifact, File)] = + private def filterArtifacts(artifacts: Seq[(Artifact, File)], config: Option[String]): Seq[(Artifact, File)] = for { (art, file) <- artifacts // TODO - Default to compile or default? if art.configurations.exists(_.name == config.getOrElse("default")) } yield art -> file - def extractArtifacts(stateTask: Task[State], ref: ProjectRef): Task[Seq[Attributed[File]]] = + private def extractArtifacts(stateTask: Task[State], ref: ProjectRef): Task[Seq[Attributed[File]]] = stateTask flatMap { state => val extracted = Project extract state // TODO - Is this correct? @@ -172,13 +157,13 @@ trait JavaApp { } // TODO - Should we pull in more than just JARs? How do native packages come in? - def isRuntimeArtifact(dep: Attributed[File]): Boolean = + private def isRuntimeArtifact(dep: Attributed[File]): Boolean = dep.get(sbt.Keys.artifact.key).map(_.`type` == "jar").getOrElse { val name = dep.data.getName !(name.endsWith(".jar") || name.endsWith("-sources.jar") || name.endsWith("-javadoc.jar")) } - def findProjectDependencyArtifacts: Def.Initialize[Task[Seq[Attributed[File]]]] = + private def findProjectDependencyArtifacts: Def.Initialize[Task[Seq[Attributed[File]]]] = (sbt.Keys.buildDependencies, sbt.Keys.thisProjectRef, sbt.Keys.state) apply { (build, thisProject, stateTask) => val refs = thisProject +: dependencyProjectRefs(build, thisProject) // Dynamic lookup of dependencies... @@ -193,7 +178,7 @@ trait JavaApp { allArtifactsTask } - def findRealDep(dep: Attributed[File], projectArts: Seq[Attributed[File]]): Option[Attributed[File]] = { + private def findRealDep(dep: Attributed[File], projectArts: Seq[Attributed[File]]): Option[Attributed[File]] = { if (dep.data.isFile) Some(dep) else { projectArts.find { art => @@ -211,9 +196,73 @@ trait JavaApp { } // Converts a managed classpath into a set of lib mappings. - def universalDepMappings(deps: Seq[Attributed[File]], projectArts: Seq[Attributed[File]]): Seq[(File, String)] = + private def universalDepMappings(deps: Seq[Attributed[File]], projectArts: Seq[Attributed[File]]): Seq[(File, String)] = for { dep <- deps realDep <- findRealDep(dep, projectArts) } yield realDep.data -> ("lib/" + getJarFullFilename(realDep)) -} \ No newline at end of file +} + +/** + * Mixin this trait to generate startup scripts provided in the classpath of native packager. + * + * @example A simple plugin definition could look like this + * + * {{{ + * object AkkaAppPackaging extends AutoPlugin with JavaAppStartScript { + * // templates have to be placed inside the com/typesafe/sbt.packager/archetypes/ resource folder + * // the name is also used to find user-defined scripts + * val bashTemplate = "your-bash-template" + * val batTemplate = "your-bat-template" + * + * override def requires = JavaAppPackaging + * + * override def projectSettings = settings + * + * import JavaAppPackaging.autoImport._ + * + * private def settings: Seq[Setting[_]] = Seq( + * makeBashScript <<= (bashScriptDefines, target in Universal, executableScriptName, sourceDirectory) map makeUniversalBinScript(bashTemplate), + * makeBatScript <<= (batScriptReplacements, target in Universal, executableScriptName, sourceDirectory) map makeUniversalBatScript(batTemplate) + * ) + * } + * + * }}} + */ +trait JavaAppStartScript { + + def makeUniversalBinScript(bashTemplate: String)(defines: Seq[String], tmpDir: File, name: String, sourceDir: File): Option[File] = + if (defines.isEmpty) None + else { + val defaultTemplateLocation = sourceDir / "templates" / bashTemplate + val defaultTemplateSource = getClass getResource bashTemplate + + val template = if (defaultTemplateLocation.exists) + defaultTemplateLocation.toURI.toURL + else defaultTemplateSource + + val scriptBits = JavaAppBashScript.generateScript(defines, template) + val script = tmpDir / "tmp" / "bin" / name + IO.write(script, scriptBits) + // TODO - Better control over this! + script.setExecutable(true) + Some(script) + } + + def makeUniversalBatScript(batTemplate: String)(replacements: Seq[(String, String)], tmpDir: File, name: String, sourceDir: File): Option[File] = + if (replacements.isEmpty) None + else { + val defaultTemplateLocation = sourceDir / "templates" / batTemplate + val defaultTemplateSource = getClass.getResource(batTemplate) + val template = if (defaultTemplateLocation.exists) + defaultTemplateLocation.toURI.toURL + else defaultTemplateSource + + val scriptBits = JavaAppBatScript.generateScript(replacements, template) + val script = tmpDir / "tmp" / "bin" / (name + ".bat") + IO.write(script, scriptBits) + Some(script) + } + +} + diff --git a/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaAppKeys.scala b/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaAppKeys.scala new file mode 100644 index 000000000..53c14d092 --- /dev/null +++ b/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaAppKeys.scala @@ -0,0 +1,27 @@ +package com.typesafe.sbt.packager.archetypes + +import sbt._ + +/** + * Available settings/tasks for the [[com.typesafe.sbt.packager.archetypes.JavaAppPackaging]] + * and all depending archetypes. + */ +trait JavaAppKeys { + + val makeBashScript = TaskKey[Option[File]]("makeBashScript", "Creates or discovers the bash script used by this project.") + val bashScriptDefines = TaskKey[Seq[String]]("bashScriptDefines", "A list of definitions that should be written to the bash file template.") + val bashScriptExtraDefines = TaskKey[Seq[String]]("bashScriptExtraDefines", "A list of extra definitions that should be written to the bash file template.") + val bashScriptConfigLocation = TaskKey[Option[String]]("bashScriptConfigLocation", "The location where the bash script will load default argument configuration from.") + val batScriptExtraDefines = TaskKey[Seq[String]]("batScriptExtraDefines", "A list of extra definitions that should be written to the bat file template.") + val scriptClasspathOrdering = TaskKey[Seq[(File, String)]]("scriptClasspathOrdering", "The order of the classpath used at runtime for the bat/bash scripts.") + val projectDependencyArtifacts = TaskKey[Seq[Attributed[File]]]("projectDependencyArtifacts", "The set of exported artifacts from our dependent projects.") + val scriptClasspath = TaskKey[Seq[String]]("scriptClasspath", "A list of relative filenames (to the lib/ folder in the distribution) of what to include on the classpath.") + val makeBatScript = TaskKey[Option[File]]("makeBatScript", "Creates or discovers the bat script used by this project.") + val batScriptReplacements = TaskKey[Seq[(String, String)]]("batScriptReplacements", + """|Replacements of template parameters used in the windows bat script. + | Default supported templates: + | APP_ENV_NAME - the name of the application for defining _HOME variables + | APP_NAME - the name of the app + | APP_DEFINES - the defines to go into the app + | """.stripMargin) +} diff --git a/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaServerApplication.scala b/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaServerApplication.scala index a35d4eeea..a7c8e5528 100644 --- a/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaServerApplication.scala +++ b/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaServerApplication.scala @@ -2,13 +2,17 @@ package com.typesafe.sbt package packager package archetypes -import Keys._ import sbt._ import sbt.Keys.{ target, mainClass, sourceDirectory, streams } -import SbtNativePackager._ -import com.typesafe.sbt.packager.linux.{ LinuxFileMetaData, LinuxPackageMapping, LinuxSymlink, LinuxPlugin } -import com.typesafe.sbt.packager.debian.DebianPlugin -import com.typesafe.sbt.packager.rpm.RpmPlugin +import SbtNativePackager.{ Debian, Rpm, Universal } +import packager.Keys.{ packageName } +import linux.{ LinuxFileMetaData, LinuxPackageMapping, LinuxSymlink, LinuxPlugin } +import linux.LinuxPlugin.autoImport._ +import debian.DebianPlugin +import debian.DebianPlugin.autoImport.{ debianMakePreinstScript, debianMakePostinstScript, debianMakePrermScript, debianMakePostrmScript } +import rpm.RpmPlugin +import rpm.RpmPlugin.autoImport.{ rpmPre, rpmPost, rpmPostun, rpmPreun, rpmScriptsDirectory } +import JavaAppPackaging.autoImport.{ bashScriptConfigLocation } /** * This class contains the default settings for creating and deploying an archetypical Java application. @@ -19,14 +23,18 @@ import com.typesafe.sbt.packager.rpm.RpmPlugin * * **NOTE: EXPERIMENTAL** This currently only supports debian upstart scripts. */ -object JavaServerAppPackaging { +object JavaServerAppPackaging extends AutoPlugin { import ServerLoader._ import LinuxPlugin.Users + override def requires = JavaAppPackaging + + override def projectSettings = javaServerSettings + val ARCHETYPE = "java_server" /** These settings will be provided by this archetype*/ - def settings: Seq[Setting[_]] = JavaAppPackaging.settings ++ linuxSettings ++ debianSettings ++ rpmSettings + def javaServerSettings: Seq[Setting[_]] = linuxSettings ++ debianSettings ++ rpmSettings protected def etcDefaultTemplateSource: java.net.URL = getClass.getResource("etc-default-template") diff --git a/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaServerBashScript.scala b/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaServerBashScript.scala index 1301c0570..e305fea8a 100644 --- a/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaServerBashScript.scala +++ b/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaServerBashScript.scala @@ -3,10 +3,33 @@ package com.typesafe.sbt.packager.archetypes import sbt._ import com.typesafe.sbt.packager.archetypes.ServerLoader._ +/** + * Loads scripts from the resource path that are associated with + *
    + *
  • an archetype
  • + *
  • a sbt.Configuration
  • + *
+ * + * @example + * {{{ + * val scriptName: String = "postrm" + * val archetype: String = "java_server" + * val config: Configuration = SbtNativePackager.Debian + * val replacements: Seq[(String,String)] = linuxScriptReplacements.value + * val template: Option[URL] = None // user defined override + * + * val scriptContent = JavaServerBashScript(scriptName, archetype, config, replacements, template) getOrElse { + * sys.error(s"Couldn't load [scriptName] for config [{config.name}] in archetype [archetype]") + * } + * IO.write(scriptFile, scriptContent) + * }}} + * @see [[com.typesafe.sbt.packager.archetypes.JavaServerAppPackaging]] + */ object JavaServerBashScript { /** * + * @param script - script name * @param templateName - DebianPlugin.Names for maintainer scripts and "start" * @param loader - which startup system * @param replacements - default replacements diff --git a/src/main/scala/com/typesafe/sbt/packager/archetypes/ServerLoader.scala b/src/main/scala/com/typesafe/sbt/packager/archetypes/ServerLoader.scala index 89158fc2e..d6728a47b 100644 --- a/src/main/scala/com/typesafe/sbt/packager/archetypes/ServerLoader.scala +++ b/src/main/scala/com/typesafe/sbt/packager/archetypes/ServerLoader.scala @@ -3,6 +3,11 @@ package com.typesafe.sbt.packager.archetypes import java.io.File import java.net.URL +/** + * Stores the available types of server loaders. + * + * @note not all packaging systems support all server loaders + */ object ServerLoader extends Enumeration { type ServerLoader = Value val Upstart = Value("upstart") diff --git a/src/main/scala/com/typesafe/sbt/packager/archetypes/TemplateWriter.scala b/src/main/scala/com/typesafe/sbt/packager/archetypes/TemplateWriter.scala index 76aeeb7eb..f9a7fef28 100644 --- a/src/main/scala/com/typesafe/sbt/packager/archetypes/TemplateWriter.scala +++ b/src/main/scala/com/typesafe/sbt/packager/archetypes/TemplateWriter.scala @@ -1,5 +1,27 @@ package com.typesafe.sbt.packager.archetypes +/** + * This object provides methods to generate scripts from templates. This involves + * + *
    + *
  1. procesing - replacing a placeholders with actual values
  2. + *
  3. TODO: validating - check the script if there are no remaining placeholders
  4. + *
+ * + * @example a bash script can be generated like this + * {{{ + * val template = getClass getResource "template-your-bashscript" + * val replacements = Seq("name" -> "your-app", "custom" -> "1") + * TemplateWriter.generateScript(template, replacements) + * }}} + * + * @example a bat script can be generated like this + * {{{ + * val template = getClass getResource "template-your-batscript" + * val replacements = Seq("name" -> "your-app", "custom" -> "1") + * TemplateWriter.generateScript(template, replacements, "\r\n", TemplateWriter.batFriendlyKeySurround) + * }}} + */ object TemplateWriter { def defaultCharset: java.nio.charset.Charset = java.nio.charset.Charset.forName("UTF-8") diff --git a/src/main/scala/com/typesafe/sbt/packager/debian/DebianPlugin.scala b/src/main/scala/com/typesafe/sbt/packager/debian/DebianPlugin.scala index d18200fe1..c37969cb2 100644 --- a/src/main/scala/com/typesafe/sbt/packager/debian/DebianPlugin.scala +++ b/src/main/scala/com/typesafe/sbt/packager/debian/DebianPlugin.scala @@ -2,26 +2,91 @@ package com.typesafe.sbt package packager package debian -import Keys._ import sbt._ -import sbt.Keys.{ target, name, TaskStreams } +import sbt.Keys.{ streams, name, version, sourceDirectory, target, packageBin, TaskStreams } +import packager.Keys._ +import packager.Hashing +import linux.LinuxPlugin.autoImport.{ + packageArchitecture, + linuxScriptReplacements, + linuxPackageMappings, + linuxPackageSymlinks, + serverLoading, + daemonShell +} import linux.{ LinuxFileMetaData, LinuxPackageMapping, LinuxSymlink } -import linux.Keys.{ linuxScriptReplacements, daemonShell } -import com.typesafe.sbt.packager.Hashing -import com.typesafe.sbt.packager.archetypes.TemplateWriter -import com.typesafe.sbt.packager.linux.LinuxPackageMapping +import linux.LinuxPlugin.Users +import universal.Archives +import archetypes.TemplateWriter +import SbtNativePackager.{ Universal, Linux } -trait DebianPlugin extends Plugin with linux.LinuxPlugin with NativePackaging with JDebPackaging { - val Debian = config("debian") extend Linux - val UserNamePattern = "^[a-z][-a-z0-9_]*$".r +/** + * == Debian Plugin == + * + * This plugin provides the ability to build ''.deb'' packages. + * + * == Configuration == + * + * In order to configure this plugin take a look at the available [[com.typesafe.sbt.packager.debian.DebianKeys]] + * + * @example Enable the plugin in the `build.sbt`. By default this will use + * the native debian packaging implementation [[com.typesafe.sbt.packager.debian.DebianNativePackaging]]. + * {{{ + * enablePlugins(DebianPlugin) + * }}} + * + */ +object DebianPlugin extends AutoPlugin with DebianNativePackaging { + + override def requires = linux.LinuxPlugin + override def trigger = allRequirements + + object autoImport extends DebianKeys { + val Debian = config("debian") extend Linux + } + + import autoImport._ + + /** Debian constants */ + object Names { + val DebianSource = "debian" + val Debian = "DEBIAN" + + //maintainer script names + val Postinst = "postinst" + val Postrm = "postrm" + val Prerm = "prerm" + val Preinst = "preinst" + + val Control = "control" + val Conffiles = "conffiles" + + val Changelog = "changelog" + val Files = "files" + } - import com.typesafe.sbt.packager.universal.Archives - import DebianPlugin.Names - import DebianPlugin.defaultMaintainerScript - import linux.LinuxPlugin.Users - import SbtNativePackager.Universal + val CHOWN_REPLACEMENT = "chown-paths" - def debianSettings: Seq[Setting[_]] = Seq( + def defaultMaintainerScript(name: String, replacements: Seq[(String, String)], tmpDir: File): Option[File] = { + val url = Option(getClass getResource s"$name-template") + url map { source => + val scriptBits = TemplateWriter.generateScript(source, replacements) + val script = tmpDir / "tmp" / "etc" / "default" / name + IO.write(script, scriptBits) + script + } + } + + // TODO maybe we can put settings/debiansettings together + /** + * Enables native packaging by default + */ + override lazy val projectSettings = settings ++ inConfig(Debian)(debianSettings) ++ debianNativeSettings + + /** + * the default debian settings for the debian namespaced settings + */ + private def settings = Seq( /* ==== Debian default settings ==== */ debianPriority := "optional", debianSection := "java", @@ -51,110 +116,124 @@ trait DebianPlugin extends Plugin with linux.LinuxPlugin with NativePackaging wi debianMaintainerScripts <++= (debianMakePrermScript, debianControlScriptsDirectory) map scriptMapping(Names.Prerm), debianMaintainerScripts <++= (debianMakePreinstScript, debianControlScriptsDirectory) map scriptMapping(Names.Preinst), debianMaintainerScripts <++= (debianMakePostinstScript, debianControlScriptsDirectory) map scriptMapping(Names.Postinst), - debianMaintainerScripts <++= (debianMakePostrmScript, debianControlScriptsDirectory) map scriptMapping(Names.Postrm)) ++ - /* ==== Debian scoped settings ==== */ - inConfig(Debian)( - Seq( - packageArchitecture := "all", - debianPackageInfo <<= - (packageName, version, maintainer, packageSummary, packageDescription) apply PackageInfo, - debianPackageMetadata <<= - (debianPackageInfo, debianPriority, packageArchitecture, debianSection, - debianPackageDependencies, debianPackageRecommends) apply PackageMetaData, - debianPackageInstallSize <<= linuxPackageMappings map { mappings => - (for { - LinuxPackageMapping(files, _, zipped) <- mappings - (file, _) <- files - if !file.isDirectory && file.exists - // TODO - If zipped, heuristically figure out a reduction factor. - } yield file.length).sum / 1024 - }, - debianControlFile <<= (debianPackageMetadata, debianPackageInstallSize, target) map { - (data, size, dir) => - if (data.info.description == null || data.info.description.isEmpty) { - sys.error( - """packageDescription in Debian cannot be empty. Use + debianMaintainerScripts <++= (debianMakePostrmScript, debianControlScriptsDirectory) map scriptMapping(Names.Postrm)) + + /** + * == Debian scoped settings == + * Everything used inside the debian scope + * + */ + private def debianSettings: Seq[Setting[_]] = inConfig(Debian)( + Seq( + packageArchitecture := "all", + debianPackageInfo <<= (packageName, version, maintainer, packageSummary, packageDescription) apply PackageInfo, + debianPackageMetadata <<= (debianPackageInfo, debianPriority, packageArchitecture, debianSection, + debianPackageDependencies, debianPackageRecommends) apply PackageMetaData, + debianPackageInstallSize <<= linuxPackageMappings map { mappings => + (for { + LinuxPackageMapping(files, _, zipped) <- mappings + (file, _) <- files + if !file.isDirectory && file.exists + // TODO - If zipped, heuristically figure out a reduction factor. + } yield file.length).sum / 1024 + }, + debianControlFile <<= (debianPackageMetadata, debianPackageInstallSize, target) map { + (data, size, dir) => + if (data.info.description == null || data.info.description.isEmpty) { + sys.error( + """packageDescription in Debian cannot be empty. Use packageDescription in Debian := "My package Description"""") - } - val cfile = dir / Names.Debian / Names.Control - IO.write(cfile, data.makeContent(size), java.nio.charset.Charset.defaultCharset) - chmod(cfile, "0644") - cfile - }, - debianConffilesFile <<= (linuxPackageMappings, target) map { - (mappings, dir) => - val cfile = dir / Names.Debian / Names.Conffiles - val conffiles = for { - LinuxPackageMapping(files, meta, _) <- mappings - if meta.config != "false" - (file, name) <- files - if file.isFile - } yield name - IO.writeLines(cfile, conffiles) - chmod(cfile, "0644") - cfile - }, - debianMD5sumsFile <<= (debianExplodedPackage, target) map { - (mappings, dir) => - val md5file = dir / Names.Debian / "md5sums" - val md5sums = for { - (file, name) <- (dir.*** --- dir pair relativeTo(dir)) - if file.isFile - if !(name startsWith Names.Debian) - if !(name contains "debian-binary") - // TODO - detect symlinks with Java7 (when we can) rather than hackery... - if file.getCanonicalPath == file.getAbsolutePath - fixedName = if (name startsWith "/") name drop 1 else name - } yield Hashing.md5Sum(file) + " " + fixedName - IO.writeLines(md5file, md5sums) - chmod(md5file, "0644") - md5file + } + val cfile = dir / Names.Debian / Names.Control + IO.write(cfile, data.makeContent(size), java.nio.charset.Charset.defaultCharset) + chmod(cfile, "0644") + cfile + }, + debianConffilesFile <<= (linuxPackageMappings, target) map { + (mappings, dir) => + val cfile = dir / Names.Debian / Names.Conffiles + val conffiles = for { + LinuxPackageMapping(files, meta, _) <- mappings + if meta.config != "false" + (file, name) <- files + if file.isFile + } yield name + IO.writeLines(cfile, conffiles) + chmod(cfile, "0644") + cfile + }, + debianMD5sumsFile <<= (debianExplodedPackage, target) map { + (mappings, dir) => + val md5file = dir / Names.Debian / "md5sums" + val md5sums = for { + (file, name) <- (dir.*** --- dir pair relativeTo(dir)) + if file.isFile + if !(name startsWith Names.Debian) + if !(name contains "debian-binary") + // TODO - detect symlinks with Java7 (when we can) rather than hackery... + if file.getCanonicalPath == file.getAbsolutePath + fixedName = if (name startsWith "/") name drop 1 else name + } yield Hashing.md5Sum(file) + " " + fixedName + IO.writeLines(md5file, md5sums) + chmod(md5file, "0644") + md5file + }, + debianMakeChownReplacements <<= (linuxPackageMappings, streams) map makeChownReplacements, + debianExplodedPackage <<= (linuxPackageMappings, debianControlFile, debianMaintainerScripts, debianConffilesFile, debianChangelog, daemonShell in Linux, + linuxScriptReplacements, debianMakeChownReplacements, linuxPackageSymlinks, target, streams) + map { (mappings, _, maintScripts, _, changelog, shell, replacements, chown, symlinks, t, streams) => + + // Create files and directories + mappings foreach { + case LinuxPackageMapping(paths, perms, zipped) => + val (dirs, files) = paths.partition(_._1.isDirectory) + dirs map { + case (_, name) => t / name + } foreach { targetDir => + targetDir mkdirs () + chmod(targetDir, perms.permissions) + } + + files map { + case (file, name) => (file, t / name) + } foreach { + case (source, target) => copyAndFixPerms(source, target, perms, zipped) + } + } + // Now generate relative symlinks + LinuxSymlink.makeSymLinks(symlinks, t, false) + + // Put the maintainer files in `dir / "DEBIAN"` named as specified. + // Valid values for the name are preinst,postinst,prerm,postrm + for ((file, name) <- maintScripts) { + val targetFile = t / Names.Debian / name + copyAndFixPerms(file, targetFile, LinuxFileMetaData()) + filterAndFixPerms(targetFile, chown +: replacements, LinuxFileMetaData()) + } + t }, - debianMakeChownReplacements <<= (linuxPackageMappings, streams) map makeChownReplacements, - debianExplodedPackage <<= (linuxPackageMappings, debianControlFile, debianMaintainerScripts, debianConffilesFile, debianChangelog, daemonShell in Linux, - linuxScriptReplacements, debianMakeChownReplacements, linuxPackageSymlinks, target, streams) - map { (mappings, _, maintScripts, _, changelog, shell, replacements, chown, symlinks, t, streams) => - - // Create files and directories - mappings foreach { - case LinuxPackageMapping(paths, perms, zipped) => - val (dirs, files) = paths.partition(_._1.isDirectory) - dirs map { - case (_, name) => t / name - } foreach { targetDir => - targetDir mkdirs () - chmod(targetDir, perms.permissions) - } - - files map { - case (file, name) => (file, t / name) - } foreach { - case (source, target) => copyAndFixPerms(source, target, perms, zipped) - } - } - // Now generate relative symlinks - LinuxSymlink.makeSymLinks(symlinks, t, false) - - // Put the maintainer files in `dir / "DEBIAN"` named as specified. - // Valid values for the name are preinst,postinst,prerm,postrm - for ((file, name) <- maintScripts) { - val targetFile = t / Names.Debian / name - copyAndFixPerms(file, targetFile, LinuxFileMetaData()) - filterAndFixPerms(targetFile, chown +: replacements, LinuxFileMetaData()) - } - t - }, - // Setting the packaging strategy - packageBin <<= debianNativePackaging, - // Replacement for ${{header}} as debian control scripts are bash scripts - linuxScriptReplacements += ("header" -> "#!/bin/sh\n") - - // Adding package specific implementation settings - ) ++ debianNativeSettings ++ debianJDebSettings) - - /* ============================================= */ - /* ========== Debian Helper Methods ============ */ - /* ============================================= */ + // Replacement for ${{header}} as debian control scripts are bash scripts + linuxScriptReplacements += ("header" -> "#!/bin/sh\n") + + // Adding package specific implementation settings + )) + +} + +/** + * == Debian Helper Methods == + * + * This trait provides a set of helper methods for debian packaging + * implementations. + * + * Most of the methods are for java 6 file permission handling and + * debian script adjustements. + * + */ +trait DebianPluginLike { + + /** validate group and usernames for debian systems */ + val UserNamePattern = "^[a-z][-a-z0-9_]*$".r private[debian] final def copyAndFixPerms(from: File, to: File, perms: LinuxFileMetaData, zipped: Boolean = false): Unit = { if (zipped) { @@ -218,14 +297,6 @@ trait DebianPlugin extends Plugin with linux.LinuxPlugin with NativePackaging wi } } - private[debian] def archiveFilename(appName: String, version: String, arch: String): String = { - appName + "_" + version + "_" + arch + ".deb" - } - - private[debian] def changesFilename(appName: String, version: String, arch: String): String = { - appName + "_" + version + "_" + arch + ".changes" - } - /** * Debian assumes the application chowns the necessary files and directories in the * control scripts (Pre/Postinst). @@ -239,12 +310,12 @@ trait DebianPlugin extends Plugin with linux.LinuxPlugin with NativePackaging wi * @return (CHOWN_REPLACEMENT -> ".. list of chown commands") */ private[debian] def makeChownReplacements(mappings: Seq[LinuxPackageMapping], streams: TaskStreams): (String, String) = { - // how to create the chownCmd. TODO maybe configurable? + // how to create the chownCmd. TODO maybe configurable? def chownCmd(user: String, group: String)(path: String): String = s"chown $user:$group $path" val header = "# Chown definitions created by SBT Native Packager\n" - // Check for non root user/group and create chown commands - // filter all root mappings, map to (user,group) key, group by, append everything + // Check for non root user/group and create chown commands + // filter all root mappings, map to (user,group) key, group by, append everything val chowns = mappings filter { case LinuxPackageMapping(_, LinuxFileMetaData(Users.Root, Users.Root, _, _, _), _) => false case _ => true @@ -255,45 +326,18 @@ trait DebianPlugin extends Plugin with linux.LinuxPlugin with NativePackaging wi validateUserGroupNames(user, streams) validateUserGroupNames(group, streams) val chown = chownCmd(user, group) _ - // remove key, flatten it and then use mapping path (_.2) to create chown command + // remove key, flatten it and then use mapping path (_.2) to create chown command pathList.map(_._2).flatten map (m => chown(m._2)) } val replacement = header :: chowns.flatten.toList mkString "\n" DebianPlugin.CHOWN_REPLACEMENT -> replacement } -} - -/** - * Contains debian specific constants - */ -object DebianPlugin { - object Names { - val DebianSource = "debian" - val Debian = "DEBIAN" - - //maintainer script names - val Postinst = "postinst" - val Postrm = "postrm" - val Prerm = "prerm" - val Preinst = "preinst" - - val Control = "control" - val Conffiles = "conffiles" - - val Changelog = "changelog" - val Files = "files" + private[debian] def archiveFilename(appName: String, version: String, arch: String): String = { + appName + "_" + version + "_" + arch + ".deb" } - val CHOWN_REPLACEMENT = "chown-paths" - - def defaultMaintainerScript(name: String, replacements: Seq[(String, String)], tmpDir: File): Option[File] = { - val url = Option(getClass getResource s"$name-template") - url map { source => - val scriptBits = TemplateWriter.generateScript(source, replacements) - val script = tmpDir / "tmp" / "etc" / "default" / name - IO.write(script, scriptBits) - script - } + private[debian] def changesFilename(appName: String, version: String, arch: String): String = { + appName + "_" + version + "_" + arch + ".changes" } } diff --git a/src/main/scala/com/typesafe/sbt/packager/debian/JDebPackaging.scala b/src/main/scala/com/typesafe/sbt/packager/debian/JDebPackaging.scala index 12e289840..ebb11305b 100644 --- a/src/main/scala/com/typesafe/sbt/packager/debian/JDebPackaging.scala +++ b/src/main/scala/com/typesafe/sbt/packager/debian/JDebPackaging.scala @@ -2,39 +2,45 @@ package com.typesafe.sbt package packager package debian -import Keys._ import sbt._ -import sbt.Keys.{ target, normalizedName } -import linux.{ LinuxSymlink } -import com.typesafe.sbt.packager.linux.LinuxPackageMapping +import sbt.Keys.{ target, normalizedName, version, streams, mappings, packageBin } +import linux.{ LinuxSymlink, LinuxPackageMapping } +import linux.LinuxPlugin.autoImport.{ linuxPackageMappings, linuxPackageSymlinks, packageArchitecture } import scala.collection.JavaConversions._ import org.vafer.jdeb.{ DebMaker, DataProducer } import org.vafer.jdeb.mapping._ import org.vafer.jdeb.producers._ -import DebianPlugin.Names +import DebianPlugin.{ Names } +import DebianPlugin.autoImport._ /** + * == JDeb Plugin == * This provides a java based debian packaging implementation based * on the jdeb maven-plugin. To use this, put this into your build.sbt - * - * {{ - * packageBin in Debian <<= debianJDebPackaging in Debian - * }} + * + * @example Enable the plugin in the `build.sbt` + * {{{ + * enablePlugins(JDebPackaging) + * }}} * * @author Nepomuk Seiler - * @see https://github.com/tcurdt/jdeb/blob/master/src/main/java/org/vafer/jdeb/maven/DebMojo.java#L503 + * @see [[https://github.com/tcurdt/jdeb/blob/master/src/main/java/org/vafer/jdeb/maven/DebMojo.java#L503]] * */ -trait JDebPackaging { this: DebianPlugin with linux.LinuxPlugin => +object JDebPackaging extends AutoPlugin with DebianPluginLike { + + override def requires = DebianPlugin + + override lazy val projectSettings = inConfig(Debian)(jdebSettings) - private[debian] def debianJDebSettings: Seq[Setting[_]] = Seq( + def jdebSettings = Seq( /** * Depends on the 'debianExplodedPackage' task as this creates all the files * which are defined in the mappings. */ - debianJDebPackaging <<= (debianExplodedPackage, linuxPackageMappings, linuxPackageSymlinks, + packageBin <<= (debianExplodedPackage, linuxPackageMappings, linuxPackageSymlinks, debianControlFile, debianMaintainerScripts, debianConffilesFile, normalizedName, version, packageArchitecture, target, streams) map { (_, mappings, symlinks, controlfile, controlscripts, conffile, diff --git a/src/main/scala/com/typesafe/sbt/packager/debian/Keys.scala b/src/main/scala/com/typesafe/sbt/packager/debian/Keys.scala index 2475f1527..93fdf5db4 100644 --- a/src/main/scala/com/typesafe/sbt/packager/debian/Keys.scala +++ b/src/main/scala/com/typesafe/sbt/packager/debian/Keys.scala @@ -16,8 +16,6 @@ trait DebianKeys { val debianPackageMetadata = SettingKey[PackageMetaData]("debian-package-metadata", "Meta data used when constructing a debian package.") val debianChangelog = SettingKey[Option[File]]("debian-changelog", "The changelog for this deb file") // Package building - val debianNativePackaging = TaskKey[File]("debian-packaging-native", "Builds the debian package with native cli tools") - val debianJDebPackaging = TaskKey[File]("debian-packaging-jdeb", "Builds the debian package with jdeb (java-based)") val debianControlFile = TaskKey[File]("debian-control-file", "Makes the debian package control file.") val debianMaintainerScripts = TaskKey[Seq[(File, String)]]("debian-maintainer-scripts", "Makes the debian maintainer scripts.") @@ -42,30 +40,5 @@ trait DebianKeys { val debianMakePostrmScript = TaskKey[Option[File]]("makePostrmScript", "Creates or discovers the postrm script used by this project") val debianMakeChownReplacements = TaskKey[(String, String)]("debianMakeChownReplacements", "Creates the chown commands for correct own files and directories") -} - -/** Keys used for Debian specific settings. */ -object Keys extends DebianKeys { - // Metadata keys - def name = sbt.Keys.name - def packageName = linux.Keys.packageName - def executableScriptName = linux.Keys.executableScriptName - def version = sbt.Keys.version - def maintainer = linux.Keys.maintainer - def packageArchitecture = linux.Keys.packageArchitecture - def packageDescription = linux.Keys.packageDescription - def packageSummary = linux.Keys.packageSummary - - // Package building - def sourceDirectory = sbt.Keys.sourceDirectory - def linuxPackageMappings = linux.Keys.linuxPackageMappings - def linuxPackageSymlinks = linux.Keys.linuxPackageSymlinks - def packageBin = sbt.Keys.packageBin - def target = sbt.Keys.target - def streams = sbt.Keys.streams - - //init script parameters - def serverLoading = linux.Keys.serverLoading - val debianPackageInstallSize = TaskKey[Long]("debian-installed-size") -} +} \ No newline at end of file diff --git a/src/main/scala/com/typesafe/sbt/packager/debian/NativePackaging.scala b/src/main/scala/com/typesafe/sbt/packager/debian/NativePackaging.scala index ce922a3f7..2fdc47e6b 100644 --- a/src/main/scala/com/typesafe/sbt/packager/debian/NativePackaging.scala +++ b/src/main/scala/com/typesafe/sbt/packager/debian/NativePackaging.scala @@ -2,32 +2,47 @@ package com.typesafe.sbt package packager package debian -import Keys._ import sbt._ -import sbt.Keys.{ target, name, TaskStreams } +import sbt.Keys.{ packageBin, target, name, version, streams } +import packager.Hashing +import packager.archetypes.TemplateWriter import linux.{ LinuxFileMetaData, LinuxPackageMapping, LinuxSymlink } -import linux.Keys.{ linuxScriptReplacements, daemonShell } -import com.typesafe.sbt.packager.Hashing -import com.typesafe.sbt.packager.archetypes.TemplateWriter +import linux.LinuxPlugin.autoImport.packageArchitecture + +import DebianPlugin.autoImport._ /** + * == Native Packaging == + * * This provides a dpgk based implementation for debian packaging. - * Your machine must have dpkg installed to use this. * - * {{ - * packageBin in Debian <<= debianNativePackaging in Debian - * }} + * == Requirements == + * + * You need the debian dpkg toolchain installed. This includes + *
    + *
  • fakeroot
  • + *
  • dpkg-deb
  • + *
  • dpkg-genchanges
  • + *
* * + * @example Enable the plugin in the `build.sbt` + * {{{ + * enablePlugins(DebianNativePackaging) + * }}} * */ -trait NativePackaging { this: DebianPlugin with linux.LinuxPlugin => +trait DebianNativePackaging extends DebianPluginLike { import com.typesafe.sbt.packager.universal.Archives import DebianPlugin.Names import linux.LinuxPlugin.Users - private[debian] def debianNativeSettings: Seq[Setting[_]] = Seq( + /** + * Using the native installed dpkg-build tools to build the debian + * package. + */ + private[debian] def debianNativeSettings: Seq[Setting[_]] = inConfig(Debian)(Seq( genChanges <<= (packageBin, target, debianChangelog, name, version, debianPackageMetadata) map { (pkg, tdir, changelog, name, version, data) => changelog match { @@ -66,7 +81,7 @@ trait NativePackaging { this: DebianPlugin with linux.LinuxPlugin => }, /** Implementation of the actual packaging */ - debianNativePackaging <<= (debianExplodedPackage, debianMD5sumsFile, debianSection, debianPriority, name, version, packageArchitecture, target, streams) map { + packageBin <<= (debianExplodedPackage, debianMD5sumsFile, debianSection, debianPriority, name, version, packageArchitecture, target, streams) map { (pkgdir, _, section, priority, name, version, arch, tdir, s) => s.log.info("Building debian package with native implementation") // Make the package. We put this in fakeroot, so we can build the package with root owning files. @@ -77,22 +92,16 @@ trait NativePackaging { this: DebianPlugin with linux.LinuxPlugin => } tdir / ".." / archive } - ) + )) } -/** - * This provides the task for building a debian packaging with - * native tools - * - */ -object Native { - - /* static assets definitions */ +object DebianNativePackaging { private[debian] def postinstGroupaddTemplateSource: java.net.URL = getClass.getResource("postinst-groupadd") private[debian] def postinstUseraddTemplateSource: java.net.URL = getClass.getResource("postinst-useradd") private[debian] def postinstChownTemplateSource: java.net.URL = getClass.getResource("postinst-chown") private[debian] def postrmPurgeTemplateSource: java.net.URL = getClass.getResource("postrm-purge") private[debian] def headerSource: java.net.URL = getClass.getResource("header") -} \ No newline at end of file +} + diff --git a/src/main/scala/com/typesafe/sbt/packager/docker/DockerPlugin.scala b/src/main/scala/com/typesafe/sbt/packager/docker/DockerPlugin.scala index 60baa8c0c..03b4a09bb 100644 --- a/src/main/scala/com/typesafe/sbt/packager/docker/DockerPlugin.scala +++ b/src/main/scala/com/typesafe/sbt/packager/docker/DockerPlugin.scala @@ -1,12 +1,111 @@ package com.typesafe.sbt package packager package docker -import Keys._ -import universal._ + import sbt._ +import sbt.Keys.{ + name, + version, + target, + mappings, + publish, + publishLocal, + publishArtifact, + sourceDirectory, + streams, + cacheDirectory +} +import packager.Keys._ +import linux.LinuxPlugin.autoImport.{ daemonUser, defaultLinuxInstallLocation } +import universal.UniversalPlugin.autoImport.stage +import SbtNativePackager.Universal + +/** + * == Docker Plugin == + * + * This plugin helps you build docker containers. + * + * == Configuration == + * + * In order to configure this plugin take a look at the available [[com.typesafe.sbt.packager.docker.DockerKeys]] + * + * == Requirements == + * + * You need docker to have docker installed on your system and be able to execute commands. + * Check with a single command: + * + * {{{ + * docker version + * }}} + * + * Future versions of the Docker Plugin may use the REST API, so you don't need docker installed + * locally. + * + * @note this plugin is not inteded to build very customizable docker images, but turn your mappings + * configuration in a docker image with almost no ''any'' configuration. + * + * @example Enable the plugin in the `build.sbt` + * {{{ + * enablePlugins(DockerPlugin) + * }}} + */ +object DockerPlugin extends AutoPlugin { + + object autoImport extends DockerKeys { + val Docker = config("docker") extend Universal + } -trait DockerPlugin extends Plugin with UniversalPlugin { - val Docker = config("docker") extend Universal + import autoImport._ + + override def requires = universal.UniversalPlugin + + override def trigger = allRequirements + + override lazy val projectSettings = Seq( + dockerBaseImage := "dockerfile/java:latest", + name in Docker <<= name, + packageName in Docker <<= packageName, + executableScriptName in Docker <<= executableScriptName, + dockerRepository := None, + dockerUpdateLatest := false, + sourceDirectory in Docker <<= sourceDirectory apply (_ / "docker"), + target in Docker <<= target apply (_ / "docker") + + ) ++ mapGenericFilesToDocker ++ inConfig(Docker)(Seq( + daemonUser := "daemon", + defaultLinuxInstallLocation := "/opt/docker", + dockerExposedPorts := Seq(), + dockerExposedVolumes := Seq(), + dockerPackageMappings <<= (sourceDirectory) map { dir => + MappingsHelper contentOf dir + }, + mappings <++= dockerPackageMappings, + stage <<= (dockerGenerateConfig, dockerGenerateContext, streams) map { + (dockerfile, contextDir, s) => + s.log.success("created docker file: " + dockerfile.getPath) + contextDir + }, + dockerGenerateConfig <<= (dockerBaseImage, defaultLinuxInstallLocation, + maintainer, daemonUser, executableScriptName, + dockerExposedPorts, dockerExposedVolumes, target) map generateDockerConfig, + dockerGenerateContext := Stager.stage("docker")(streams.value, target.value / "files", mappings.value), + dockerTarget <<= (dockerRepository, packageName, version) map { + (repo, name, version) => + repo.map(_ + "/").getOrElse("") + name + ":" + version + }, + publishLocal <<= (dockerGenerateConfig, dockerGenerateContext, dockerTarget, dockerUpdateLatest, streams) map { + (config, _, target, updateLatest, s) => + publishLocalDocker(config, target, updateLatest, s.log) + }, + publish <<= (publishLocal, dockerTarget, dockerUpdateLatest, streams) map { + (_, target, updateLatest, s) => + publishDocker(target, s.log) + if (updateLatest) { + val name = target.substring(0, target.lastIndexOf(":")) + ":latest" + publishDocker(name, s.log) + } + } + )) private[this] final def makeDockerContent(dockerBaseImage: String, dockerBaseDirectory: String, maintainer: String, daemonUser: String, execScript: String, exposedPorts: Seq[Int], exposedVolumes: Seq[String]) = { val headerCommands = Seq( @@ -155,52 +254,4 @@ trait DockerPlugin extends Plugin with UniversalPlugin { log.info("Published image " + tag) } - def dockerSettings: Seq[Setting[_]] = Seq( - dockerBaseImage := "dockerfile/java:latest", - name in Docker <<= name, - packageName in Docker <<= packageName, - executableScriptName in Docker <<= executableScriptName, - dockerRepository := None, - dockerUpdateLatest := false, - sourceDirectory in Docker <<= sourceDirectory apply (_ / "docker"), - target in Docker <<= target apply (_ / "docker"), - - // TODO this must be changed, when there is a setting for the startScripts name - dockerGenerateConfig <<= - (dockerBaseImage in Docker, defaultLinuxInstallLocation in Docker, maintainer in Docker, daemonUser in Docker, - executableScriptName /* this is not scoped!*/ , dockerExposedPorts in Docker, dockerExposedVolumes in Docker, target in Docker) map - generateDockerConfig - ) ++ mapGenericFilesToDocker ++ inConfig(Docker)(Seq( - daemonUser := "daemon", - defaultLinuxInstallLocation := "/opt/docker", - dockerExposedPorts := Seq(), - dockerExposedVolumes := Seq(), - dockerPackageMappings <<= (sourceDirectory) map { dir => - MappingsHelper contentOf dir - }, - mappings <++= dockerPackageMappings, - stage <<= (dockerGenerateConfig, dockerGenerateContext) map { (configFile, contextDir) => () }, - dockerGenerateContext <<= (cacheDirectory, mappings, target) map { - (cacheDirectory, mappings, t) => - val contextDir = t / "files" - stageFiles("docker")(cacheDirectory, contextDir, mappings) - contextDir - }, - dockerTarget <<= (dockerRepository, packageName, version) map { - (repo, name, version) => - repo.map(_ + "/").getOrElse("") + name + ":" + version - }, - publishLocal <<= (dockerGenerateConfig, dockerGenerateContext, dockerTarget, dockerUpdateLatest, streams) map { - (config, _, target, updateLatest, s) => - publishLocalDocker(config, target, updateLatest, s.log) - }, - publish <<= (publishLocal, dockerTarget, dockerUpdateLatest, streams) map { - (_, target, updateLatest, s) => - publishDocker(target, s.log) - if (updateLatest) { - val name = target.substring(0, target.lastIndexOf(":")) + ":latest" - publishDocker(name, s.log) - } - } - )) } diff --git a/src/main/scala/com/typesafe/sbt/packager/docker/Keys.scala b/src/main/scala/com/typesafe/sbt/packager/docker/Keys.scala index 753bf0351..03ef6b015 100644 --- a/src/main/scala/com/typesafe/sbt/packager/docker/Keys.scala +++ b/src/main/scala/com/typesafe/sbt/packager/docker/Keys.scala @@ -4,6 +4,9 @@ package docker import sbt._ +/** + * Docker settings + */ trait DockerKeys { val dockerGenerateConfig = TaskKey[File]("docker-generate-config", "Generates configuration file for Docker.") val dockerGenerateContext = TaskKey[File]("docker-generate-context", "Generates context directory for Docker.") @@ -17,21 +20,3 @@ trait DockerKeys { val dockerUpdateLatest = SettingKey[Boolean]("dockerUpdateLatest", "Set to update latest tag") } -object Keys extends DockerKeys { - def cacheDirectory = sbt.Keys.cacheDirectory - def mappings = sbt.Keys.mappings - def name = sbt.Keys.name - def packageName = universal.Keys.packageName - def executableScriptName = universal.Keys.executableScriptName - def stage = universal.Keys.stage - def publish = sbt.Keys.publish - def publishArtifact = sbt.Keys.publishArtifact - def publishLocal = sbt.Keys.publishLocal - def sourceDirectory = sbt.Keys.sourceDirectory - def streams = sbt.Keys.streams - def target = sbt.Keys.target - def version = sbt.Keys.version - def defaultLinuxInstallLocation = packager.Keys.defaultLinuxInstallLocation - def daemonUser = linux.Keys.daemonUser - def maintainer = linux.Keys.maintainer -} diff --git a/src/main/scala/com/typesafe/sbt/packager/docker/dockerfile.scala b/src/main/scala/com/typesafe/sbt/packager/docker/dockerfile.scala index afc76608d..3165141dc 100644 --- a/src/main/scala/com/typesafe/sbt/packager/docker/dockerfile.scala +++ b/src/main/scala/com/typesafe/sbt/packager/docker/dockerfile.scala @@ -2,14 +2,51 @@ package com.typesafe.sbt package packager package docker +/** + * a single line in a dockerfile. See subclasses for more detail + * + */ trait CmdLike { + + /** + * Creates the command which can be placed inside a Dockerfile. + * + * @return the docker command + */ def makeContent: String } +/** + * Executable command + * + * @example {{{ + * ExecCmd("RUN", "chown", "-R", daemonUser, ".") + * }}} + * + * @example {{{ + * ExecCmd("ENTRYPOINT", "bin/%s" format execScript), + * }}} + * + * @example {{{ + * ExecCmd("CMD") + * }}} + * + * @example {{{ + * ExecCmd("VOLUME", exposedVolumes: _*) + * }}} + */ case class ExecCmd(cmd: String, args: String*) extends CmdLike { def makeContent = "%s [%s]\n" format (cmd, args.map('"' + _ + '"').mkString(", ")) } +/** + * An arbitrary command + * + * @example + * {{{ + * val add = Cmd("ADD", "src/resource/LICENSE.txt /opt/docker/LICENSE.txt") + * }}} + */ case class Cmd(cmd: String, arg: String) extends CmdLike { def makeContent = "%s %s\n" format (cmd, arg) } diff --git a/src/main/scala/com/typesafe/sbt/packager/linux/Keys.scala b/src/main/scala/com/typesafe/sbt/packager/linux/Keys.scala index a1023608a..16ef46871 100644 --- a/src/main/scala/com/typesafe/sbt/packager/linux/Keys.scala +++ b/src/main/scala/com/typesafe/sbt/packager/linux/Keys.scala @@ -6,11 +6,8 @@ import sbt._ import com.typesafe.sbt.packager.archetypes.ServerLoader.ServerLoader /** Linux packaging generic build targets. */ -trait Keys { +trait LinuxKeys { val packageArchitecture = SettingKey[String]("package-architecture", "The architecture used for this linux package.") - val packageSummary = SettingKey[String]("package-summary", "Summary of the contents of a linux package.") - val packageDescription = SettingKey[String]("package-description", "The description of the package. Used when searching.") - val maintainer = SettingKey[String]("maintainer", "The name/email address of a maintainer for the native package.") val daemonUser = SettingKey[String]("daemon-user", "User to start application daemon") val daemonGroup = SettingKey[String]("daemon-group", "Group to start application daemon") val daemonShell = SettingKey[String]("daemon-shell", "Shell provided for the daemon user") @@ -42,11 +39,10 @@ trait Keys { """.stripMargin) val makeEtcDefault = TaskKey[Option[File]]("makeEtcDefault", "Creates or discovers the /etc/default/ script") -} -object Keys extends Keys { - def name = sbt.Keys.name - def packageName = packager.Keys.packageName - def executableScriptName = packager.Keys.executableScriptName - def sourceDirectory = sbt.Keys.sourceDirectory -} \ No newline at end of file + val defaultLinuxInstallLocation = SettingKey[String]("defaultLinuxInstallLocation", "The location where we will install generic linux packages.") + val defaultLinuxLogsLocation = SettingKey[String]("defaultLinuxLogsLocation", "The location where application logs will be stored.") + val defaultLinuxConfigLocation = SettingKey[String]("defaultLinuxConfigLocation", "The location where application config files will be stored") + val defaultLinuxStartScriptLocation = SettingKey[String]("defaultLinuxStartScriptLocation", "The location where start script for server application will be stored") + +} diff --git a/src/main/scala/com/typesafe/sbt/packager/linux/LinuxMappingDSL.scala b/src/main/scala/com/typesafe/sbt/packager/linux/LinuxMappingDSL.scala new file mode 100644 index 000000000..bfdbe2d13 --- /dev/null +++ b/src/main/scala/com/typesafe/sbt/packager/linux/LinuxMappingDSL.scala @@ -0,0 +1,34 @@ +package com.typesafe.sbt.packager.linux + +import sbt._ + +trait LinuxMappingDSL { + + /** DSL for packaging files into .deb */ + def packageMapping(files: (File, String)*) = LinuxPackageMapping(files) + + /** + * @param dir - use some directory, e.g. target.value + * @param files + */ + def packageTemplateMapping(files: String*)(dir: File = new File(sys.props("java.io.tmpdir"))) = LinuxPackageMapping(files map ((dir, _))) + + // TODO can the packager.MappingsHelper be used here? + /** + * @see #mapDirectoryAndContents + * @param dirs - directories to map + */ + def packageDirectoryAndContentsMapping(dirs: (File, String)*) = LinuxPackageMapping(mapDirectoryAndContents(dirs: _*)) + + /** + * This method includes files and directories. + * + * @param dirs - directories to map + */ + def mapDirectoryAndContents(dirs: (File, String)*): Seq[(File, String)] = for { + (src, dest) <- dirs + path <- (src ***).get + } yield path -> path.toString.replaceFirst(src.toString, dest) +} + +object Mapper extends LinuxMappingDSL \ No newline at end of file diff --git a/src/main/scala/com/typesafe/sbt/packager/linux/LinuxPackageMapping.scala b/src/main/scala/com/typesafe/sbt/packager/linux/LinuxPackageMapping.scala index 1a7915354..5c91d28f0 100644 --- a/src/main/scala/com/typesafe/sbt/packager/linux/LinuxPackageMapping.scala +++ b/src/main/scala/com/typesafe/sbt/packager/linux/LinuxPackageMapping.scala @@ -28,7 +28,7 @@ case class LinuxPackageMapping( def withGroup(group: String) = copy(fileData = fileData withGroup group) def withPerms(perms: String) = copy(fileData = fileData withPerms perms) def withConfig(c: String = "true") = copy(fileData = fileData withConfig c) - def withContents() = copy(mappings = SbtNativePackager.mapDirectoryAndContents(mappings.toSeq: _*)) + def withContents() = copy(mappings = Mapper.mapDirectoryAndContents(mappings.toSeq: _*)) def asDocs() = copy(fileData = fileData asDocs ()) /** Modifies the current package mapping to have gzipped data. */ diff --git a/src/main/scala/com/typesafe/sbt/packager/linux/LinuxPlugin.scala b/src/main/scala/com/typesafe/sbt/packager/linux/LinuxPlugin.scala index c741e245a..ff9462267 100644 --- a/src/main/scala/com/typesafe/sbt/packager/linux/LinuxPlugin.scala +++ b/src/main/scala/com/typesafe/sbt/packager/linux/LinuxPlugin.scala @@ -2,25 +2,46 @@ package com.typesafe.sbt package packager package linux -import Keys._ import sbt._ -import sbt.Keys.{ normalizedName } -import packager.Keys.{ - defaultLinuxInstallLocation, - defaultLinuxConfigLocation, - defaultLinuxLogsLocation -} -import com.typesafe.sbt.packager.linux.LinuxPlugin.Users -import com.typesafe.sbt.packager.archetypes.{ ServerLoader, TemplateWriter } +import sbt.Keys.{ name, normalizedName, mappings, sourceDirectory } +import linux.LinuxPlugin.Users +import packager.Keys._ +import packager.archetypes.{ ServerLoader, TemplateWriter } +import SbtNativePackager.Universal /** - * Plugin trait containing all the generic values used for + * Plugin containing all the generic values used for * packaging linux software. + * + * @example Enable the plugin in the `build.sbt` + * {{{ + * enablePlugins(LinuxPlugin) + * }}} */ -trait LinuxPlugin extends Plugin { - // TODO - is this needed - val Linux = config("linux") +object LinuxPlugin extends AutoPlugin { + + override def requires = universal.UniversalPlugin + override def trigger = allRequirements + override lazy val projectSettings = linuxSettings ++ mapGenericFilesToLinux + + object autoImport extends LinuxKeys with LinuxMappingDSL { + val Linux = config("linux") + } + + import autoImport._ + + /** default users available for */ + object Users { + val Root = "root" + } + /** key for replacement in linuxScriptReplacements */ + val CONTROL_FUNCTIONS = "control-functions" + def controlFunctions(): URL = getClass getResource CONTROL_FUNCTIONS + + /** + * default linux settings + */ def linuxSettings: Seq[Setting[_]] = Seq( linuxPackageMappings := Seq.empty, linuxPackageSymlinks := Seq.empty, @@ -66,31 +87,51 @@ trait LinuxPlugin extends Plugin { ) - /** DSL for packaging files into .deb */ - def packageMapping(files: (File, String)*) = LinuxPackageMapping(files) - /** - * @param dir - use some directory, e.g. target.value - * @param files + * maps the `mappings` content into `linuxPackageMappings` and + * `linuxPackageSymlinks`. */ - def packageTemplateMapping(files: String*)(dir: File = new File(sys.props("java.io.tmpdir"))) = LinuxPackageMapping(files map ((dir, _))) + def mapGenericFilesToLinux: Seq[Setting[_]] = Seq( + + // First we look at the src/linux files + linuxPackageMappings <++= (sourceDirectory in Linux) map { dir => + mapGenericMappingsToLinux(MappingsHelper contentOf dir, Users.Root, Users.Root)(identity) + }, + // Now we look at the src/universal files. + linuxPackageMappings <++= (packageName in Linux, mappings in Universal, defaultLinuxInstallLocation) map { + (pkg, mappings, installLocation) => + // TODO - More windows filters... + def isWindowsFile(f: (File, String)): Boolean = + f._2 endsWith ".bat" + + mapGenericMappingsToLinux(mappings filterNot isWindowsFile, Users.Root, Users.Root) { name => + installLocation + "/" + pkg + "/" + name + } + }, + // Now we generate symlinks. + linuxPackageSymlinks <++= (packageName in Linux, mappings in Universal, defaultLinuxInstallLocation) map { (pkg, mappings, installLocation) => + for { + (file, name) <- mappings + if !file.isDirectory + if name startsWith "bin/" + if !(name endsWith ".bat") // IGNORE windows-y things. + } yield LinuxSymlink("/usr/" + name, installLocation + "/" + pkg + "/" + name) + }, + // Map configuration files + linuxPackageSymlinks <++= (packageName in Linux, mappings in Universal, defaultLinuxInstallLocation, defaultLinuxConfigLocation) + map { (pkg, mappings, installLocation, configLocation) => + val needsConfLink = + mappings exists { + case (file, name) => + (name startsWith "conf/") && !file.isDirectory + } + if (needsConfLink) Seq(LinuxSymlink( + link = configLocation + "/" + pkg, + destination = installLocation + "/" + pkg + "/conf")) + else Seq.empty + }) - // TODO can the packager.MappingsHelper be used here? - /** - * @see #mapDirectoryAndContents - * @param dirs - directories to map - */ - def packageDirectoryAndContentsMapping(dirs: (File, String)*) = LinuxPackageMapping(mapDirectoryAndContents(dirs: _*)) - /** - * This method includes files and directories. - * - * @param dirs - directories to map - */ - def mapDirectoryAndContents(dirs: (File, String)*): Seq[(File, String)] = for { - (src, dest) <- dirs - path <- (src ***).get - } yield path -> path.toString.replaceFirst(src.toString, dest) /** * @@ -143,13 +184,48 @@ trait LinuxPlugin extends Plugin { /** Create a ascii friendly string for a man page. */ final def makeMan(file: File): String = Process("groff -man -Tascii " + file.getAbsolutePath).!! -} -object LinuxPlugin { - object Users { - val Root = "root" + // This method wires a lot of hand-coded generalities about how to map directories + // into linux, and the conventions we expect. + // It is by no means 100% accurate, but should be ok for the simplest cases. + // For advanced users, use the underlying APIs. + // Right now, it's also pretty focused on command line scripts packages. + + /** + * Maps linux file format from the universal from the conventions: + * + * `/src/linux` files are mapped directly into linux packages. + * `` files are placed under `/usr/share/` + * `/bin` files are given symlinks in `/usr/bin` + * `/conf` directory is given a symlink to `/etc/` + * Files in `conf/` or `etc/` directories are automatically marked as configuration. + * `../man/...1` files are automatically compressed into .gz files. + * + */ + def mapGenericMappingsToLinux(mappings: Seq[(File, String)], user: String, group: String)(rename: String => String): Seq[LinuxPackageMapping] = { + val (directories, nondirectories) = mappings.partition(_._1.isDirectory) + val (binaries, nonbinaries) = nondirectories.partition(_._1.canExecute) + val (manPages, nonManPages) = nonbinaries partition { + case (file, name) => (name contains "man/") && (name endsWith ".1") + } + val compressedManPages = + for ((file, name) <- manPages) + yield file -> (name + ".gz") + val (configFiles, remaining) = nonManPages partition { + case (file, name) => (name contains "etc/") || (name contains "conf/") + } + def packageMappingWithRename(mappings: (File, String)*): LinuxPackageMapping = { + val renamed = + for ((file, name) <- mappings) + yield file -> rename(name) + packageMapping(renamed: _*) + } + + Seq( + packageMappingWithRename((binaries ++ directories): _*) withUser user withGroup group withPerms "0755", + packageMappingWithRename(compressedManPages: _*).gzipped withUser user withGroup group withPerms "0644", + packageMappingWithRename(configFiles: _*) withConfig () withUser user withGroup group withPerms "0644", + packageMappingWithRename(remaining: _*) withUser user withGroup group withPerms "0644") } - val CONTROL_FUNCTIONS = "control-functions" - def controlFunctions(): URL = getClass getResource CONTROL_FUNCTIONS -} \ No newline at end of file +} diff --git a/src/main/scala/com/typesafe/sbt/packager/rpm/Keys.scala b/src/main/scala/com/typesafe/sbt/packager/rpm/Keys.scala index c97dfbb83..e1cdc82d9 100644 --- a/src/main/scala/com/typesafe/sbt/packager/rpm/Keys.scala +++ b/src/main/scala/com/typesafe/sbt/packager/rpm/Keys.scala @@ -59,33 +59,3 @@ trait RpmKeys { val rpmLint = TaskKey[Unit]("rpm-lint", "Runs rpmlint program against the genreated RPM, if available.") } - -/** Keys used in RPM Settings. */ -object Keys extends RpmKeys { - // METADATA keys. - def name = sbt.Keys.name - def packageName = packager.Keys.packageName - def executableScriptName = linux.Keys.executableScriptName - def version = sbt.Keys.version - def maintainer = linux.Keys.maintainer - def packageArchitecture = linux.Keys.packageArchitecture - def packageDescription = linux.Keys.packageDescription - def packageSummary = linux.Keys.packageSummary - - // DESCRIPTION KEYS - - // DEPENDENCIES - - // SPEC - def linuxPackageMappings = linux.Keys.linuxPackageMappings - def linuxPackageSymlinks = linux.Keys.linuxPackageSymlinks - - // Building - def target = sbt.Keys.target - def packageBin = sbt.Keys.packageBin - - //init script parameters - def serverLoading = linux.Keys.serverLoading - - def streams = sbt.Keys.streams -} diff --git a/src/main/scala/com/typesafe/sbt/packager/rpm/RpmPlugin.scala b/src/main/scala/com/typesafe/sbt/packager/rpm/RpmPlugin.scala index 38ef8a6cb..15d931194 100644 --- a/src/main/scala/com/typesafe/sbt/packager/rpm/RpmPlugin.scala +++ b/src/main/scala/com/typesafe/sbt/packager/rpm/RpmPlugin.scala @@ -3,18 +3,47 @@ package packager package rpm import sbt._ -import sbt.Keys.sourceDirectory -import rpm.Keys._ import linux._ import java.nio.charset.Charset +import SbtNativePackager.Linux -/** Plugin trait containing all generic values used for packaging linux software. */ -trait RpmPlugin extends Plugin with LinuxPlugin { - val Rpm = config("rpm") extend Linux +import sbt.Keys.{ name, version, sourceDirectory, target, packageBin, streams } +import linux.LinuxPlugin.autoImport.{ linuxPackageMappings, linuxPackageSymlinks, serverLoading, packageArchitecture } +import packager.Keys._ - import RpmPlugin.Names +/** + * Plugin containing all generic values used for packaging rpms. + * + * @example Enable the plugin in the `build.sbt` + * {{{ + * enablePlugins(RpmPlugin) + * }}} + */ +object RpmPlugin extends AutoPlugin { - def rpmSettings: Seq[Setting[_]] = Seq( + override def requires = LinuxPlugin + override def trigger = allRequirements + + object autoImport extends RpmKeys { + val Rpm = config("rpm") extend Linux + } + + import autoImport._ + + def osPostInstallMacro: java.net.URL = getClass getResource "brpJavaRepackJar" + + /** RPM specific names */ + object Names { + val Scriptlets = "scriptlets" + + //maintainer script names + val Post = "postinst" + val Pre = "preinst" + val Postun = "postun" + val Preun = "preun" + } + + override lazy val projectSettings = Seq( rpmOs := "Linux", // TODO - default to something else? rpmRelease := "0", rpmPrefix := None, @@ -80,17 +109,3 @@ trait RpmPlugin extends Plugin with LinuxPlugin { )) } -object RpmPlugin { - - def osPostInstallMacro: java.net.URL = getClass getResource "brpJavaRepackJar" - - object Names { - val Scriptlets = "scriptlets" - - //maintainer script names - val Post = "postinst" - val Pre = "preinst" - val Postun = "postun" - val Preun = "preun" - } -} diff --git a/src/main/scala/com/typesafe/sbt/packager/universal/Keys.scala b/src/main/scala/com/typesafe/sbt/packager/universal/Keys.scala index 0adda621a..bba14759c 100644 --- a/src/main/scala/com/typesafe/sbt/packager/universal/Keys.scala +++ b/src/main/scala/com/typesafe/sbt/packager/universal/Keys.scala @@ -8,21 +8,7 @@ trait UniversalKeys { val packageZipTarball = TaskKey[File]("package-zip-tarball", "Creates a tgz package.") val packageXzTarball = TaskKey[File]("package-xz-tarball", "Creates a txz package.") val packageOsxDmg = TaskKey[File]("package-osx-dmg", "Creates a dmg package for OSX (only on osx).") - val stage = TaskKey[Unit]("stage", "Create a local directory with all the files laid out as they would be in the final distribution.") + val stage = TaskKey[File]("stage", "Create a local directory with all the files laid out as they would be in the final distribution.") val dist = TaskKey[File]("dist", "Creates the distribution packages.") val stagingDirectory = SettingKey[File]("stagingDirectory", "Directory where we stage distributions/releases.") } - -object Keys extends UniversalKeys { - def mappings = sbt.Keys.mappings - def packageBin = sbt.Keys.packageBin - def packageSrc = sbt.Keys.packageSrc - def packageDoc = sbt.Keys.packageDoc - def name = sbt.Keys.name - def packageName = packager.Keys.packageName - def executableScriptName = packager.Keys.executableScriptName - def target = sbt.Keys.target - def sourceDirectory = sbt.Keys.sourceDirectory - def streams = sbt.Keys.streams - def version = sbt.Keys.version -} \ No newline at end of file diff --git a/src/main/scala/com/typesafe/sbt/packager/universal/UniversalPlugin.scala b/src/main/scala/com/typesafe/sbt/packager/universal/UniversalPlugin.scala index 4ba702df2..917dadfdb 100644 --- a/src/main/scala/com/typesafe/sbt/packager/universal/UniversalPlugin.scala +++ b/src/main/scala/com/typesafe/sbt/packager/universal/UniversalPlugin.scala @@ -3,34 +3,74 @@ package packager package universal import sbt._ -import sbt.Keys.cacheDirectory -import Keys._ +import sbt.Keys.{ + cacheDirectory, + name, + normalizedName, + version, + mappings, + packageBin, + packageSrc, + packageDoc, + target, + sourceDirectory, + streams +} +import packager.Keys._ import Archives._ import sbt.Keys.TaskStreams -/** Defines behavior to construct a 'universal' zip for installation. */ -trait UniversalPlugin extends Plugin { - val Universal = config("universal") - val UniversalDocs = config("universal-docs") - val UniversalSrc = config("universal-src") +/** + * == Universal Plugin == + * + * Defines behavior to construct a 'universal' zip for installation. + * + * == Configuration == + * + * In order to configure this plugin take a look at the available [[com.typesafe.sbt.packager.universal.UniversalKeys]] + * + * @example Enable the plugin in the `build.sbt` + * {{{ + * enablePlugins(UniversalPlugin) + * }}} + */ +object UniversalPlugin extends AutoPlugin { + + object autoImport extends UniversalKeys { + val Universal = config("universal") + val UniversalDocs = config("universal-docs") + val UniversalSrc = config("universal-src") + + /** + * Use native zipping instead of java based zipping + */ + def useNativeZip: Seq[Setting[_]] = + makePackageSettings(packageBin, Universal)(makeNativeZip) ++ + makePackageSettings(packageBin, UniversalDocs)(makeNativeZip) ++ + makePackageSettings(packageBin, UniversalSrc)(makeNativeZip) + } + + import autoImport._ + + override def requires = SbtNativePackager + override def trigger = allRequirements /** The basic settings for the various packaging types. */ - def universalSettings: Seq[Setting[_]] = - Seq[Setting[_]]( - // For now, we provide delegates from dist/stage to universal... - dist <<= dist in Universal, - stage <<= stage in Universal, - // TODO - New default to naming, is this right? - // TODO - We may need to do this for UniversalSrcs + UnviersalDocs - name in Universal <<= name, - name in UniversalDocs <<= name in Universal, - name in UniversalSrc <<= name in Universal, - packageName in Universal <<= packageName, - executableScriptName in Universal <<= executableScriptName - ) ++ - makePackageSettingsForConfig(Universal) ++ - makePackageSettingsForConfig(UniversalDocs) ++ - makePackageSettingsForConfig(UniversalSrc) + override lazy val projectSettings = Seq[Setting[_]]( + // For now, we provide delegates from dist/stage to universal... + dist <<= dist in Universal, + stage <<= stage in Universal, + // TODO - New default to naming, is this right? + // TODO - We may need to do this for UniversalSrcs + UnviersalDocs + name in Universal <<= name, + name in UniversalDocs <<= name in Universal, + name in UniversalSrc <<= name in Universal, + packageName in Universal <<= packageName, + executableScriptName in Universal <<= executableScriptName + ) ++ + makePackageSettingsForConfig(Universal) ++ + makePackageSettingsForConfig(UniversalDocs) ++ + makePackageSettingsForConfig(UniversalSrc) /** Creates all package types for a given configuration */ private[this] def makePackageSettingsForConfig(config: Configuration): Seq[Setting[_]] = @@ -43,17 +83,12 @@ trait UniversalPlugin extends Plugin { mappings <<= sourceDirectory map findSources, dist <<= (packageBin, streams) map printDist, stagingDirectory <<= target apply (_ / "stage"), - stage <<= (cacheDirectory, stagingDirectory, mappings) map stageFiles(config.name) + stage <<= (streams, stagingDirectory, mappings) map Stager.stage(config.name) )) ++ Seq( sourceDirectory in config <<= sourceDirectory apply (_ / config.name), target in config <<= target apply (_ / config.name) ) - def useNativeZip: Seq[Setting[_]] = - makePackageSettings(packageBin, Universal)(makeNativeZip) ++ - makePackageSettings(packageBin, UniversalDocs)(makeNativeZip) ++ - makePackageSettings(packageBin, UniversalSrc)(makeNativeZip) - private[this] def printDist(dist: File, streams: TaskStreams): File = { streams.log.info("") streams.log.info("Your package is ready in " + dist.getCanonicalPath) @@ -61,20 +96,6 @@ trait UniversalPlugin extends Plugin { dist } - def stageFiles(config: String)(cacheDirectory: File, to: File, mappings: Seq[(File, String)]): Unit = { - val cache = cacheDirectory / ("packager-mappings-" + config) - val copies = mappings map { - case (file, path) => file -> (to / path) - } - Sync(cache)(copies) - // Now set scripts to executable using Java's lack of understanding of permissions. - // TODO - Config file user-readable permissions.... - for { - (from, to) <- copies - if from.canExecute - } to.setExecutable(true) - } - private type Packager = (File, String, Seq[(File, String)]) => File /** Creates packaging settings for a given package key, configuration + archive type. */ private[this] def makePackageSettings(packageKey: TaskKey[File], config: Configuration)(packager: Packager): Seq[Setting[_]] = @@ -90,6 +111,6 @@ trait UniversalPlugin extends Plugin { /** Finds all sources in a source directory. */ private[this] def findSources(sourceDir: File): Seq[(File, String)] = - sourceDir.*** --- sourceDir x relativeTo(sourceDir) + sourceDir.*** --- sourceDir pair relativeTo(sourceDir) } diff --git a/src/main/scala/com/typesafe/sbt/packager/windows/Keys.scala b/src/main/scala/com/typesafe/sbt/packager/windows/Keys.scala index 361e3a66e..05104a41a 100644 --- a/src/main/scala/com/typesafe/sbt/packager/windows/Keys.scala +++ b/src/main/scala/com/typesafe/sbt/packager/windows/Keys.scala @@ -4,6 +4,7 @@ package windows import sbt._ +/** windows settings */ trait WindowsKeys { val wixProductId = SettingKey[String]("wix-product-id", "The uuid of the windows package.") @@ -14,23 +15,6 @@ trait WindowsKeys { val wixProductConfig = TaskKey[xml.Node]("wix-product-xml", "The WIX XML configuration for a product (nested in Wix/Product elements).") val wixConfig = TaskKey[xml.Node]("wix-xml", "The WIX XML configuration for this package.") val wixFile = TaskKey[File]("wix-file", "The WIX XML file to package with.") - @deprecated("use packageBin instead!", "0.7.0") - val packageMsi = TaskKey[File]("package-msi", "creates a new windows CAB file containing everything for the installation.") val candleOptions = SettingKey[Seq[String]]("candle-options", "Options to pass to the candle.exe program.") val lightOptions = SettingKey[Seq[String]]("light-options", "Options to pass to the light.exe program.") } - -object Keys extends WindowsKeys { - def version = sbt.Keys.version - def target = sbt.Keys.target - def mappings = sbt.Keys.mappings - def name = sbt.Keys.name - def packageName = packager.Keys.packageName - def executableScriptName = packager.Keys.executableScriptName - def streams = sbt.Keys.streams - def sourceDirectory = sbt.Keys.sourceDirectory - def packageBin = sbt.Keys.packageBin - def maintainer = packager.Keys.maintainer - def packageSummary = packager.Keys.packageSummary - def packageDescription = packager.Keys.packageDescription -} \ No newline at end of file diff --git a/src/main/scala/com/typesafe/sbt/packager/windows/WindowsPlugin.scala b/src/main/scala/com/typesafe/sbt/packager/windows/WindowsPlugin.scala index b18709552..5ccaaaa8e 100644 --- a/src/main/scala/com/typesafe/sbt/packager/windows/WindowsPlugin.scala +++ b/src/main/scala/com/typesafe/sbt/packager/windows/WindowsPlugin.scala @@ -2,19 +2,53 @@ package com.typesafe.sbt package packager package windows -import Keys._ import sbt._ -import sbt.Keys.{ normalizedName } +import sbt.Keys.{ normalizedName, name, version, sourceDirectory, target, mappings, packageBin, streams } +import packager.Keys.{ packageName, maintainer, packageSummary, packageDescription } +import SbtNativePackager.Universal -trait WindowsPlugin extends Plugin { - val Windows = config("windows") +/** + * == Windows Plugin == + * + * This plugin generates ''msi'' packages that can be installed on windows systems. + * + * == Configuration == + * + * In order to configure this plugin take a look at the available [[com.typesafe.sbt.packager.windows.WindowsKeys]] + * + * == Requirements == + * + *
    + *
  • Windows System
  • + *
  • Wix Toolset ([[http://wixtoolset.org/]]) installed + *
+ * + * @example Enable the plugin in the `build.sbt` + * {{{ + * enablePlugins(WindowsPlugin) + * }}} + */ +object WindowsPlugin extends AutoPlugin { + object autoImport extends WindowsKeys { + val Windows = config("windows") + } + + import autoImport._ + + override lazy val projectSettings = windowsSettings ++ mapGenericFilesToWindows + override def requires = universal.UniversalPlugin + override def trigger = allRequirements + + /** + * default windows settings + */ def windowsSettings: Seq[Setting[_]] = Seq( sourceDirectory in Windows <<= sourceDirectory(_ / "windows"), target in Windows <<= target apply (_ / "windows"), // TODO - Should this use normalized name like the linux guys? name in Windows <<= name, - packageName in Windows <<= normalizedName, + packageName in Windows <<= packageName, // Defaults so that our simplified building works candleOptions := Seq("-ext", "WixUtilExtension"), lightOptions := Seq("-ext", "WixUIExtension", @@ -63,13 +97,7 @@ trait WindowsPlugin extends Plugin { f } ) ++ inConfig(Windows)(Seq( - // Disable windows generation by default. - mappings := Seq.empty, - mappings in packageBin <<= mappings, - // TODO - Remove packageMsi after next major release. - mappings in packageMsi <<= mappings in packageBin, - packageMsi <<= packageBin, - packageBin <<= (mappings in packageMsi, wixFile, name, target, candleOptions, lightOptions, streams) map { (m, f, n, t, co, lo, s) => + packageBin <<= (mappings, wixFile, name, target, candleOptions, lightOptions, streams) map { (m, f, n, t, co, lo, s) => val msi = t / (n + ".msi") // First we have to move everything (including the wix file) to our target directory. val wix = t / (n + ".wix") @@ -94,4 +122,57 @@ trait WindowsPlugin extends Plugin { msi } )) + + /** + * set the `mappings in Windows` and the `wixFeatures` + */ + def mapGenericFilesToWindows: Seq[Setting[_]] = Seq( + mappings in Windows <<= mappings in Universal, + wixFeatures <<= (packageName in Windows, mappings in Windows) map makeWindowsFeatures) + + /** + * Generates the wix configuration features + * + * @param name - title of the core package + * @param mappings - use to generate different features + * @return windows features + */ + def makeWindowsFeatures(name: String, mappings: Seq[(File, String)]): Seq[windows.WindowsFeature] = { + // TODO select main script! Filter Config links! + import windows._ + + val files = + for { + (file, name) <- mappings + if !file.isDirectory + } yield ComponentFile(name, editable = (name startsWith "conf")) + val corePackage = + WindowsFeature( + id = WixHelper.cleanStringForId(name + "_core").takeRight(38), // Must be no longer + title = name, + desc = "All core files.", + absent = "disallow", + components = files) + // TODO - Detect bat files to add paths... + val addBinToPath = + // TODO - we may have issues here... + WindowsFeature( + id = "AddBinToPath", + title = "Update Enviornment Variables", + desc = "Update PATH environment variables (requires restart).", + components = Seq(AddDirectoryToPath("bin"))) + val configLinks = for { + (file, name) <- mappings + if !file.isDirectory + if name startsWith "conf/" + } yield name.replaceAll("//", "/").stripSuffix("/").stripSuffix("/") + val menuLinks = + WindowsFeature( + id = "AddConfigLinks", + title = "Configuration start menu links", + desc = "Adds start menu shortcuts to edit configuration files.", + components = Seq(AddShortCuts(configLinks))) + // TODO - Add feature for shortcuts to binary scripts. + Seq(corePackage, addBinToPath, menuLinks) + } } \ No newline at end of file diff --git a/src/sbt-test/cygwin/java-app-archetype/build.sbt b/src/sbt-test/cygwin/java-app-archetype/build.sbt index 22d3cb7f4..86d83ae27 100644 --- a/src/sbt-test/cygwin/java-app-archetype/build.sbt +++ b/src/sbt-test/cygwin/java-app-archetype/build.sbt @@ -1,6 +1,4 @@ -import NativePackagerKeys._ - -packageArchetype.java_application +enablePlugins(JavaServerAppPackaging) name := "windows-test" diff --git a/src/sbt-test/debian/daemon-user-deb/build.sbt b/src/sbt-test/debian/daemon-user-deb/build.sbt index 9a33baac5..dad7b630b 100644 --- a/src/sbt-test/debian/daemon-user-deb/build.sbt +++ b/src/sbt-test/debian/daemon-user-deb/build.sbt @@ -1,7 +1,6 @@ -import NativePackagerKeys._ import com.typesafe.sbt.packager.archetypes.ServerLoader -packageArchetype.java_server +enablePlugins(JavaServerAppPackaging) serverLoading in Debian := ServerLoader.Upstart diff --git a/src/sbt-test/debian/daemon-user-shell-deb/build.sbt b/src/sbt-test/debian/daemon-user-shell-deb/build.sbt index 90afa2886..f5157d658 100644 --- a/src/sbt-test/debian/daemon-user-shell-deb/build.sbt +++ b/src/sbt-test/debian/daemon-user-shell-deb/build.sbt @@ -1,7 +1,6 @@ -import NativePackagerKeys._ import com.typesafe.sbt.packager.archetypes.ServerLoader -packageArchetype.java_server +enablePlugins(JavaServerAppPackaging) serverLoading in Debian := ServerLoader.Upstart diff --git a/src/sbt-test/debian/gen-changes/build.sbt b/src/sbt-test/debian/gen-changes/build.sbt index dff9b9351..dd46a812f 100644 --- a/src/sbt-test/debian/gen-changes/build.sbt +++ b/src/sbt-test/debian/gen-changes/build.sbt @@ -1,8 +1,4 @@ -import NativePackagerKeys._ - -packagerSettings - -mapGenericFilesToLinux +enablePlugins(SbtNativePackager) name := "debian-test" diff --git a/src/sbt-test/debian/java-app-archetype/build.sbt b/src/sbt-test/debian/java-app-archetype/build.sbt index fb9163f5c..6d7c12f06 100644 --- a/src/sbt-test/debian/java-app-archetype/build.sbt +++ b/src/sbt-test/debian/java-app-archetype/build.sbt @@ -1,6 +1,4 @@ -import NativePackagerKeys._ - -packageArchetype.java_application +enablePlugins(JavaServerAppPackaging) name := "debian-test" diff --git a/src/sbt-test/debian/jdeb-dir-mappings/build.sbt b/src/sbt-test/debian/jdeb-dir-mappings/build.sbt index a59e4f1d3..49b7247f2 100644 --- a/src/sbt-test/debian/jdeb-dir-mappings/build.sbt +++ b/src/sbt-test/debian/jdeb-dir-mappings/build.sbt @@ -1,8 +1,4 @@ -import NativePackagerKeys._ - -packagerSettings - -mapGenericFilesToLinux +enablePlugins(JavaServerAppPackaging, JDebPackaging) name := "debian-test" @@ -25,8 +21,6 @@ linuxPackageMappings in Debian += packageDirectoryAndContentsMapping( linuxPackageMappings in Debian += packageDirectoryAndContentsMapping( (baseDirectory.value / "src" / "resources" / "empty") -> "/var/empty") -packageBin in Debian <<= debianJDebPackaging in Debian - TaskKey[Unit]("check-dir-mappings") <<= (target, streams) map { (target, out) => // val tmpDir = java.nio.file.Files.createTempDirectory("jdeb") val extracted = file("/tmp/jdeb" + System.currentTimeMillis().toString) diff --git a/src/sbt-test/debian/log-directory/build.sbt b/src/sbt-test/debian/log-directory/build.sbt index 30b643a5c..bd7191f31 100644 --- a/src/sbt-test/debian/log-directory/build.sbt +++ b/src/sbt-test/debian/log-directory/build.sbt @@ -1,7 +1,6 @@ -import NativePackagerKeys._ import com.typesafe.sbt.packager.archetypes.ServerLoader -packageArchetype.java_server +enablePlugins(JavaServerAppPackaging) serverLoading in Debian := ServerLoader.Upstart diff --git a/src/sbt-test/debian/override-control-files/build.sbt b/src/sbt-test/debian/override-control-files/build.sbt index 48cd4b449..7a31729dd 100644 --- a/src/sbt-test/debian/override-control-files/build.sbt +++ b/src/sbt-test/debian/override-control-files/build.sbt @@ -1,7 +1,6 @@ -import NativePackagerKeys._ import com.typesafe.sbt.packager.archetypes.ServerLoader -packageArchetype.java_server +enablePlugins(JavaServerAppPackaging) mainClass in Compile := Some("empty") diff --git a/src/sbt-test/debian/simple-deb/build.sbt b/src/sbt-test/debian/simple-deb/build.sbt index a856482cd..4a33180f3 100644 --- a/src/sbt-test/debian/simple-deb/build.sbt +++ b/src/sbt-test/debian/simple-deb/build.sbt @@ -1,8 +1,4 @@ -import NativePackagerKeys._ - -packagerSettings - -mapGenericFilesToLinux +enablePlugins(DebianPlugin) name := "debian-test" diff --git a/src/sbt-test/debian/simple-jdeb/build.sbt b/src/sbt-test/debian/simple-jdeb/build.sbt index 9351893a3..091996703 100644 --- a/src/sbt-test/debian/simple-jdeb/build.sbt +++ b/src/sbt-test/debian/simple-jdeb/build.sbt @@ -1,8 +1,4 @@ -import NativePackagerKeys._ - -packagerSettings - -mapGenericFilesToLinux +enablePlugins(JDebPackaging) name := "debian-test" @@ -18,5 +14,3 @@ packageDescription := """A fun package description of our software, debianPackageDependencies in Debian ++= Seq("java2-runtime", "bash (>= 2.05a-11)") debianPackageRecommends in Debian += "git" - -packageBin in Debian <<= debianJDebPackaging in Debian diff --git a/src/sbt-test/debian/systemd-deb/build.sbt b/src/sbt-test/debian/systemd-deb/build.sbt index 16ce883b8..8a8366df4 100644 --- a/src/sbt-test/debian/systemd-deb/build.sbt +++ b/src/sbt-test/debian/systemd-deb/build.sbt @@ -1,7 +1,6 @@ -import NativePackagerKeys._ import com.typesafe.sbt.packager.archetypes.ServerLoader -packageArchetype.java_server +enablePlugins(JavaServerAppPackaging) serverLoading in Debian := ServerLoader.Systemd diff --git a/src/sbt-test/debian/sysvinit-deb/build.sbt b/src/sbt-test/debian/sysvinit-deb/build.sbt index 09f83fa64..7ae94b82b 100644 --- a/src/sbt-test/debian/sysvinit-deb/build.sbt +++ b/src/sbt-test/debian/sysvinit-deb/build.sbt @@ -1,7 +1,6 @@ -import NativePackagerKeys._ import com.typesafe.sbt.packager.archetypes.ServerLoader -packageArchetype.java_server +enablePlugins(JavaServerAppPackaging) serverLoading in Debian := ServerLoader.SystemV diff --git a/src/sbt-test/debian/test-executableScriptName/build.sbt b/src/sbt-test/debian/test-executableScriptName/build.sbt index 8384e6597..d2ac73a2d 100644 --- a/src/sbt-test/debian/test-executableScriptName/build.sbt +++ b/src/sbt-test/debian/test-executableScriptName/build.sbt @@ -1,8 +1,4 @@ -import NativePackagerKeys._ - -packagerSettings - -packageArchetype.java_server +enablePlugins(JavaServerAppPackaging) name := "debian-test" diff --git a/src/sbt-test/debian/test-mapping-helpers/build.sbt b/src/sbt-test/debian/test-mapping-helpers/build.sbt index 0e7c61a08..744ba548b 100644 --- a/src/sbt-test/debian/test-mapping-helpers/build.sbt +++ b/src/sbt-test/debian/test-mapping-helpers/build.sbt @@ -1,9 +1,6 @@ -import NativePackagerKeys._ import NativePackagerHelper._ -packagerSettings - -mapGenericFilesToLinux +enablePlugins(SbtNativePackager) name := "debian-test" diff --git a/src/sbt-test/debian/test-mapping/build.sbt b/src/sbt-test/debian/test-mapping/build.sbt index 67ab0c4df..c816c071a 100644 --- a/src/sbt-test/debian/test-mapping/build.sbt +++ b/src/sbt-test/debian/test-mapping/build.sbt @@ -1,8 +1,4 @@ -import NativePackagerKeys._ - -packagerSettings - -mapGenericFilesToLinux +enablePlugins(DebianPlugin) name := "debian-test" diff --git a/src/sbt-test/debian/test-packageName/build.sbt b/src/sbt-test/debian/test-packageName/build.sbt index 8c85d3f35..dd15839ea 100644 --- a/src/sbt-test/debian/test-packageName/build.sbt +++ b/src/sbt-test/debian/test-packageName/build.sbt @@ -1,8 +1,4 @@ -import NativePackagerKeys._ - -packagerSettings - -packageArchetype.java_server +enablePlugins(JavaServerAppPackaging) name := "debian-test" diff --git a/src/sbt-test/debian/upstart-deb-facilities/build.sbt b/src/sbt-test/debian/upstart-deb-facilities/build.sbt index 246baf489..8b7b14855 100644 --- a/src/sbt-test/debian/upstart-deb-facilities/build.sbt +++ b/src/sbt-test/debian/upstart-deb-facilities/build.sbt @@ -1,7 +1,6 @@ -import NativePackagerKeys._ import com.typesafe.sbt.packager.archetypes.ServerLoader -packageArchetype.java_server +enablePlugins(JavaServerAppPackaging) serverLoading in Debian := ServerLoader.Upstart diff --git a/src/sbt-test/debian/upstart-deb/build.sbt b/src/sbt-test/debian/upstart-deb/build.sbt index e2b6fe53c..54aa7fbb9 100644 --- a/src/sbt-test/debian/upstart-deb/build.sbt +++ b/src/sbt-test/debian/upstart-deb/build.sbt @@ -1,7 +1,6 @@ -import NativePackagerKeys._ import com.typesafe.sbt.packager.archetypes.ServerLoader -packageArchetype.java_server +enablePlugins(JavaServerAppPackaging) serverLoading in Debian := ServerLoader.Upstart diff --git a/src/sbt-test/docker/simple-docker/build.sbt b/src/sbt-test/docker/simple-docker/build.sbt index 9d13ebcbe..f812fe56d 100644 --- a/src/sbt-test/docker/simple-docker/build.sbt +++ b/src/sbt-test/docker/simple-docker/build.sbt @@ -1,7 +1,4 @@ -import com.typesafe.sbt.SbtNativePackager._ -import NativePackagerKeys._ - -packageArchetype.java_application +enablePlugins(JavaAppPackaging) name := "docker-test" diff --git a/src/sbt-test/docker/staging/build.sbt b/src/sbt-test/docker/staging/build.sbt index 8c687dea6..3f024b51e 100644 --- a/src/sbt-test/docker/staging/build.sbt +++ b/src/sbt-test/docker/staging/build.sbt @@ -1,6 +1,4 @@ -import NativePackagerKeys._ - -packagerSettings +enablePlugins(SbtNativePackager) name := "simple-test" diff --git a/src/sbt-test/docker/staging/test b/src/sbt-test/docker/staging/test index 23231c0b6..430b59004 100644 --- a/src/sbt-test/docker/staging/test +++ b/src/sbt-test/docker/staging/test @@ -1,4 +1,4 @@ # Stage the distribution and ensure files show up. -> show docker:stage +> docker:stage $ exists target/docker/Dockerfile $ exists target/docker/files diff --git a/src/sbt-test/docker/test-executableScriptName/build.sbt b/src/sbt-test/docker/test-executableScriptName/build.sbt index c2c5eb358..c08a67139 100644 --- a/src/sbt-test/docker/test-executableScriptName/build.sbt +++ b/src/sbt-test/docker/test-executableScriptName/build.sbt @@ -1,7 +1,4 @@ -import com.typesafe.sbt.SbtNativePackager._ -import NativePackagerKeys._ - -packageArchetype.java_application +enablePlugins(JavaAppPackaging) name := "docker-test" diff --git a/src/sbt-test/docker/test-packageName-universal/build.sbt b/src/sbt-test/docker/test-packageName-universal/build.sbt index fa360f39e..fdb6f8ee7 100644 --- a/src/sbt-test/docker/test-packageName-universal/build.sbt +++ b/src/sbt-test/docker/test-packageName-universal/build.sbt @@ -1,11 +1,8 @@ -import com.typesafe.sbt.SbtNativePackager._ -import NativePackagerKeys._ - -packageArchetype.java_application +enablePlugins(JavaAppPackaging) name := "docker-test" -packageName := "docker-package" // sets the executable script, too +packageName := "docker-package" version := "0.1.0" @@ -13,7 +10,7 @@ maintainer := "Gary Coady " TaskKey[Unit]("check-dockerfile") <<= (target, streams) map { (target, out) => val dockerfile = IO.read(target / "docker" / "Dockerfile") - assert(dockerfile.contains("ENTRYPOINT [\"bin/docker-package\"]\n"), "dockerfile doesn't contain ENTRYPOINT [\"docker-package\"]\n" + dockerfile) + assert(dockerfile.contains("ENTRYPOINT [\"bin/docker-test\"]\n"), "dockerfile doesn't contain ENTRYPOINT [\"docker-test\"]\n" + dockerfile) out.log.success("Successfully tested control script") () } \ No newline at end of file diff --git a/src/sbt-test/docker/test-packageName-universal/test b/src/sbt-test/docker/test-packageName-universal/test index c1208e7f4..857cfe0ac 100644 --- a/src/sbt-test/docker/test-packageName-universal/test +++ b/src/sbt-test/docker/test-packageName-universal/test @@ -1,6 +1,6 @@ # Generate the Docker image locally > docker:publishLocal $ exists target/docker/Dockerfile -$ exists target/docker/files/opt/docker/bin/docker-package +$ exists target/docker/files/opt/docker/bin/docker-test > check-dockerfile $ exec bash -c 'docker run docker-package:0.1.0 | grep -q "Hello world"' \ No newline at end of file diff --git a/src/sbt-test/docker/test-packageName/build.sbt b/src/sbt-test/docker/test-packageName/build.sbt index 5592141fd..8b4d929fe 100644 --- a/src/sbt-test/docker/test-packageName/build.sbt +++ b/src/sbt-test/docker/test-packageName/build.sbt @@ -1,7 +1,4 @@ -import com.typesafe.sbt.SbtNativePackager._ -import NativePackagerKeys._ - -packageArchetype.java_application +enablePlugins(JavaAppPackaging) name := "docker-test" diff --git a/src/sbt-test/docker/update-latest/build.sbt b/src/sbt-test/docker/update-latest/build.sbt index c47083808..dfcd240e1 100644 --- a/src/sbt-test/docker/update-latest/build.sbt +++ b/src/sbt-test/docker/update-latest/build.sbt @@ -1,7 +1,4 @@ -import com.typesafe.sbt.SbtNativePackager._ -import NativePackagerKeys._ - -packageArchetype.java_application +enablePlugins(JavaAppPackaging) name := "docker-test" diff --git a/src/sbt-test/docker/volumes/build.sbt b/src/sbt-test/docker/volumes/build.sbt index e04a596cd..72989d106 100644 --- a/src/sbt-test/docker/volumes/build.sbt +++ b/src/sbt-test/docker/volumes/build.sbt @@ -1,6 +1,4 @@ -import NativePackagerKeys._ - -packagerSettings +enablePlugins(SbtNativePackager) name := "simple-test" diff --git a/src/sbt-test/rpm/changelog-test/build.sbt b/src/sbt-test/rpm/changelog-test/build.sbt index 23938119e..24da95e94 100644 --- a/src/sbt-test/rpm/changelog-test/build.sbt +++ b/src/sbt-test/rpm/changelog-test/build.sbt @@ -1,6 +1,4 @@ -import NativePackagerKeys._ - -packageArchetype.java_server +enablePlugins(JavaServerAppPackaging) name := "rpm-test" diff --git a/src/sbt-test/rpm/scriptlets-override-build-rpm/build.sbt b/src/sbt-test/rpm/scriptlets-override-build-rpm/build.sbt index 36cf88397..149cb9903 100644 --- a/src/sbt-test/rpm/scriptlets-override-build-rpm/build.sbt +++ b/src/sbt-test/rpm/scriptlets-override-build-rpm/build.sbt @@ -1,6 +1,4 @@ -import NativePackagerKeys._ - -packageArchetype.java_server +enablePlugins(JavaServerAppPackaging) name := "rpm-test" diff --git a/src/sbt-test/rpm/scriptlets-override-rpm/build.sbt b/src/sbt-test/rpm/scriptlets-override-rpm/build.sbt index e7e4231d3..aa68c95a1 100644 --- a/src/sbt-test/rpm/scriptlets-override-rpm/build.sbt +++ b/src/sbt-test/rpm/scriptlets-override-rpm/build.sbt @@ -1,7 +1,5 @@ -import NativePackagerKeys._ - // only works with java_server archetype -packageArchetype.java_server +enablePlugins(JavaServerAppPackaging) name := "rpm-test" diff --git a/src/sbt-test/rpm/scriptlets-rpm/build.sbt b/src/sbt-test/rpm/scriptlets-rpm/build.sbt index 0f9b0fd47..a85e3eb43 100644 --- a/src/sbt-test/rpm/scriptlets-rpm/build.sbt +++ b/src/sbt-test/rpm/scriptlets-rpm/build.sbt @@ -1,8 +1,4 @@ -import NativePackagerKeys._ - -packagerSettings - -mapGenericFilesToLinux +enablePlugins(SbtNativePackager) name := "rpm-test" diff --git a/src/sbt-test/rpm/simple-rpm/build.sbt b/src/sbt-test/rpm/simple-rpm/build.sbt index 38bbcd9aa..81bea06e5 100644 --- a/src/sbt-test/rpm/simple-rpm/build.sbt +++ b/src/sbt-test/rpm/simple-rpm/build.sbt @@ -1,8 +1,4 @@ -import NativePackagerKeys._ - -packagerSettings - -mapGenericFilesToLinux +enablePlugins(SbtNativePackager) name := "rpm-test" diff --git a/src/sbt-test/rpm/systemd-rpm/build.sbt b/src/sbt-test/rpm/systemd-rpm/build.sbt index e0e7d9b19..b8c8e7759 100644 --- a/src/sbt-test/rpm/systemd-rpm/build.sbt +++ b/src/sbt-test/rpm/systemd-rpm/build.sbt @@ -1,7 +1,6 @@ -import NativePackagerKeys._ import com.typesafe.sbt.packager.archetypes.ServerLoader -packageArchetype.java_server +enablePlugins(JavaServerAppPackaging) serverLoading in Rpm := ServerLoader.Systemd diff --git a/src/sbt-test/rpm/sysvinit-rpm/build.sbt b/src/sbt-test/rpm/sysvinit-rpm/build.sbt index f9e3909a2..1ab3d6296 100644 --- a/src/sbt-test/rpm/sysvinit-rpm/build.sbt +++ b/src/sbt-test/rpm/sysvinit-rpm/build.sbt @@ -1,6 +1,4 @@ -import NativePackagerKeys._ - -packageArchetype.java_server +enablePlugins(JavaServerAppPackaging) name := "rpm-test" diff --git a/src/sbt-test/rpm/test-executableScriptName/build.sbt b/src/sbt-test/rpm/test-executableScriptName/build.sbt index 8acf73577..c208ff9d5 100644 --- a/src/sbt-test/rpm/test-executableScriptName/build.sbt +++ b/src/sbt-test/rpm/test-executableScriptName/build.sbt @@ -1,6 +1,4 @@ -import NativePackagerKeys._ - -packageArchetype.java_server +enablePlugins(JavaServerAppPackaging) name := "rpm-test" diff --git a/src/sbt-test/rpm/test-packageName/build.sbt b/src/sbt-test/rpm/test-packageName/build.sbt index 3f7397033..f7d44f2b9 100644 --- a/src/sbt-test/rpm/test-packageName/build.sbt +++ b/src/sbt-test/rpm/test-packageName/build.sbt @@ -1,6 +1,4 @@ -import NativePackagerKeys._ - -packageArchetype.java_server +enablePlugins(JavaServerAppPackaging) name := "rpm-test" diff --git a/src/sbt-test/universal/clear-stage-dir/build.sbt b/src/sbt-test/universal/clear-stage-dir/build.sbt index 8c687dea6..3f024b51e 100644 --- a/src/sbt-test/universal/clear-stage-dir/build.sbt +++ b/src/sbt-test/universal/clear-stage-dir/build.sbt @@ -1,6 +1,4 @@ -import NativePackagerKeys._ - -packagerSettings +enablePlugins(SbtNativePackager) name := "simple-test" diff --git a/src/sbt-test/universal/dist/build.sbt b/src/sbt-test/universal/dist/build.sbt index 8c687dea6..3f024b51e 100644 --- a/src/sbt-test/universal/dist/build.sbt +++ b/src/sbt-test/universal/dist/build.sbt @@ -1,6 +1,4 @@ -import NativePackagerKeys._ - -packagerSettings +enablePlugins(SbtNativePackager) name := "simple-test" diff --git a/src/sbt-test/universal/multiproject-classifiers/project/Build.scala b/src/sbt-test/universal/multiproject-classifiers/project/Build.scala index f89ce14f2..7e2ae497f 100644 --- a/src/sbt-test/universal/multiproject-classifiers/project/Build.scala +++ b/src/sbt-test/universal/multiproject-classifiers/project/Build.scala @@ -1,6 +1,7 @@ import sbt._ import Keys._ import com.typesafe.sbt.SbtNativePackager._ +import com.typesafe.sbt.packager.Keys._ object MutliBuild extends Build { diff --git a/src/sbt-test/universal/publish/build.sbt b/src/sbt-test/universal/publish/build.sbt index 3e4cc1b36..66429d0bd 100644 --- a/src/sbt-test/universal/publish/build.sbt +++ b/src/sbt-test/universal/publish/build.sbt @@ -1,6 +1,4 @@ -import NativePackagerKeys._ - -packagerSettings +enablePlugins(SbtNativePackager) deploymentSettings diff --git a/src/sbt-test/universal/staging-custom-main/build.sbt b/src/sbt-test/universal/staging-custom-main/build.sbt index 130d8bc5e..78a8361b0 100644 --- a/src/sbt-test/universal/staging-custom-main/build.sbt +++ b/src/sbt-test/universal/staging-custom-main/build.sbt @@ -1,6 +1,4 @@ -import NativePackagerKeys._ - -packageArchetype.java_application +enablePlugins(JavaAppPackaging) name := "stage-custom-main" diff --git a/src/sbt-test/universal/staging/build.sbt b/src/sbt-test/universal/staging/build.sbt index 8c687dea6..3f024b51e 100644 --- a/src/sbt-test/universal/staging/build.sbt +++ b/src/sbt-test/universal/staging/build.sbt @@ -1,6 +1,4 @@ -import NativePackagerKeys._ - -packagerSettings +enablePlugins(SbtNativePackager) name := "simple-test" diff --git a/src/sbt-test/universal/test-akka/build.sbt b/src/sbt-test/universal/test-akka/build.sbt index dbf3afefd..d260fe601 100644 --- a/src/sbt-test/universal/test-akka/build.sbt +++ b/src/sbt-test/universal/test-akka/build.sbt @@ -1,6 +1,4 @@ -import NativePackagerKeys._ - -packageArchetype.akka_application +enablePlugins(AkkaAppPackaging) name := """test-akka""" diff --git a/src/sbt-test/universal/test-executableScriptName/build.sbt b/src/sbt-test/universal/test-executableScriptName/build.sbt index e59fb9447..6e8f4e3f6 100644 --- a/src/sbt-test/universal/test-executableScriptName/build.sbt +++ b/src/sbt-test/universal/test-executableScriptName/build.sbt @@ -1,6 +1,4 @@ -import NativePackagerKeys._ - -packageArchetype.java_application +enablePlugins(JavaAppPackaging) name := "simple-test" diff --git a/src/sbt-test/universal/test-mapping-helpers/build.sbt b/src/sbt-test/universal/test-mapping-helpers/build.sbt index 12f5c4243..34fe6002b 100644 --- a/src/sbt-test/universal/test-mapping-helpers/build.sbt +++ b/src/sbt-test/universal/test-mapping-helpers/build.sbt @@ -1,9 +1,6 @@ -import NativePackagerKeys._ import com.typesafe.sbt.packager.MappingsHelper._ -packagerSettings - -packagerSettings +enablePlugins(JavaAppPackaging) name := "simple-test" diff --git a/src/sbt-test/universal/test-native-zip/build.sbt b/src/sbt-test/universal/test-native-zip/build.sbt index 94d2c3997..95cbe0949 100644 --- a/src/sbt-test/universal/test-native-zip/build.sbt +++ b/src/sbt-test/universal/test-native-zip/build.sbt @@ -1,6 +1,4 @@ -import NativePackagerKeys._ - -packagerSettings +enablePlugins(JavaAppPackaging) name := "simple-test" diff --git a/src/sbt-test/universal/test-packageName/build.sbt b/src/sbt-test/universal/test-packageName/build.sbt index d49aa28d7..a2a05de59 100644 --- a/src/sbt-test/universal/test-packageName/build.sbt +++ b/src/sbt-test/universal/test-packageName/build.sbt @@ -1,6 +1,4 @@ -import NativePackagerKeys._ - -packageArchetype.java_application +enablePlugins(JavaAppPackaging) name := "simple-test" diff --git a/src/sbt-test/universal/test-zips/build.sbt b/src/sbt-test/universal/test-zips/build.sbt index 8c687dea6..b72e96af7 100644 --- a/src/sbt-test/universal/test-zips/build.sbt +++ b/src/sbt-test/universal/test-zips/build.sbt @@ -1,6 +1,4 @@ -import NativePackagerKeys._ - -packagerSettings +enablePlugins(JavaAppPackaging) name := "simple-test" diff --git a/src/sbt-test/windows/java-app-archetype/build.sbt b/src/sbt-test/windows/java-app-archetype/build.sbt index 6523ceee6..1ab1dcab1 100644 --- a/src/sbt-test/windows/java-app-archetype/build.sbt +++ b/src/sbt-test/windows/java-app-archetype/build.sbt @@ -1,6 +1,6 @@ import NativePackagerKeys._ -packageArchetype.java_application +enablePlugins(JavaAppPackaging) name := "windows-test" diff --git a/test-project-simple/build.sbt b/test-project-simple/build.sbt new file mode 100644 index 000000000..85b2a6b06 --- /dev/null +++ b/test-project-simple/build.sbt @@ -0,0 +1,19 @@ +name := "test-project-simple" +version := "0.2.0" +libraryDependencies ++= Seq( + "com.typesafe" % "config" % "1.2.1" +) + +mainClass in Compile := Some("ExampleApp") + +enablePlugins(JavaAppPackaging, JDebPackaging) + +maintainer := "Josh Suereth " +packageSummary := "Minimal Native Packager" +packageDescription := """A fun package description of our software, + with multiple lines.""" + +// RPM SETTINGS +rpmVendor := "typesafe" +rpmLicense := Some("BSD") +rpmChangelogFile := Some("changelog.txt") diff --git a/test-project-simple/project/build.properties b/test-project-simple/project/build.properties new file mode 100644 index 000000000..39d2a9a80 --- /dev/null +++ b/test-project-simple/project/build.properties @@ -0,0 +1 @@ +sbt.version=0.13.7-M3 diff --git a/test-project-simple/project/plugins.sbt b/test-project-simple/project/plugins.sbt new file mode 100644 index 000000000..5070c4644 --- /dev/null +++ b/test-project-simple/project/plugins.sbt @@ -0,0 +1,3 @@ +lazy val root = Project("plugins", file(".")) dependsOn(packager) + +lazy val packager = file("..").getAbsoluteFile.toURI diff --git a/test-project-simple/src/main/scala/ExampleApp.scala b/test-project-simple/src/main/scala/ExampleApp.scala new file mode 100644 index 000000000..66a697b6e --- /dev/null +++ b/test-project-simple/src/main/scala/ExampleApp.scala @@ -0,0 +1,4 @@ +object ExampleApp extends App { + + println("Hello, world!") +} diff --git a/test-project/README.md b/test-project/README.md index 4f66576cd..1daf39cd5 100644 --- a/test-project/README.md +++ b/test-project/README.md @@ -1,3 +1,5 @@ +> This project is based on play and won't work on the current master. Use the test-project-simple + This is a test project. If you run sbt here, it will pull in the sbt-native-packager local repo as a *source* dependency and allow you to make source changes, `reload` and exercise them in this build. A good way to test, and gives people an example project to look at. diff --git a/test-project/build.sbt b/test-project/build.sbt index 449a44ef1..59df27145 100644 --- a/test-project/build.sbt +++ b/test-project/build.sbt @@ -1,6 +1,4 @@ -import NativePackagerKeys._ - -play.Project.playScalaSettings +enablePlugins(PlayScala) name := "dtest" @@ -12,7 +10,7 @@ val dtestProj = ProjectRef(buildLoc, "np") version in dtestProj := "0.2.0" -packageArchetype.java_server +enablePlugins(JDebPackaging) maintainer := "Josh Suereth " @@ -20,10 +18,10 @@ packageSummary := "Test debian package" packageDescription := """A fun package description of our software, with multiple lines.""" - -debianPackageDependencies in Debian ++= Seq("java2-runtime", "bash (>= 2.05a-11)") - -debianPackageRecommends in Debian += "git" +// +//debianPackageDependencies in Debian ++= Seq("java2-runtime", "bash (>= 2.05a-11)") +// +//debianPackageRecommends in Debian += "git" rpmVendor := "typesafe" @@ -31,9 +29,6 @@ rpmLicense := Some("BSD") rpmChangelogFile := Some("changelog.txt") -//debianMakePrermScript := Some(sourceDirectory.value / "deb" / "control" / "prerm") //change defaults - - TaskKey[Unit]("check-script") <<= (NativePackagerKeys.stagingDirectory in Universal, target in Debian, name, version, maintainer in Debian, streams) map { (dir, debTarget, name, version, author, streams) => val script = dir / "bin" / name @@ -59,5 +54,4 @@ TaskKey[Unit]("check-script") <<= (NativePackagerKeys.stagingDirectory in Univer val prermOutput = Process("bash " + prerm.getAbsolutePath).!! val prermExpected = "removing ${{name}}-${{version}} from ${{author}}" assert(prermOutput contains prermExpected, s"Failed to correctly run the prerm script!. Found [${prermOutput}] wanted [${prermExpected}]") -} - +} \ No newline at end of file diff --git a/test-project/project/build.properties b/test-project/project/build.properties index be6c454fb..64abd373f 100644 --- a/test-project/project/build.properties +++ b/test-project/project/build.properties @@ -1 +1 @@ -sbt.version=0.13.5 +sbt.version=0.13.6 diff --git a/test-project/project/plugins.sbt b/test-project/project/plugins.sbt index 3edf28d10..90db06313 100644 --- a/test-project/project/plugins.sbt +++ b/test-project/project/plugins.sbt @@ -1,4 +1,4 @@ resolvers += Resolver.typesafeRepo("releases") -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.2.0") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.3.4")