dr 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +19 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +339 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/bin/dr +276 -0
- data/dr.spec +30 -0
- data/lib/dr.rb +6 -0
- data/lib/dr/buildroot.rb +103 -0
- data/lib/dr/config.rb +44 -0
- data/lib/dr/debpackage.rb +24 -0
- data/lib/dr/gitpackage.rb +252 -0
- data/lib/dr/gnupg.rb +74 -0
- data/lib/dr/logger.rb +80 -0
- data/lib/dr/package.rb +26 -0
- data/lib/dr/pkgversion.rb +69 -0
- data/lib/dr/repo.rb +300 -0
- data/lib/dr/shellcmd.rb +67 -0
- data/lib/dr/version.rb +3 -0
- metadata +149 -0
data/dr.spec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'dr/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "dr"
|
8
|
+
spec.version = Dr::VERSION
|
9
|
+
spec.authors = ["Radek Pazdera"]
|
10
|
+
spec.email = ["radek@kano.me"]
|
11
|
+
spec.summary = %q{dr is a packaging tool that helps you make,
|
12
|
+
distribute and maintain you own disto packages.}
|
13
|
+
spec.description = %q{This tool works with distribution-level packaging
|
14
|
+
tools and helps you make and distribute your own
|
15
|
+
Debian packages through your own repository.}
|
16
|
+
spec.homepage = "http://github.com/KanoComputing/kano-package-system"
|
17
|
+
spec.license = "GPLv2"
|
18
|
+
|
19
|
+
spec.files = `git ls-files`.split($/)
|
20
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
21
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
22
|
+
spec.require_paths = ["lib"]
|
23
|
+
|
24
|
+
spec.add_dependency "thor", "~> 0.18.1"
|
25
|
+
spec.add_dependency "grit", "~> 2.5.0"
|
26
|
+
|
27
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
28
|
+
spec.add_development_dependency "rake"
|
29
|
+
spec.add_development_dependency "rspec"
|
30
|
+
end
|
data/lib/dr.rb
ADDED
data/lib/dr/buildroot.rb
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
require "tco"
|
2
|
+
|
3
|
+
require "dr/logger"
|
4
|
+
require "dr/shellcmd"
|
5
|
+
|
6
|
+
module Dr
|
7
|
+
class BuildRoot
|
8
|
+
include Logger
|
9
|
+
|
10
|
+
def initialize(arch, br_archive=nil)
|
11
|
+
@location = br_archive
|
12
|
+
|
13
|
+
@extra_pkgs = "sudo,vim,ca-certificates,fakeroot,build-essential," +
|
14
|
+
"devscripts,debhelper,git,bc,locales,equivs,pkg-config"
|
15
|
+
@repo = "http://mirrordirector.raspbian.org/raspbian/"
|
16
|
+
|
17
|
+
if br_archive == nil || !File.exists?(br_archive)
|
18
|
+
setup
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def open
|
23
|
+
Dir.mktmpdir do |tmp|
|
24
|
+
log :info, "Preparing the build root"
|
25
|
+
ShellCmd.new "sudo tar xz -C #{tmp} -f #{@location}", :tag => "tar"
|
26
|
+
begin
|
27
|
+
yield tmp
|
28
|
+
ensure
|
29
|
+
log :info, "Cleaning up the buildroot"
|
30
|
+
ShellCmd.new "sudo rm -rf #{tmp}/*", :tag => "rm"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
def setup(arch)
|
37
|
+
Dir.mktmpdir do |tmp|
|
38
|
+
broot = "#{tmp}/broot"
|
39
|
+
FileUtils.mkdir_p "#{tmp}/broot"
|
40
|
+
|
41
|
+
log :info, "Setting up the buildroot"
|
42
|
+
|
43
|
+
begin
|
44
|
+
log :info, "Bootstrapping Raspian (first stage)"
|
45
|
+
|
46
|
+
cmd = "sudo debootstrap --foreign --variant=buildd --no-check-gpg " +
|
47
|
+
"--include=#{@extra_pkgs} --arch=#{arch} wheezy #{broot} #{@repo}"
|
48
|
+
debootsrap = ShellCmd.new cmd, {
|
49
|
+
:tag => "debootstrap",
|
50
|
+
:show_out => true
|
51
|
+
}
|
52
|
+
|
53
|
+
static_qemu = Dir["/usr/bin/qemu-*-static"]
|
54
|
+
static_qemu.each do |path|
|
55
|
+
cp = ShellCmd.new "sudo cp #{path} #{broot}/usr/bin", {
|
56
|
+
:tag => "cp"
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
log :info, "Bootstrapping Raspian (#{arch} stage)"
|
61
|
+
cmd = "sudo chroot #{broot} /debootstrap/debootstrap --second-stage"
|
62
|
+
debootstrap = ShellCmd.new cmd, {
|
63
|
+
:tag => "debootstrap",
|
64
|
+
:show_out => true
|
65
|
+
}
|
66
|
+
|
67
|
+
log :info, "Configuring the build root"
|
68
|
+
cmd = "sudo chroot #{broot} <<EOF
|
69
|
+
echo 'deb #{@repo} wheezy main contrib non-free rpi' >> /etc/apt/sources.list
|
70
|
+
echo 'deb-src #{@repo} wheezy main contrib non-free rpi' >> /etc/apt/sources.list
|
71
|
+
|
72
|
+
echo 'en_US.UTF-8 UTF-8' >/etc/locale.gen
|
73
|
+
locale-gen en_US.UTF-8
|
74
|
+
|
75
|
+
cat >>/etc/bash.bashrc <<EOF2
|
76
|
+
export LANG=en_US.UTF-8
|
77
|
+
export LC_TYPE=en_US.UTF-8
|
78
|
+
export LC_ALL=en_US.UTF-8
|
79
|
+
export LANGUAGE=en_US.UTF8
|
80
|
+
EOF2
|
81
|
+
EOF"
|
82
|
+
cfg = ShellCmd.new cmd, :tag => "chroot"
|
83
|
+
|
84
|
+
log :info, "Updating package lists"
|
85
|
+
update = ShellCmd.new "sudo chroot #{broot} apt-get update", {
|
86
|
+
:tag => "chroot",
|
87
|
+
:show_out => true
|
88
|
+
}
|
89
|
+
|
90
|
+
# TODO: is this necessary?
|
91
|
+
#Kernel.system "sudo chroot #{broot} useradd -m -s /bin/bash raspbian"
|
92
|
+
|
93
|
+
log :info, "Creating the build root archive"
|
94
|
+
cmd = "sudo tar cz -C #{broot} -f #{@location} `ls -1 #{broot}`"
|
95
|
+
tar = ShellCmd.new cmd, :tag => "tar"
|
96
|
+
ensure
|
97
|
+
log :info, "Cleaning up"
|
98
|
+
ShellCmd.new "sudo rm -rf #{broot}", :tag => "rm"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
data/lib/dr/config.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require "yaml"
|
2
|
+
|
3
|
+
module Dr
|
4
|
+
class Config
|
5
|
+
attr_reader :default_repo, :repositories
|
6
|
+
|
7
|
+
def initialize(locations)
|
8
|
+
@default_repo = nil
|
9
|
+
@repositories = {}
|
10
|
+
|
11
|
+
locations.each do |conf_file|
|
12
|
+
conf_file = File.expand_path conf_file
|
13
|
+
next unless File.exists? conf_file
|
14
|
+
load conf_file
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
def load(path)
|
20
|
+
conf_file = YAML::load_file path
|
21
|
+
|
22
|
+
if conf_file.has_key? "repositories"
|
23
|
+
if conf_file["repositories"].is_a? Array
|
24
|
+
conf_file["repositories"].each do |repo|
|
25
|
+
raise "Repo name missing in the config." unless repo.has_key? "name"
|
26
|
+
raise "Repo location missing in the config" unless repo.has_key? "location"
|
27
|
+
@repositories[repo["name"]] = {
|
28
|
+
:location => repo["location"]
|
29
|
+
}
|
30
|
+
end
|
31
|
+
else
|
32
|
+
raise "The 'repositories' config option must be an array."
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
if conf_file.has_key? "default_repo"
|
37
|
+
@default_repo = conf_file["default_repo"]
|
38
|
+
unless @repositories.has_key? @default_repo
|
39
|
+
raise "Default repo #{@default_repo} doesn't exist"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require "dr/package"
|
2
|
+
|
3
|
+
module Dr
|
4
|
+
class DebPackage < Package
|
5
|
+
def self.setup(repo, deb_file)
|
6
|
+
puts "Adding the #{File.basename deb_file} package ..."
|
7
|
+
src_name = `dpkg-deb --field #{deb_file} Source`.chomp
|
8
|
+
if src_name == ""
|
9
|
+
src_name = `dpkg-deb --field #{deb_file} Package`.chomp
|
10
|
+
end
|
11
|
+
puts "Source package: #{src_name}"
|
12
|
+
|
13
|
+
version = `dpkg-deb --field #{deb_file} Version`.chomp
|
14
|
+
|
15
|
+
deb_dir = "#{repo.location}/packages/#{src_name}/builds/#{version}"
|
16
|
+
FileUtils.mkdir_p deb_dir
|
17
|
+
FileUtils.cp "#{deb_file}", "#{deb_dir}/"
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(name, repo)
|
21
|
+
super name, repo
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,252 @@
|
|
1
|
+
require "dr/package"
|
2
|
+
require "dr/pkgversion"
|
3
|
+
require "dr/shellcmd"
|
4
|
+
|
5
|
+
module Dr
|
6
|
+
class GitPackage < Package
|
7
|
+
def self.setup(repo, git_addr, default_branch, force=false)
|
8
|
+
Dir.mktmpdir do |tmp|
|
9
|
+
git_cmd = "git clone --branch #{default_branch} #{git_addr} #{tmp}/git"
|
10
|
+
ShellCmd.new git_cmd, :tag => "git", :show_out => true
|
11
|
+
|
12
|
+
unless File.exists? "#{tmp}/git/debian/control"
|
13
|
+
log :err, "The debian packaging files not found in the repository"
|
14
|
+
raise "Adding a package from #{git_addr} failed"
|
15
|
+
end
|
16
|
+
|
17
|
+
src_name = nil
|
18
|
+
File.open "#{tmp}/git/debian/control", "r" do |f|
|
19
|
+
f.each_line do |line|
|
20
|
+
match = line.match /^Source: (.+)$/
|
21
|
+
if match
|
22
|
+
src_name = match.captures[0]
|
23
|
+
break
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
unless src_name
|
29
|
+
log :err, "Couldn't identify the source package"
|
30
|
+
raise "Adding a package from #{git_addr} failed"
|
31
|
+
end
|
32
|
+
|
33
|
+
pkg_dir = "#{repo.location}/packages/#{src_name}"
|
34
|
+
if File.exists? pkg_dir
|
35
|
+
log :warn, "The package already exists. Add -f to insert it anyway."
|
36
|
+
raise "Adding failed"
|
37
|
+
end
|
38
|
+
|
39
|
+
log :info, "Adding #{src_name.fg "blue"} to the repository"
|
40
|
+
FileUtils.mkdir_p "#{pkg_dir}"
|
41
|
+
|
42
|
+
log :info, "Setting up builds directory"
|
43
|
+
FileUtils.mkdir_p "#{pkg_dir}/builds"
|
44
|
+
|
45
|
+
log :info, "Setting up the source directory"
|
46
|
+
FileUtils.mv "#{tmp}/git/.git", "#{pkg_dir}/source"
|
47
|
+
|
48
|
+
log :info, "Package #{src_name} added successfully"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def initialize(name, repo)
|
53
|
+
super name, repo
|
54
|
+
|
55
|
+
@git_dir = "#{repo.location}/packages/#{name}/source"
|
56
|
+
|
57
|
+
git_cmd = ShellCmd.new "git --git-dir #{@git_dir} branch", {
|
58
|
+
:tag => "git-clone"
|
59
|
+
}
|
60
|
+
@default_branch = git_cmd.out.chomp.lines.grep(/^*/)[0][2..-1]
|
61
|
+
end
|
62
|
+
|
63
|
+
def build(branch=nil, force=false)
|
64
|
+
branch = @default_branch unless branch
|
65
|
+
|
66
|
+
version = nil
|
67
|
+
orig_rev, curr_rev = update_from_origin branch
|
68
|
+
if curr_rev != orig_rev || force
|
69
|
+
Dir.mktmpdir do |src_dir|
|
70
|
+
log :info, "Extracting the sources"
|
71
|
+
git_cmd ="git --git-dir #{@git_dir} archive " +
|
72
|
+
"--format tar #{branch} | tar x -C #{src_dir}"
|
73
|
+
ShellCmd.new git_cmd, :tag => "git", :show_out => true
|
74
|
+
|
75
|
+
version = PkgVersion.new get_version "#{src_dir}/debian/changelog"
|
76
|
+
log :info, "Source version: #{version}"
|
77
|
+
|
78
|
+
while build_exists? version
|
79
|
+
version.increment!
|
80
|
+
end
|
81
|
+
log :info, "Building version: #{version}"
|
82
|
+
|
83
|
+
log :info, "Updating changelog"
|
84
|
+
now = Time.new.strftime("%a, %-d %b %Y %T %z")
|
85
|
+
ch_entry = "#{@name} (#{version}) kano; urgency=low\n"
|
86
|
+
ch_entry << "\n"
|
87
|
+
ch_entry << " * Package rebuilt, updated to revision #{curr_rev[0..7]}.\n"
|
88
|
+
ch_entry << "\n"
|
89
|
+
ch_entry << " -- Team Kano <dev@kano.me> #{now}\n\n"
|
90
|
+
|
91
|
+
changelog = ""
|
92
|
+
File.open "#{src_dir}/debian/changelog", "r" do |f|
|
93
|
+
changelog = f.read
|
94
|
+
end
|
95
|
+
|
96
|
+
File.open "#{src_dir}/debian/changelog", "w" do |f|
|
97
|
+
f.write ch_entry
|
98
|
+
f.write changelog
|
99
|
+
end
|
100
|
+
|
101
|
+
repo_arches = @repo.get_architectures
|
102
|
+
pkg_arches = get_architectures("#{src_dir}/debian/control")
|
103
|
+
arches = case
|
104
|
+
when pkg_arches.include?("any") || pkg_arches.include?("all")
|
105
|
+
repo_arches
|
106
|
+
else
|
107
|
+
repo_arches & pkg_arches
|
108
|
+
end
|
109
|
+
arches.each do |arch|
|
110
|
+
@repo.buildroot(arch).open do |br|
|
111
|
+
log :info, "Building the #{@name.fg("blue")} package " +
|
112
|
+
"v#{version} for #{arch}"
|
113
|
+
# Moving to the proper directory
|
114
|
+
build_dir_name = "#{@name}-#{version.upstream}"
|
115
|
+
build_dir = "#{br}/#{build_dir_name}"
|
116
|
+
FileUtils.cp_r src_dir, build_dir
|
117
|
+
|
118
|
+
# Make orig tarball
|
119
|
+
log :info, "Creating orig source tarball"
|
120
|
+
tar = "tar cz -C #{build_dir} --exclude=debian " +
|
121
|
+
"-f #{br}/#{@name}_#{version.upstream}.orig.tar.gz " +
|
122
|
+
"`ls -1 #{build_dir}`"
|
123
|
+
ShellCmd.new tar, :tag => "tar"
|
124
|
+
|
125
|
+
apt = "sudo chroot #{br} apt-get update"
|
126
|
+
deps = <<-EOS
|
127
|
+
sudo chroot #{br} <<EOF
|
128
|
+
dpkg-source -b "/#{build_dir_name}"
|
129
|
+
mk-build-deps *.dsc -i -t "apt-get --no-install-recommends -y"
|
130
|
+
rm -rf #{@name}-build-deps_*
|
131
|
+
EOF
|
132
|
+
EOS
|
133
|
+
build = <<-EOS
|
134
|
+
sudo chroot #{br} <<EOF
|
135
|
+
cd /#{build_dir_name}
|
136
|
+
debuild -i -uc -us -b
|
137
|
+
EOF
|
138
|
+
EOS
|
139
|
+
|
140
|
+
log :info, "Updating the sources lists"
|
141
|
+
ShellCmd.new apt, :tag => "apt-get", :show_out => true
|
142
|
+
|
143
|
+
log :info, "Installing build dependencies"
|
144
|
+
ShellCmd.new deps, :tag => "mk-build-deps", :show_out => true
|
145
|
+
|
146
|
+
log :info, "Building the package"
|
147
|
+
ShellCmd.new build, :tag => "debuild", :show_out => true
|
148
|
+
|
149
|
+
debs = Dir["#{br}/*.deb"]
|
150
|
+
expected_pkgs = get_subpackage_names "#{src_dir}/debian/control"
|
151
|
+
expected_pkgs.each do |subpkg_name|
|
152
|
+
includes = debs.inject(false) do |r, n|
|
153
|
+
r || ((/^#{br}\/#{subpkg_name}_#{version}/ =~ n) != nil)
|
154
|
+
end
|
155
|
+
|
156
|
+
unless includes
|
157
|
+
log :err, "Subpackage #{subpkg_name} did not build properly"
|
158
|
+
raise "Building #{name} failed"
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
build_dir = "#{@repo.location}/packages/#{@name}/builds/#{version}"
|
163
|
+
FileUtils.mkdir_p build_dir
|
164
|
+
debs.each do |pkg|
|
165
|
+
FileUtils.cp pkg, build_dir
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
else
|
171
|
+
log :info, "There were no changes in the #{pkg.name.fg("blue")} package"
|
172
|
+
log :info, "Build stopped (add -f to build anyway)"
|
173
|
+
end
|
174
|
+
version
|
175
|
+
end
|
176
|
+
|
177
|
+
def build_exists?(version)
|
178
|
+
File.directory? "#{@repo.location}/packages/#{@name}/builds/#{version}"
|
179
|
+
end
|
180
|
+
|
181
|
+
def remove_build(version)
|
182
|
+
raise "Build #{version.fg("blue")} not found" unless build_exists? version
|
183
|
+
FileUtils.rm_rf "#{@repo.location}/packages/#{@name}/builds/#{version}"
|
184
|
+
end
|
185
|
+
|
186
|
+
private
|
187
|
+
def update_from_origin(branch)
|
188
|
+
log :info, "Pulling changes from origin"
|
189
|
+
|
190
|
+
git_cmd = "git --git-dir #{@git_dir} rev-parse #{branch} 2>/dev/null"
|
191
|
+
git = ShellCmd.new git_cmd, :tag => "git"
|
192
|
+
|
193
|
+
original_rev = git.out.chomp
|
194
|
+
original_rev = nil if original_rev == branch
|
195
|
+
|
196
|
+
begin
|
197
|
+
if @default_branch == branch
|
198
|
+
git_cmd = "git --git-dir #{@git_dir} pull origin #{branch}"
|
199
|
+
ShellCmd.new git_cmd, :tag => "git", :show_out => true
|
200
|
+
else
|
201
|
+
git_cmd = "git --git-dir #{@git_dir} fetch origin #{branch}:#{branch}"
|
202
|
+
ShellCmd.new git_cmd, :tag => "git", :show_out => true
|
203
|
+
end
|
204
|
+
rescue Exception => e
|
205
|
+
log :err, "Unable to pull from origin"
|
206
|
+
raise e
|
207
|
+
end
|
208
|
+
|
209
|
+
git_cmd = "git --git-dir #{@git_dir} rev-parse #{branch} 2>/dev/null"
|
210
|
+
git = ShellCmd.new git_cmd, :tag => "git"
|
211
|
+
current_rev = git.out.chomp
|
212
|
+
|
213
|
+
[original_rev, current_rev]
|
214
|
+
end
|
215
|
+
|
216
|
+
def get_version(changelog_file)
|
217
|
+
File.open changelog_file, "r" do |f|
|
218
|
+
f.each_line do |l|
|
219
|
+
version = l.match /^#{@name} \(([^\)]+)\) .+;/
|
220
|
+
return version.captures[0] if version
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
nil
|
225
|
+
end
|
226
|
+
|
227
|
+
def get_subpackage_names(control_file)
|
228
|
+
packages = []
|
229
|
+
File.open control_file, "r" do |f|
|
230
|
+
f.each_line do |l|
|
231
|
+
if /^Package: / =~ l
|
232
|
+
packages.push l.split(" ")[1]
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
packages
|
238
|
+
end
|
239
|
+
|
240
|
+
def get_architectures(control_file)
|
241
|
+
arches = []
|
242
|
+
File.open control_file, "r" do |f|
|
243
|
+
f.each_line do |l|
|
244
|
+
m = l.match /^Architecture: (.+)/
|
245
|
+
arches += m.captures[0].chomp.split(" ") if m
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
arches.uniq
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|