Repository navigation
181 lines (165 loc) · 9.52 KB
/
Copy pathbake-rootfs.yml
File metadata and controls
181 lines (165 loc) · 9.52 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
name: Bake & publish K2Go RootFS
# ADFA-5334. Builds the rootfs images with tools/rootfs-builder/build-iiab-rootfs.sh
# (native proot on arm64 runners) and publishes them to Cloudflare R2 (k2go-rootfs).
# Supersedes the old QEMU/Docker bake-rootfs.yml.disabled.
#
# Split by architecture into two parallel jobs (one arm64 node each), each building
# ALL tiers (basic, standard, full, matomo). ~1.5h per node. The APK is NOT touched
# here; ADFA-5368 re-pointed the app at this bucket (config/DownloadEndpoints).
#
# A one-shot `seed-base` job runs first: it puts the STATIC proot-distro base (the two
# Debian tarballs K2Go builds on) into R2 exactly once, then only verifies its presence
# on every later run. Retention: the prune keeps the newest 7 batches per (tier,arch).
on:
workflow_dispatch: # manual only for now
inputs:
binaries_tag:
description: "Native-binaries release tag to pin (blank = binary_version.txt on main). e.g. binaries-2026-09-01_03-22 to bake on a specific proot build without merging."
required: false
default: ""
# Re-enable when we want it scheduled:
# schedule:
# - cron: '0 4 * * 0' # Sundays 04:00 UTC
env:
BUCKET_NAME: k2go-rootfs
R2_ACCOUNT_ID: ${{ vars.CLOUDFLARE_ACCOUNT_ID }}
# Public base that goes into the .meta4 <url> — where the APK actually downloads the
# rootfs FROM, so it has to name the bucket that holds it. The two buckets are separate
# and so are their names: k2go-download.appdevforall.org serves the APK repo only.
# K2GO-90: this defaulted to k2go-download.appdevforall.org/rootfs, a path-based route that
# was never created. The metalinks published with it resolved to 404 — and since
# --reset-mirrors leaves a single source, there was nothing to fall back to.
ROOTFS_PUBLIC_BASE: ${{ vars.ROOTFS_PUBLIC_BASE || 'https://pub-d64c885cef6c42db8c7925144d73d0ee.r2.dev' }}
# Key prefix inside the bucket. Empty: the keys sit at the bucket root (only the static
# proot-distro base lives in a folder). Set it only if the layout gains a prefix.
R2_KEY_PREFIX: ${{ vars.ROOTFS_KEY_PREFIX }}
jobs:
# The Debian base every tier is built on is a proot-distro v4.29.0 release asset
# (github.com/termux/proot-distro), fetched from the upstream release. It is STATIC, so seed it
# into R2 ONCE; every later run only HEADs it (no re-download). Bumping PD_VERSION seeds a
# fresh proot-distro-v<ver>/ dir once. Only the two bases K2Go consumes are mirrored
# (Android arm64-v8a -> aarch64, armeabi-v7a -> arm); the rest of the release is not.
seed-base:
name: Seed proot-distro base (once) + verify
runs-on: ubuntu-latest # pure download+upload; no arm needed, frees the arm runners
timeout-minutes: 30
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Ensure AWS CLI is available
run: aws --version || { sudo apt-get update && sudo apt-get install -y awscli; }
- name: Seed-or-verify the static proot-distro base on R2
env:
AWS_ACCESS_KEY_ID: ${{ vars.CLOUDFLARE_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.CLOUDFLARE_SECRET_ACCESS_KEY }}
run: |
set -euo pipefail
# Single source of truth: read the version the builder actually downloads,
# so a PD_VERSION bump in build-iiab-rootfs.sh seeds the new base automatically.
BUILDER="tools/rootfs-builder/build-iiab-rootfs.sh"
PD_VERSION="$(grep -oP '^PD_VERSION="?\K[0-9.]+' "$BUILDER" | head -1)"
[ -n "$PD_VERSION" ] || { echo "FATAL: could not read PD_VERSION from $BUILDER" >&2; exit 1; }
echo "proot-distro base version (from builder): v${PD_VERSION}"
GH_BASE="https://github.com/termux/proot-distro/releases/download/v${PD_VERSION}"
ENDPOINT="https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com"
PFX="${R2_KEY_PREFIX:-}" # '' or 'rootfs/', same as the bake job
DIR="proot-distro-v${PD_VERSION}"
# Only the bases K2Go builds on. Names match build-iiab-rootfs.sh BASE_TARBALL.
TARBALLS=( "debian-trixie-aarch64-pd-v${PD_VERSION}.tar.xz" \
"debian-trixie-arm-pd-v${PD_VERSION}.tar.xz" )
for tb in "${TARBALLS[@]}"; do
key="${PFX}${DIR}/${tb}"
# Present on R2 -> verify-only: a HEAD, never a re-download (the base is static).
if aws s3api head-object --bucket "$BUCKET_NAME" --key "$key" \
--endpoint-url "$ENDPOINT" >/dev/null 2>&1; then
echo ">> present (verify-only, no download): $key"
continue
fi
# Missing -> seed once from the pinned release, with a sha256 record beside it.
echo ">> missing -> seed once from the pinned release: $tb"
curl -fL --retry 3 --retry-delay 5 -o "$tb" "${GH_BASE}/${tb}"
sha256sum "$tb" > "${tb}.sha256"
aws s3 cp "$tb" "s3://${BUCKET_NAME}/${key}" --endpoint-url "$ENDPOINT"
aws s3 cp "${tb}.sha256" "s3://${BUCKET_NAME}/${key}.sha256" --endpoint-url "$ENDPOINT"
rm -f "$tb" "${tb}.sha256"
done
echo "Base seed/verify complete for proot-distro-v${PD_VERSION}."
bake:
needs: seed-base # the static base must be present on R2 before we publish artifacts
name: RootFS ${{ matrix.arch }}
# Native arm64 runner (the builder uses native proot, not QEMU). Adjust the label
# if the org uses a different arm64 runner (e.g. a self-hosted one).
runs-on: ubuntu-24.04-arm
timeout-minutes: 330 # under the 6h hard cap; all-tier per node is ~1.5h
strategy:
fail-fast: false # one arch must not cancel the other
max-parallel: 2 # one node per arch, both at once
matrix:
arch: [arm64-v8a, armeabi-v7a]
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Ensure AWS CLI is available
run: aws --version || { sudo apt-get update && sudo apt-get install -y awscli; }
- name: Build all tiers for ${{ matrix.arch }}
run: |
TAG_ARG=""
[ -n "${{ inputs.binaries_tag }}" ] && TAG_ARG="--binaries-tag ${{ inputs.binaries_tag }}"
sudo bash ./tools/rootfs-builder/build-iiab-rootfs.sh \
--all-tier --arch "${{ matrix.arch }}" \
$TAG_ARG \
--publish-url "$ROOTFS_PUBLIC_BASE" --reset-mirrors
# the builder runs as root; hand dist/ back to the runner so the upload step can read it
sudo chown -R "$(id -u):$(id -g)" dist
- name: Publish to R2 + prune (keep newest 7 per tier/arch)
env:
AWS_ACCESS_KEY_ID: ${{ vars.CLOUDFLARE_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.CLOUDFLARE_SECRET_ACCESS_KEY }}
run: |
set -euo pipefail
ARCH="${{ matrix.arch }}"
KEEP=7 # keep newest 7 batches per (tier,arch); heavy uploads, ~100GB at 7
ENDPOINT="https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com"
PFX="${R2_KEY_PREFIX:-}" # '' or 'rootfs/'
cd dist
# 1) Big artifacts FIRST (data before any pointer that resolves to them).
shopt -s nullglob
for f in k2go_*.tar.gz; do
echo ">> upload artifact $f"
aws s3 cp "$f" "s3://${BUCKET_NAME}/${PFX}${f}" --endpoint-url "$ENDPOINT"
done
# 2) Everything ELSE in dist/ EXCEPT the stable pointers. Publish the WHOLE dist/,
# nothing is dropped: per-artifact sidecars (.meta4/.sha256/.installed/.torrent),
# the build-<tier>-<arch>.log installer logs (consumed to compute catalogs), and
# PUBLISH_QUEUE.tsv. Tarballs are already up (step 1); the stable latest_* pointers
# are held for step 3 so a client never reads a pointer to a half-uploaded file.
aws s3 cp . "s3://${BUCKET_NAME}/${PFX}" --recursive \
--exclude "*.tar.gz" \
--exclude "latest_*.meta4" --exclude "latest_*.installed" \
--endpoint-url "$ENDPOINT"
# 3) Flip the stable pointers LAST (they overwrite; only after the data is fully up,
# so a client reading the new latest_*.meta4 never points at a half-uploaded tarball).
for f in latest_*.meta4 latest_*.installed; do
echo ">> pointer $f"
aws s3 cp "$f" "s3://${BUCKET_NAME}/${PFX}${f}" --endpoint-url "$ENDPOINT"
done
# 4) Prune: keep only the newest $KEEP batches per (tier,arch); delete older + sidecars.
# Only the heavy dated tarballs accumulate (they carry <date>_<sha>); logs and the
# TSV overwrite in place, so they never pile up. The prefix filter matches k2go_*
# only, so proot-distro-v*/ is never touched.
for meta in latest_*_"${ARCH}".meta4; do
base="${meta%.meta4}"; te="${base#latest_}"; tier="${te%_${ARCH}}"
mapfile -t keys < <(aws s3api list-objects-v2 --bucket "$BUCKET_NAME" \
--prefix "${PFX}k2go_" --endpoint-url "$ENDPOINT" \
--query "sort_by(Contents,&LastModified)[?contains(Key,'_${tier}_') && ends_with(Key,'_${ARCH}.tar.gz')].Key" \
--output text | tr '\t' '\n' | sed '/^$/d')
n=${#keys[@]}
if (( n > KEEP )); then
for key in "${keys[@]:0:n-KEEP}"; do
echo ">> prune $key (+ sidecars)"
for ext in '' .meta4 .sha256 .installed .torrent; do
aws s3 rm "s3://${BUCKET_NAME}/${key}${ext}" --endpoint-url "$ENDPOINT" || true
done
done
fi
done