From e4c01ac8ba012f903d5e7b4a15749c80ae0b0df1 Mon Sep 17 00:00:00 2001 From: Nepomuk Seiler Date: Wed, 19 Feb 2014 23:26:25 +0100 Subject: [PATCH 1/8] Replacing the _appUser_ and _appGroup_ with _daemonUser_ and _daemonGroup_ --- .../sbt/packager/GenericPackageSettings.scala | 130 +++++++++--------- .../sbt/packager/archetypes/JavaApp.scala | 14 +- .../archetypes/JavaAppUpstartScript.scala | 4 +- .../archetypes/JavaServerApplication.scala | 14 +- .../typesafe/sbt/packager/debian/Keys.scala | 5 - .../typesafe/sbt/packager/linux/Keys.scala | 7 +- .../sbt/packager/linux/LinuxPlugin.scala | 3 +- src/sbt-test/debian/daemon-user-deb/build.sbt | 18 +-- 8 files changed, 94 insertions(+), 101 deletions(-) diff --git a/src/main/scala/com/typesafe/sbt/packager/GenericPackageSettings.scala b/src/main/scala/com/typesafe/sbt/packager/GenericPackageSettings.scala index a66f1d365..a82203e9c 100644 --- a/src/main/scala/com/typesafe/sbt/packager/GenericPackageSettings.scala +++ b/src/main/scala/com/typesafe/sbt/packager/GenericPackageSettings.scala @@ -3,16 +3,16 @@ package packager import Keys._ import sbt._ -import sbt.Keys.{name, mappings, sourceDirectory} +import sbt.Keys.{ name, mappings, sourceDirectory } import linux.LinuxSymlink import linux.LinuxPackageMapping -trait GenericPackageSettings - extends linux.LinuxPlugin - with debian.DebianPlugin - with rpm.RpmPlugin - with windows.WindowsPlugin - with universal.UniversalPlugin { +trait GenericPackageSettings + extends linux.LinuxPlugin + with debian.DebianPlugin + with rpm.RpmPlugin + with windows.WindowsPlugin + with universal.UniversalPlugin { import linux.LinuxPlugin.Users @@ -21,10 +21,10 @@ trait GenericPackageSettings // 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` @@ -40,24 +40,23 @@ trait GenericPackageSettings case (file, name) => (name contains "man/") && (name endsWith ".1") } val compressedManPages = - for((file, name) <- manPages) - yield file -> (name + ".gz") + 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:_*) + 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" - ) + 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( @@ -67,50 +66,49 @@ trait GenericPackageSettings defaultLinuxLogsLocation := "/var/log", // First we look at the src/linux files - linuxPackageMappings <++= (sourceDirectory in Linux, appUser in Linux, appGroup in Linux) map { (dir, user, group) => + linuxPackageMappings <++= (sourceDirectory in Linux, daemonUser in Linux, daemonGroup in Linux) map { (dir, user, group) => mapGenericMappingsToLinux((dir.*** --- dir) x relativeTo(dir), user, group)(identity) }, // Now we look at the src/universal files. - linuxPackageMappings <++= (normalizedName in Universal, mappings in Universal, defaultLinuxInstallLocation, appUser in Linux, appGroup in Linux) map { + linuxPackageMappings <++= (normalizedName in Universal, mappings in Universal, defaultLinuxInstallLocation, daemonUser in Linux, daemonGroup in Linux) map { (pkg, mappings, installLocation, user, group) => - // TODO - More windows filters... - def isWindowsFile(f: (File, String)): Boolean = - f._2 endsWith ".bat" - - mapGenericMappingsToLinux(mappings filterNot isWindowsFile, user, group) { name => - installLocation + "/" + pkg + "/" + name - } + // TODO - More windows filters... + def isWindowsFile(f: (File, String)): Boolean = + f._2 endsWith ".bat" + + mapGenericMappingsToLinux(mappings filterNot isWindowsFile, user, group) { name => + installLocation + "/" + pkg + "/" + name + } }, // Now we generate symlinks. linuxPackageSymlinks <++= (normalizedName in Universal, 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) + 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 <++= (normalizedName in Universal, mappings in Universal, defaultLinuxInstallLocation) map { (pkg, mappings, installLocation) => val needsConfLink = - mappings exists { case (file, name) => - (name startsWith "conf/") && !file.isDirectory + mappings exists { + case (file, name) => + (name startsWith "conf/") && !file.isDirectory } - if(needsConfLink) Seq(LinuxSymlink( - link="/etc/" + pkg, - destination=installLocation+"/"+pkg+"/conf")) + if (needsConfLink) Seq(LinuxSymlink( + link = "/etc/" + 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 - ) + 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 @@ -118,36 +116,32 @@ trait GenericPackageSettings } 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 - ) + 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")) - ) + 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("/") + (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)) - ) + 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/archetypes/JavaApp.scala b/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaApp.scala index 3f5ab6da9..44f945da5 100644 --- a/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaApp.scala +++ b/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaApp.scala @@ -6,7 +6,7 @@ import Keys._ import sbt._ import sbt.Project.Initialize import sbt.Keys.{ mappings, target, name, mainClass, normalizedName, sourceDirectory } -import com.typesafe.sbt.packager.linux.{LinuxFileMetaData, LinuxPackageMapping} +import com.typesafe.sbt.packager.linux.{ LinuxFileMetaData, LinuxPackageMapping } import SbtNativePackager._ /** @@ -64,9 +64,9 @@ object JavaAppPackaging { s <- script.toSeq } yield s -> ("bin/" + name + ".bat") }, - linuxPackageMappings in Debian <+= (normalizedName, defaultLinuxInstallLocation, target in Debian, appUser in Linux, appGroup in Linux) map { + linuxPackageMappings in Debian <+= (normalizedName, defaultLinuxInstallLocation, target in Debian, daemonUser in Linux, daemonGroup in Linux) map { (name, installLocation, target, user, group) => - // create empty var/log directory + // create empty var/log directory val d = target / installLocation d.mkdirs() LinuxPackageMapping(Seq(d -> (installLocation + "/" + name)), LinuxFileMetaData(user, group)) @@ -86,8 +86,8 @@ object JavaAppPackaging { if (defines.isEmpty) None else { val defaultTemplateLocation = sourceDir / "templates" / "bash-template" - val scriptBits = - if(defaultTemplateLocation.exists) JavaAppBashScript.generateScript(defines, defaultTemplateLocation.toURI.toURL) + val scriptBits = + if (defaultTemplateLocation.exists) JavaAppBashScript.generateScript(defines, defaultTemplateLocation.toURI.toURL) else JavaAppBashScript.generateScript(defines) val script = tmpDir / "tmp" / "bin" / name IO.write(script, scriptBits) @@ -100,8 +100,8 @@ object JavaAppPackaging { if (replacements.isEmpty) None else { val defaultTemplateLocation = sourceDir / "templates" / "bat-template" - val scriptBits = - if(defaultTemplateLocation.exists) JavaAppBatScript.generateScript(replacements, defaultTemplateLocation.toURI.toURL) + val scriptBits = + if (defaultTemplateLocation.exists) JavaAppBatScript.generateScript(replacements, defaultTemplateLocation.toURI.toURL) else JavaAppBatScript.generateScript(replacements) val script = tmpDir / "tmp" / "bin" / (name + ".bat") IO.write(script, scriptBits) diff --git a/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaAppUpstartScript.scala b/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaAppUpstartScript.scala index d5602ace5..c8dbba48d 100644 --- a/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaAppUpstartScript.scala +++ b/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaAppUpstartScript.scala @@ -89,6 +89,7 @@ object JavaAppStartScript { chdir: String, appName: String, daemonUser: String, + daemonGroup: String, retries: Int = 0, retryTimeout: Int = 60): Seq[(String, String)] = Seq( @@ -99,7 +100,8 @@ object JavaAppStartScript { "retries" -> retries.toString, "retryTimeout" -> retryTimeout.toString, "app_name" -> appName, - "daemon_user" -> daemonUser) + "daemon_user" -> daemonUser, + "daemon_group" -> daemonGroup) } object ServerLoader extends Enumeration { 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 a9c8d4619..9488185b8 100644 --- a/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaServerApplication.scala +++ b/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaServerApplication.scala @@ -29,12 +29,11 @@ object JavaServerAppPackaging { def debianSettings: Seq[Setting[_]] = Seq( serverLoading := Upstart, - daemonUser <<= appUser in Linux, // This one is begging for sbt 0.13 syntax... debianScriptReplacements <<= ( - maintainer in Debian, packageSummary in Debian, serverLoading in Debian, daemonUser in Debian, normalizedName, + maintainer in Debian, packageSummary in Debian, serverLoading in Debian, daemonUser in Linux, daemonGroup in Linux, normalizedName, sbt.Keys.version, defaultLinuxInstallLocation) - map { (author, descr, loader, daemonUser, name, version, installLocation) => + map { (author, descr, loader, daemonUser, daemonGroup, name, version, installLocation) => val appDir = installLocation + "/" + name JavaAppStartScript.makeReplacements( @@ -43,7 +42,8 @@ object JavaServerAppPackaging { execScript = name, chdir = appDir, appName = name, - daemonUser = daemonUser) + daemonUser = daemonUser, + daemonGroup = daemonGroup) }, // TODO - Default locations shouldn't be so hacky. @@ -55,7 +55,7 @@ object JavaServerAppPackaging { map { (tmpDir, loader, replacements, template) => makeDebianMaintainerScript(JavaAppStartScript.startScript, Some(template))(tmpDir, loader, replacements) }, - linuxPackageMappings in Debian <++= (debianMakeStartScript, normalizedName, serverLoading in Debian, appUser in Linux, appGroup in Linux) + linuxPackageMappings in Debian <++= (debianMakeStartScript, normalizedName, serverLoading in Debian, daemonUser in Linux, daemonGroup in Linux) map { (script, name, loader, owner, ownerGroup) => val (path, permissions) = loader match { case Upstart => ("/etc/init/" + name + ".conf", "0644") @@ -75,13 +75,13 @@ object JavaServerAppPackaging { }, debianMakeEtcDefault <<= (normalizedName, target in Universal, linuxEtcDefaultTemplate in Debian, debianScriptReplacements) map makeEtcDefaultScript, - linuxPackageMappings in Debian <++= (debianMakeEtcDefault, normalizedName, appUser in Linux, appGroup in Linux) map { (conf, name, owner, ownerGroup) => + linuxPackageMappings in Debian <++= (debianMakeEtcDefault, normalizedName, daemonUser in Linux, daemonGroup in Linux) map { (conf, name, owner, ownerGroup) => conf.map(c => LinuxPackageMapping(Seq(c -> ("/etc/default/" + name)), LinuxFileMetaData(owner, ownerGroup)).withConfig()).toSeq }, // TODO should we specify daemonGroup in configs? // === logging directory mapping === - linuxPackageMappings in Debian <+= (normalizedName, defaultLinuxLogsLocation, target in Debian, appUser in Linux, appGroup in Linux) map { + linuxPackageMappings in Debian <+= (normalizedName, defaultLinuxLogsLocation, target in Debian, daemonUser in Linux, daemonGroup in Linux) map { (name, logsDir, target, user, group) => // create empty var/log directory val d = target / logsDir 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 9d6bafeb0..3fa869939 100644 --- a/src/main/scala/com/typesafe/sbt/packager/debian/Keys.scala +++ b/src/main/scala/com/typesafe/sbt/packager/debian/Keys.scala @@ -83,12 +83,7 @@ object Keys extends DebianKeys { def target = sbt.Keys.target def streams = sbt.Keys.streams - // file ownership - def appUser = linux.Keys.appUser - def appGroup = linux.Keys.appGroup - //init script parameters - def daemonUser = linux.Keys.daemonUser def serverLoading = linux.Keys.serverLoading val debianPackageInstallSize = TaskKey[Long]("debian-installed-size") 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 36305bcc9..d81649d18 100644 --- a/src/main/scala/com/typesafe/sbt/packager/linux/Keys.scala +++ b/src/main/scala/com/typesafe/sbt/packager/linux/Keys.scala @@ -11,14 +11,13 @@ trait Keys { 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 appUser = SettingKey[String]("app-user", "The owner of the files in the package") - val appGroup = SettingKey[String]("app-group", "The group owner of the files in the package") val daemonUser = SettingKey[String]("daemon-user", "User to start application daemon") + val daemonGroup = SettingKey[String]("daemon-group", "Group to start application daemon") val serverLoading = SettingKey[ServerLoader]("server-loader", "Loading system to be used for application start script") val linuxPackageMappings = TaskKey[Seq[LinuxPackageMapping]]("linux-package-mappings", "File to install location mappings including owner and privileges.") val linuxPackageSymlinks = TaskKey[Seq[LinuxSymlink]]("linux-package-symlinks", "Symlinks we should produce in the underlying package.") - val generateManPages = TaskKey[Unit]("generate-man-pages", "Shows all the man files in the current project") - + val generateManPages = TaskKey[Unit]("generate-man-pages", "Shows all the man files in the current project") + val linuxStartScriptTemplate = TaskKey[URL]("linuxStartScriptTemplate", "The location of the template start script file we use for debian (upstart or init.d") val linuxEtcDefaultTemplate = TaskKey[URL]("linuxEtcDefaultTemplate", "The location of the /etc/default/ template script.") } 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 89a21c55e..83a133985 100644 --- a/src/main/scala/com/typesafe/sbt/packager/linux/LinuxPlugin.scala +++ b/src/main/scala/com/typesafe/sbt/packager/linux/LinuxPlugin.scala @@ -28,7 +28,8 @@ trait LinuxPlugin extends Plugin { }, packageSummary in Linux <<= packageSummary, packageDescription in Linux <<= packageDescription, - appUser <<= normalizedName, appGroup <<= appUser in Linux) + daemonUser in Linux <<= normalizedName, + daemonGroup <<= daemonUser in Linux) /** DSL for packaging files into .deb */ def packageMapping(files: (File, String)*) = LinuxPackageMapping(files) diff --git a/src/sbt-test/debian/daemon-user-deb/build.sbt b/src/sbt-test/debian/daemon-user-deb/build.sbt index ea88419d9..09a8697dc 100644 --- a/src/sbt-test/debian/daemon-user-deb/build.sbt +++ b/src/sbt-test/debian/daemon-user-deb/build.sbt @@ -5,9 +5,9 @@ packageArchetype.java_server serverLoading in Debian := ServerLoader.Upstart -appUser in Linux := "daemonUser" +daemonUser in Linux := "daemonuser" -appGroup in Linux := "daemonGroup" +daemonGroup in Linux := "daemongroup" mainClass in Compile := Some("empty") @@ -26,12 +26,14 @@ TaskKey[Unit]("check-control-files") <<= (target, streams) map { (target, out) = val debian = target / "debian-test-0.1.0" / "DEBIAN" val postinst = IO.read(debian / "postinst") val postrm = IO.read(debian / "postrm") - assert(postinst contains "addgroup --system daemonGroup", "postinst misses addgroup for daemonGroup: " + postinst) - assert(postinst contains "useradd --system --no-create-home --gid daemonGroup --shell /bin/false daemonUser", "postinst misses useradd for daemonUser: " + postinst) - assert(postinst contains "chown daemonUser:daemonGroup /var/log/debian-test", "postinst misses chown daemonUser /var/log/debian-test: " + postinst) - assert(postinst contains "chown daemonUser:daemonGroup /usr/share/debian-test/bin/debian-test", "postinst misses chown daemonUser /usr/share/debian-test/bin/debian-test: " + postinst) - assert(postrm contains "deluser --quiet --system daemonUser > /dev/null || true", "postrm misses purging daemonUser user: " + postrm) - assert(postrm contains "delgroup --quiet --system daemonGroup > /dev/null || true", "postrm misses purging daemonGroup group: " + postrm) + assert(postinst contains "addgroup --system daemongroup", "postinst misses addgroup for daemongroup: " + postinst) + assert(postinst contains "useradd --system --no-create-home --gid daemongroup --shell /bin/false daemonuser", "postinst misses useradd for daemonuser: " + postinst) + assert(postinst contains "chown daemonuser:daemongroup /var/log/debian-test", "postinst misses chown daemonuser /var/log/debian-test: " + postinst) + assert(postinst contains "chown daemonuser:daemongroup /usr/share/debian-test/bin/debian-test", "postinst misses chown daemonuser /usr/share/debian-test/bin/debian-test: " + postinst) + assert(!(postinst contains "addgroup --system daemonuser"), "postinst has addgroup for daemonuser: " + postinst) + assert(!(postinst contains "useradd --system --no-create-home --gid daemonuser --shell /bin/false daemonuser"), "postinst has useradd for daemongroup: " + postinst) + assert(postrm contains "deluser --quiet --system daemonuser > /dev/null || true", "postrm misses purging daemonuser user: " + postrm) + assert(postrm contains "delgroup --quiet --system daemongroup > /dev/null || true", "postrm misses purging daemongroup group: " + postrm) out.log.success("Successfully tested upstart control files") () } From 5f615bdba62012acae6c79bec35a8e90c251bb76 Mon Sep 17 00:00:00 2001 From: Nepomuk Seiler Date: Sat, 22 Feb 2014 11:45:07 +0100 Subject: [PATCH 2/8] Follow up changes (re check them) --- .../typesafe/sbt/packager/GenericPackageSettings.scala | 1 + src/main/scala/com/typesafe/sbt/packager/Keys.scala | 1 + .../packager/archetypes/JavaServerApplication.scala | 10 +++++----- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/scala/com/typesafe/sbt/packager/GenericPackageSettings.scala b/src/main/scala/com/typesafe/sbt/packager/GenericPackageSettings.scala index a82203e9c..7437b18f4 100644 --- a/src/main/scala/com/typesafe/sbt/packager/GenericPackageSettings.scala +++ b/src/main/scala/com/typesafe/sbt/packager/GenericPackageSettings.scala @@ -64,6 +64,7 @@ trait GenericPackageSettings // Default place to install code. defaultLinuxInstallLocation := "/usr/share", defaultLinuxLogsLocation := "/var/log", + defaultLinuxConfigLocation := "/etc", // First we look at the src/linux files linuxPackageMappings <++= (sourceDirectory in Linux, daemonUser in Linux, daemonGroup in Linux) map { (dir, user, group) => diff --git a/src/main/scala/com/typesafe/sbt/packager/Keys.scala b/src/main/scala/com/typesafe/sbt/packager/Keys.scala index ebc2842c8..5359e7241 100644 --- a/src/main/scala/com/typesafe/sbt/packager/Keys.scala +++ b/src/main/scala/com/typesafe/sbt/packager/Keys.scala @@ -31,4 +31,5 @@ object Keys extends linux.Keys | """.stripMargin) 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") } \ No newline at end of file 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 9488185b8..0ea2c6ebf 100644 --- a/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaServerApplication.scala +++ b/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaServerApplication.scala @@ -55,15 +55,15 @@ object JavaServerAppPackaging { map { (tmpDir, loader, replacements, template) => makeDebianMaintainerScript(JavaAppStartScript.startScript, Some(template))(tmpDir, loader, replacements) }, - linuxPackageMappings in Debian <++= (debianMakeStartScript, normalizedName, serverLoading in Debian, daemonUser in Linux, daemonGroup in Linux) - map { (script, name, loader, owner, ownerGroup) => + linuxPackageMappings in Debian <++= (debianMakeStartScript, normalizedName, serverLoading in Debian) + map { (script, name, loader) => val (path, permissions) = loader match { case Upstart => ("/etc/init/" + name + ".conf", "0644") case SystemV => ("/etc/init.d/" + name, "0755") } for { s <- script.toSeq - } yield LinuxPackageMapping(Seq(s -> path), LinuxFileMetaData(owner, ownerGroup, permissions, "true")) + } yield LinuxPackageMapping(Seq(s -> path), LinuxFileMetaData(Users.Root, Users.Root, permissions, "true")) }, // === etc config mapping === @@ -75,8 +75,8 @@ object JavaServerAppPackaging { }, debianMakeEtcDefault <<= (normalizedName, target in Universal, linuxEtcDefaultTemplate in Debian, debianScriptReplacements) map makeEtcDefaultScript, - linuxPackageMappings in Debian <++= (debianMakeEtcDefault, normalizedName, daemonUser in Linux, daemonGroup in Linux) map { (conf, name, owner, ownerGroup) => - conf.map(c => LinuxPackageMapping(Seq(c -> ("/etc/default/" + name)), LinuxFileMetaData(owner, ownerGroup)).withConfig()).toSeq + linuxPackageMappings in Debian <++= (debianMakeEtcDefault, normalizedName) map { (conf, name) => + conf.map(c => LinuxPackageMapping(Seq(c -> ("/etc/default/" + name)), LinuxFileMetaData(Users.Root, Users.Root)).withConfig()).toSeq }, // TODO should we specify daemonGroup in configs? From 13fe73d047c1f45fd70ace5c5401a9f6c56e7d76 Mon Sep 17 00:00:00 2001 From: Nepomuk Seiler Date: Fri, 28 Feb 2014 08:58:53 +0100 Subject: [PATCH 3/8] Fix #149 First draft of rpm systemV init script - Added linuxSettings for server archetype - Added pid folder in server archetype - Refactored JavaStartScript class/object - Tested with CentOS 6.5 --- .../sbt/packager/archetypes/bash-template | 2 +- .../{start-template => start-debian-template} | 0 .../archetypes/systemv/start-rpm-template | 132 ++++++++++++++ .../{start-template => start-rpm-template} | 0 ...tScript.scala => JavaAppStartScript.scala} | 54 ++++-- .../archetypes/JavaServerApplication.scala | 164 +++++++++++------- .../typesafe/sbt/packager/debian/Keys.scala | 4 +- .../typesafe/sbt/packager/linux/Keys.scala | 19 ++ 8 files changed, 287 insertions(+), 88 deletions(-) rename src/main/resources/com/typesafe/sbt/packager/archetypes/systemv/{start-template => start-debian-template} (100%) create mode 100644 src/main/resources/com/typesafe/sbt/packager/archetypes/systemv/start-rpm-template rename src/main/resources/com/typesafe/sbt/packager/archetypes/upstart/{start-template => start-rpm-template} (100%) rename src/main/scala/com/typesafe/sbt/packager/archetypes/{JavaAppUpstartScript.scala => JavaAppStartScript.scala} (74%) diff --git a/src/main/resources/com/typesafe/sbt/packager/archetypes/bash-template b/src/main/resources/com/typesafe/sbt/packager/archetypes/bash-template index c03121648..1a48b55cf 100644 --- a/src/main/resources/com/typesafe/sbt/packager/archetypes/bash-template +++ b/src/main/resources/com/typesafe/sbt/packager/archetypes/bash-template @@ -128,7 +128,7 @@ execRunner () { } # We Don't use "exec" here for our pids to be accurate. - "$@" + exec "$@" } addJava () { dlog "[addJava] arg = '$1'" diff --git a/src/main/resources/com/typesafe/sbt/packager/archetypes/systemv/start-template b/src/main/resources/com/typesafe/sbt/packager/archetypes/systemv/start-debian-template similarity index 100% rename from src/main/resources/com/typesafe/sbt/packager/archetypes/systemv/start-template rename to src/main/resources/com/typesafe/sbt/packager/archetypes/systemv/start-debian-template diff --git a/src/main/resources/com/typesafe/sbt/packager/archetypes/systemv/start-rpm-template b/src/main/resources/com/typesafe/sbt/packager/archetypes/systemv/start-rpm-template new file mode 100644 index 000000000..9e61bf17b --- /dev/null +++ b/src/main/resources/com/typesafe/sbt/packager/archetypes/systemv/start-rpm-template @@ -0,0 +1,132 @@ +#!/bin/sh +# +# ${{app_name}} <${{app_name}}> +# +# chkconfig: - 20 80 +# description: ${{descr}} +# + +### BEGIN INIT INFO +# Provides: ${{app_name}} +# Required-Start: +# Required-Stop: +# Should-Start: +# Should-Stop: +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: ${{descr}} +# Description: ${{descr}} +### END INIT INFO + +### ----------------- +# This script was created using following sources +# +# http://stackoverflow.com/questions/8124345/call-to-daemon-in-a-etc-init-d-script-is-blocking-not-running-in-background +# https://fedoraproject.org/wiki/Packaging:SysVInitScript#Initscript_template +### ----------------- + +# Source function library. +. /etc/rc.d/init.d/functions + +prog="${{app_name}}" + +# FIXME The pid file should be handled by the executed script +# The pid can be filled in in this script +PIDFILE=/var/run/${{app_name}}/running.pid + +if [ -z "$DAEMON_USER" ]; then + DAEMON_USER=${{daemon_user}} +fi + + +# smb could define some additional options in $RUN_OPTS +RUN_CMD="${{chdir}}/bin/${{app_name}}" + +[ -e /etc/sysconfig/$prog ] && . /etc/sysconfig/$prog + +lockfile=/var/lock/subsys/$prog + +start() { + [ -x $RUN_CMD ] || exit 5 + echo -n $"Starting $prog: " + cd ${{chdir}} + + # exec="nohup ${RUN_CMD} >/dev/null 2>&1 &" # doesn't work + # daemon --user $DAEMON_USER --pidfile $PIDFILE $exec # doesn't work + + # FIXME figure out how to use daemon correctly + nohup ${RUN_CMD} >/dev/null 2>&1 & + + + retval=$? # last error code + PID=$! # pid of last backgrounded process + [ $retval -eq 0 ] && touch ${lockfile} && success || failure + + # Insert pid into pid file for CentOS killproc function + echo + echo $PID > ${PIDFILE} + return $retval +} + +stop() { + echo -n $"Stopping $prog: " + killproc -p $PIDFILE $prog + retval=$? + [ $retval -eq 0 ] && rm -f $lockfile && rm -f $PIDFILE + return $retval +} + +restart() { + stop + start +} + +reload() { + restart +} + +force_reload() { + restart +} + +rh_status() { + # run checks to determine if the service is running or use generic status + status -p $PIDFILE -l $lockfile $prog +} + +rh_status_q() { + rh_status >/dev/null 2>&1 +} + + +case "$1" in + start) + rh_status_q && exit 0 + $1 + ;; + stop) + rh_status_q || exit 0 + $1 + ;; + restart) + $1 + ;; + reload) + rh_status || exit 7 + $1 + ;; + force-reload) + force_reload + ;; + status) + rh_status + ;; + condrestart|try-restart) + rh_status || exit 0 + restart + ;; + *) + echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload}" + exit 2 +esac +exit $? \ No newline at end of file diff --git a/src/main/resources/com/typesafe/sbt/packager/archetypes/upstart/start-template b/src/main/resources/com/typesafe/sbt/packager/archetypes/upstart/start-rpm-template similarity index 100% rename from src/main/resources/com/typesafe/sbt/packager/archetypes/upstart/start-template rename to src/main/resources/com/typesafe/sbt/packager/archetypes/upstart/start-rpm-template diff --git a/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaAppUpstartScript.scala b/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaAppStartScript.scala similarity index 74% rename from src/main/scala/com/typesafe/sbt/packager/archetypes/JavaAppUpstartScript.scala rename to src/main/scala/com/typesafe/sbt/packager/archetypes/JavaAppStartScript.scala index c8dbba48d..795ef483a 100644 --- a/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaAppUpstartScript.scala +++ b/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaAppStartScript.scala @@ -4,17 +4,20 @@ import java.io.File import java.net.URL /** - * Constructs an start script for running a java application. - * + * Trait for building start scripts. */ -object JavaAppStartScript { +trait JavaAppStartScriptBuilder { import ServerLoader._ - import com.typesafe.sbt.packager.debian.DebianPlugin.Names._ - val startScript = "start" - private val upstartScripts = Seq(startScript, Postinst, Prerm) - private val systemvScripts = Seq(startScript, Postinst, Prerm, Postrm) + /** Name of the start script template without '-template' suffix */ + val startScript: String + + /** Scripts to include for upstart. By default only startScript */ + val upstartScripts: Seq[String] + + /** Scripts to include for ssystemV. By default only startScript */ + val systemvScripts: Seq[String] /** * Generating the URL to the startScript template. @@ -28,18 +31,6 @@ object JavaAppStartScript { if (defaultLocation.exists) defaultLocation.toURI.toURL else templateUrl(startScript, loader) getOrElse sys.error("Default startscript not available for loader: " + loader) - /** - * Generating the start script depending on the serverLoader. - * - * @param loader - which startup system - * @param replacements - default replacements - * @param template - if specified, it will override the default one - */ - def generateStartScript( - loader: ServerLoader, - replacements: Seq[(String, String)], - template: Option[URL] = None): Option[String] = generateTemplate(startScript, loader, replacements, template) - /** * * @param templateName - DebianPlugin.Names for maintainer scripts and "start" @@ -62,6 +53,9 @@ object JavaAppStartScript { } } + /** + * @return url to the template if it's defined for the server loader + */ def templateUrl(templateName: String, loader: ServerLoader, template: Option[URL] = None): Option[URL] = template orElse { Option(loader match { case Upstart if (upstartScripts contains templateName) => @@ -104,6 +98,28 @@ object JavaAppStartScript { "daemon_group" -> daemonGroup) } +/** + * Constructs an start script for running a java application. + * Can build the neccessary maintainer scripts, too. + */ +object JavaAppStartScript { + + object Rpm extends JavaAppStartScriptBuilder { + val startScript = "start-rpm" + val upstartScripts = Seq(startScript) + val systemvScripts = Seq(startScript) + } + + object Debian extends JavaAppStartScriptBuilder { + import com.typesafe.sbt.packager.debian.DebianPlugin.Names._ + + val startScript = "start-debian" + val upstartScripts = Seq(startScript, Postinst, Prerm) + val systemvScripts = Seq(startScript, Postinst, Prerm, Postrm) + } + +} + object ServerLoader extends Enumeration { type ServerLoader = Value val Upstart, SystemV = Value 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 0ea2c6ebf..84f7687ca 100644 --- a/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaServerApplication.scala +++ b/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaServerApplication.scala @@ -4,7 +4,7 @@ package archetypes import Keys._ import sbt._ -import sbt.Keys.{ target, mainClass, normalizedName, sourceDirectory } +import sbt.Keys.{ target, mainClass, normalizedName, sourceDirectory, streams } import SbtNativePackager._ import com.typesafe.sbt.packager.linux.{ LinuxFileMetaData, LinuxPackageMapping, LinuxSymlink, LinuxPlugin } import com.typesafe.sbt.packager.debian.DebianPlugin @@ -23,84 +23,118 @@ object JavaServerAppPackaging { import LinuxPlugin.Users import DebianPlugin.Names.{ Preinst, Postinst, Prerm, Postrm } - def settings: Seq[Setting[_]] = JavaAppPackaging.settings ++ debianSettings + def settings: Seq[Setting[_]] = JavaAppPackaging.settings ++ linuxSettings ++ debianSettings ++ rpmSettings protected def etcDefaultTemplateSource: java.net.URL = getClass.getResource("etc-default-template") + /** + * general settings which apply to all linux server archetypes + * + * - script replacements + * - logging directory + * - config directory + */ + def linuxSettings: Seq[Setting[_]] = Seq( + // This one is begging for sbt 0.13 syntax... + linuxScriptReplacements <<= ( + maintainer in Debian, packageSummary in Debian, serverLoading in Debian, daemonUser in Linux, daemonGroup in Linux, normalizedName, + sbt.Keys.version, defaultLinuxInstallLocation, linuxJavaAppStartScriptBuilder in Debian) + map { (author, descr, loader, daemonUser, daemonGroup, name, version, installLocation, builder) => + val appDir = installLocation + "/" + name + + builder.makeReplacements( + author = author, + description = descr, + execScript = name, + chdir = appDir, + appName = name, + daemonUser = daemonUser, + daemonGroup = daemonGroup) + }, + // === logging directory mapping === + linuxPackageMappings <+= (normalizedName, defaultLinuxLogsLocation, target in Debian, daemonUser in Linux, daemonGroup in Linux) map { + (name, logsDir, target, user, group) => + // TODO use packageTemplateMapping here + // create empty var/log directory + val d = target / logsDir + d.mkdirs() + LinuxPackageMapping(Seq(d -> (logsDir + "/" + name)), LinuxFileMetaData(user, group)) + }, + linuxPackageSymlinks <+= (normalizedName, defaultLinuxInstallLocation, defaultLinuxLogsLocation) map { + (name, install, logsDir) => LinuxSymlink(install + "/" + name + "/logs", logsDir + "/" + name) + }, + // === etc config mapping === + bashScriptConfigLocation <<= normalizedName map (name => Some("/etc/default/" + name)), + linuxEtcDefaultTemplate <<= sourceDirectory map { dir => + val overrideScript = dir / "templates" / "etc-default" + if (overrideScript.exists) overrideScript.toURI.toURL + else etcDefaultTemplateSource + }, + makeEtcDefault <<= (normalizedName, target in Universal, linuxEtcDefaultTemplate in Debian, linuxScriptReplacements) + map makeEtcDefaultScript, + linuxPackageMappings <++= (makeEtcDefault, normalizedName) map { (conf, name) => + conf.map(c => LinuxPackageMapping(Seq(c -> ("/etc/default/" + name)), + LinuxFileMetaData(Users.Root, Users.Root)).withConfig()).toSeq + }, + + // === /var/run/app pid folder === + linuxPackageMappings <+= (normalizedName, daemonUser in Linux, daemonGroup in Linux) map { (name, user, group) => + packageTemplateMapping("/var/run/" + name)() withUser user withGroup group withPerms "755" + }) + def debianSettings: Seq[Setting[_]] = Seq( + linuxJavaAppStartScriptBuilder in Debian := JavaAppStartScript.Debian, serverLoading := Upstart, - // This one is begging for sbt 0.13 syntax... - debianScriptReplacements <<= ( - maintainer in Debian, packageSummary in Debian, serverLoading in Debian, daemonUser in Linux, daemonGroup in Linux, normalizedName, - sbt.Keys.version, defaultLinuxInstallLocation) - map { (author, descr, loader, daemonUser, daemonGroup, name, version, installLocation) => - val appDir = installLocation + "/" + name - - JavaAppStartScript.makeReplacements( - author = author, - description = descr, - execScript = name, - chdir = appDir, - appName = name, - daemonUser = daemonUser, - daemonGroup = daemonGroup) - }, - // TODO - Default locations shouldn't be so hacky. // === Startscript creation === - linuxStartScriptTemplate in Debian <<= (serverLoading in Debian, sourceDirectory) map { (loader, dir) => - JavaAppStartScript.defaultStartScriptTemplate(loader, dir / "templates" / "start") + linuxStartScriptTemplate in Debian <<= (serverLoading in Debian, sourceDirectory, linuxJavaAppStartScriptBuilder in Debian) map { + (loader, dir, builder) => builder.defaultStartScriptTemplate(loader, dir / "templates" / "start") }, - debianMakeStartScript <<= (target in Universal, serverLoading in Debian, debianScriptReplacements, linuxStartScriptTemplate in Debian) - map { (tmpDir, loader, replacements, template) => - makeDebianMaintainerScript(JavaAppStartScript.startScript, Some(template))(tmpDir, loader, replacements) - }, - linuxPackageMappings in Debian <++= (debianMakeStartScript, normalizedName, serverLoading in Debian) - map { (script, name, loader) => - val (path, permissions) = loader match { - case Upstart => ("/etc/init/" + name + ".conf", "0644") - case SystemV => ("/etc/init.d/" + name, "0755") - } - for { - s <- script.toSeq - } yield LinuxPackageMapping(Seq(s -> path), LinuxFileMetaData(Users.Root, Users.Root, permissions, "true")) + linuxMakeStartScript in Debian <<= (target in Universal, serverLoading in Debian, linuxScriptReplacements, linuxStartScriptTemplate in Debian, linuxJavaAppStartScriptBuilder in Debian) + map { (tmpDir, loader, replacements, template, builder) => + makeMaintainerScript(builder.startScript, Some(template))(tmpDir, loader, replacements, builder) }, - - // === etc config mapping === - bashScriptConfigLocation <<= normalizedName map (name => Some("/etc/default/" + name)), - linuxEtcDefaultTemplate in Debian <<= sourceDirectory map { dir => - val overrideScript = dir / "templates" / "etc-default" - if (overrideScript.exists) overrideScript.toURI.toURL - else etcDefaultTemplateSource - }, - debianMakeEtcDefault <<= (normalizedName, target in Universal, linuxEtcDefaultTemplate in Debian, debianScriptReplacements) - map makeEtcDefaultScript, - linuxPackageMappings in Debian <++= (debianMakeEtcDefault, normalizedName) map { (conf, name) => - conf.map(c => LinuxPackageMapping(Seq(c -> ("/etc/default/" + name)), LinuxFileMetaData(Users.Root, Users.Root)).withConfig()).toSeq - }, + linuxPackageMappings in Debian <++= (normalizedName, linuxMakeStartScript in Debian, serverLoading in Debian) map startScriptMapping, // TODO should we specify daemonGroup in configs? - // === logging directory mapping === - linuxPackageMappings in Debian <+= (normalizedName, defaultLinuxLogsLocation, target in Debian, daemonUser in Linux, daemonGroup in Linux) map { - (name, logsDir, target, user, group) => - // create empty var/log directory - val d = target / logsDir - d.mkdirs() - LinuxPackageMapping(Seq(d -> (logsDir + "/" + name)), LinuxFileMetaData(user, group)) - }, - linuxPackageSymlinks in Debian <+= (normalizedName, defaultLinuxInstallLocation, defaultLinuxLogsLocation) map { - (name, install, logsDir) => LinuxSymlink(install + "/" + name + "/logs", logsDir + "/" + name) + // === Maintainer scripts === + debianMakePreinstScript <<= (target in Universal, serverLoading in Debian, linuxScriptReplacements, linuxJavaAppStartScriptBuilder in Debian) map makeMaintainerScript(Preinst), + debianMakePostinstScript <<= (target in Universal, serverLoading in Debian, linuxScriptReplacements, linuxJavaAppStartScriptBuilder in Debian) map makeMaintainerScript(Postinst), + debianMakePrermScript <<= (target in Universal, serverLoading in Debian, linuxScriptReplacements, linuxJavaAppStartScriptBuilder in Debian) map makeMaintainerScript(Prerm), + debianMakePostrmScript <<= (target in Universal, serverLoading in Debian, linuxScriptReplacements, linuxJavaAppStartScriptBuilder in Debian) map makeMaintainerScript(Postrm)) + + def rpmSettings: Seq[Setting[_]] = Seq( + linuxJavaAppStartScriptBuilder in Rpm := JavaAppStartScript.Rpm, + serverLoading in Rpm := SystemV, + + // === Startscript creation === + linuxStartScriptTemplate in Rpm <<= (serverLoading in Rpm, sourceDirectory, linuxJavaAppStartScriptBuilder in Rpm) map { + (loader, dir, builder) => + builder.defaultStartScriptTemplate(loader, dir / "templates" / "start") + }, + linuxMakeStartScript in Rpm <<= (target in Universal, serverLoading in Rpm, linuxScriptReplacements, linuxStartScriptTemplate in Rpm, linuxJavaAppStartScriptBuilder in Rpm) + map { (tmpDir, loader, replacements, template, builder) => + makeMaintainerScript(builder.startScript, Some(template))(tmpDir, loader, replacements, builder) }, + linuxPackageMappings in Rpm <++= (normalizedName, linuxMakeStartScript in Rpm, serverLoading in Rpm) map startScriptMapping) - // === Maintainer scripts === - debianMakePreinstScript <<= (target in Universal, serverLoading in Debian, debianScriptReplacements) map makeDebianMaintainerScript(Preinst), - debianMakePostinstScript <<= (target in Universal, serverLoading in Debian, debianScriptReplacements) map makeDebianMaintainerScript(Postinst), - debianMakePrermScript <<= (target in Universal, serverLoading in Debian, debianScriptReplacements) map makeDebianMaintainerScript(Prerm), - debianMakePostrmScript <<= (target in Universal, serverLoading in Debian, debianScriptReplacements) map makeDebianMaintainerScript(Postrm)) + /* ========================================== */ + /* ============ Helper Methods ============== */ + /* ========================================== */ + + protected def startScriptMapping(name: String, script: Option[File], loader: ServerLoader): Seq[LinuxPackageMapping] = { + val (path, permissions) = loader match { + case Upstart => ("/etc/init/" + name + ".conf", "0644") + case SystemV => ("/etc/init.d/" + name, "0755") + } + for { + s <- script.toSeq + } yield LinuxPackageMapping(Seq(s -> path), LinuxFileMetaData(Users.Root, Users.Root, permissions, "true")) + } - protected def makeDebianMaintainerScript(scriptName: String, template: Option[URL] = None)( - tmpDir: File, loader: ServerLoader, replacements: Seq[(String, String)]): Option[File] = { - JavaAppStartScript.generateTemplate(scriptName, loader, replacements, template) map { scriptBits => + protected def makeMaintainerScript(scriptName: String, template: Option[URL] = None)( + tmpDir: File, loader: ServerLoader, replacements: Seq[(String, String)], builder: JavaAppStartScriptBuilder): Option[File] = { + builder.generateTemplate(scriptName, loader, replacements, template) map { scriptBits => val script = tmpDir / "tmp" / "bin" / ("debian-" + scriptName) IO.write(script, scriptBits) script 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 3fa869939..a9a115c07 100644 --- a/src/main/scala/com/typesafe/sbt/packager/debian/Keys.scala +++ b/src/main/scala/com/typesafe/sbt/packager/debian/Keys.scala @@ -46,7 +46,7 @@ trait DebianKeys { | version - app version """.stripMargin) - val debianMakeStartScript = TaskKey[Option[File]]("makeStartScript", "Creates or discovers the start script used by this project") + @deprecated("use linuxScriptReplacements", "0.7.0") val debianScriptReplacements = TaskKey[Seq[(String, String)]]("upstartScriptReplacements", """|Replacements of template parameters used in the upstart script. | Default supported templates: @@ -61,10 +61,8 @@ trait DebianKeys { | appMainClass - main class to start | daemonUser - daemon user """.stripMargin) - val debianMakeEtcDefault = TaskKey[Option[File]]("makeEtcDefault", "Creates or discovers the /etc/default/ script") } - /** Keys used for Debian specific settings. */ object Keys extends DebianKeys { // Metadata keys 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 d81649d18..65d242af4 100644 --- a/src/main/scala/com/typesafe/sbt/packager/linux/Keys.scala +++ b/src/main/scala/com/typesafe/sbt/packager/linux/Keys.scala @@ -4,6 +4,7 @@ package linux import sbt._ import com.typesafe.sbt.packager.archetypes.ServerLoader.ServerLoader +import com.typesafe.sbt.packager.archetypes.JavaAppStartScriptBuilder /** Linux packaging generic build targets. */ trait Keys { @@ -18,8 +19,26 @@ trait Keys { val linuxPackageSymlinks = TaskKey[Seq[LinuxSymlink]]("linux-package-symlinks", "Symlinks we should produce in the underlying package.") val generateManPages = TaskKey[Unit]("generate-man-pages", "Shows all the man files in the current project") + val linuxMakeStartScript = TaskKey[Option[File]]("makeStartScript", "Creates or discovers the start script used by this project") val linuxStartScriptTemplate = TaskKey[URL]("linuxStartScriptTemplate", "The location of the template start script file we use for debian (upstart or init.d") val linuxEtcDefaultTemplate = TaskKey[URL]("linuxEtcDefaultTemplate", "The location of the /etc/default/ template script.") + val linuxJavaAppStartScriptBuilder = SettingKey[JavaAppStartScriptBuilder]("linuxJavaAppStartScriptBuilder", "Responsible for loading the start scripts. Only used with archetype.java_server") + val linuxScriptReplacements = TaskKey[Seq[(String, String)]]("linuxScriptReplacements", + """|Replacements of template parameters used in linux scripts. + | Default supported templates: + | execScript - name of the script in /usr/bin + | author - author of this project + | descr - short description + | chdir - execution path of the script + | retries - on fail, how often should a restart be tried + | retryTimeout - pause between retries + | appName - name of application + | appClasspath - application classpath + | appMainClass - main class to start + | daemonUser - daemon user + """.stripMargin) + + val makeEtcDefault = TaskKey[Option[File]]("makeEtcDefault", "Creates or discovers the /etc/default/ script") } object Keys extends Keys { From 75a4a70eddf168a5c030da86f4e7399c03d85081 Mon Sep 17 00:00:00 2001 From: Nepomuk Seiler Date: Fri, 28 Feb 2014 14:21:58 +0100 Subject: [PATCH 4/8] Adding uncommited debian start template --- .../archetypes/upstart/start-debian-template | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 src/main/resources/com/typesafe/sbt/packager/archetypes/upstart/start-debian-template diff --git a/src/main/resources/com/typesafe/sbt/packager/archetypes/upstart/start-debian-template b/src/main/resources/com/typesafe/sbt/packager/archetypes/upstart/start-debian-template new file mode 100644 index 000000000..0738298b5 --- /dev/null +++ b/src/main/resources/com/typesafe/sbt/packager/archetypes/upstart/start-debian-template @@ -0,0 +1,30 @@ +# generated upstart config + +description "${{descr}}" +author "${{author}}" + +# Stanzas +# +# Stanzas control when and how a process is started and stopped +# See a list of stanzas here: http://upstart.ubuntu.com/wiki/Stanzas#respawn + +# When to start the service +start on runlevel [2345] + +# When to stop the service +stop on runlevel [016] + +# Automatically restart process if crashed. Tries ${{retries}} times every ${{retryTimeout}} seconds +respawn +respawn limit ${{retries}} ${{retryTimeout}} + +# set the working directory of the job processes +chdir ${{chdir}} + +# changes to the user and group before running the job's process +setuid ${{daemon_user}} + +# Start the process +script + exec ./bin/${{exec}} +end script From 04010ea5193f2ef6569d3bd29b9d44b799570a47 Mon Sep 17 00:00:00 2001 From: Nepomuk Seiler Date: Fri, 28 Feb 2014 16:10:09 +0100 Subject: [PATCH 5/8] Adding an additional test. Need to generate mainClass for bin generation (works with play) --- .../archetypes/JavaAppStartScript.scala | 5 +++ .../archetypes/JavaServerApplication.scala | 17 ++++------ .../com/typesafe/sbt/packager/rpm/Keys.scala | 6 +++- src/sbt-test/rpm/sysvinit-rpm/build.sbt | 32 +++++++++++++++++++ .../rpm/sysvinit-rpm/project/plugins.sbt | 1 + .../rpm/sysvinit-rpm/src/universal/conf/test | 1 + src/sbt-test/rpm/sysvinit-rpm/test | 22 +++++++++++++ 7 files changed, 72 insertions(+), 12 deletions(-) create mode 100644 src/sbt-test/rpm/sysvinit-rpm/build.sbt create mode 100644 src/sbt-test/rpm/sysvinit-rpm/project/plugins.sbt create mode 100644 src/sbt-test/rpm/sysvinit-rpm/src/universal/conf/test create mode 100644 src/sbt-test/rpm/sysvinit-rpm/test diff --git a/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaAppStartScript.scala b/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaAppStartScript.scala index 795ef483a..fd4207743 100644 --- a/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaAppStartScript.scala +++ b/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaAppStartScript.scala @@ -10,6 +10,9 @@ trait JavaAppStartScriptBuilder { import ServerLoader._ + /** Used for prefix files generated with this builder */ + val name: String + /** Name of the start script template without '-template' suffix */ val startScript: String @@ -105,6 +108,7 @@ trait JavaAppStartScriptBuilder { object JavaAppStartScript { object Rpm extends JavaAppStartScriptBuilder { + val name = "rpm" val startScript = "start-rpm" val upstartScripts = Seq(startScript) val systemvScripts = Seq(startScript) @@ -113,6 +117,7 @@ object JavaAppStartScript { object Debian extends JavaAppStartScriptBuilder { import com.typesafe.sbt.packager.debian.DebianPlugin.Names._ + val name = "debian" val startScript = "start-debian" val upstartScripts = Seq(startScript, Postinst, Prerm) val systemvScripts = Seq(startScript, Postinst, Prerm, Postrm) 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 84f7687ca..272cab312 100644 --- a/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaServerApplication.scala +++ b/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaServerApplication.scala @@ -36,9 +36,9 @@ object JavaServerAppPackaging { def linuxSettings: Seq[Setting[_]] = Seq( // This one is begging for sbt 0.13 syntax... linuxScriptReplacements <<= ( - maintainer in Debian, packageSummary in Debian, serverLoading in Debian, daemonUser in Linux, daemonGroup in Linux, normalizedName, + maintainer in Linux, packageSummary in Linux, daemonUser in Linux, daemonGroup in Linux, normalizedName, sbt.Keys.version, defaultLinuxInstallLocation, linuxJavaAppStartScriptBuilder in Debian) - map { (author, descr, loader, daemonUser, daemonGroup, name, version, installLocation, builder) => + map { (author, descr, daemonUser, daemonGroup, name, version, installLocation, builder) => val appDir = installLocation + "/" + name builder.makeReplacements( @@ -51,13 +51,8 @@ object JavaServerAppPackaging { daemonGroup = daemonGroup) }, // === logging directory mapping === - linuxPackageMappings <+= (normalizedName, defaultLinuxLogsLocation, target in Debian, daemonUser in Linux, daemonGroup in Linux) map { - (name, logsDir, target, user, group) => - // TODO use packageTemplateMapping here - // create empty var/log directory - val d = target / logsDir - d.mkdirs() - LinuxPackageMapping(Seq(d -> (logsDir + "/" + name)), LinuxFileMetaData(user, group)) + linuxPackageMappings <+= (normalizedName, defaultLinuxLogsLocation, daemonUser in Linux, daemonGroup in Linux) map { + (name, logsDir, user, group) => packageTemplateMapping(logsDir + "/" + name)() withUser user withGroup group withPerms "755" }, linuxPackageSymlinks <+= (normalizedName, defaultLinuxInstallLocation, defaultLinuxLogsLocation) map { (name, install, logsDir) => LinuxSymlink(install + "/" + name + "/logs", logsDir + "/" + name) @@ -69,7 +64,7 @@ object JavaServerAppPackaging { if (overrideScript.exists) overrideScript.toURI.toURL else etcDefaultTemplateSource }, - makeEtcDefault <<= (normalizedName, target in Universal, linuxEtcDefaultTemplate in Debian, linuxScriptReplacements) + makeEtcDefault <<= (normalizedName, target in Universal, linuxEtcDefaultTemplate, linuxScriptReplacements) map makeEtcDefaultScript, linuxPackageMappings <++= (makeEtcDefault, normalizedName) map { (conf, name) => conf.map(c => LinuxPackageMapping(Seq(c -> ("/etc/default/" + name)), @@ -135,7 +130,7 @@ object JavaServerAppPackaging { protected def makeMaintainerScript(scriptName: String, template: Option[URL] = None)( tmpDir: File, loader: ServerLoader, replacements: Seq[(String, String)], builder: JavaAppStartScriptBuilder): Option[File] = { builder.generateTemplate(scriptName, loader, replacements, template) map { scriptBits => - val script = tmpDir / "tmp" / "bin" / ("debian-" + scriptName) + val script = tmpDir / "tmp" / "bin" / (builder.name + scriptName) IO.write(script, scriptBits) script } 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 5db93068c..ce1a2ca83 100644 --- a/src/main/scala/com/typesafe/sbt/packager/rpm/Keys.scala +++ b/src/main/scala/com/typesafe/sbt/packager/rpm/Keys.scala @@ -36,7 +36,7 @@ trait RpmKeys { // SPEC val rpmSpecConfig = TaskKey[RpmSpec]("rpm-spec-config", "All the configuration for an RPM .spec file.") - // SCRIPTS + // SCRIPTS val rpmScripts = SettingKey[RpmScripts]("rpm-scripts", "Configuration of pre- and post-integration scripts.") val rpmPretrans = SettingKey[Option[String]]("rpm-pretrans", "%pretrans scriptlet") @@ -49,6 +49,7 @@ trait RpmKeys { // Building val rpmLint = TaskKey[Unit]("rpm-lint", "Runs rpmlint program against the genreated RPM, if available.") + } /** Keys used in RPM Settings. */ @@ -73,5 +74,8 @@ object Keys extends RpmKeys { 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/sbt-test/rpm/sysvinit-rpm/build.sbt b/src/sbt-test/rpm/sysvinit-rpm/build.sbt new file mode 100644 index 000000000..f89f33ed6 --- /dev/null +++ b/src/sbt-test/rpm/sysvinit-rpm/build.sbt @@ -0,0 +1,32 @@ +import NativePackagerKeys._ + +packageArchetype.java_server + +name := "rpm-test" + +version := "0.1.0" + +maintainer := "Josh Suereth " + +packageSummary := "Test rpm package" + +packageDescription := """A fun package description of our software, + with multiple lines.""" + +rpmRelease := "1" + +rpmVendor := "typesafe" + +rpmUrl := Some("http://github.com/sbt/sbt-native-packager") + +rpmLicense := Some("BSD") + +TaskKey[Unit]("unzipAndCheck") <<= (packageBin in Rpm, streams) map { (rpmFile, streams) => + val rpmPath = Seq(rpmFile.getAbsolutePath) + Process(s"cp ${rpmFile.getAbsolutePath} /home/muki") ! streams.log + Process("rpm2cpio" , rpmPath) #| Process("cpio -i --make-directories") ! streams.log + Process("ls -Al usr/share/rpm-test/") ! streams.log + // TODO check symlinks + () +} + diff --git a/src/sbt-test/rpm/sysvinit-rpm/project/plugins.sbt b/src/sbt-test/rpm/sysvinit-rpm/project/plugins.sbt new file mode 100644 index 000000000..b53de154c --- /dev/null +++ b/src/sbt-test/rpm/sysvinit-rpm/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % sys.props("project.version")) diff --git a/src/sbt-test/rpm/sysvinit-rpm/src/universal/conf/test b/src/sbt-test/rpm/sysvinit-rpm/src/universal/conf/test new file mode 100644 index 000000000..92e38dfb2 --- /dev/null +++ b/src/sbt-test/rpm/sysvinit-rpm/src/universal/conf/test @@ -0,0 +1 @@ +# Test configuration file! diff --git a/src/sbt-test/rpm/sysvinit-rpm/test b/src/sbt-test/rpm/sysvinit-rpm/test new file mode 100644 index 000000000..64d6f032d --- /dev/null +++ b/src/sbt-test/rpm/sysvinit-rpm/test @@ -0,0 +1,22 @@ +# Run the debian packaging. +> rpm:package-bin +$ exists target/rpm/RPMS/noarch/rpm-test-0.1.0-1.noarch.rpm + +# Check rpm contents +> unzipAndCheck +$ exists etc/default/rpm-test +$ exists etc/init.d/rpm-test + +$ exists usr/share/rpm-test + +# TODO generate a mainClass to generate bin files +# $ exists usr/share/rpm-test/bin +# $ exists usr/share/rpm-test/bin/rpm-test + +$ exists usr/share/rpm-test/conf +$ exists usr/share/rpm-test/lib + +$ exists var/log/rpm-test +$ exists var/run/rpm-test + +# TODO symlinks aren't checked From 13097aff0ed3485661092fc7750bc143b661a60d Mon Sep 17 00:00:00 2001 From: Nepomuk Seiler Date: Fri, 28 Feb 2014 16:11:00 +0100 Subject: [PATCH 6/8] Removing crappy debugging stuff --- src/sbt-test/rpm/sysvinit-rpm/build.sbt | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sbt-test/rpm/sysvinit-rpm/build.sbt b/src/sbt-test/rpm/sysvinit-rpm/build.sbt index f89f33ed6..c0abf94cf 100644 --- a/src/sbt-test/rpm/sysvinit-rpm/build.sbt +++ b/src/sbt-test/rpm/sysvinit-rpm/build.sbt @@ -23,9 +23,7 @@ rpmLicense := Some("BSD") TaskKey[Unit]("unzipAndCheck") <<= (packageBin in Rpm, streams) map { (rpmFile, streams) => val rpmPath = Seq(rpmFile.getAbsolutePath) - Process(s"cp ${rpmFile.getAbsolutePath} /home/muki") ! streams.log Process("rpm2cpio" , rpmPath) #| Process("cpio -i --make-directories") ! streams.log - Process("ls -Al usr/share/rpm-test/") ! streams.log // TODO check symlinks () } From b34a38dd4e9edf3230bb9bc63aba05704f37689d Mon Sep 17 00:00:00 2001 From: Nepomuk Seiler Date: Fri, 28 Feb 2014 18:15:05 +0100 Subject: [PATCH 7/8] Adding checks for start script --- src/sbt-test/rpm/sysvinit-rpm/build.sbt | 2 ++ .../rpm/sysvinit-rpm/src/main/scala/com/example/App.scala | 3 +++ .../sysvinit-rpm/src/main/scala/com/example/MainApp.scala | 0 src/sbt-test/rpm/sysvinit-rpm/test | 5 ++--- 4 files changed, 7 insertions(+), 3 deletions(-) create mode 100644 src/sbt-test/rpm/sysvinit-rpm/src/main/scala/com/example/App.scala create mode 100644 src/sbt-test/rpm/sysvinit-rpm/src/main/scala/com/example/MainApp.scala diff --git a/src/sbt-test/rpm/sysvinit-rpm/build.sbt b/src/sbt-test/rpm/sysvinit-rpm/build.sbt index c0abf94cf..80da254b9 100644 --- a/src/sbt-test/rpm/sysvinit-rpm/build.sbt +++ b/src/sbt-test/rpm/sysvinit-rpm/build.sbt @@ -21,6 +21,8 @@ rpmUrl := Some("http://github.com/sbt/sbt-native-packager") rpmLicense := Some("BSD") +mainClass in (Compile, run) := Some("com.example.MainApp") + TaskKey[Unit]("unzipAndCheck") <<= (packageBin in Rpm, streams) map { (rpmFile, streams) => val rpmPath = Seq(rpmFile.getAbsolutePath) Process("rpm2cpio" , rpmPath) #| Process("cpio -i --make-directories") ! streams.log diff --git a/src/sbt-test/rpm/sysvinit-rpm/src/main/scala/com/example/App.scala b/src/sbt-test/rpm/sysvinit-rpm/src/main/scala/com/example/App.scala new file mode 100644 index 000000000..faa497b3e --- /dev/null +++ b/src/sbt-test/rpm/sysvinit-rpm/src/main/scala/com/example/App.scala @@ -0,0 +1,3 @@ +object MainApp extends App { + println("Hello World") +} diff --git a/src/sbt-test/rpm/sysvinit-rpm/src/main/scala/com/example/MainApp.scala b/src/sbt-test/rpm/sysvinit-rpm/src/main/scala/com/example/MainApp.scala new file mode 100644 index 000000000..e69de29bb diff --git a/src/sbt-test/rpm/sysvinit-rpm/test b/src/sbt-test/rpm/sysvinit-rpm/test index 64d6f032d..c7a875837 100644 --- a/src/sbt-test/rpm/sysvinit-rpm/test +++ b/src/sbt-test/rpm/sysvinit-rpm/test @@ -9,9 +9,8 @@ $ exists etc/init.d/rpm-test $ exists usr/share/rpm-test -# TODO generate a mainClass to generate bin files -# $ exists usr/share/rpm-test/bin -# $ exists usr/share/rpm-test/bin/rpm-test +$ exists usr/share/rpm-test/bin +$ exists usr/share/rpm-test/bin/rpm-test $ exists usr/share/rpm-test/conf $ exists usr/share/rpm-test/lib From d6f88be1cef7dcc54c126ba52e01e82e750d843f Mon Sep 17 00:00:00 2001 From: Nepomuk Seiler Date: Fri, 28 Feb 2014 18:33:23 +0100 Subject: [PATCH 8/8] Adding daemon_user to nohup command. Adding preinst and postuninstall scripts (wip) --- .../archetypes/systemv/start-rpm-template | 11 +++-- .../typesafe/sbt/packager/rpm/postuninstall | 11 +++++ .../com/typesafe/sbt/packager/rpm/preinstall | 11 +++++ .../archetypes/JavaServerApplication.scala | 17 ++++++- .../typesafe/sbt/packager/linux/Keys.scala | 2 +- .../typesafe/sbt/packager/rpm/RpmPlugin.scala | 48 +++++++++++-------- 6 files changed, 71 insertions(+), 29 deletions(-) create mode 100644 src/main/resources/com/typesafe/sbt/packager/rpm/postuninstall create mode 100644 src/main/resources/com/typesafe/sbt/packager/rpm/preinstall diff --git a/src/main/resources/com/typesafe/sbt/packager/archetypes/systemv/start-rpm-template b/src/main/resources/com/typesafe/sbt/packager/archetypes/systemv/start-rpm-template index 9e61bf17b..57727568d 100644 --- a/src/main/resources/com/typesafe/sbt/packager/archetypes/systemv/start-rpm-template +++ b/src/main/resources/com/typesafe/sbt/packager/archetypes/systemv/start-rpm-template @@ -51,11 +51,12 @@ start() { echo -n $"Starting $prog: " cd ${{chdir}} - # exec="nohup ${RUN_CMD} >/dev/null 2>&1 &" # doesn't work - # daemon --user $DAEMON_USER --pidfile $PIDFILE $exec # doesn't work - # FIXME figure out how to use daemon correctly - nohup ${RUN_CMD} >/dev/null 2>&1 & + nohup runuser $DAEMON_USER ${RUN_CMD} >/dev/null 2>&1 & + + # The way to go, but doesn't work properly + # If the app creates the pid file this gets messy + # daemon --user $DAEMON_USER --pidfile $PIDFILE $RUN_CMD & retval=$? # last error code @@ -72,7 +73,7 @@ stop() { echo -n $"Stopping $prog: " killproc -p $PIDFILE $prog retval=$? - [ $retval -eq 0 ] && rm -f $lockfile && rm -f $PIDFILE + [ $retval -eq 0 ] && rm -f $lockfile return $retval } diff --git a/src/main/resources/com/typesafe/sbt/packager/rpm/postuninstall b/src/main/resources/com/typesafe/sbt/packager/rpm/postuninstall new file mode 100644 index 000000000..447adf375 --- /dev/null +++ b/src/main/resources/com/typesafe/sbt/packager/rpm/postuninstall @@ -0,0 +1,11 @@ +# Adding system user/group : ${{daemon_user}} and ${{daemon_group}} +if ! getent group | grep -q "^${{daemon_group}}:" ; +then + echo "Deleting system group: ${{daemon_group}}" + groupdel ${{daemon_group}} +fi +if ! getent passwd | grep -q "^${{daemon_user}}:"; +then + echo "Deleting system user: ${{daemon_user}}" + userdel ${{daemon_user}} +fi \ No newline at end of file diff --git a/src/main/resources/com/typesafe/sbt/packager/rpm/preinstall b/src/main/resources/com/typesafe/sbt/packager/rpm/preinstall new file mode 100644 index 000000000..904a22cc8 --- /dev/null +++ b/src/main/resources/com/typesafe/sbt/packager/rpm/preinstall @@ -0,0 +1,11 @@ +# Adding system user/group : ${{daemon_user}} and ${{daemon_group}} +if ! getent group | grep -q "^${{daemon_group}}:" ; +then + echo "Creating system group: ${{daemon_group}}" + groupadd --system ${{daemon_group}} +fi +if ! getent passwd | grep -q "^${{daemon_user}}:"; +then + echo "Creating system user: ${{daemon_user}}" + useradd --gid ${{daemon_group}} --no-create-home --system -c '${{descr}}' ${{daemon_user}} +fi \ No newline at end of file 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 272cab312..0a7324e1b 100644 --- a/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaServerApplication.scala +++ b/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaServerApplication.scala @@ -8,6 +8,7 @@ import sbt.Keys.{ target, mainClass, normalizedName, 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 /** * This class contains the default settings for creating and deploying an archetypical Java application. @@ -38,7 +39,7 @@ object JavaServerAppPackaging { linuxScriptReplacements <<= ( maintainer in Linux, packageSummary in Linux, daemonUser in Linux, daemonGroup in Linux, normalizedName, sbt.Keys.version, defaultLinuxInstallLocation, linuxJavaAppStartScriptBuilder in Debian) - map { (author, descr, daemonUser, daemonGroup, name, version, installLocation, builder) => + apply { (author, descr, daemonUser, daemonGroup, name, version, installLocation, builder) => val appDir = installLocation + "/" + name builder.makeReplacements( @@ -111,7 +112,19 @@ object JavaServerAppPackaging { map { (tmpDir, loader, replacements, template, builder) => makeMaintainerScript(builder.startScript, Some(template))(tmpDir, loader, replacements, builder) }, - linuxPackageMappings in Rpm <++= (normalizedName, linuxMakeStartScript in Rpm, serverLoading in Rpm) map startScriptMapping) + linuxPackageMappings in Rpm <++= (normalizedName, linuxMakeStartScript in Rpm, serverLoading in Rpm) map startScriptMapping, + + // == Maintainer scripts === + // TODO this is very basic - align debian and rpm plugin + rpmPre <<= (rpmPre, linuxScriptReplacements) apply { (pre, replacements) => + val scriptBits = TemplateWriter.generateScript(RpmPlugin.postinstTemplateSource, replacements) + Some(pre.map(_ + "\n").getOrElse("") + scriptBits) + }, + rpmPostun <<= (rpmPostun, linuxScriptReplacements) apply { (post, replacements) => + val scriptBits = TemplateWriter.generateScript(RpmPlugin.postinstTemplateSource, replacements) + Some(post.map(_ + "\n").getOrElse("") + scriptBits) + } + ) /* ========================================== */ /* ============ Helper Methods ============== */ 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 65d242af4..249f7813e 100644 --- a/src/main/scala/com/typesafe/sbt/packager/linux/Keys.scala +++ b/src/main/scala/com/typesafe/sbt/packager/linux/Keys.scala @@ -23,7 +23,7 @@ trait Keys { val linuxStartScriptTemplate = TaskKey[URL]("linuxStartScriptTemplate", "The location of the template start script file we use for debian (upstart or init.d") val linuxEtcDefaultTemplate = TaskKey[URL]("linuxEtcDefaultTemplate", "The location of the /etc/default/ template script.") val linuxJavaAppStartScriptBuilder = SettingKey[JavaAppStartScriptBuilder]("linuxJavaAppStartScriptBuilder", "Responsible for loading the start scripts. Only used with archetype.java_server") - val linuxScriptReplacements = TaskKey[Seq[(String, String)]]("linuxScriptReplacements", + val linuxScriptReplacements = SettingKey[Seq[(String, String)]]("linuxScriptReplacements", """|Replacements of template parameters used in linux scripts. | Default supported templates: | execScript - name of the script in /usr/bin 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 adf733e9e..d5579af3c 100644 --- a/src/main/scala/com/typesafe/sbt/packager/rpm/RpmPlugin.scala +++ b/src/main/scala/com/typesafe/sbt/packager/rpm/RpmPlugin.scala @@ -9,11 +9,11 @@ import sbt._ /** Plugin trait containing all generic values used for packaging linux software. */ trait RpmPlugin extends Plugin with LinuxPlugin { val Rpm = config("rpm") extend Linux - + def rpmSettings: Seq[Setting[_]] = Seq( - rpmOs := "Linux", // TODO - default to something else? + rpmOs := "Linux", // TODO - default to something else? rpmRelease := "0", - rpmVendor := "", // TODO - Maybe pull in organization? + rpmVendor := "", // TODO - Maybe pull in organization? rpmLicense := None, rpmDistribution := None, rpmUrl := None, @@ -38,26 +38,32 @@ trait RpmPlugin extends Plugin with LinuxPlugin { packageDescription in Rpm <<= packageDescription in Linux, target in Rpm <<= target(_ / "rpm") ) ++ inConfig(Rpm)(Seq( - packageArchitecture := "noarch", - rpmMetadata <<= - (name, version, rpmRelease, packageArchitecture, rpmVendor, rpmOs, packageSummary, packageDescription, rpmAutoprov, rpmAutoreq) apply (RpmMetadata.apply), - rpmDescription <<= - (rpmLicense, rpmDistribution, rpmUrl, rpmGroup, rpmPackager, rpmIcon) apply RpmDescription, - rpmDependencies <<= - (rpmProvides, rpmRequirements, rpmPrerequisites, rpmObsoletes, rpmConflicts) apply RpmDependencies, - rpmScripts <<= - (rpmPretrans,rpmPre,rpmPost,rpmVerifyscript,rpmPosttrans,rpmPreun,rpmPostun) apply RpmScripts, - rpmSpecConfig <<= - (rpmMetadata, rpmDescription, rpmDependencies, rpmScripts, linuxPackageMappings, linuxPackageSymlinks) map RpmSpec, - packageBin <<= (rpmSpecConfig, target, streams) map { (spec, dir, s) => + packageArchitecture := "noarch", + rpmMetadata <<= + (name, version, rpmRelease, packageArchitecture, rpmVendor, rpmOs, packageSummary, packageDescription, rpmAutoprov, rpmAutoreq) apply (RpmMetadata.apply), + rpmDescription <<= + (rpmLicense, rpmDistribution, rpmUrl, rpmGroup, rpmPackager, rpmIcon) apply RpmDescription, + rpmDependencies <<= + (rpmProvides, rpmRequirements, rpmPrerequisites, rpmObsoletes, rpmConflicts) apply RpmDependencies, + rpmScripts <<= + (rpmPretrans, rpmPre, rpmPost, rpmVerifyscript, rpmPosttrans, rpmPreun, rpmPostun) apply RpmScripts, + rpmSpecConfig <<= + (rpmMetadata, rpmDescription, rpmDependencies, rpmScripts, linuxPackageMappings, linuxPackageSymlinks) map RpmSpec, + packageBin <<= (rpmSpecConfig, target, streams) map { (spec, dir, s) => spec.validate(s.log) RpmHelper.buildRpm(spec, dir, s.log) - }, - rpmLint <<= (packageBin, streams) map { (rpm, s) => - (Process(Seq("rpmlint", "-v", rpm.getAbsolutePath)) ! s.log) match { + }, + rpmLint <<= (packageBin, streams) map { (rpm, s) => + (Process(Seq("rpmlint", "-v", rpm.getAbsolutePath)) ! s.log) match { case 0 => () case x => sys.error("Failed to run rpmlint, exit status: " + x) - } - } - )) + } + } + )) +} + +object RpmPlugin { + + def postuninstallTemplateSource: java.net.URL = getClass.getResource("postuninstall") + def postinstTemplateSource: java.net.URL = getClass.getResource("preinstall") }