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..57727568d --- /dev/null +++ b/src/main/resources/com/typesafe/sbt/packager/archetypes/systemv/start-rpm-template @@ -0,0 +1,133 @@ +#!/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}} + + # FIXME figure out how to use daemon correctly + 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 + 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 + 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-debian-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-debian-template diff --git a/src/main/resources/com/typesafe/sbt/packager/archetypes/upstart/start-rpm-template b/src/main/resources/com/typesafe/sbt/packager/archetypes/upstart/start-rpm-template new file mode 100644 index 000000000..0738298b5 --- /dev/null +++ b/src/main/resources/com/typesafe/sbt/packager/archetypes/upstart/start-rpm-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 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/JavaAppUpstartScript.scala b/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaAppStartScript.scala similarity index 72% 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..fd4207743 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,23 @@ 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) + /** Used for prefix files generated with this builder */ + val name: String + + /** 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 +34,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 +56,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 +101,30 @@ 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 name = "rpm" + 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 name = "debian" + 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..0a7324e1b 100644 --- a/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaServerApplication.scala +++ b/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaServerApplication.scala @@ -4,10 +4,11 @@ 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 +import com.typesafe.sbt.packager.rpm.RpmPlugin /** * This class contains the default settings for creating and deploying an archetypical Java application. @@ -23,85 +24,126 @@ 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 Linux, packageSummary in Linux, daemonUser in Linux, daemonGroup in Linux, normalizedName, + sbt.Keys.version, defaultLinuxInstallLocation, linuxJavaAppStartScriptBuilder in Debian) + apply { (author, descr, 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, 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) + }, + // === 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, 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) + 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) }, - 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")) - }, - - // === 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)) - - 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 => - val script = tmpDir / "tmp" / "bin" / ("debian-" + scriptName) + // == 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 ============== */ + /* ========================================== */ + + 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 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" / (builder.name + 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..249f7813e 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 = SettingKey[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 { 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/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") } 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..80da254b9 --- /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") + +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 + // 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/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/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..c7a875837 --- /dev/null +++ b/src/sbt-test/rpm/sysvinit-rpm/test @@ -0,0 +1,21 @@ +# 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 + +$ 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