dr 0.0.1

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.
@@ -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