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