pkgr 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/README.md +16 -2
  2. data/data/build_dependencies/centos.yml +13 -0
  3. data/{lib/pkgr/data/distributions/debian/build_dependencies.yml → data/build_dependencies/debian.yml} +2 -2
  4. data/data/build_dependencies/ubuntu.yml +15 -0
  5. data/data/buildpacks/centos-6 +2 -0
  6. data/data/buildpacks/debian-6 +2 -0
  7. data/{lib/pkgr/data/distributions/debian/buildpacks/debian_wheezy → data/buildpacks/debian-7} +1 -1
  8. data/data/buildpacks/ubuntu-10.04 +2 -0
  9. data/{lib/pkgr/data/distributions/debian/buildpacks/ubuntu_precise → data/buildpacks/ubuntu-12.04} +1 -1
  10. data/{lib/pkgr/data/distributions/debian/runner.erb → data/cli/cli.sh.erb} +105 -57
  11. data/data/dependencies/centos.yml +9 -0
  12. data/{lib/pkgr/data/distributions/debian/dependencies.yml → data/dependencies/debian.yml} +2 -2
  13. data/data/dependencies/ubuntu.yml +21 -0
  14. data/data/environment/default.erb +9 -0
  15. data/{lib/pkgr/data/distributions/debian → data}/hooks/postinstall.sh +7 -4
  16. data/data/hooks/preinstall.sh +13 -0
  17. data/{lib/pkgr/data/distributions/debian/sysv → data/init/sysv/lsb-3.1}/master.erb +0 -0
  18. data/{lib/pkgr/data/distributions/debian/sysv → data/init/sysv/lsb-3.1}/process.erb +84 -11
  19. data/{lib/pkgr/data/distributions/debian/sysv → data/init/sysv/lsb-3.1}/process_master.erb +0 -0
  20. data/{lib/pkgr/data/distributions/debian/upstart → data/init/upstart/1.5}/init.d.sh.erb +0 -0
  21. data/{lib/pkgr/data/distributions/debian/upstart → data/init/upstart/1.5}/master.conf.erb +0 -0
  22. data/{lib/pkgr/data/distributions/debian/upstart → data/init/upstart/1.5}/process.conf.erb +0 -0
  23. data/{lib/pkgr/data/distributions/debian/upstart → data/init/upstart/1.5}/process_master.conf.erb +0 -0
  24. data/{lib/pkgr/data/distributions/debian → data/logrotate}/logrotate.erb +0 -0
  25. data/lib/pkgr.rb +1 -1
  26. data/lib/pkgr/builder.rb +8 -2
  27. data/lib/pkgr/buildpack.rb +1 -0
  28. data/lib/pkgr/cli.rb +12 -6
  29. data/lib/pkgr/config.rb +20 -2
  30. data/lib/pkgr/distributions.rb +9 -7
  31. data/lib/pkgr/distributions/base.rb +159 -0
  32. data/lib/pkgr/distributions/centos.rb +12 -0
  33. data/lib/pkgr/distributions/debian.rb +39 -163
  34. data/lib/pkgr/distributions/redhat.rb +51 -0
  35. data/lib/pkgr/distributions/runner.rb +39 -0
  36. data/lib/pkgr/distributions/ubuntu.rb +17 -0
  37. data/lib/pkgr/version.rb +1 -1
  38. metadata +30 -29
  39. data/lib/pkgr/app.rb +0 -279
  40. data/lib/pkgr/data/bin/executable +0 -36
  41. data/lib/pkgr/data/config/pre_boot.rb +0 -15
  42. data/lib/pkgr/data/distributions/debian/buildpacks/debian_squeeze +0 -2
  43. data/lib/pkgr/data/distributions/debian/buildpacks/ubuntu_lucid +0 -2
  44. data/lib/pkgr/data/distributions/debian/cron.d +0 -4
  45. data/lib/pkgr/data/distributions/debian/default.erb +0 -12
  46. data/lib/pkgr/data/distributions/debian/hooks/preinstall.sh +0 -9
  47. data/lib/pkgr/data/pkgr.yml +0 -32
  48. data/lib/pkgr/distributions/debian_squeeze.rb +0 -11
  49. data/lib/pkgr/distributions/debian_wheezy.rb +0 -11
  50. data/lib/pkgr/distributions/ubuntu_lucid.rb +0 -15
  51. data/lib/pkgr/distributions/ubuntu_precise.rb +0 -15
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -e
4
+
5
+ APP_USER="<%= user %>"
6
+
7
+ if ! getent passwd "${APP_USER}" > /dev/null; then
8
+ if [ -f /etc/redhat-release ]; then
9
+ adduser "${APP_USER}" --user-group --system --create-home --shell /bin/bash
10
+ else
11
+ adduser "${APP_USER}" --disabled-login --group --system --quiet --shell /bin/bash
12
+ fi
13
+ fi
@@ -26,7 +26,7 @@ pidfile="/var/run/${full_process_name}.pid"
26
26
 
27
27
  start() {
28
28
  # Run the program!
29
- /usr/local/bin/${name} run ${process_name} >> /var/log/${name}/${process_name}-PROCESS_NUM.log 2>&1 &
29
+ ${name} run ${process_name} >> /var/log/${name}/${process_name}-PROCESS_NUM.log 2>&1 &
30
30
 
31
31
  # Generate the pidfile from here. If we instead made the forked process
32
32
  # generate it there will be a race condition between the pidfile writing
@@ -37,18 +37,86 @@ start() {
37
37
  return 0
38
38
  }
39
39
 
40
+ subpids() {
41
+ local parent=$1
42
+ ps -o pid --ppid ${parent} --noheaders
43
+ }
44
+
45
+ process_kill() {
46
+ local pid=$1
47
+ local sig=$2
48
+ process_status $pid && kill -${sig} ${pid}
49
+ }
50
+
51
+ process_status() {
52
+ local pid=$1
53
+ if kill -0 $pid > /dev/null 2> /dev/null ; then
54
+ return 0
55
+ else
56
+ return 2
57
+ fi
58
+ }
59
+
60
+ stop_childs() {
61
+ local pids=$1
62
+
63
+ if [ ! "$pids" = "" ]; then
64
+ echo "Subprocesses detected ($pids), attempting to kill them first"
65
+ for pid in $pids ; do
66
+
67
+ # start in subshell, otherwise pid variable pollutes parent functions
68
+ (
69
+ stop_childs $(subpids $pid)
70
+ )
71
+
72
+ process_kill $pid "TERM"
73
+
74
+ for i in 1 2 3 4 5 ; do
75
+ echo -n "Waiting for pid $pid to die... "
76
+ process_status $pid || break
77
+ echo ""
78
+ sleep 1
79
+ done
80
+
81
+ if $(process_status $pid) ; then
82
+ echo "subprocess $pid stop failed; still running."
83
+ return 1
84
+ else
85
+ echo "stopped."
86
+ fi
87
+ done
88
+ fi
89
+ }
90
+
91
+ force_stop_childs() {
92
+ local pids=$1
93
+ for pid in $pids ; do
94
+ (
95
+ force_stop_childs $(subpids $pid)
96
+ )
97
+ process_kill $pid "KILL"
98
+ done
99
+ }
100
+
101
+
40
102
  stop() {
41
103
  # Try a few times to kill TERM the program
42
104
  if status ; then
43
- pid=`cat "$pidfile"`
105
+ local pid=`cat "$pidfile"`
44
106
  echo "Killing ${full_process_name} (pid $pid) with SIGTERM"
45
- kill -TERM $pid
46
- # Wait for it to exit.
47
- for i in 1 2 3 4 5 ; do
48
- echo "Waiting ${full_process_name} (pid $pid) to die..."
49
- status || break
50
- sleep 1
51
- done
107
+
108
+ # start in subshell, otherwise pid variable pollutes parent functions
109
+ if ( stop_childs $(subpids $pid) ) ; then
110
+ process_kill $pid "TERM"
111
+
112
+ # Wait for it to exit.
113
+ for i in 1 2 3 4 5 ; do
114
+ echo "Waiting ${full_process_name} (pid $pid) to die..."
115
+ status || break
116
+ sleep 1
117
+ done
118
+ fi
119
+
52
120
  if status ; then
53
121
  echo "${full_process_name} stop failed; still running."
54
122
  else
@@ -59,7 +127,7 @@ stop() {
59
127
 
60
128
  status() {
61
129
  if [ -f "$pidfile" ] ; then
62
- pid=`cat "$pidfile"`
130
+ local pid=`cat "$pidfile"`
63
131
  if kill -0 $pid > /dev/null 2> /dev/null ; then
64
132
  return 0
65
133
  else
@@ -72,8 +140,13 @@ status() {
72
140
 
73
141
  force_stop() {
74
142
  if status ; then
143
+ pid=$(cat "$pidfile")
75
144
  stop
76
- status && kill -KILL `cat "$pidfile"`
145
+ # start in subshell, otherwise pid variable pollutes parent functions
146
+ (
147
+ force_stop_childs $(subpids $pid)
148
+ )
149
+ process_kill $pid "KILL"
77
150
  fi
78
151
  }
79
152
 
@@ -14,7 +14,7 @@ module Pkgr
14
14
  end
15
15
 
16
16
  def data_dir
17
- File.expand_path("../pkgr/data", __FILE__)
17
+ File.expand_path("../../data", __FILE__)
18
18
  end
19
19
  module_function :data_dir
20
20
  end
@@ -38,7 +38,7 @@ module Pkgr
38
38
  # Setup the build directory structure
39
39
  def setup
40
40
  Dir.chdir(build_dir) do
41
- distribution.templates(config.name).each do |template|
41
+ distribution.templates(config).each do |template|
42
42
  template.install(config.sesame)
43
43
  end
44
44
  end
@@ -66,6 +66,12 @@ module Pkgr
66
66
  Pkgr.debug "Loading #{distribution.slug} from #{config_file}."
67
67
  @config = Config.load_file(config_file, distribution.slug).merge(config)
68
68
  Pkgr.debug "Found .pkgr.yml file. Updated config is now: #{config.inspect}"
69
+
70
+ # FIXME: make Config the authoritative source of the runner config (distribution only tells the default runner)
71
+ if @config.runner
72
+ type, *version = @config.runner.split("-")
73
+ distribution.runner = Distributions::Runner.new(type, version.join("-"))
74
+ end
69
75
  end
70
76
  end
71
77
 
@@ -162,7 +168,7 @@ module Pkgr
162
168
 
163
169
  # Path to the directory containing the main app files.
164
170
  def source_dir
165
- File.join(build_dir, "opt/#{config.name}")
171
+ File.join(build_dir, config.home)
166
172
  end
167
173
 
168
174
  # Build directory. Will be used by fpm to make the package.
@@ -85,6 +85,7 @@ module Pkgr
85
85
  def install
86
86
  FileUtils.mkdir_p(buildpack_cache_dir)
87
87
  Dir.chdir(buildpack_cache_dir) do
88
+ puts "-----> Fetching buildpack #{url} at #{branch}"
88
89
  buildpack_install = Mixlib::ShellOut.new("git clone \"#{url}\"")
89
90
  buildpack_install.logger = Pkgr.logger
90
91
  buildpack_install.run_command
@@ -21,7 +21,7 @@ module Pkgr
21
21
  :desc => "Directory where to store the buildpacks",
22
22
  :default => Pkgr::Buildpack.buildpacks_cache_dir
23
23
 
24
- desc "package TARBALL", "Package the given tarball or directory"
24
+ desc "package TARBALL", "Package the given tarball or directory, as a deb or rpm depending on the build machine"
25
25
 
26
26
  method_option :buildpack,
27
27
  :type => :string,
@@ -29,10 +29,6 @@ module Pkgr
29
29
  method_option :buildpack_list,
30
30
  :type => :string,
31
31
  :desc => "Specify a file containing a list of buildpacks to use (--buildpack takes precedence if given)"
32
- method_option :target,
33
- :type => :string,
34
- :default => "deb",
35
- :desc => "Target package to build (only 'deb' supported for now)"
36
32
  method_option :changelog,
37
33
  :type => :string,
38
34
  :desc => "Changelog"
@@ -43,6 +39,9 @@ module Pkgr
43
39
  :type => :string,
44
40
  :default => "x86_64",
45
41
  :desc => "Target architecture for the package"
42
+ method_option :runner,
43
+ :type => :string,
44
+ :desc => "Force a specific runner (e.g. upstart-1.5, sysv-lsb-1.3)"
46
45
  method_option :homepage,
47
46
  :type => :string,
48
47
  :desc => "Project homepage"
@@ -56,6 +55,10 @@ module Pkgr
56
55
  :type => :string,
57
56
  :default => Time.now.strftime("%Y%m%d%H%M%S"),
58
57
  :desc => "Package iteration (you should keep the default here)"
58
+ method_option :license,
59
+ :type => :string,
60
+ :default => nil,
61
+ :desc => "The license of your package (see <https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/#license-short-name>)"
59
62
  method_option :user,
60
63
  :type => :string,
61
64
  :desc => "User to run the app under (defaults to your app name)"
@@ -68,6 +71,9 @@ module Pkgr
68
71
  method_option :before_precompile,
69
72
  :type => :string,
70
73
  :desc => "Provide a script to run just before the buildpack compilation. Path will be resolved from the temporary code repository folder, so use absolute paths if needed."
74
+ method_option :after_precompile,
75
+ :type => :string,
76
+ :desc => "Provide a script to run just after the buildpack compilation. Path will be resolved from the temporary code repository folder, so use absolute paths if needed."
71
77
  method_option :dependencies,
72
78
  :type => :array,
73
79
  :default => [],
@@ -97,7 +103,7 @@ module Pkgr
97
103
  :desc => 'Specify environment variables for buildpack (--env "CURL_TIMEOUT=2" "BUNDLE_WITHOUT=development test")'
98
104
  method_option :force_os,
99
105
  :type => :string,
100
- :desc => 'Force a specific distribution to build for (e.g. --force-os "debian-wheezy")'
106
+ :desc => 'Force a specific distribution to build for (e.g. --force-os "ubuntu-12.04"). This may result in a broken package.'
101
107
 
102
108
  def package(tarball)
103
109
  Pkgr.level = Logger::INFO if options[:verbose]
@@ -3,12 +3,27 @@ require 'yaml'
3
3
 
4
4
  module Pkgr
5
5
  class Config < OpenStruct
6
+ DISTRO_COMPATIBILITY_MAPPING = {
7
+ "ubuntu-lucid" => "ubuntu-10.04",
8
+ "ubuntu-precise" => "ubuntu-12.04",
9
+ "debian-squeeze" => "debian-6",
10
+ "debian-wheezy" => "debian-7"
11
+ }
12
+
6
13
  class << self
7
14
  def load_file(path, distribution)
8
- config = YAML.load_file(path)
15
+ config = YAML.load_file(path) || {}
9
16
  Pkgr.debug "Configuration from file: #{config.inspect} - Distribution: #{distribution.inspect}."
10
17
 
11
18
  targets = config.delete("targets") || {}
19
+
20
+ # backward compatibility
21
+ DISTRO_COMPATIBILITY_MAPPING.each do |from, to|
22
+ if targets.has_key?(from)
23
+ targets[to] = targets.delete(from)
24
+ end
25
+ end
26
+
12
27
  (targets[distribution.to_s] || {}).each do |k,v|
13
28
  config[k] = v
14
29
  end
@@ -128,6 +143,7 @@ module Pkgr
128
143
  end
129
144
  end
130
145
 
146
+ # TODO: DRY this with cli.rb
131
147
  def to_args
132
148
  args = [
133
149
  "--name \"#{name}\"",
@@ -137,7 +153,6 @@ module Pkgr
137
153
  "--iteration \"#{iteration}\"",
138
154
  "--homepage \"#{homepage}\"",
139
155
  "--architecture \"#{architecture}\"",
140
- "--target \"#{target}\"",
141
156
  "--description \"#{description}\"",
142
157
  "--maintainer \"#{maintainer}\""
143
158
  ]
@@ -145,9 +160,12 @@ module Pkgr
145
160
  args.push "--build-dependencies #{build_dependencies.map{|d| "\"#{d}\""}.join}" unless build_dependencies.nil? || build_dependencies.empty?
146
161
  args.push "--compile-cache-dir \"#{compile_cache_dir}\"" unless compile_cache_dir.nil? || compile_cache_dir.empty?
147
162
  args.push "--before-precompile \"#{before_precompile}\"" unless before_precompile.nil? || before_precompile.empty?
163
+ args.push "--after-precompile \"#{after_precompile}\"" unless after_precompile.nil? || after_precompile.empty?
164
+ args.push "--license \"#{license}\"" unless license.nil? || license.empty?
148
165
  args.push "--buildpack \"#{buildpack}\"" unless buildpack.nil? || buildpack.empty?
149
166
  args.push "--buildpack_list \"#{buildpack_list}\"" unless buildpack_list.nil? || buildpack_list.empty?
150
167
  args.push "--force-os \"#{force_os}\"" unless force_os.nil? || force_os.empty?
168
+ args.push "--runner \"#{runner}\"" unless runner.nil? || runner.empty?
151
169
  args.push "--env #{env.variables.map{|v| "\"#{v}\""}.join(" ")}" if env.present?
152
170
  args.push "--auto" if auto
153
171
  args.push "--verbose" if verbose
@@ -1,21 +1,23 @@
1
1
  require 'pkgr/templates/file_template'
2
2
  require 'pkgr/templates/dir_template'
3
- require 'pkgr/distributions/debian'
3
+ require 'pkgr/distributions/base'
4
4
  require 'facter'
5
5
 
6
6
  module Pkgr
7
7
  module Distributions
8
8
  def current(force_os = nil)
9
- distro = if force_os.nil?
10
- [Facter.value('operatingsystem'), Facter.value('lsbdistcodename')]
9
+ os, release = if force_os.nil?
10
+ [Facter.value('operatingsystem'), Facter.value('operatingsystemrelease')]
11
11
  else
12
12
  force_os.split("-")
13
- end.map(&:capitalize).join("")
13
+ end
14
14
 
15
- klass = const_get(distro)
16
- klass.new
15
+ os.downcase!
16
+
17
+ klass = const_get(os.capitalize)
18
+ klass.new(release)
17
19
  rescue NameError => e
18
- raise Errors::UnknownDistribution, "Don't know about the current distribution you're on: #{distro}"
20
+ raise Errors::UnknownDistribution, "Don't know about the current distribution you're on: #{os}-#{release}"
19
21
  end
20
22
  module_function :current
21
23
  end
@@ -0,0 +1,159 @@
1
+ require 'pkgr/buildpack'
2
+ require 'pkgr/env'
3
+ require 'pkgr/distributions/runner'
4
+ require 'yaml'
5
+
6
+ module Pkgr
7
+ module Distributions
8
+ # Base components and behaviors for all distributions.
9
+ class Base
10
+ attr_reader :release
11
+ attr_writer :runner
12
+
13
+ def initialize(release)
14
+ @release = release
15
+ end
16
+
17
+ def os
18
+ self.class.name.split("::")[-1].downcase
19
+ end # def os
20
+
21
+ # e.g. ubuntu-12.04
22
+ def slug
23
+ [os, release].join("-")
24
+ end # def slug
25
+
26
+ def package_test_command(package)
27
+ raise NotImplementedError, "package_test_command must be implemented"
28
+ end
29
+
30
+ def package_install_command(packages)
31
+ raise NotImplementedError, "package_install_command must be implemented"
32
+ end
33
+
34
+ # Check if all build dependencies are present.
35
+ def check(config)
36
+ missing_packages = (build_dependencies(config.build_dependencies) || []).select do |package|
37
+ test_command = package_test_command(package)
38
+ Pkgr.debug "sh(#{test_command})"
39
+ ! system(test_command)
40
+ end
41
+
42
+ unless missing_packages.empty?
43
+ install_command = package_install_command(missing_packages)
44
+ if config.auto
45
+ package_install = Mixlib::ShellOut.new(install_command)
46
+ package_install.logger = Pkgr.logger
47
+ package_install.run_command
48
+ package_install.error!
49
+ else
50
+ Pkgr.warn("Missing build dependencies detected. Run the following to fix: #{install_command}")
51
+ end
52
+ end
53
+ end
54
+
55
+ # e.g. data/buildpacks/ubuntu/12.04
56
+ def default_buildpack_list
57
+ data_file(File.join("buildpacks", slug))
58
+ end # def default_buildpack_list
59
+
60
+ # Returns a list of Buildpack objects
61
+ def buildpacks(config)
62
+ custom_buildpack_uri = config.buildpack
63
+ if custom_buildpack_uri
64
+ uuid = Digest::SHA1.hexdigest(custom_buildpack_uri)
65
+ [Buildpack.new(custom_buildpack_uri, :custom, config.env)]
66
+ else
67
+ load_buildpack_list(config)
68
+ end
69
+ end # def buildpacks
70
+
71
+ def dependencies(other_dependencies = nil)
72
+ deps = YAML.load_file(data_file("dependencies", "#{os}.yml"))
73
+ (deps["default"] || []) | (deps[slug] || []) | (other_dependencies || [])
74
+ end # def dependencies
75
+
76
+ def build_dependencies(other_dependencies = nil)
77
+ deps = YAML.load_file(data_file("build_dependencies", "#{os}.yml"))
78
+ (deps["default"] || []) | (deps[slug] || []) | (other_dependencies || [])
79
+ end # def build_dependencies
80
+
81
+ # Returns a list of file and directory templates.
82
+ def templates(config)
83
+ app_name = config.name
84
+ list = []
85
+
86
+ # directories
87
+ [
88
+ "usr/bin",
89
+ config.home.gsub(/^\//, ""),
90
+ "etc/#{app_name}/conf.d",
91
+ "etc/default",
92
+ "etc/init",
93
+ "var/log/#{app_name}"
94
+ ].each{|dir| list.push Templates::DirTemplate.new(dir) }
95
+
96
+ list.push Templates::FileTemplate.new("etc/default/#{app_name}", data_file("environment", "default.erb"))
97
+ list.push Templates::FileTemplate.new("etc/logrotate.d/#{app_name}", data_file("logrotate", "logrotate.erb"))
98
+
99
+ # Put cli in /usr/bin, as redhat based distros don't have /usr/local/bin in their sudo PATH.
100
+ list.push Templates::FileTemplate.new("usr/bin/#{app_name}", data_file("cli", "cli.sh.erb"), mode: 0755)
101
+
102
+ # NOTE: /etc/appname/conf.d/* files are no longer installed here, since we don't want to overwrite any pre-existing config.
103
+ # They're now installed in the postinstall script.
104
+ list
105
+ end
106
+
107
+ # Returns a list of <Process, FileTemplate> tuples.
108
+ def initializers_for(app_name, procfile_entries)
109
+ list = []
110
+ procfile_entries.select(&:daemon?).each do |process|
111
+ Pkgr.debug "Adding #{process.inspect} to initialization scripts"
112
+ runner.templates(process, app_name).each do |template|
113
+ list.push [process, template]
114
+ end
115
+ end
116
+ list
117
+ end
118
+
119
+ def preinstall_file(config)
120
+ @preinstall_file ||= generate_hook_file("preinstall.sh", config)
121
+ @preinstall_file.path
122
+ end
123
+
124
+ def postinstall_file(config)
125
+ @postinstall_file ||= generate_hook_file("postinstall.sh", config)
126
+ @postinstall_file.path
127
+ end
128
+
129
+ protected
130
+
131
+ def load_buildpack_list(config)
132
+ file = config.buildpack_list || default_buildpack_list
133
+ return [] if file.nil?
134
+
135
+ File.read(file).split("\n").map do |line|
136
+ url, *raw_env = line.split(",")
137
+ buildpack_env = (config.env || Env.new).merge(Env.new(raw_env))
138
+ Buildpack.new(url, :builtin, buildpack_env)
139
+ end
140
+ end # def load_buildpack_list
141
+
142
+ def data_file(*names)
143
+ File.new(File.join(Pkgr.data_dir, *names))
144
+ end
145
+
146
+ def generate_hook_file(hook_name, config)
147
+ source = data_file("hooks", hook_name)
148
+ file = Tempfile.new("postinstall")
149
+ file.write ERB.new(File.read(source)).result(config.sesame)
150
+ file.rewind
151
+ file
152
+ end
153
+
154
+ end # class Base
155
+ end # module Distributions
156
+ end # module Pkgr
157
+
158
+ require 'pkgr/distributions/debian'
159
+ require 'pkgr/distributions/redhat'