pkgr 0.3.4 → 1.0.1.pre

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. data/README.md +68 -463
  2. data/bin/pkgr +4 -52
  3. data/lib/pkgr.rb +13 -29
  4. data/lib/pkgr/app.rb +19 -4
  5. data/lib/pkgr/builder.rb +207 -0
  6. data/lib/pkgr/buildpack.rb +80 -0
  7. data/lib/pkgr/cli.rb +34 -158
  8. data/lib/pkgr/config.rb +65 -0
  9. data/lib/pkgr/data/distributions/debian/build_dependencies.yml +14 -0
  10. data/lib/pkgr/data/{debian → distributions/debian}/cron.d +0 -0
  11. data/lib/pkgr/data/distributions/debian/default.erb +12 -0
  12. data/lib/pkgr/data/distributions/debian/dependencies.yml +17 -0
  13. data/lib/pkgr/data/distributions/debian/hooks/postinstall.sh +27 -0
  14. data/lib/pkgr/data/distributions/debian/hooks/preinstall.sh +9 -0
  15. data/lib/pkgr/data/{debian → distributions/debian}/logrotate.erb +0 -0
  16. data/lib/pkgr/data/distributions/debian/runner.erb +122 -0
  17. data/lib/pkgr/data/distributions/debian/upstart/master.conf.erb +7 -0
  18. data/lib/pkgr/data/distributions/debian/upstart/process.conf.erb +7 -0
  19. data/lib/pkgr/data/distributions/debian/upstart/process_master.conf.erb +2 -0
  20. data/lib/pkgr/dispatcher.rb +51 -0
  21. data/lib/pkgr/distributions.rb +18 -0
  22. data/lib/pkgr/distributions/debian.rb +157 -0
  23. data/lib/pkgr/git.rb +24 -0
  24. data/lib/pkgr/process.rb +18 -0
  25. data/lib/pkgr/templates/dir_template.rb +14 -0
  26. data/lib/pkgr/templates/file_template.rb +38 -0
  27. data/lib/pkgr/version.rb +1 -1
  28. metadata +93 -26
  29. data/lib/pkgr/data/debian/changelog +0 -0
  30. data/lib/pkgr/data/debian/compat.erb +0 -1
  31. data/lib/pkgr/data/debian/control.erb +0 -12
  32. data/lib/pkgr/data/debian/copyright.erb +0 -17
  33. data/lib/pkgr/data/debian/default.erb +0 -11
  34. data/lib/pkgr/data/debian/dirs.erb +0 -2
  35. data/lib/pkgr/data/debian/docs.erb +0 -0
  36. data/lib/pkgr/data/debian/init.d.erb +0 -155
  37. data/lib/pkgr/data/debian/install.erb +0 -16
  38. data/lib/pkgr/data/debian/links.erb +0 -5
  39. data/lib/pkgr/data/debian/postinst.erb +0 -59
  40. data/lib/pkgr/data/debian/prerm.erb +0 -57
  41. data/lib/pkgr/data/debian/rules.erb +0 -7
  42. data/lib/pkgr/pkgr.rake +0 -50
  43. data/lib/pkgr/railtie.rb +0 -7
@@ -0,0 +1,65 @@
1
+ require 'ostruct'
2
+
3
+ module Pkgr
4
+ class Config < OpenStruct
5
+ def sesame
6
+ binding
7
+ end
8
+
9
+ def home
10
+ "/opt/#{name}"
11
+ end
12
+
13
+ def user
14
+ @table[:user] || name
15
+ end
16
+
17
+ def group
18
+ @table[:group] || user
19
+ end
20
+
21
+ def architecture
22
+ @table[:architecture] || "x86_64"
23
+ end
24
+
25
+ def homepage
26
+ @table[:homepage] || "http://example.com/no-uri-given"
27
+ end
28
+
29
+ def valid?
30
+ @errors = []
31
+ @errors.push("name can't be blank") if name.nil? || name.empty?
32
+ @errors.push("version can't be blank") if version.nil? || version.empty?
33
+ @errors.push("iteration can't be blank") if iteration.nil? || iteration.empty?
34
+ @errors.push("user can't be blank") if user.nil? || user.empty?
35
+ @errors.push("group can't be blank") if group.nil? || group.empty?
36
+ @errors.empty?
37
+ end
38
+
39
+ def errors
40
+ @errors ||= []
41
+ end
42
+
43
+ def to_args
44
+ args = [
45
+ "--name \"#{name}\"",
46
+ "--version \"#{version}\"",
47
+ "--user \"#{user}\"",
48
+ "--group \"#{group}\"",
49
+ "--iteration \"#{iteration}\"",
50
+ "--homepage \"#{homepage}\"",
51
+ "--architecture \"#{architecture}\"",
52
+ "--target \"#{target}\"",
53
+ "--description \"#{description}\"",
54
+ ]
55
+ args.push "--dependencies #{dependencies.map{|d| "\"#{d}\""}.join("")}" unless dependencies.nil? || dependencies.empty?
56
+ args.push "--build-dependencies #{build_dependencies.map{|d| "\"#{d}\""}.join("")}" unless build_dependencies.nil? || build_dependencies.empty?
57
+ args.push "--compile-cache-dir \"#{compile_cache_dir}\"" unless compile_cache_dir.nil? || compile_cache_dir.empty?
58
+ args.push "--before-precompile \"#{before_precompile}\"" unless compile_cache_dir.nil? || compile_cache_dir.empty?
59
+ args.push "--auto" if auto
60
+ args.push "--verbose" if verbose
61
+ args.push "--debug" if debug
62
+ args
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,14 @@
1
+ default:
2
+ - curl
3
+ - libssl0.9.8
4
+ - libmysqlclient-dev
5
+ - libpq-dev
6
+ - libsqlite3-0
7
+ - libevent-dev
8
+ - libssl-dev
9
+ - libxml2-dev
10
+ - libxslt1-dev
11
+ - libreadline-dev
12
+ - build-essential
13
+ squeeze:
14
+ wheezy:
@@ -0,0 +1,12 @@
1
+ HOME="<%= home %>"
2
+ APP_NAME="<%= name %>"
3
+ APP_GROUP="<%= group %>"
4
+ APP_USER="<%= user %>"
5
+
6
+ for file in /etc/${APP_NAME}/conf.d/*; do
7
+ if [ -f $file ]; then . $file; fi
8
+ done
9
+
10
+ for file in ${HOME}/.profile.d/*.sh; do
11
+ if [ -f $file ]; then . $file; fi
12
+ done
@@ -0,0 +1,17 @@
1
+ default:
2
+ - upstart
3
+ - mysql-common
4
+ - libpq5
5
+ - libsqlite3-0
6
+ - libevent-1.4-2
7
+ - libevent-core-1.4-2
8
+ - libevent-extra-1.4-2
9
+ - libssl0.9.8
10
+ - openssl
11
+ - libxml2
12
+ - libxslt1.1
13
+ - libreadline6
14
+ squeeze:
15
+ - libmysqlclient16
16
+ wheezy:
17
+ - libmysqlclient18
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -e
4
+
5
+ APP_NAME="<%= name %>"
6
+ APP_USER="<%= user %>"
7
+ APP_GROUP="<%= group %>"
8
+ HOME="/opt/${APP_NAME}"
9
+ HOME_LOGS="${HOME}/log"
10
+ LOGS="/var/log/${APP_NAME}"
11
+
12
+ chown -R ${APP_USER}.${APP_GROUP} ${HOME}
13
+
14
+ # link app log directory to /var/log/NAME
15
+ rm -rf ${HOME_LOGS}
16
+ ln -fs ${LOGS} ${HOME_LOGS}
17
+ chown -R ${APP_USER}.${APP_GROUP} ${LOGS}
18
+
19
+ # Add default conf.d file
20
+ [ -f /etc/${APP_NAME}/conf.d/other ] || cat > /etc/${APP_NAME}/conf.d/other <<CONF
21
+ # This file contains variables set via \`${APP_NAME} config:set\`
22
+ # Database URL. E.g. : mysql2://root:pass@127.0.0.1/my-app-db
23
+ export DATABASE_URL=db_adapter://db_user:db_password@db_host/db_name
24
+ export PORT=\${PORT:=6000}
25
+ CONF
26
+
27
+ chmod -R 0600 /etc/${APP_NAME}
@@ -0,0 +1,9 @@
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
+ adduser ${APP_USER} --disabled-login --group --system --quiet --shell /bin/bash
9
+ fi
@@ -0,0 +1,122 @@
1
+ #!/bin/bash
2
+
3
+ set -e
4
+
5
+ EXECUTABLE=$(basename $0)
6
+
7
+ function usage {
8
+ echo "Usage:"
9
+ echo " $EXECUTABLE run COMMAND [options]"
10
+ echo " $EXECUTABLE scale TYPE=NUM"
11
+ echo " $EXECUTABLE config:get VAR"
12
+ echo " $EXECUTABLE config:set VAR=VALUE"
13
+ exit 1
14
+ }
15
+
16
+ if [ ! 0 -eq ${EUID} ]; then
17
+ echo "You must be executing with root privileges to launch commands. Either log in as root, use sudo, or add sudo privileges for running ${EXECUTABLE} with your user."
18
+ exit 1
19
+ fi
20
+
21
+ DEFAULT_FILE=/etc/default/<%= name %>
22
+
23
+ . ${DEFAULT_FILE}
24
+
25
+ while : ; do
26
+ case "$1" in
27
+ run)
28
+ [ $# -lt 2 ] && usage
29
+ COMMAND="$2"
30
+ shift 2
31
+
32
+ exec su -m - "${APP_USER}" -c ". ${DEFAULT_FILE} && cd ${HOME} && vendor/pkgr/processes/${COMMAND} $@"
33
+ break ;;
34
+
35
+ scale)
36
+ shift
37
+ while : ; do
38
+ case "$1" in
39
+ *)
40
+ [ "$1" = "" ] && usage
41
+
42
+ PROCESS=(${1//=/ })
43
+
44
+ PROCESS_NAME=${PROCESS[0]}
45
+ NEW_SCALE=${PROCESS[1]}
46
+ CURRENT_SCALE=$(ls -rv1 /etc/init/${APP_NAME}-${PROCESS_NAME}-*.conf 2>/dev/null | head -1 | sed -r 's/.*\-([0-9]+)\.conf/\1/g')
47
+ CURRENT_SCALE=${CURRENT_SCALE:="0"}
48
+ SCALE_DELTA=$(expr ${NEW_SCALE} - ${CURRENT_SCALE})
49
+
50
+ if [ $NEW_SCALE -eq 0 ]; then
51
+ echo "Stopping all ${PROCESS_NAME} processes... "
52
+ for file in `ls -1 /etc/init/${APP_NAME}-${PROCESS_NAME}-*.conf`; do
53
+ service "$(basename ${file} .conf)" stop || true
54
+ rm "${file}"
55
+ done
56
+ # Finally, remove master process
57
+ rm "/etc/init/${APP_NAME}-${PROCESS_NAME}.conf"
58
+ echo "--> done."
59
+ exit 0
60
+ fi
61
+
62
+ if [ $SCALE_DELTA -gt 0 ]; then
63
+ echo "Scaling up..."
64
+ cp "${HOME}/vendor/pkgr/scaling/${APP_NAME}-${PROCESS_NAME}.conf" /etc/init/
65
+ for i in $(seq ${SCALE_DELTA}); do
66
+ PROCESS_ID="${APP_NAME}-${PROCESS_NAME}-${i}"
67
+ i=$(expr ${i} + ${CURRENT_SCALE})
68
+ cp "${HOME}/vendor/pkgr/scaling/${APP_NAME}-${PROCESS_NAME}-PROCESS_NUM.conf" "/etc/init/${PROCESS_ID}.conf"
69
+ sed -i "s/PROCESS_NUM/${i}/g" "/etc/init/${PROCESS_ID}.conf"
70
+ if [ "${PROCESS_NAME}" = "web" ]; then
71
+ port=$(expr ${PORT} + ${i} - 1)
72
+ sed -i "s/PORT_NUM/${port}/g" "/etc/init/${PROCESS_ID}.conf"
73
+ else
74
+ sed -i "s/^env .*PORT_NUM.*$//g" "/etc/init/${PROCESS_ID}.conf"
75
+ fi
76
+ service "${PROCESS_ID}" start
77
+ done
78
+ echo "--> done."
79
+ elif [ $SCALE_DELTA -lt 0 ]; then
80
+ echo "Scaling down..."
81
+ for i in $(seq $(expr $SCALE_DELTA \* -1)); do
82
+ PROCESS_ID="${APP_NAME}-${PROCESS_NAME}-${i}"
83
+ i=$(expr ${i} + ${NEW_SCALE})
84
+ service "${PROCESS_ID}" stop || true
85
+ rm -f "/etc/init/${PROCESS_ID}.conf"
86
+ done
87
+ echo "--> done."
88
+ else
89
+ echo "Nothing to do."
90
+ fi
91
+ break ;;
92
+ esac
93
+ done
94
+ break ;;
95
+
96
+ config:set)
97
+ [ $# -lt 2 ] && usage
98
+
99
+ CONFIG=(${2//=/ })
100
+
101
+ VAR=${CONFIG[0]:?"Invalid variable name"}
102
+ VALUE=${CONFIG[1]:?"Invalid value"}
103
+
104
+ DEFAULT_CONF_FILE="/etc/${APP_NAME}/conf.d/other"
105
+
106
+ sed -i -r "s/^.*${VAR}.*$//g" /etc/${APP_NAME}/conf.d/*
107
+
108
+ echo "export ${VAR}=${VALUE}" >> "${DEFAULT_CONF_FILE}"
109
+
110
+ break;;
111
+
112
+ config:get)
113
+ [ $# -lt 2 ] && usage
114
+ result=$(grep --no-filename "${2}" ${DEFAULT_FILE} /etc/${APP_NAME}/conf.d/*)
115
+ echo "${result}" | sed -r "s/^export\s+${2}=//g"
116
+ break;;
117
+
118
+ *)
119
+ usage
120
+ break ;;
121
+ esac
122
+ done
@@ -0,0 +1,7 @@
1
+ start on (started network-interface
2
+ or started network-manager
3
+ or started networking)
4
+
5
+ stop on (stopping network-interface
6
+ or stopping network-manager
7
+ or stopping networking)
@@ -0,0 +1,7 @@
1
+ start on starting <%= name %>-<%= process_name %>
2
+ stop on stopping <%= name %>-<%= process_name %>
3
+ respawn
4
+
5
+ env PORT=PORT_NUM
6
+
7
+ exec <%= name %> run <%= process_name %> >> /var/log/<%= name %>/<%= process_name %>-PROCESS_NUM.log 2>&1
@@ -0,0 +1,2 @@
1
+ start on starting <%= name %>
2
+ stop on stopping <%= name %>
@@ -0,0 +1,51 @@
1
+ require 'pkgr/builder'
2
+ require 'pkgr/git'
3
+
4
+ module Pkgr
5
+ class Dispatcher
6
+ attr_reader :path, :host, :config
7
+
8
+ def initialize(path, opts = {})
9
+ opts = opts.dup
10
+ @path = path
11
+ @host = opts.delete(:host)
12
+ @config = Config.new(opts)
13
+ end
14
+
15
+ def setup
16
+ tarify if File.directory?(path)
17
+ end
18
+
19
+ def call
20
+ setup
21
+
22
+ if remote?
23
+ command = %{ ( cat "#{path}" | ssh "#{host}" pkgr package - #{config.to_args.join(" ")} ) && rsync "#{host}":~/*.deb .}
24
+ Pkgr.debug command
25
+ IO.popen(command) do |io|
26
+ until io.eof?
27
+ data = io.gets
28
+ print data
29
+ end
30
+ end
31
+ raise "Error when running remote packaging command. Please make sure to run `sudo apt-get install -y ruby1.9.1-full build-essential git-core && sudo gem install pkgr --version #{Pkgr::VERSION}`" unless $?.exitstatus.zero?
32
+ else
33
+ Builder.new(path, config).call
34
+ end
35
+ end
36
+
37
+ def tarify
38
+ tmpfile = Tempfile.new(["pkgr-tarball", ".tar.gz"])
39
+ system("tar czf #{tmpfile.path} --exclude .git --exclude .svn -C \"#{path}\" .") || raise(Pkgr::Errors::Base, "Can't compress input directory")
40
+ # Remove any non-digit characters that may be before the version number
41
+ config.version ||= (Git.new(path).latest_tag || "").gsub(/^[^\d](\d.*)/, '\1')
42
+ config.compile_cache_dir ||= File.join(path, ".git", "cache")
43
+ config.name ||= File.basename(path)
44
+ @path = tmpfile.path
45
+ end
46
+
47
+ def remote?
48
+ !host.nil?
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,18 @@
1
+ require 'pkgr/templates/file_template'
2
+ require 'pkgr/templates/dir_template'
3
+ require 'pkgr/distributions/debian'
4
+
5
+
6
+ module Pkgr
7
+ module Distributions
8
+ def current
9
+ if File.exist?("/etc/debian_version")
10
+ distro = File.read("/etc/debian_version").split("/")[0]
11
+ Debian.new(distro)
12
+ else
13
+ raise "Don't know about the current distribution you're on"
14
+ end
15
+ end
16
+ module_function :current
17
+ end
18
+ end
@@ -0,0 +1,157 @@
1
+ require 'pkgr/buildpack'
2
+ require 'pkgr/process'
3
+ require 'yaml'
4
+ require 'erb'
5
+
6
+ module Pkgr
7
+ module Distributions
8
+ class Debian
9
+
10
+ attr_reader :version
11
+ def initialize(version)
12
+ @version = version
13
+ end
14
+
15
+ def templates(app_name)
16
+ list = []
17
+
18
+ # directories
19
+ [
20
+ "usr/local/bin",
21
+ "opt/#{app_name}",
22
+ "etc/#{app_name}/conf.d",
23
+ "etc/default",
24
+ "etc/init",
25
+ "var/log/#{app_name}"
26
+ ].each{|dir| list.push Templates::DirTemplate.new(dir) }
27
+
28
+ # default
29
+ list.push Templates::FileTemplate.new("etc/default/#{app_name}", File.new(File.join(data_dir, "default.erb")))
30
+ # upstart master
31
+ list.push Templates::FileTemplate.new("etc/init/#{app_name}.conf", data_file("upstart/master.conf.erb"))
32
+ # executable
33
+ list.push Templates::FileTemplate.new("usr/local/bin/#{app_name}", File.new(File.join(data_dir, "runner.erb")), mode: 0755)
34
+ # logrotate
35
+ list.push Templates::FileTemplate.new("etc/logrotate.d/#{app_name}", File.new(File.join(data_dir, "logrotate.erb")))
36
+
37
+ # NOTE: conf.d files are no longer installed here, since we don't want to overwrite any pre-existing config.
38
+ # They're now installed in the postinstall script.
39
+
40
+ list
41
+ end
42
+
43
+ def initializers_for(app_name, procfile_entries)
44
+ list = []
45
+ procfile_entries.select(&:daemon?).each do |process|
46
+ Pkgr.debug "Adding #{process.inspect} to initialization scripts"
47
+ list.push [process, Templates::FileTemplate.new("#{app_name}-#{process.name}.conf", data_file("upstart/process_master.conf.erb"))]
48
+ list.push [process, Templates::FileTemplate.new("#{app_name}-#{process.name}-PROCESS_NUM.conf", data_file("upstart/process.conf.erb"))]
49
+ end
50
+ list
51
+ end
52
+
53
+ def check(config)
54
+ missing_packages = (build_dependencies(config.build_dependencies) || []).select do |package|
55
+ test_command = "dpkg -s '#{package}' > /dev/null 2>&1"
56
+ Pkgr.debug "Running #{test_command}"
57
+ ! system(test_command)
58
+ end
59
+
60
+ unless missing_packages.empty?
61
+ package_install_command = "sudo apt-get install -y #{missing_packages.map{|package| "\"#{package}\""}.join(" ")}"
62
+ if config.auto
63
+ Pkgr.debug "Running command: #{package_install_command}"
64
+ package_install = Mixlib::ShellOut.new(package_install_command)
65
+ package_install.run_command
66
+ package_install.error!
67
+ else
68
+ Pkgr.warn("Missing build dependencies detected. Run the following to fix: #{package_install_command}")
69
+ end
70
+ end
71
+ end
72
+
73
+ def fpm_command(build_dir, config)
74
+ %{
75
+ fpm -t deb -s dir --verbose --force \
76
+ -C "#{build_dir}" \
77
+ -n "#{config.name}" \
78
+ --version "#{config.version}" \
79
+ --iteration "#{config.iteration}" \
80
+ --url "#{config.homepage}" \
81
+ --provides "#{config.name}" \
82
+ --deb-user "root" \
83
+ --deb-group "root" \
84
+ -a "#{config.architecture}" \
85
+ --template-scripts \
86
+ --before-install #{preinstall_file(config)} \
87
+ --after-install #{postinstall_file(config)} \
88
+ #{dependencies(config.dependencies).map{|d| "-d '#{d}'"}.join(" ")} \
89
+ .
90
+ }
91
+ end
92
+
93
+ def buildpacks
94
+ case version
95
+ when "wheezy"
96
+ %w{
97
+ https://github.com/heroku/heroku-buildpack-ruby.git
98
+ https://github.com/heroku/heroku-buildpack-nodejs.git
99
+ https://github.com/heroku/heroku-buildpack-java.git
100
+ https://github.com/heroku/heroku-buildpack-play.git
101
+ https://github.com/heroku/heroku-buildpack-python.git
102
+ https://github.com/heroku/heroku-buildpack-php.git
103
+ https://github.com/heroku/heroku-buildpack-clojure.git
104
+ https://github.com/kr/heroku-buildpack-go.git
105
+ https://github.com/miyagawa/heroku-buildpack-perl.git
106
+ https://github.com/heroku/heroku-buildpack-scala
107
+ https://github.com/igrigorik/heroku-buildpack-dart.git
108
+ https://github.com/rhy-jot/buildpack-nginx.git
109
+ https://github.com/Kloadut/heroku-buildpack-static-apache.git
110
+ }.map{|url| Buildpack.new(url)}
111
+ end
112
+ end
113
+
114
+ def preinstall_file(config)
115
+ @preinstall_file ||= begin
116
+ source = File.join(data_dir, "hooks", "preinstall.sh")
117
+ file = Tempfile.new("preinstall")
118
+ file.write ERB.new(File.read(source)).result(config.sesame)
119
+ file.rewind
120
+ file
121
+ end
122
+
123
+ @preinstall_file.path
124
+ end
125
+
126
+ def postinstall_file(config)
127
+ @postinstall_file ||= begin
128
+ source = File.join(data_dir, "hooks", "postinstall.sh")
129
+ file = Tempfile.new("postinstall")
130
+ file.write ERB.new(File.read(source)).result(config.sesame)
131
+ file.rewind
132
+ file
133
+ end
134
+
135
+ @postinstall_file.path
136
+ end
137
+
138
+ def dependencies(other_dependencies = nil)
139
+ deps = YAML.load_file(File.join(data_dir, "dependencies.yml"))
140
+ (deps["default"] || []) | (deps[version] || []) | (other_dependencies || [])
141
+ end
142
+
143
+ def build_dependencies(other_dependencies = nil)
144
+ deps = YAML.load_file(File.join(data_dir, "build_dependencies.yml"))
145
+ (deps["default"] || []) | (deps[version] || []) | (other_dependencies || [])
146
+ end
147
+
148
+ def data_file(name)
149
+ File.new(File.join(data_dir, name))
150
+ end
151
+
152
+ def data_dir
153
+ File.join(Pkgr.data_dir, "distributions", "debian")
154
+ end
155
+ end
156
+ end
157
+ end