spriggan 0.1.11

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.
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: []