spriggan 0.1.11

Sign up to get free protection for your applications and to get access to all the features.
data/bin/sprig-purge ADDED
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'pathname'
4
+ apt_root = Pathname.new(ENV['HOME']) + 'apt'
5
+
6
+ unless apt_root.directory?
7
+ warn "Not running as spriggan user; aborting."
8
+ Kernel.exit 1
9
+ end
10
+
11
+ metapkg_name = ARGV[0]
12
+
13
+ pkg_names = Dir.chdir(apt_root) do
14
+ a = `find . -name "#{metapkg_name}_*.deb"`.split("\n")
15
+ b = `find . -name "#{metapkg_name}-*.deb"`.split("\n")
16
+ (a + b).map{ |pkg| Pathname.new(pkg).basename.to_s.split('_').first }
17
+ end
18
+
19
+ pkg_names.each do |pkg_name|
20
+ system("reprepro -b #{apt_root} remove stable #{pkg_name}")
21
+ end
22
+
23
+ sprig_dir = Pathname.new(ENV['HOME']) + 'project' + "#{metapkg_name}.git" + 'spriggan'
24
+ sprig_dir.rmtree if sprig_dir.directory?
25
+
26
+ puts "OK"
data/bin/spriggan ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'spriggan'
4
+
5
+ unless ARGV.length == 2
6
+ warn "usage: spriggan git-dir apt-dir"
7
+ Kernel.exit 1
8
+ end
9
+
10
+ job = Spriggan::BuildJob.new(ARGV[0], ARGV[1])
11
+ job.run!
data/lib/spriggan.rb ADDED
@@ -0,0 +1,4 @@
1
+ module Spriggan; end
2
+
3
+ require 'spriggan/version'
4
+ require 'spriggan/buildjob'
@@ -0,0 +1,231 @@
1
+ require 'shellwords'
2
+ require 'pathname'
3
+ require 'yaml'
4
+ require 'digest'
5
+ require 'tmpdir'
6
+
7
+ require 'spriggan'
8
+ require 'spriggan/system_calls_patch'
9
+
10
+ class Spriggan::BuildJob
11
+ def initialize(source_dir, dest_dir)
12
+ @bare_repo = Pathname.new(source_dir).expand_path
13
+ @apt_root = Pathname.new(dest_dir).expand_path
14
+
15
+ unless @bare_repo.directory? and (@bare_repo + 'config').file?
16
+ raise ArgumentError, 'invalid source'
17
+ end
18
+
19
+ unless @apt_root.directory? and (@apt_root + 'conf' + 'distributions').file?
20
+ raise ArgumentError, 'invalid destination'
21
+ end
22
+
23
+ @project = {}
24
+ @project[:root] = @bare_repo + 'spriggan'
25
+ @project[:name] = @bare_repo.basename.to_s.sub(/\.git$/, '')
26
+ @project[:name] = @bare_repo.parent.basename.to_s if @project[:name] == ''
27
+ @project[:cache] = @project[:root] + 'cache'
28
+
29
+ @build = {}
30
+ @build[:timestamp] = Time.now.strftime("%Y%m%d%H%M%S")
31
+ @build[:id] = @build[:timestamp] + '-' + rand(2**40).to_s(32).rjust(8, '0')
32
+ @build[:root] = @project[:root] + 'jobs' + @build[:id]
33
+
34
+ @build[:files] = @build[:root] + 'fs'
35
+ @build[:pkgs] = @build[:root] + 'pkgs'
36
+ @build[:debs] = @build[:root] + 'debs'
37
+
38
+ @packages = {}
39
+ @signatures = {'packages' => {}, 'metapackage' => nil}
40
+ end
41
+
42
+ def run!
43
+ [@project[:cache], @build[:files], @build[:pkgs], @build[:debs]].each(&:mkpath)
44
+
45
+ begin
46
+ execute_mutations!
47
+ ensure
48
+ @build[:root].rmtree if @build[:root].directory?
49
+ end
50
+ end
51
+
52
+
53
+ private
54
+
55
+ def execute_mutations!
56
+ checkout_repo!
57
+
58
+ do_project_specific_build!
59
+
60
+ generate_packages!
61
+ generate_metapackage!
62
+
63
+ publish_packages!
64
+ end
65
+
66
+ def checkout_repo!
67
+ @project[:sha] = `git --git-dir=#{@bare_repo} show-ref -s master`.strip
68
+ @project[:tag] = `git --git-dir=#{@bare_repo} describe --tags #{@project[:sha]}`.strip
69
+
70
+ @build[:files].parent.mkpath
71
+ system!("git clone -s #{@bare_repo} #{@build[:files]}")
72
+
73
+ Dir.chdir(@build[:files]) do
74
+ system!("git checkout -q -f #{@project[:sha]}")
75
+ end
76
+
77
+ @project[:conf] = @build[:files] + 'spriggan'
78
+ @project.update(YAML.load((@project[:conf] + 'project.yml').open))
79
+
80
+ @project[:signatures] = @project[:root] + 'signatures.yml'
81
+ if @project[:signatures].file?
82
+ @signatures = YAML.load(@project[:signatures].open)
83
+ end
84
+ end
85
+
86
+ def do_project_specific_build!
87
+ Dir.chdir(@build[:files]) do
88
+ if bootstrap = @project['bootstrap']
89
+ system!(bootstrap['command'])
90
+ end
91
+
92
+ if resolve_deps = @project['resolve dependencies']
93
+ if mountpoint = resolve_deps['keep in shared cache']
94
+ with_cache_mounted(@project[:cache], @build[:files] + mountpoint) do
95
+ system!(resolve_deps['command'])
96
+ end
97
+ else
98
+ system!(resolve_deps['command'])
99
+ end
100
+ end
101
+
102
+ if build = @project['build']
103
+ system!(build['command'])
104
+ end
105
+
106
+ @project['packages'].each do |pkg_name, pkg|
107
+ pkg_dir = @build[:pkgs] + pkg_name
108
+ pkg_dir.mkpath
109
+
110
+ system!(pkg['install command'] + " " + Shellwords.escape(pkg_dir.to_s))
111
+ end
112
+ end
113
+ end
114
+
115
+ def generate_packages!
116
+ @packages = {}
117
+
118
+ @build[:pkgs].children.each do |pkg_dir|
119
+ next unless pkg_dir.directory? and (pkg_dir.children.length > 0)
120
+ @packages[pkg_dir.basename.to_s] = generate_package!(pkg_dir)
121
+ end
122
+ end
123
+
124
+ def generate_package!(pkg_dir)
125
+ pkg = {}
126
+
127
+ pkg[:shortname] = pkg_dir.basename.to_s
128
+ pkg[:name] = "#{@project[:name]}-#{pkg[:shortname]}"
129
+ pkg[:signature] = hash_package(pkg_dir)
130
+ pkg[:vsn] = debian_version_str(@project[:tag], @build[:timestamp], pkg[:signature])
131
+
132
+ pkg.update(@project['packages'][pkg_dir.basename.to_s])
133
+
134
+ deps_str = ""
135
+ if pkg_deps = pkg['dependencies']
136
+ deps_str = pkg_deps.map{ |dep| "-d #{Shellwords.escape(dep.to_s)}" }.join(" ")
137
+ end
138
+
139
+ if pkg[:signature] != @signatures['packages'][pkg[:shortname]]
140
+ Dir.chdir(@build[:debs]) do
141
+ system("fpm -s dir -t deb --deb-user root --deb-group root -C #{Shellwords.escape(pkg_dir.to_s)} -n #{Shellwords.escape(pkg[:name])} -v #{Shellwords.escape(pkg[:vsn])} -a all #{deps_str} .")
142
+ end
143
+
144
+ @signatures['packages'][pkg[:shortname]] = pkg[:signature]
145
+ end
146
+
147
+ pkg
148
+ end
149
+
150
+ def hash_package(pkg_dir)
151
+ hashes = []
152
+ pkg_dir.find do |f|
153
+ next unless f.file?
154
+ hashes << [f.relative_path_from(pkg_dir).to_s, Digest::SHA1.file(f).hexdigest]
155
+ end
156
+ manifest = hashes.sort.map{ |(name, hsh)| "#{name} #{hsh}" }.join("\n")
157
+ Digest::SHA1.hexdigest(manifest)
158
+ end
159
+
160
+ def generate_metapackage!
161
+ @build[:signature] = hash_metapackage
162
+ @build[:vsn] = debian_version_str(@project[:tag], @build[:timestamp], @build[:signature])
163
+
164
+ deps_str = @packages.map{ |pkg_name, pkg| "-d #{pkg[:name]}" }.sort.join(' ')
165
+ hooks_str = %w(before-install after-install before-remove after-remove).map{ |h| @project[:conf] + h }.find_all{ |h| h.file? }.map{ |h| "--#{h.basename.to_s} #{h}" }.join(" ")
166
+
167
+ if @build[:signature] != @signatures['metapackage']
168
+ Dir.mktmpdir do |tmpdir|
169
+ Dir.chdir(@build[:debs]) do
170
+ system("fpm -s dir -t deb --deb-user root --deb-group root -C #{Shellwords.escape(tmpdir.to_s)} -n #{Shellwords.escape(@project[:name])} -v #{@build[:vsn]} -a all #{hooks_str} #{deps_str} .")
171
+ end
172
+ end
173
+
174
+ @signatures['metapackage'] = @build[:signature]
175
+ end
176
+ end
177
+
178
+ def hash_metapackage
179
+ sigs = {}
180
+ @packages.each{ |pkg_name, pkg| sigs[pkg_name] = @signatures['packages'][pkg_name] }
181
+ manifest = sigs.to_a.sort.map{ |(pkg_name,sha)| "#{pkg_name} #{sha}" }.join("\n")
182
+ Digest::SHA1.hexdigest(manifest)
183
+ end
184
+
185
+ def publish_packages!
186
+ @build[:debs].children.each do |deb|
187
+ system("dpkg-sig -k spriggan@peripia.com --sign builder #{Shellwords.escape(deb.to_s)}")
188
+ system("reprepro -b #{Shellwords.escape(@apt_root.to_s)} includedeb stable #{Shellwords.escape(deb.to_s)}")
189
+ end
190
+
191
+ @project[:signatures].open('w'){ |f| f.write( @signatures.to_yaml ) }
192
+ end
193
+
194
+ def with_cache_mounted(cache_dir, mountpoint)
195
+ cache_dir.mkpath
196
+ mountpoint.parent.mkpath
197
+
198
+ mountpoint.make_symlink(cache_dir)
199
+
200
+ begin
201
+ yield
202
+ ensure
203
+ mountpoint.unlink
204
+ FileUtils.cp_r(cache_dir, mountpoint)
205
+ end
206
+ end
207
+
208
+ def debian_version_str(project_tag, build_timestamp, pkg_sha)
209
+ rev_parts = project_tag.split('-')
210
+ rev_base_version = rev_parts.shift.sub(/^v/, '')
211
+
212
+ rev_advance = nil
213
+ if rev_parts.last =~ /^g[0-9a-f]{6,}$/
214
+ rev_cur_ref = rev_parts.pop
215
+ rev_commits_since = rev_parts.pop
216
+ rev_advance = [rev_commits_since, rev_cur_ref].join('+')
217
+ end
218
+
219
+ if rev_parts.length > 0
220
+ rev_full_version = [rev_base_version, rev_parts.join('+')].join('~')
221
+ else
222
+ rev_full_version = rev_base_version
223
+ end
224
+
225
+ upstream_ver = [rev_full_version, rev_advance].compact.join('+')
226
+ deb_ver = [build_timestamp, pkg_sha[0...7]].join('+')
227
+
228
+ [upstream_ver, deb_ver].join('-')
229
+ end
230
+
231
+ end
@@ -0,0 +1,16 @@
1
+ module Kernel
2
+ PURGED_ENV = {
3
+ 'GIT_DIR' => nil,
4
+ 'GEM_HOME' => nil,
5
+ 'GEM_PATH' => nil,
6
+ 'BUNDLE_BIN_PATH' => nil,
7
+ 'BUNDLE_GEMFILE' => nil,
8
+ 'RUBYOPT' => nil
9
+ }
10
+
11
+ def system!(cmd_line)
12
+ succeeded = system(PURGED_ENV, cmd_line)
13
+ raise SystemCallError.new(nil, $?.exitstatus) unless succeeded
14
+ true
15
+ end
16
+ end
@@ -0,0 +1,3 @@
1
+ module Spriggan
2
+ VERSION = '0.1.11'
3
+ end
data/spriggan.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ require File.expand_path("../lib/spriggan/version", __FILE__)
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = 'spriggan'
5
+ s.rubyforge_project = 'spriggan'
6
+
7
+ s.platform = Gem::Platform::RUBY
8
+ s.version = Spriggan::VERSION
9
+
10
+ s.summary = "Git-to-deb compiler"
11
+ s.description = "Git post-receive hook debian-package build-system"
12
+ s.license = 'MIT'
13
+
14
+ s.authors = ["Sean Keith McAuley"]
15
+ s.email = 'tsu@peripia.com'
16
+ s.homepage = 'https://github.com/tsutsu/spriggan'
17
+
18
+ s.files = `git ls-files`.split("\n")
19
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
20
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
21
+ s.require_paths = ["lib"]
22
+
23
+ s.add_dependency 'fpm'
24
+ end
metadata ADDED
@@ -0,0 +1,70 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: spriggan
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.11
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Sean Keith McAuley
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-04-27 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: fpm
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ description: Git post-receive hook debian-package build-system
31
+ email: tsu@peripia.com
32
+ executables:
33
+ - sprig-purge
34
+ - spriggan
35
+ extensions: []
36
+ extra_rdoc_files: []
37
+ files:
38
+ - bin/sprig-purge
39
+ - bin/spriggan
40
+ - lib/spriggan.rb
41
+ - lib/spriggan/buildjob.rb
42
+ - lib/spriggan/system_calls_patch.rb
43
+ - lib/spriggan/version.rb
44
+ - spriggan.gemspec
45
+ homepage: https://github.com/tsutsu/spriggan
46
+ licenses:
47
+ - MIT
48
+ post_install_message:
49
+ rdoc_options: []
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ! '>='
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ! '>='
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ requirements: []
65
+ rubyforge_project: spriggan
66
+ rubygems_version: 1.8.23
67
+ signing_key:
68
+ specification_version: 3
69
+ summary: Git-to-deb compiler
70
+ test_files: []