-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbuilder.sh
448 lines (415 loc) · 14.1 KB
/
builder.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
#!/usr/bin/env bash
#
# This script will compile the deb packages for the package names given as
# argument using the most recent source available in the Debian sid
# repositories for the specified version of Ubuntu or Debian.
#
# The version of Ubuntu or Debian specified does not have to be the same as the
# installed version of Ubuntu or Debian because the packages will be built in a
# chrooted environment using pbuilder-dist. This assumes that your Ubuntu or
# Debian version has the required package dependency versions available (which
# for xrdp and xorgxrdp it likely will).
#
# This allows you to upgrade to a more recent version of xrdp than is available
# in the system repositories.
#
# The downloaded source packages are verified using the Debian GPG keys. The
# compiled deb packages can be installed with `apt-get install -f` as usual.
#
# This script will prompt for a sudo password because it calls pbuilder-dist.
#
# Example: To compile the version of xrdp currently available in Debian sid for
# Ubuntu 20.04 LTS run the following:
# $ builder.sh build focal xrdp
#
# This script will then download the source package of xrdp from the
# repositories of Debian sid. The xrdp source package will be verified using
# the Debian GPG keys. A pbuilder environment will be created for Ubuntu 20.04.
# This will work even if the system is running a different version of Ubuntu or
# distribution, such as Ubuntu 21.10 or Debian 11. Thus a single system can
# generate deb packages for multiple different systems.
#
# Multiple packages can be downloaded and with a single run of the script.
#
# Example:
# $ builder.sh build focal xrdp xorgxrdp
#
# The packages will be built in the order written, i.e., first xrdp then
# xorgxrdp. Because xorgxrdp depends on xrdp, we need to first build xrdp then
# xorgxrdp in that order.
#
# If you already have pbuilder or pbuilder-dist configured on your system then
# this script ignore the any configuration or existing distribution base.tgz.
#
# This script creates three sub-directories in the same directory as this
# script:
# - The 'source-packages' directory will contain the downloaded source
# packages.
# - The 'DIST-deb-packages' directory will contain the deb packages built for
# the Ubuntu or Debian release with codename DIST.
# - The 'pbuilder-working-dir' directory is the working directory for pbuilder
# and contains all pbuilder files.
#
# One way to archive the source packages and deb packages is to check them into
# version control: check this script into a git repository, and commit any new
# source packages and deb packages in the 'source-packages' and 'deb-packages'
# directories each time the script is run. The 'pbuilder-working-dir' directory
# should be added to the .gitignore file.
#
# You can search for the most recent version of a package on the Debian Package
# Tracker: https://tracker.debian.org/
#
# Running this script again with the same arguments will fetch the most recent
# versions of the packages and build the corresponding deb packages.
#
set -o pipefail
#
# Directory that contains this script.
#
script_dir="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
#
# Root all the files created by pbuilder-dist in the specified directory to
# keep things tidy.
#
# pbuilder-dist will store all the files it generates in directory set by the
# PBUILDFOLDER environment variable. See man pbuilder-dist.
#
export PBUILDFOLDER="$script_dir/pbuilder-working-dir"
#
# Print the help message.
#
function print_help
{
echo "Build source packages from Debian unstable."
echo
echo "Available commands:"
echo " build DIST PACKAGES... Build the packages for the specified distribution,"
echo " where DIST is the codename of the version of Ubuntu"
echo " or Debian and PACKAGES... is the packages to build."
echo
echo " clean Delete all temporary files."
echo " clean DIST Delete all temporary files for the specified"
echo " distribution only."
echo
echo " help Show this help screen."
echo
echo "Examples:"
echo " $0 build focal xrdp xorgxrdp"
echo " $0 build buster xrdp"
}
#
# Command line options.
#
if [[ "$#" -eq 0 || ("$#" -eq 1 && "$1" = "help") ]]
then
print_help
exit 0
elif [[ "$#" -eq 1 && "$1" = "clean" ]]
then
rm -rf "$PBUILDFOLDER"
exit 0
elif [[ "$#" -eq 2 && "$1" = "clean" ]]
then
#
# Check whether the distribution has been previously downloaded by checking
# fpr the file $PBUILDFOLDER/${2}-base.tgz.
#
if [ -f "$PBUILDFOLDER/${2}-base.tgz" ]
then
#
# These paths depend on the directory layout we use in the script below
# and on the names of the files generated by pbuilder-dist.
#
rm -f "$PBUILDFOLDER/${2}-base.tgz"
rm -rf "$PBUILDFOLDER/${2}_build_result"
rm -rf "$PBUILDFOLDER/${2}_result"
exit 0
else
>&2 echo "ERROR: No files for distribution $2."
exit 1
fi
elif [[ "$#" -lt 3 || "$1" != "build" ]]
then
print_help
exit 1
fi
#
# The packages to be built in order (starting from third argument).
#
packages_to_build="${@:3}"
#
# We call this variable debian_dist so as not to get it confused with the DIST
# variable set by pbuilder and exported to the .pbuilderrc and hook files.
#
debian_dist="$2"
#
# We do not want to maintain a list of valid codenames but instead check if the
# codename is all lowercase letters and no spaces.
#
if [[ ! $debian_dist =~ ^[a-z]+$ ]]
then
>&2 echo "ERROR: Not a valid Ubuntu or Debian codename."
exit 1
fi
#
# Checks that the named external program exists on the path, and if not exits.
#
# param: $program_name - name of the executable that must be on the path.
#
function require_program
{
local readonly program_name="$1"
if ! command -v $program_name &> /dev/null
then
>&2 echo "ERROR: ${program_name} could not be found."
exit 1
fi
}
#
# Check that the required programs are installed on the PATH so that the script
# does not fail halfway through.
#
require_program rsync
require_program pull-debian-source
require_program dscverify
require_program pbuilder-dist
#
# Directory to which the source packages will be downloaded.
#
source_package_dir="$script_dir/source-packages"
#
# Directory to which the Debian binary packages will be put.
#
deb_package_output_dir="$script_dir/${debian_dist}-deb-packages"
if [ ! -d "$PBUILDFOLDER" ]
then
echo "Creating PBUILDFOLDER directory."
mkdir -p "$PBUILDFOLDER"
if [ $? -ne 0 ]
then
>&2 echo "ERROR: Could not create PBUILDFOLDER directory."
exit 1
fi
fi
#
# The directory for the resultant files of the pbuilder build. This directory
# will be created by pbuilder-dist.
#
build_result_dir="$PBUILDFOLDER/${debian_dist}_build_result"
#
# Directory containing the .pbuilderrc file and the hooks directory.
#
config_dir="$PBUILDFOLDER/config"
config_file="$config_dir/.pbuilderrc"
#
# Hooks directory containing the single hook file we need.
#
hooks_dir="$config_dir/pbuilder-hooks"
hook_file="$hooks_dir/D10addsource"
#
# Directory to store the Debian keyring file.
#
keyring_dir="$PBUILDFOLDER/keyring"
#
# Make directories that do not exist.
#
[ -d $build_result_dir ] || mkdir $build_result_dir
[ -d $source_package_dir ] || mkdir $source_package_dir
[ -d $deb_package_output_dir ] || mkdir $deb_package_output_dir
[ -d $config_dir ] || mkdir $config_dir
[ -d $hooks_dir ] || mkdir $hooks_dir
[ -d $keyring_dir ] || mkdir $keyring_dir
#
# Download the current keyring for verifying packages.
#
rsync -az keyring.debian.org::keyrings/keyrings/debian-keyring.gpg "$keyring_dir"
if [ $? -ne 0 ]
then
>&2 echo "ERROR: Could not download Debian keyring."
exit 1
fi
#
# Make `.pbuilderrc` remembering to escape dollar where necessary.
#
# There is not good way to obtain the build result directory from within
# `.pbuilderrc` when writing a standalone `.pbuilderrc` file because
# `pbuilder-dist` does not export an environment variable with the build result
# directory. This can be confirmed by inspecting the output of `printenv`.
# However, in this case we are generating it with a heredoc and so can just
# hardcode it into the generated file.
#
# We check the existence of the directory to aid debugging in case anything
# goes wrong.
#
# The pbuilder-dist man page says that the default build result directory is
# `~/pbuilder/` unless the `--buildresult` option or the `PBUILDFOLDER`
# environment variable is set.
#
# Need to change working directory to $BUILDRESULT before running
# `apt-ftparchive` so that the path of the filenames in the Packages file are
# relative to $BUILDRESULT.
#
# Mount the $BUILDRESULT directory can be used as a local directory within the
# chroot.
#
cat << EOF > "$config_file"
BUILDRESULT="$build_result_dir"
if [ -d "\$BUILDRESULT" ]
then
echo "INFO: Assuming build result directory located at '\$BUILDRESULT'."
else
echo "ERROR: Build result directory '\$BUILDRESULT' does not exist."
exit 1
fi
( cd \$BUILDRESULT; apt-ftparchive packages . > \$BUILDRESULT/Packages )
BINDMOUNTS="\$BUILDRESULT"
HOOKDIR="$hooks_dir"
EOF
#
# Generate the hook file.
#
# The first 7 lines are the same as the above heredoc.
#
# The purpose of the hook is to add the previously build packages from the
# build result directory satisfy dependencies.
#
# We need to manually add the local repository to /etc/apt/sources.list
# because setting OTHERMIRROR has not effect.
# See https://bugs.launchpad.net/ubuntu/+source/ubuntu-dev-tools/+bug/1004579
# https://bugs.launchpad.net/ubuntu/+source/ubuntu-dev-tools/+bug/371221
#
cat << EOF > "$hook_file"
BUILDRESULT="$build_result_dir"
if [ -d "\$BUILDRESULT" ]
then
echo "INFO: Assuming build result directory located at '\$BUILDRESULT'."
else
echo "ERROR: Build result directory '\$BUILDRESULT' does not exist."
exit 1
fi
echo "deb [trusted=yes] file:\$BUILDRESULT ./" >> /etc/apt/sources.list
apt-get update
EOF
#
# The hook file needs execute permission set.
#
chmod +x "$hook_file"
#
# Map from a package to the filename (excluding file extension).
#
declare -A package_filename
#
# Download each source packages from Debian sid into the source directory.
#
# We could have instead taken the source packages from another Ubuntu version.
# For example:
# $ pull-lp-source xrdp jammy
#
# Get the packages with `--download-only` to skip extracting the package. We
# skip verifying the packages because we do this separately using the most
# recent GPG keys.
#
pushd "$source_package_dir"
for p in $packages_to_build
do
#
# Output from pull-debian-source when package already in working directory:
# $ pull-debian-source --download-only --no-verify-signature xorgxrdp
# Found xorgxrdp 1:0.2.17-1 in sid
# Downloading xorgxrdp_0.2.17.orig.tar.gz from deb.debian.org (0.469 MiB)
# Downloading xorgxrdp_0.2.17-1.debian.tar.xz from deb.debian.org (0.007 MiB)
#
# Output from pull-debian-source when package not in working directory:
# $ pull-debian-source --download-only --no-verify-signature xorgxrdp
# Found xorgxrdp 1:0.2.17-1 in sid
#
# We obtain the version of the package from the first line of output (there
# does not seem to be a better way).
#
# Get the version from the first line output while still printing the
# output to the terminal.
#
# Pipeline errors were set to not be masked above (hence error code check).
#
{ version=$(pull-debian-source --no-verify-signature --download-only $p 2>&1 | tee /dev/fd/3 | grep "^Found" | cut -d' ' -f3); } 3>&1
if [ $? -ne 0 ]
then
>&2 echo "ERROR: Failed to pull $p."
exit 1
fi
#
# Remove the epoch: from the version number (e.g., xorgxrdp current has
# Debian version 1:0.2.17-1. See `man deb-version` for details.
#
version=${version#*:}
filename="${p}_${version}.dsc"
package_filename[$p]="${p}_${version}"
#
# Check the file exists: if it does not then something has gone wrong
# (e.g., we did not extract the version correctly from the output).
#
if [ ! -f "$filename" ]
then
>&2 echo "ERROR: File $filename does not exist for package $p."
exit 1
fi
#
# Verify the package with the current set of keys.
#
dscverify --keyring "$keyring_dir/debian-keyring.gpg" "$filename"
if [ $? -ne 0 ]
then
>&2 echo "ERROR: Failed to verify $filename for package $p."
exit 1
fi
done
popd
#
# If the distribution for the environment does not exist then create it,
# otherwise update it.
#
pbuilder_operation="create"
if [ -f "$PBUILDFOLDER/$debian_dist-base.tgz" ]
then
pbuilder_operation="update"
fi
#
# We specify `--configfile` here because we do not want to use the
# `.pbuilderrc` in the home directory if it exists.
#
pbuilder-dist $debian_dist $pbuilder_operation --configfile "$config_file"
if [ $? -ne 0 ]
then
>&2 echo "ERROR: Failed to $pbuilder_operation $debian_dist environment."
exit 1
fi
#
# Build each package and copy to the $deb_package_output_dir directory.
#
for p in $packages_to_build
do
echo "===================================================================="
echo "== Running pbuilder for package $p"
echo "===================================================================="
pbuilder-dist $debian_dist build --configfile "$config_file" --buildresult "$build_result_dir" "$source_package_dir/${package_filename[$p]}.dsc"
if [ $? -ne 0 ]
then
>&2 echo "ERROR: Failed to build $p."
exit 1
fi
#
# The deb package filename has the format package_version_arch.deb.
#
# TODO: Here we assume that the architecture is amd64. We should check this
# and not just assume it (although the script will exit with an error if
# that assumption is wrong).
#
debfilename="${package_filename[$p]}_amd64.deb"
cp "$build_result_dir/$debfilename" "$deb_package_output_dir"
if [ $? -ne 0 ]
then
>&2 echo "ERROR: Failed to copy $debfilename to $deb_package_output_dir."
exit 1
fi
done
echo "Done."