Skip to content

Commit ffd3840

Browse files
committed
ci(void): publish XBPS repository to R2
(cherry picked from commit 0fdfac3)
1 parent 447b4d8 commit ffd3840

2 files changed

Lines changed: 241 additions & 37 deletions

File tree

.github/scripts/publish-void-r2.sh

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
#!/usr/bin/env bash
2+
3+
set -euo pipefail
4+
5+
usage() {
6+
echo "usage: $0 download|publish" >&2
7+
exit 2
8+
}
9+
10+
require_env() {
11+
local name
12+
for name in "$@"; do
13+
if [[ -z "${!name:-}" ]]; then
14+
echo "error: $name is required" >&2
15+
exit 2
16+
fi
17+
done
18+
}
19+
20+
require_env R2_BUCKET R2_ENDPOINT R2_PREFIX REPOSITORY_DIR
21+
22+
aws_r2() {
23+
aws --endpoint-url "$R2_ENDPOINT" "$@"
24+
}
25+
26+
download() {
27+
mkdir -p "$REPOSITORY_DIR/current" "$REPOSITORY_DIR/previous-current"
28+
29+
aws_r2 s3 sync \
30+
"s3://${R2_BUCKET}/${R2_PREFIX}/current/" \
31+
"$REPOSITORY_DIR/current/" \
32+
--only-show-errors
33+
34+
cp -a "$REPOSITORY_DIR/current/." "$REPOSITORY_DIR/previous-current/"
35+
}
36+
37+
build_manifest() {
38+
local packages='[]'
39+
local file filename pkgver name version revision sha size
40+
41+
for file in "$REPOSITORY_DIR"/current/*.xbps; do
42+
filename="$(basename "$file")"
43+
pkgver="$(xbps-uhelper binpkgver "$filename")"
44+
name="$(xbps-uhelper getpkgname "$pkgver")"
45+
version="$(xbps-uhelper getpkgversion "$pkgver")"
46+
revision="$(xbps-uhelper getpkgrevision "$pkgver")"
47+
version="${version%_"${revision}"}"
48+
sha="$(sha256sum "$file" | cut -d' ' -f1)"
49+
size="$(stat -c '%s' "$file")"
50+
packages="$(jq \
51+
--arg name "$name" \
52+
--arg version "$version" \
53+
--arg revision "$revision" \
54+
--arg filename "$filename" \
55+
--arg sha256 "$sha" \
56+
--argjson size "$size" \
57+
'. + [{name: $name, version: $version, revision: $revision, filename: $filename, sha256: $sha256, size: $size}]' \
58+
<<<"$packages")"
59+
done
60+
61+
jq -n \
62+
--arg repository "$R2_PREFIX" \
63+
--arg source_commit "$SOURCE_COMMIT" \
64+
--arg published_at "$PUBLISHED_AT" \
65+
--argjson packages "$packages" \
66+
'{schema: 1, repository: $repository, source_commit: $source_commit, published_at: $published_at, packages: $packages}' \
67+
> "$REPOSITORY_DIR/current/manifest.json"
68+
}
69+
70+
verify_immutable_packages() {
71+
local file previous
72+
73+
for file in "$REPOSITORY_DIR"/current/*.xbps "$REPOSITORY_DIR"/current/*.sig2; do
74+
previous="$REPOSITORY_DIR/previous-current/$(basename "$file")"
75+
if [[ -f "$previous" ]] && ! cmp -s "$previous" "$file"; then
76+
echo "error: refusing to replace immutable object $(basename "$file")" >&2
77+
echo "bump the XBPS revision or version before publishing a changed build" >&2
78+
exit 1
79+
fi
80+
done
81+
}
82+
83+
archive_retired() {
84+
local old filename
85+
local archive_prefix="archive/${R2_PREFIX}/${PUBLISHED_AT//:/-}"
86+
87+
shopt -s nullglob
88+
for old in "$REPOSITORY_DIR"/previous-current/*.xbps "$REPOSITORY_DIR"/previous-current/*.sig2; do
89+
filename="$(basename "$old")"
90+
if [[ ! -e "$REPOSITORY_DIR/current/$filename" ]]; then
91+
aws_r2 s3 cp \
92+
"$old" \
93+
"s3://${R2_BUCKET}/${archive_prefix}/${filename}" \
94+
--cache-control 'private,no-store' \
95+
--only-show-errors
96+
fi
97+
done
98+
}
99+
100+
upload_current() {
101+
local file filename old
102+
103+
# Versioned package objects must exist before repodata can reference them.
104+
for file in "$REPOSITORY_DIR"/current/*.xbps "$REPOSITORY_DIR"/current/*.sig2; do
105+
filename="$(basename "$file")"
106+
aws_r2 s3 cp \
107+
"$file" \
108+
"s3://${R2_BUCKET}/${R2_PREFIX}/current/${filename}" \
109+
--cache-control 'public,max-age=31536000,immutable' \
110+
--only-show-errors
111+
done
112+
113+
aws_r2 s3 cp \
114+
"$REPOSITORY_DIR/current/x86_64-repodata" \
115+
"s3://${R2_BUCKET}/${R2_PREFIX}/current/x86_64-repodata" \
116+
--cache-control 'no-cache' \
117+
--only-show-errors
118+
119+
# The manifest is the publication marker and is always uploaded last.
120+
aws_r2 s3 cp \
121+
"$REPOSITORY_DIR/current/manifest.json" \
122+
"s3://${R2_BUCKET}/${R2_PREFIX}/current/manifest.json" \
123+
--cache-control 'no-cache' \
124+
--only-show-errors
125+
126+
# Once the new index and marker are live, remove objects no longer referenced.
127+
shopt -s nullglob
128+
for old in "$REPOSITORY_DIR"/previous-current/*; do
129+
filename="$(basename "$old")"
130+
if [[ ! -e "$REPOSITORY_DIR/current/$filename" ]]; then
131+
aws_r2 s3 rm \
132+
"s3://${R2_BUCKET}/${R2_PREFIX}/current/${filename}" \
133+
--only-show-errors
134+
fi
135+
done
136+
}
137+
138+
publish() {
139+
require_env SOURCE_COMMIT
140+
PUBLISHED_AT="${PUBLISHED_AT:-$(date -u +'%Y-%m-%dT%H:%M:%SZ')}"
141+
export PUBLISHED_AT
142+
143+
shopt -s nullglob
144+
local packages=("$REPOSITORY_DIR"/current/*.xbps)
145+
if (( ${#packages[@]} == 0 )); then
146+
echo "error: refusing to publish an empty XBPS repository" >&2
147+
exit 1
148+
fi
149+
[[ -s "$REPOSITORY_DIR/current/x86_64-repodata" ]] || {
150+
echo "error: x86_64-repodata is missing or empty" >&2
151+
exit 1
152+
}
153+
for file in "${packages[@]}"; do
154+
[[ -s "${file}.sig2" ]] || {
155+
echo "error: signature is missing for $(basename "$file")" >&2
156+
exit 1
157+
}
158+
done
159+
160+
verify_immutable_packages
161+
build_manifest
162+
archive_retired
163+
upload_current
164+
}
165+
166+
case "${1:-}" in
167+
download) download ;;
168+
publish) publish ;;
169+
*) usage ;;
170+
esac

.github/workflows/run-xbps.yml

Lines changed: 71 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -34,31 +34,63 @@ on:
3434
default: true
3535

3636
permissions:
37-
contents: write
37+
contents: read
38+
39+
concurrency:
40+
group: void-xbps-${{ github.ref }}
41+
cancel-in-progress: false
3842

3943
jobs:
44+
preflight:
45+
name: Check for unpublished changes
46+
runs-on: ubuntu-latest
47+
outputs:
48+
should_build: ${{ steps.check.outputs.should_build }}
49+
steps:
50+
- name: Check published commit
51+
id: check
52+
env:
53+
MANIFEST_URL: https://void.danklinux.com/dms/current/manifest.json
54+
run: |
55+
SHOULD_BUILD=true
56+
if [ "${{ github.event_name }}" = "schedule" ]; then
57+
PUBLISHED_COMMIT="$(curl -fsSL --retry 3 "$MANIFEST_URL" | jq -r '.source_commit // empty' 2>/dev/null || true)"
58+
if [ "$PUBLISHED_COMMIT" = "${{ github.sha }}" ]; then
59+
SHOULD_BUILD=false
60+
echo "${{ github.sha }} is already published; skipping the scheduled build."
61+
fi
62+
fi
63+
echo "should_build=$SHOULD_BUILD" >> "$GITHUB_OUTPUT"
64+
4065
build-and-deploy:
4166
name: Build & Deploy XBPS packages
4267
runs-on: ubuntu-latest
43-
if: github.repository == 'AvengeMedia/DankMaterialShell'
68+
needs: preflight
69+
if: github.repository == 'AvengeMedia/DankMaterialShell' && needs.preflight.outputs.should_build == 'true'
70+
env:
71+
AWS_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
72+
AWS_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
73+
AWS_DEFAULT_REGION: auto
74+
AWS_EC2_METADATA_DISABLED: true
75+
R2_BUCKET: danklinux-void
76+
R2_ENDPOINT: https://${{ secrets.CLOUDFLARE_ACCOUNT_ID }}.r2.cloudflarestorage.com
77+
R2_PREFIX: dms
78+
REPOSITORY_DIR: ${{ github.workspace }}/r2-repo
79+
SOURCE_COMMIT: ${{ github.sha }}
4480
steps:
4581
- name: Checkout repository
4682
uses: actions/checkout@v6
4783
with:
4884
ref: ${{ github.event.release.tag_name || (github.event.inputs.version && (startsWith(github.event.inputs.version, 'v') && github.event.inputs.version || format('v{0}', github.event.inputs.version))) || github.ref }}
4985
fetch-depth: 0
5086

51-
- name: Checkout gh-pages branch
87+
- name: Record checked-out source commit
88+
run: echo "SOURCE_COMMIT=$(git rev-parse HEAD)" >> "$GITHUB_ENV"
89+
90+
- name: Download current R2 repository
5291
run: |
53-
git clone --branch gh-pages https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git gh-pages-repo || {
54-
echo "⚠️ gh-pages branch not found or empty, initializing a new one..."
55-
mkdir gh-pages-repo
56-
cd gh-pages-repo
57-
git init
58-
git checkout -b gh-pages
59-
git remote add origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git
60-
cd ..
61-
}
92+
aws --version
93+
bash .github/scripts/publish-void-r2.sh download
6294
6395
- name: Install XBPS Static Utilities
6496
run: |
@@ -132,19 +164,19 @@ jobs:
132164
- name: Configure repositories
133165
run: |
134166
# Append the repository to repos-remote templates so xbps-src translates it automatically
135-
echo "repository=https://avengemedia.github.io/DankLinux/current" >> void-packages/etc/xbps.d/repos-remote.conf
136-
echo "repository=https://avengemedia.github.io/DankLinux/current" >> void-packages/etc/xbps.d/repos-remote-x86_64-multilib.conf
167+
echo "repository=https://void.danklinux.com/danklinux/current" >> void-packages/etc/xbps.d/repos-remote.conf
168+
echo "repository=https://void.danklinux.com/danklinux/current" >> void-packages/etc/xbps.d/repos-remote-x86_64-multilib.conf
137169
138170
# Add any existing compiled packages to the build cache directory to avoid rebuilds
139-
if [ -d "gh-pages-repo/current" ]; then
171+
if [ -d "r2-repo/current" ]; then
140172
mkdir -p void-packages/hostdir/binpkgs
141-
cp -L gh-pages-repo/current/*.xbps void-packages/hostdir/binpkgs/ 2>/dev/null || true
173+
cp -L r2-repo/current/*.xbps void-packages/hostdir/binpkgs/ 2>/dev/null || true
142174
xbps-rindex -a void-packages/hostdir/binpkgs/*.xbps 2>/dev/null || true
143175
fi
144176
145177
- name: Build packages
146178
run: |
147-
mkdir -p gh-pages-repo/current
179+
mkdir -p r2-repo/current
148180
149181
BUILD_DMS="${{ github.event.inputs.build_dms || 'true' }}"
150182
BUILD_GREETER="${{ github.event.inputs.build_greeter || 'true' }}"
@@ -216,7 +248,7 @@ jobs:
216248
mkdir -p "$SRC_CACHE"
217249
tar -czf "${SRC_CACHE}/dms-git-${GIT_VER}.tar.gz" \
218250
--exclude=void-packages \
219-
--exclude=gh-pages-repo \
251+
--exclude=r2-repo \
220252
--exclude=.git \
221253
--exclude=danklinux \
222254
-C .. .
@@ -230,13 +262,13 @@ jobs:
230262
231263
EXPECTED_GIT_FILE="dms-git-${GIT_VER}_1.x86_64.xbps"
232264
233-
if [ -f "../gh-pages-repo/current/$EXPECTED_GIT_FILE" ] && [ "$FORCE_REBUILD" != "true" ]; then
265+
if [ -f "../r2-repo/current/$EXPECTED_GIT_FILE" ] && [ "$FORCE_REBUILD" != "true" ]; then
234266
echo "✅ $EXPECTED_GIT_FILE already exists, skipping build."
235267
else
236268
echo "🔨 Compiling dms-git..."
237269
./xbps-src pkg dms-git
238-
rm -f "../gh-pages-repo/current/${EXPECTED_GIT_FILE}"
239-
cp -L hostdir/binpkgs/dms-git-*.xbps ../gh-pages-repo/current/
270+
rm -f "../r2-repo/current/${EXPECTED_GIT_FILE}"
271+
cp -L "hostdir/binpkgs/${EXPECTED_GIT_FILE}" ../r2-repo/current/
240272
fi
241273
fi
242274
@@ -246,13 +278,13 @@ jobs:
246278
STABLE_REV=$(grep -E '^revision=' srcpkgs/dms/template | cut -d= -f2 | tr -d '"')
247279
248280
EXPECTED_DMS_FILE="dms-${STABLE_VER}_${STABLE_REV}.x86_64.xbps"
249-
if [ -f "../gh-pages-repo/current/$EXPECTED_DMS_FILE" ] && [ "$FORCE_REBUILD" != "true" ]; then
281+
if [ -f "../r2-repo/current/$EXPECTED_DMS_FILE" ] && [ "$FORCE_REBUILD" != "true" ]; then
250282
echo "✅ $EXPECTED_DMS_FILE already exists, skipping build."
251283
else
252284
echo "🔨 Compiling dms ($STABLE_VER)..."
253285
./xbps-src pkg dms
254-
rm -f "../gh-pages-repo/current/${EXPECTED_DMS_FILE}"
255-
cp -L hostdir/binpkgs/dms-${STABLE_VER}_${STABLE_REV}.x86_64.xbps ../gh-pages-repo/current/
286+
rm -f "../r2-repo/current/${EXPECTED_DMS_FILE}"
287+
cp -L hostdir/binpkgs/dms-${STABLE_VER}_${STABLE_REV}.x86_64.xbps ../r2-repo/current/
256288
fi
257289
fi
258290
@@ -262,22 +294,21 @@ jobs:
262294
GREETER_REV=$(grep -E '^revision=' srcpkgs/dms-greeter/template | cut -d= -f2 | tr -d '"')
263295
264296
EXPECTED_GREETER_FILE="dms-greeter-${GREETER_VER}_${GREETER_REV}.x86_64.xbps"
265-
if [ -f "../gh-pages-repo/current/$EXPECTED_GREETER_FILE" ] && [ "$FORCE_REBUILD" != "true" ]; then
297+
if [ -f "../r2-repo/current/$EXPECTED_GREETER_FILE" ] && [ "$FORCE_REBUILD" != "true" ]; then
266298
echo "✅ $EXPECTED_GREETER_FILE already exists, skipping build."
267299
else
268300
echo "🔨 Compiling dms-greeter ($GREETER_VER)..."
269301
./xbps-src pkg dms-greeter
270-
rm -f "../gh-pages-repo/current/${EXPECTED_GREETER_FILE}"
271-
cp -L hostdir/binpkgs/dms-greeter-${GREETER_VER}_${GREETER_REV}.x86_64.xbps ../gh-pages-repo/current/
302+
rm -f "../r2-repo/current/${EXPECTED_GREETER_FILE}"
303+
cp -L hostdir/binpkgs/dms-greeter-${GREETER_VER}_${GREETER_REV}.x86_64.xbps ../r2-repo/current/
272304
fi
273305
fi
274306
275307
- name: Index and sign repository
276308
run: |
277-
cd gh-pages-repo/current
309+
cd r2-repo/current
278310
279-
# Clean up any stale or dangling signature files to prevent O_CREAT ENOENT errors
280-
rm -f *.sig2 *.sig
311+
rm -f *.sig
281312
282313
# Guard: nothing to index if no .xbps files exist
283314
if ! ls *.xbps 1>/dev/null 2>&1; then
@@ -287,6 +318,14 @@ jobs:
287318
288319
# Regenerate repo index
289320
xbps-rindex -a $(pwd)/*.xbps
321+
xbps-rindex --remove-obsoletes $(pwd)
322+
323+
# Remove signatures left behind by obsolete packages. Signatures for
324+
# unchanged immutable packages are retained byte-for-byte.
325+
for sig in *.sig2; do
326+
[ -e "$sig" ] || break
327+
[ -f "${sig%.sig2}" ] || rm -f "$sig"
328+
done
290329
291330
# Sign repository
292331
echo "${{ secrets.XBPS_PRIVATE_KEY }}" > /tmp/xbps_privkey.pem
@@ -297,10 +336,5 @@ jobs:
297336
298337
rm -f /tmp/xbps_privkey.pem
299338
300-
- name: Deploy to gh-pages branch
301-
run: |
302-
cd gh-pages-repo
303-
git config user.name "github-actions[bot]"
304-
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
305-
git add current/
306-
git diff --quiet && git diff --staged --quiet || (git commit -m "Update XBPS packages [skip ci]" && git push origin gh-pages)
339+
- name: Publish repository to R2
340+
run: bash .github/scripts/publish-void-r2.sh publish

0 commit comments

Comments
 (0)