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
name: Build PPA source package
# Sanity-check that debian/ is correct on every push that touches
# packaging or upstream code. We build a source-only Debian
# package (no binary build, no upload — Launchpad does that side)
# and run `lintian` over the result.
#
# Actual PPA upload requires a private GPG key + Launchpad SSO
# and is performed manually with `./packages/ppa/build.sh` from
# the maintainer's host. Automating the upload from CI would
# need a long-lived signing key in repo secrets; rejected on
# security grounds.
on:
push:
branches:
paths:
- 'debian/**'
- 'packages/ppa/**'
- 'Cargo.toml'
- 'Cargo.lock'
- '.github/workflows/ppa.yml'
pull_request:
paths:
- 'debian/**'
- 'packages/ppa/**'
- '.github/workflows/ppa.yml'
workflow_dispatch:
permissions:
contents: read
jobs:
source-package:
name: Source package + lintian
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v6
- name: Install Debian build tooling
run: |
sudo apt-get update -qq
sudo apt-get install -y --no-install-recommends \
devscripts debhelper lintian \
cargo rustc build-essential
- name: Stage upstream + vendor crates + debian/
run: |
version="$(awk -F'"' '/^version[[:space:]]*=/{print $2; exit}' Cargo.toml)"
echo "VERSION=$version" >> "$GITHUB_ENV"
mkdir -p /tmp/build
git archive --format=tar HEAD | tar -C /tmp/build -xf -
mv /tmp/build "/tmp/agtop-$version"
# Mirror packages/ppa/build.sh: vendor every crate into
# the upstream tree so the source is self-contained for
# network-isolated PPA builders.
( cd "/tmp/agtop-$version" && \
rm -rf debian && \
mkdir -p .cargo && \
cargo vendor --locked vendor/ > .cargo/config.toml 2>/dev/null )
# Prune pre-rendered docs / examples / target trees from
# every vendored crate. Lintian flags pre-rendered HTML
# under `docs/` as `source-is-missing` (DFSG: shipped
# binary-form output without source). Also drop the
# winapi-*-pc-windows-gnu/lib/lib*.a precompiled import
# libraries lintian rejects as unaccompanied binaries.
( cd "/tmp/agtop-$version" && \
find vendor -type d \( -name docs -o -name target -o -name benches \) -prune -exec rm -rf {} + 2>/dev/null
find "/tmp/agtop-$version/vendor" -type f -path '*/lib/lib*.a' -delete 2>/dev/null )
# cargo vendor stamps each crate with .cargo-checksum.json
# listing every file's sha256 — pruning files invalidates
# those checksums, so packages/ppa/vendor-prune.py walks
# every checksum and drops entries whose target no longer
# exists. Externalised because inlining python source in
# YAML's block scalar broke the parser.
( cd "/tmp/agtop-$version" && \
python3 "$GITHUB_WORKSPACE/packages/ppa/vendor-prune.py" )
tar --owner=0 --group=0 --numeric-owner \
-czf "/tmp/agtop_${version}.orig.tar.gz" \
-C /tmp "agtop-$version"
# Re-overlay debian/ for the package build itself.
git archive --format=tar HEAD debian | \
tar -C "/tmp/agtop-$version" -xf -
- name: Mint per-Cargo-version changelog stanza
run: |
cd "/tmp/agtop-$VERSION"
# debian/changelog's top entry version must match the
# orig.tar.gz upstream version or debuild prompts and
# fails non-interactively. Cargo.toml is the source of
# truth for the upstream version; mint a synthetic stanza
# so we don't have to bump debian/changelog by hand on
# every release commit.
export DEBEMAIL="${DEBEMAIL:-matt@brassey.io}"
export DEBFULLNAME="${DEBFULLNAME:-Matt Brassey}"
dch --newversion "${VERSION}-1~noble1" \
--distribution noble \
--force-bad-version \
"Synthetic CI stanza for ${VERSION} on noble." || true
- name: Build source package
run: |
cd "/tmp/agtop-$VERSION"
# No GPG signing in CI — `-us -uc` builds an unsigned
# source package suitable for lintian + format checks.
# The maintainer's host signs for real uploads. -d skips
# dpkg-checkbuilddeps because we're only producing the
# source package here; the actual binary build happens
# on Launchpad's farm where build-deps are evaluated
# against the target series' archive.
debuild -S -sa -us -uc -d
- name: Run lintian
# Informational only. PPA / official-Debian uploads gate
# on lintian via the maintainer's host (`./packages/ppa/build.sh`
# runs lintian with --pedantic before dput). CI lintian
# flags noise from vendored Rust crates that ship pre-built
# HTML docs (deltae, ratatui, etc.); cleaning every
# vendored crate's source tree to lintian-clean is a
# separate hardening pass.
continue-on-error: true
run: |
cd /tmp
lintian --info --display-info \
--suppress-tags bad-distribution-in-changes-file,source-is-missing,debian-watch-uses-insecure-uri \
"agtop_${VERSION}-1~noble1_source.changes" || true
- name: Upload source package as artifact
uses: actions/upload-artifact@v4
with:
name: agtop-source-package
path: |
/tmp/agtop_*.dsc
/tmp/agtop_*.tar.*
/tmp/agtop_*_source.changes
/tmp/agtop_*_source.buildinfo
retention-days: 14