dr 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,74 @@
1
+ require "dr/shellcmd"
2
+ require "dr/logger"
3
+
4
+ module Dr
5
+ class GnuPG
6
+ include Logger
7
+
8
+ def initialize(keyring)
9
+ @keyring = keyring
10
+
11
+ # initialise the keyring
12
+ FileUtils.mkdir_p @keyring
13
+ FileUtils.chmod_R 0700, @keyring
14
+ end
15
+
16
+ def generate_key(name, mail, pass)
17
+ #kill_rngd = false
18
+ #unless File.exists? "/var/run/rngd.pid"
19
+ # print "Starting rngd (root permissions required) ... "
20
+ # Kernel.system "sudo rngd -p #{@keyring}/rngd.pid -r /dev/urandom"
21
+ # kill_rngd = true
22
+ # puts "[OK]"
23
+ #end
24
+
25
+ log(:info, tag("gpg", "Generating the GPG key"))
26
+
27
+ passphrase = "Passphrase: #{pass}" if pass.length > 0
28
+ cmd = <<-END
29
+ gpg --batch --gen-key --homedir #{@keyring} <<EOF
30
+ Key-Type: RSA
31
+ Key-Length: 2048
32
+ Subkey-Type: ELG-E
33
+ Subkey-Length: 2048
34
+ Name-Real: #{name}
35
+ Name-Email: #{mail}
36
+ #{passphrase}
37
+ Expire-Date: 0
38
+ %commit
39
+ EOF
40
+ END
41
+ # TODO: Add timeout to this one
42
+ gpg_cmd = ShellCmd.new cmd, :tag => "gpg"
43
+
44
+ cmd = "gpg --list-keys --with-colons --homedir #{@keyring}"
45
+ gpg_cmd = ShellCmd.new cmd, :tag => "gpg"
46
+ key_list = gpg_cmd.out.split "\n"
47
+ key_entry = key_list.grep(/^pub/).grep(/#{name}/).grep(/#{mail}/)
48
+ key = key_entry[0].split(":")[4][8..-1]
49
+
50
+ log(:info, tag("gpg", "Key done"))
51
+
52
+ #if kill_rngd
53
+ # print "Stopping rngd (root permissions required) ... "
54
+ # Kernel.system "sudo kill `cat #{@keyring}/rngd.pid`"
55
+ # Kernel.system "sudo rm -f #{@keyring}/rngd.pid"
56
+ # puts "[OK]"
57
+ #end
58
+
59
+ key
60
+ end
61
+
62
+ def get_id
63
+ end
64
+
65
+ def export_pub(key, location)
66
+ # TODO: Remove the key before exporting (so gpg doesn't ask about it)
67
+ log(:info, tag("gpg", "Exporting key"))
68
+ cmd = "gpg --armor --homedir #{@keyring} \
69
+ --output #{location} \
70
+ --export #{key}"
71
+ gpg_cmd = ShellCmd.new cmd, :tag => "gpg"
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,80 @@
1
+ require "tco"
2
+
3
+ tco_conf = Tco::config
4
+
5
+ tco_conf.names["green"] = "#99ad6a"
6
+ tco_conf.names["yellow"] = "#d8ad4c"
7
+ tco_conf.names["red"] = "#cf6a4c"
8
+ tco_conf.names["light-grey"] = "#ababab"
9
+ tco_conf.names["dark-grey"] = "#2b2b2b"
10
+ tco_conf.names["purple"] = "#90559e"
11
+ tco_conf.names["blue"] = "#1b8efa"
12
+
13
+ tco_conf.styles["info"] = {
14
+ :fg => "green",
15
+ :bg => "dark-grey",
16
+ :bright => false,
17
+ :underline => false
18
+ }
19
+ tco_conf.styles["warn"] = {
20
+ :fg => "dark-grey",
21
+ :bg => "yellow",
22
+ :bright => false,
23
+ :underline => false
24
+ }
25
+ tco_conf.styles["err"] = {
26
+ :fg => "dark-grey",
27
+ :bg => "red",
28
+ :bright => false,
29
+ :underline => false
30
+ }
31
+
32
+ tco_conf.styles["debug"] = {
33
+ :fg => "light-grey",
34
+ :bg => "dark-grey",
35
+ :bright => false,
36
+ :underline => false
37
+ }
38
+
39
+ tco_conf.styles["log-head"] = {
40
+ :fg => "purple",
41
+ :bg => "dark-grey",
42
+ :bright => false,
43
+ :underline => false
44
+ }
45
+
46
+ Tco::reconfigure tco_conf
47
+
48
+ module Dr
49
+ module Logger
50
+ @@logger_options = {
51
+ :info => "info",
52
+ :warn => "warn",
53
+ :err => "err",
54
+ :debug => "debug"
55
+ }
56
+
57
+ def self.log(level, msg)
58
+ out = "dr".style("log-head") << " "
59
+
60
+ case level
61
+ when :info then out << "info".style(@@logger_options[:info])
62
+ when :warn then out << "WARN".style(@@logger_options[:warn])
63
+ when :err then out << "ERR!".style(@@logger_options[:err])
64
+ when :debug then out << "dbg?".style(@@logger_options[:debug])
65
+ end
66
+
67
+ out << " " << msg.chomp
68
+ puts out
69
+ STDOUT.flush
70
+ end
71
+
72
+ def log(level, msg)
73
+ Logger::log level, msg
74
+ end
75
+
76
+ def tag(tag, msg)
77
+ tag.fg("blue") << " " << msg
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,26 @@
1
+ require "dr/logger"
2
+
3
+ module Dr
4
+ class Package
5
+ attr_reader :name
6
+
7
+ include Logger
8
+ class << self
9
+ include Logger
10
+ end
11
+
12
+ def initialize(name, repo)
13
+ @name = name
14
+ @repo = repo
15
+ end
16
+
17
+ def history
18
+ versions = []
19
+ Dir.foreach "#{@repo.location}/packages/#{name}/builds/" do |v|
20
+ versions.push v unless v =~ /^\./
21
+ end
22
+
23
+ versions.sort.reverse
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,69 @@
1
+ module Dr
2
+ class PkgVersion
3
+ attr_accessor :upstream, :debian, :date, :build
4
+
5
+ def initialize(version_string)
6
+ @upstream = nil
7
+ @debian = nil
8
+ @date = nil
9
+ @build = 0
10
+
11
+ v = version_string.split "-"
12
+ @upstream = v[0] if v.length > 0
13
+ if v.length > 1
14
+ dv = v[1].split "."
15
+
16
+ @debian = dv[0] if dv.length > 0
17
+ if dv.length > 1
18
+ if dv[1] =~ /^[0-9]{8}/
19
+ @date = dv[1][0..7]
20
+ end
21
+
22
+ match = dv[1].match /build([0-9]+)$/
23
+ if match
24
+ @build = match.captures[0]
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ def increment!
31
+ if @date == today
32
+ @build += 1
33
+ else
34
+ @date = today
35
+ end
36
+
37
+ self
38
+ end
39
+
40
+ def ==(o)
41
+ @upstream == o.upstream && @debian == o.debian &&
42
+ @date == o.date && @build == o.build
43
+ end
44
+
45
+ def to_s
46
+ v = @upstream.clone
47
+ if @debian
48
+ v << "-#{@debian}"
49
+ else
50
+ v << "-0"
51
+ end
52
+
53
+ if @date
54
+ v << ".#{@date}"
55
+ else
56
+ v << ".#{today}"
57
+ end
58
+
59
+ v << "build#{@build}" if @build > 0
60
+
61
+ v
62
+ end
63
+
64
+ private
65
+ def today
66
+ Time.now.strftime "%Y%m%d"
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,300 @@
1
+ require "dr/gitpackage"
2
+ require "dr/debpackage"
3
+
4
+ require "dr/shellcmd"
5
+ require "dr/logger"
6
+ require "dr/gnupg"
7
+ require "dr/buildroot"
8
+
9
+ require "fileutils"
10
+
11
+ module Dr
12
+ class Repo
13
+ include Logger
14
+
15
+ attr_reader :location
16
+
17
+ def initialize(loc)
18
+ @location = File.expand_path loc
19
+
20
+ @packages_dir = "#{@location}/packages"
21
+ end
22
+
23
+ def setup(conf)
24
+ log :info, "Creating the archive directory"
25
+
26
+ begin
27
+ FileUtils.mkdir_p location
28
+ rescue Exception => e
29
+ log :err, "Unable to create a directory at '#{@location.fg("blue")}'"
30
+ raise e
31
+ end
32
+
33
+ FileUtils.mkdir_p "#{@location}/archive"
34
+
35
+ gpg = GnuPG.new "#{@location}/gnupg-keyring"
36
+ key = gpg.generate_key conf[:gpg_name], conf[:gpg_mail], conf[:gpg_pass]
37
+ gpg.export_pub key, "#{@location}/archive/repo.gpg.key"
38
+
39
+ log :info, "Writing the configuration file"
40
+ FileUtils.mkdir_p "#{@location}/archive/conf"
41
+ File.open "#{@location}/archive/conf/distributions", "w" do |f|
42
+ conf[:suites].each_with_index do |s, i|
43
+ f.puts "Suite: #{s}"
44
+
45
+ if conf[:codenames][i].length > 0
46
+ f.puts "Codename: #{conf[:codenames][i]}"
47
+ end
48
+
49
+ if conf[:name][i].length > 0
50
+ f.puts "Origin: #{conf[:name]} - #{s}"
51
+ f.puts "Label: #{conf[:name]} - #{s}"
52
+ end
53
+
54
+ if conf[:desc].length > 0
55
+ f.puts "Description: #{conf[:desc]}"
56
+ end
57
+
58
+ f.puts "Architectures: #{conf[:arches].join " "}"
59
+ f.puts "Components: #{conf[:components].join " "}"
60
+
61
+ f.puts "SignWith: #{key}"
62
+ f.puts ""
63
+ end
64
+ end
65
+
66
+ FileUtils.mkdir_p @packages_dir
67
+
68
+ conf[:arches].each do |arch|
69
+ BuildRoot.new arch, "#{@location}/build-root-#{arch}.tar.gz"
70
+ end
71
+ end
72
+
73
+ def list_packages(suite=nil)
74
+ pkgs = []
75
+ if suite
76
+ a = 1
77
+ else
78
+ Dir.foreach @packages_dir do |pkg_name|
79
+ pkgs.push get_package pkg_name unless pkg_name =~ /^\./
80
+ end
81
+ end
82
+
83
+ pkgs
84
+ end
85
+
86
+ def buildroot(arch)
87
+ BuildRoot.new arch, "#{@location}/build-root-#{arch}.tar.gz"
88
+ end
89
+
90
+ def get_package(name)
91
+ unless File.exists? "#{@packages_dir}/#{name}"
92
+ raise "Package '#{name}' doesn't exist in the repo."
93
+ end
94
+
95
+ if File.exists? "#{@packages_dir}/#{name}/source"
96
+ GitPackage.new name, self
97
+ else
98
+ DebPackage.new name, self
99
+ end
100
+ end
101
+
102
+ def get_suites
103
+ suites = nil
104
+ File.open "#{@location}/archive/conf/distributions", "r" do |f|
105
+ suites = f.read.split "\n\n"
106
+ end
107
+
108
+ suites.map do |s|
109
+ suite = nil
110
+ codename = nil
111
+ s.each_line do |l|
112
+ m = l.match /^Suite: (.+)/
113
+ suite = m.captures[0].chomp if m
114
+
115
+ m = l.match /^Codename: (.+)/
116
+ codename = m.captures[0].chomp if m
117
+ end
118
+ [suite, codename]
119
+ end
120
+ end
121
+
122
+ def get_architectures
123
+ arches = []
124
+ File.open "#{@location}/archive/conf/distributions", "r" do |f|
125
+ f.each_line do |l|
126
+ m = l.match /^Architectures: (.+)/
127
+ arches += m.captures[0].chomp.split(" ") if m
128
+ end
129
+ end
130
+
131
+ arches.uniq
132
+ end
133
+
134
+ def query_for_deb_version(suite, pkg_name)
135
+ reprepro_cmd = "reprepro --basedir #{location}/archive " +
136
+ "--list-format '${version}' list #{suite} " +
137
+ "#{pkg_name} 2>/dev/null"
138
+ reprepro = ShellCmd.new reprepro_cmd, :tag => "reprepro"
139
+ v = reprepro.out.chomp
140
+ v = nil unless v.length > 0
141
+ v
142
+ end
143
+
144
+ def get_subpackage_versions(pkg_name)
145
+ pkg = get_package pkg_name
146
+ suites = get_suites
147
+
148
+ versions = {}
149
+ suites.each do |suite, codename|
150
+ versions[suite] = {}
151
+ reprepro_cmd = "reprepro --basedir #{location}/archive " +
152
+ "--list-format '${package} ${version}\n' " +
153
+ "listfilter #{suite} 'Source (== raspberrypi-firmware)' " +
154
+ "2>/dev/null"
155
+ reprepro = ShellCmd.new reprepro_cmd, :tag => "reprepro"
156
+ reprepro.out.chomp.each_line do |line|
157
+ subpkg, version = line.split(" ").map(&:chomp)
158
+ versions[suite][subpkg] = version
159
+ end
160
+ end
161
+ versions
162
+ end
163
+
164
+ def push(pkg_name, version, suite, force=false)
165
+ pkg = get_package pkg_name
166
+
167
+ if version
168
+ unless pkg.build_exists? version
169
+ raise "Build version '#{version}' not found"
170
+ end
171
+ else
172
+ if pkg.history.length == 0
173
+ log :err, "Package #{pkg_name} has not been built yet"
174
+ log :err, "Please, run a build first and the push."
175
+ raise "Push failed"
176
+ end
177
+ version = pkg.history[0]
178
+ end
179
+
180
+ if suite
181
+ cmp = get_suites.map { |n, cn| suite == n || suite == cn }
182
+ suite_exists = cmp.inject(false) { |r, o| r || o }
183
+ raise "Suite '#{suite}' doesn't exist." unless suite_exists
184
+ else
185
+ # FIXME: This should be configurable
186
+ suite = "testing"
187
+ end
188
+
189
+ # FIXME: This will not work with packages that don't build a deb
190
+ # with the same name as the source package
191
+ current_version = query_for_deb_version suite, pkg.name
192
+
193
+ if current_version != nil && current_version >= version
194
+ log :warn, "Version #{version.fg("blue")} already available in #{suite}"
195
+ if force
196
+ reprepro = "reprepro -b #{@location}/archive " +
197
+ "--gnupghome #{location}/gnupg-keyring/ removesrc " +
198
+ "#{suite} #{pkg.name}"
199
+ ShellCmd.new reprepro, :tag => "reprepro", :show_out => true
200
+ else
201
+ log :err, "The same package of a higher version is already in the repo."
202
+ raise "Push failed"
203
+ end
204
+ end
205
+
206
+ log :info, "Pushing #{pkg_name} version #{version} to #{suite}"
207
+ debs = Dir["#{@location}/packages/#{pkg.name}/builds/#{version}/*"]
208
+ reprepro = "reprepro -b #{@location}/archive " +
209
+ "--gnupghome #{location}/gnupg-keyring/ includedeb " +
210
+ "#{suite} #{debs.join " "}"
211
+ ShellCmd.new reprepro, :tag => "reprepro", :show_out => true
212
+ end
213
+
214
+ def unpush(pkg_name, suite)
215
+ pkg = get_package pkg_name
216
+
217
+ cmp = get_suites.map { |n, cn| suite == n || suite == cn }
218
+ suite_exists = cmp.inject(false) { |r, o| r || o }
219
+ unless suite_exists
220
+ log :err, "Suite '#{suite}' doesn't exist."
221
+ raise "Unpush failed"
222
+ end
223
+
224
+ log :info, "Removing #{pkg_name} from #{suite}"
225
+ reprepro = "reprepro -b #{@location}/archive " +
226
+ "--gnupghome #{location}/gnupg-keyring/ removesrc " +
227
+ "#{suite} #{pkg.name}"
228
+ ShellCmd.new reprepro, :tag => "reprepro", :show_out => true
229
+ end
230
+
231
+ def remove(pkg_name, force=false)
232
+ pkg = get_package pkg_name
233
+
234
+ if is_used? pkg_name
235
+ log :warn, "Package #{pkg_name} is still used"
236
+ raise "The '#{pkg_name}' package is still used." unless force
237
+
238
+ log :info, "Will be force-removed anyway"
239
+ get_suites.zip(versions).each do |suite, version|
240
+ log :info, "Removing #{pkg_name} v#{version} from #{suite}"
241
+ unpush pkg_name, suite[0] if version != nil
242
+ end
243
+ end
244
+
245
+ if !is_used?(pkg_name) || force
246
+ log :info, "Removing #{pkg_name} from the repository"
247
+ FileUtils.rm_rf "#{location}/packages/#{pkg_name}"
248
+ end
249
+ end
250
+
251
+ def remove_build(pkg_name, version, force=false)
252
+ pkg = get_package pkg_name
253
+
254
+ if is_used?(pkg_name, version)
255
+ if force
256
+ log :info, "Force-removing #{version.fg("blue")} version of " +
257
+ "#{pkg_name.fg("blue")}"
258
+ versions_by_suite = get_subpackage_versions pkg_name
259
+ versions_by_suite.each do |suite, versions|
260
+ unpush pkg_name, suite if versions.has_value? version
261
+ end
262
+ else
263
+ log :warn, "This build of #{pkg_name} is " +
264
+ "still being used, add -f to force-remove"
265
+ return
266
+ end
267
+ else
268
+ log :info, "Removing the #{version.fg("blue")} version of " +
269
+ "#{pkg_name.fg("blue")}"
270
+ end
271
+
272
+ pkg.remove_build version
273
+ end
274
+
275
+ def get_build(pkg_name, version=nil)
276
+ pkg = get_package pkg_name
277
+
278
+ hist = pkg.history
279
+ raise "The package hasn't been built yet." unless hist.length > 0
280
+ version = hist[0] unless version
281
+
282
+ raise "Build #{version} doesn't exist" unless pkg.build_exists? version
283
+
284
+ Dir["#{@location}/packages/#{pkg.name}/builds/#{version}/*"]
285
+ end
286
+
287
+ private
288
+ def is_used?(pkg_name, version=nil)
289
+ versions_by_suite = get_subpackage_versions pkg_name
290
+ versions_by_suite.inject(false) do |rslt, hash_pair|
291
+ suite, versions = hash_pair
292
+ if version == nil
293
+ rslt || !versions.empty?
294
+ else
295
+ rslt || versions.has_value?(version)
296
+ end
297
+ end
298
+ end
299
+ end
300
+ end