-
-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathdebcraft.sh
More file actions
executable file
·501 lines (440 loc) · 16.3 KB
/
Copy pathdebcraft.sh
File metadata and controls
executable file
·501 lines (440 loc) · 16.3 KB
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
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
#!/bin/bash
# stop on any unhandled error
set -e
# @TODO: pipefail not in POSIX
set -o pipefail
# Expand non-matching globs to nothing (not even an empty string)
shopt -s nullglob
# show commands (debug)
#set -x
display_help() {
cat << EOF
usage: debcraft <build|improve|test|release|update|shell|logs|prune> [options] [<path|pkg|srcpkg|dsc|git-url>]
Debcraft is a tool to easily build .deb packages. The 'build' argument accepts
any of the following:
* path to directory with program sources including a debian/ subdirectory with
the Debian packaging instructions
* path to a .dsc file and source tarballs that can be built into a .deb
* Debian package name or source package name that apt can download
* git http(s) or ssh URL that can be downloaded and built
The command 'improve' will try to apply various improvements to the package
based on tools in Debian that automate package maintenance. The command 'test'
will run the Debian-specific regression test suite if the package has
autopkgtest support, and drops to a shell for investigation if tests fail to
pass. The command 'release' uploads a package that is ready to be released and
the command 'update' tries to update the package to the latest upstream version
if the package git repository layout is compatible.
The command 'shell' can be used to explore the container and 'prune' will
clean up temporary files created by Debcraft.
In addition to parameters below, anything passed in DEB_BUILD_OPTIONS will also
be honored (currently DEB_BUILD_OPTIONS='$DEB_BUILD_OPTIONS'). Successful builds
include running './debian/rules clean' to clean up artifacts, while failed
builds will leave them around for inspection.
Note that Debcraft builds never runs as root, and thus packages with
DEB_RULES_REQUIRES_ROOT are not supported.
optional arguments:
--build-dirs-path Path for writing build files and artifacts (default: ~/.cache/debcraft)
--distribution Linux distribution to build in (default: debian:sid)
--container-command Container command to use (default: podman)
--host-architecture Host architecture to use when performing a cross build
--skip-sources Build only binaries and skip creating a source
tarball to make the build slightly faster
('debcraft build' only)
--with-binaries Create a release with both source and binaries,
for example with the intent to upload to NEW
('debcraft release' only)
--pull Ensure container base is updated
--copy Perform the build on a copy of the package directory
--clean Ensure sources are clean before and after build
(only needed for packages with incomplete 'debian/clean'
or 'debian/.gitignore' definitions)
--extra-repository Use directory as local package repository for builds
--config Path to debcraft configuration file
--release-to After build or release, copy artefacts to specified dir
--debug Emit debug information
-h, --help Display this help and exit
--version Display version and exit
To learn more, or to contribute to Debcraft, see project page at
$(clickable_link "salsa.debian.org/debian/debcraft")
To gain more Debian Developer knowledge, please read
$(clickable_link "www.debian.org/doc/manuals/developers-reference/")
and $(clickable_link "www.debian.org/doc/debian-policy/")
EOF
}
# Canonicalize script name if it was run via symlink
DEBCRAFT_CMD_PATH="$(readlink --canonicalize-existing --verbose "$0")"
DEBCRAFT_LIB_DIR="$(dirname "$DEBCRAFT_CMD_PATH")/src"
# Assume system installation directory if not running from source directory
if [ ! -r "$DEBCRAFT_LIB_DIR/container/output.inc.sh" ]
then
DEBCRAFT_LIB_DIR="/usr/share/debcraft"
fi
# If Debcraft itself is run from a git repository, include the git commit id
display_version() {
if [ -e "$(dirname "$DEBCRAFT_CMD_PATH")/.git" ]
then
cd "$(dirname "$DEBCRAFT_CMD_PATH")"
if [ -z "$(git tag --list)" ]
then
echo "DEBCRAFT ERROR: Unable to find latest git tag. Please run 'git fetch --tags'."
exit 1
fi
LATEST_TAG="$(git describe --first-parent --abbrev=0)"
LATEST_VERSION="$(echo "$LATEST_TAG" | grep --only-matching --basic-regexp '[0-9.]*')"
LATEST_COMMIT="$(git rev-parse --short HEAD)"
echo "Debcraft $LATEST_VERSION-$LATEST_COMMIT"
elif [ -f /usr/share/doc/debcraft/changelog.gz ]
then
VERSION="$(
zgrep --only-matching --max-count=1 --perl-regexp '\(\K[^\)]*' \
/usr/share/doc/debcraft/changelog.gz
)"
echo "Debcraft $VERSION"
else
echo "Debcraft version unknown: neither git version control nor installed package was found"
fi
}
# Debug flag detection must run before output.inc.sh is loaded
case "$@" in
*--debug*)
export DEBUG="true"
;;
esac
# Output formatting library is reused inside container as well
# shellcheck source=src/container/output.inc.sh
source "$DEBCRAFT_LIB_DIR/container/output.inc.sh"
# Initialize TITLE_UPDATE_PID early to prevent unbound variable errors
# and allow setting title before package name is known (e.g. during download)
TITLE_UPDATE_PID=""
if [ -z "$1" ]
then
log_error "Missing argument <build|improve|test|release|shell|logs|prune>"
echo
display_help
exit 1
fi
while :
do
log_debug "Parse option/argument: $1"
case "$1" in
--build-dirs-path)
if [ -z "$2" ] || [ ! -d "$2" ]
then
log_error "Parameter --build-dirs-path requires a path"
exit 1
fi
BUILD_DIRS_PATH="$(readlink -f "$2")"
export BUILD_DIRS_PATH
log_debug "Using BUILD_DIRS_PATH=$BUILD_DIRS_PATH"
shift 2
;;
--distribution)
# The value is the next argument, e.g. '--distribution bookworm'
export DISTRIBUTION="$2"
log_debug "Using DISTRIBUTION=$2"
shift 2
;;
--container-command)
export CONTAINER_CMD="$2"
log_debug "Using CONTAINER_CMD=$2"
shift 2
;;
--host-architecture)
export HOST_ARCH="$2"
shift 2
;;
--skip-sources)
export SKIP_SOURCES="true"
shift
;;
--with-binaries)
export WITH_BINARIES="true"
shift
;;
--pull)
export PULL="true"
shift
;;
--clean)
export CLEAN="true"
shift
;;
--copy)
export COPY="true"
shift
;;
--extra-repository)
export DEBCRAFT_EXTRA_REPOSITORY="$2"
shift 2
;;
--release-to)
export DEBCRAFT_RELEASE_TO="$2"
shift 2
;;
--yes)
export DEBCRAFT_YES="true"
shift
;;
--config)
log_debug "Using CONFIG=$2"
export CONFIG="$2"
shift 2
;;
--debug)
# Debug mode detection is already done earlier, ignore it at this stage
shift
;;
-h | --help)
display_help # Call your function
exit 0
;;
--version)
display_version
exit 0
;;
--)
# No more options
shift
break
;;
-*=*)
log_error "Equals sign syntax in options unsupported: $1"
exit 1
;;
-*)
log_error "Unknown option: $1"
## or call function display_help
exit 1
;;
build | improve | test | release | update | shell | logs | prune)
export ACTION="$1"
shift
;;
*)
export TARGET="$1"
# No more options or arguments
break
;;
esac
done
if [ -z "$ACTION" ]
then
# If ACTION is empty the TARGET might have been populated
log_error "Argument '$TARGET' is not one of <build|improve|test|release|update|shell|logs|prune>"
echo
display_help
exit 1
fi
if [ -n "$SKIP_SOURCES" ] && [ "$ACTION" != "build" ]
then
log_error "Parameter --skip-sources can only be used with action 'build'"
echo
display_help
exit 1
fi
if [ -n "$WITH_BINARIES" ] && [ "$ACTION" != "release" ]
then
log_error "Parameter --with-binaries can only be used with action 'release'"
echo
display_help
exit 1
fi
if [ -n "$DISTRIBUTION" ] &&
{
[ "$ACTION" == "logs" ] || [ "$ACTION" == "update" ]
}
then
log_error "Parameter --distribution is not supported for action '$ACTION'"
echo
display_help
exit 1
fi
log_debug_var ACTION
# This variable allows debcraft to map distribution names to specific container
# images using a configuration file sourced below.
declare -A DEBCRAFT_DISTRIBUTION_MAPPING
# Source debcraft's configuration files if they exist
# shellcheck source=/dev/null
[[ -f /etc/debcraft ]] && source /etc/debcraft
# shellcheck source=/dev/null
[[ -f "$HOME/.config/debcraft" ]] && source "$HOME/.config/debcraft"
# shellcheck source=/dev/null
[[ -n "$CONFIG" && -f "$CONFIG" ]] && source "$CONFIG"
# shellcheck source=src/distributions.inc.sh
source "$DEBCRAFT_LIB_DIR/distributions.inc.sh"
# shellcheck source=src/generic.inc.sh
source "$DEBCRAFT_LIB_DIR/generic.inc.sh"
# Configure general program behavior after user options and arguments have been parsed
# shellcheck source=src/config-general.inc.sh
source "$DEBCRAFT_LIB_DIR/config-general.inc.sh"
# Check that dependencies are available
for cmd in git tee sed
do
if ! command -v "$cmd" > /dev/null 2>&1
then
log_error "Required dependency '$cmd' is not installed"
exit 1
fi
done
# Bash must be new enough to have 'mapfile'.
if ! type mapfile > /dev/null 2>&1
then
log_error "Bash version too old - 'mapfile' builtin is required"
exit 1
fi
# Docker does not support '--noheading', so the command will always output at
# least one line
# Hack: Listing images is very slow on Podman, so branch off two different tests
# shellcheck disable=2235 # two subshells necessary in this case
if ([ "$CONTAINER_TYPE" == 'podman' ] && \
! grep --quiet debcraft ~/.local/share/containers/storage/overlay-images/images.json 2>/dev/null) \
|| \
([ "$CONTAINER_TYPE" == 'docker' ] && \
[ "$($CONTAINER_CMD images 'debcraft*' | wc -l)" -eq 1 ])
then
log_warn "No previous Debcraft container was found and thus the first run of"
log_warn "this tool is expected to be slow as the container base layer needs"
log_warn "to be built. Re-runs of Debcraft will be fast."
fi
log_debug_var TARGET
if [ -z "$TARGET" ]
then
# If no argument defined, default to current directory
TARGET="$(pwd)"
elif [ -d "$TARGET" ]
then
# If an argument was given, and it is a directory, use TARGET as-is
:
elif [ ! -d "$TARGET" ] && [[ "build improve update" =~ $ACTION ]]
then
# If the argument exists, but didn't point to a valid path, try to use the
# argument to download the package
# Set title early to show activity during potentially long download
title_running "download $TARGET"
# Ensure title is cleaned up if download fails before main trap is set
trap 'kill $TITLE_UPDATE_PID 2>/dev/null || true; title_clear' EXIT
trap 'kill $TITLE_UPDATE_PID 2>/dev/null || true; title_clear; exit 130' INT TERM
# shellcheck source=src/downloader-container.inc.sh
source "$DEBCRAFT_LIB_DIR/downloader-container.inc.sh"
# shellcheck source=src/downloader.inc.sh
source "$DEBCRAFT_LIB_DIR/downloader.inc.sh"
# After a download, a subdirectory <TARGET>-<VERSION> might be present, and
# if a container build already ran, also a debcraft-container-<TARGET> will
# be present and that should be excluded from the listing below that aims to
# find the downloaded sources.
# shellcheck disable=SC2012
NEWEST_DIRECTORY="$(ls --sort=time --time=ctime --format=single-column --group-directories-first --hide="debcraft-container-*" | head --lines=1 || true)"
# Note! Use ctime above as dpkg-source will keep original mtime for packages
# and ensure it never emits exit codes, only text output
# Remove shortest pattern from end of variable, e.g. 'xz-utils-5.2.4' -> 'xz-utils'
# (https://tldp.org/LDP/abs/html/parameter-substitution.html)
#PACKAGE="${NEWEST_DIRECTORY%-*}"
# Ensure source directory exists with the package name, e.g. 'grep' -> 'grep-3.4'
#ln --verbose --force --symbolic "$NEWEST_DIRECTORY" "$PACKAGE"
# Newest directory contains the downloaded source package now, use it as TARGET
export TARGET="$NEWEST_DIRECTORY"
else
log_error "Debcraft command '$ACTION' can only be used after a build has run," \
"and a directory with the Debian package source exists."
exit 1
fi
log_debug_var TARGET
# The previous step guarantees that the source directory either exists, was
# downloaded or the script execution stopped. From here onward the script can
# assume that $PWD is a working directory with sources.
cd "$TARGET" || (log_error "Unable to change directory to $TARGET"; exit 1)
log_debug_var PWD
if [ -f "debian/changelog" ]
then
# If parsing the changelog emits exit code, intentionally stop here
PACKAGE="$(head -n 1 debian/changelog | cut -d ' ' -f 1)"
else
log_error "Directory '$TARGET' is not a valid source package directory as" \
"debian/changelog was not found"
exit 1
fi
log_info "Running in directory $PWD that has Debian package sources for '$PACKAGE'"
# Start title animation in background and ensure cleanup on exit
# Note: TITLE_UPDATE_PID was already initialized at script start and may
# contain a PID from the download phase; title_animate will kill it
title_running "$ACTION $PACKAGE"
# Trap EXIT, INT (Ctrl+C), and TERM to ensure title always resets
trap 'kill $TITLE_UPDATE_PID 2>/dev/null || true; title_clear' EXIT
trap 'kill $TITLE_UPDATE_PID 2>/dev/null || true; title_clear; exit 130' INT TERM
# Make sure sources are clean on actions that depend on it
if [ "$ACTION" == "build" ] || [ "$ACTION" == "release" ]
then
# Every deb build potentially generates these temporary files, and it is safe
# to assume that they should be deleted before restarting the build, so do it
# automatically to make the overall experience friendlier
rm -rfv debian/.debhelper debian/debhelper-build-stamp \
debian/debhelper.log debian/*.debhelper.log \
debian/substvars debian/*.substvars debian/files
# If there are still more files, the user needs to make a decision
if [ -z "$CLEAN" ] &&
[ -z "$COPY" ] &&
[ -d "$PWD/.git" ] &&
[ -n "$(git status --porcelain --ignored --untracked-files=all)" ]
then
log_error "Modified or additional files found"\
"\n$(git status --porcelain --ignored --untracked-files=all | head)"\
"\nThese may be leftovers of a failed build, or an incomplete"\
"\nrun of './debian/rules clean' after a successful build." \
"\nUse --clean to remove all untracked and uncommitted files"\
"\nor run with --copy to build in a separate new directory."
exit 1
fi
fi
# @TODO: If repository is a git clone, but not a `gbp clone`, it can be converted
# to gbp with `gbp pull --verbose --ignore-branch --pristine-tar --track-missing`
# Clean up before the build if applicable
reset_if_source_repository_and_option_clean
# Configure program behavior after user options and arguments have been parsed
# shellcheck source=src/config-package.inc.sh
source "$DEBCRAFT_LIB_DIR/config-package.inc.sh"
# If the action needs to run in a container, automatically create it
if [ "$ACTION" != "update" ] && [ "$ACTION" != "prune" ] && [ "$ACTION" != "logs" ]
then
# shellcheck source=src/container.inc.sh
source "$DEBCRAFT_LIB_DIR/container.inc.sh"
fi
case "$ACTION" in
build)
# shellcheck source=src/build.inc.sh
source "$DEBCRAFT_LIB_DIR/build.inc.sh"
;;
improve)
# shellcheck source=src/improve.inc.sh
source "$DEBCRAFT_LIB_DIR/improve.inc.sh"
;;
test)
# shellcheck source=src/test.inc.sh
source "$DEBCRAFT_LIB_DIR/test.inc.sh"
;;
release)
# shellcheck source=src/release.inc.sh
source "$DEBCRAFT_LIB_DIR/release.inc.sh"
# shellcheck source=src/release-ppa.inc.sh
source "$DEBCRAFT_LIB_DIR/release-ppa.inc.sh"
# shellcheck source=src/release-dput.inc.sh
source "$DEBCRAFT_LIB_DIR/release-dput.inc.sh"
;;
update)
# shellcheck source=src/update.inc.sh
source "$DEBCRAFT_LIB_DIR/update.inc.sh"
;;
shell)
# shellcheck source=src/shell.inc.sh
source "$DEBCRAFT_LIB_DIR/shell.inc.sh"
;;
logs)
# shellcheck source=src/logs.inc.sh
source "$DEBCRAFT_LIB_DIR/logs.inc.sh"
;;
prune)
# shellcheck source=src/prune.inc.sh
source "$DEBCRAFT_LIB_DIR/prune.inc.sh"
;;
esac
# Clean up after a successful build if applicable
reset_if_source_repository_and_option_clean