From 38ac8f1cbac16a43f3ce5406e55ccaa0602749d8 Mon Sep 17 00:00:00 2001 From: Rick Peters Date: Wed, 30 Sep 2026 11:32:25 +0200 Subject: [PATCH 1/2] feat(ci): iso-1-github-tag.yml takes the kind of build (release, dev, auto) so it always runs from master --- .github/workflows/iso-1-github-tag.yml | 19 +++++++++++++++---- AGENTS.md | 2 +- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/.github/workflows/iso-1-github-tag.yml b/.github/workflows/iso-1-github-tag.yml index 616dd73..53b53b9 100644 --- a/.github/workflows/iso-1-github-tag.yml +++ b/.github/workflows/iso-1-github-tag.yml @@ -4,13 +4,20 @@ # GitHub releases take at most 2 GB per file, the ISO is bigger, and a release on the mirror only survives its # syncs when its tag comes from here. # Name: the Steamify version the ISO gets (its newest release) plus the time (UTC, GitHub's clock when the release starts): -# master releases v--, feat/steamify a pre-release v-dev.<...>. +# kind release: v--, a real release; kind dev: a pre-release v-dev.<...>. name: ISO 1/2 · Tag and release (GitHub) -# Started by a new Steamify release (its bundle.yml, secret ISO_DISPATCH_TOKEN) or by hand (Run workflow, -# on master for a release, on feat/steamify for a test build); not by every push. +# Always run from master. Started by steamify-cachyos (its bundle.yml, secret ISO_DISPATCH_TOKEN), which says what to +# build: a version published from main -> kind release, a version on a release branch -> kind dev. Or by hand (Run +# workflow); `auto` is a release on master and a dev build on any other branch. Not started by pushes. on: workflow_dispatch: + inputs: + kind: + description: "release (a real ISO), dev (a pre-release test build) or auto (release on master, dev elsewhere)" + type: choice + options: [auto, release, dev] + default: auto permissions: contents: write @@ -33,7 +40,11 @@ jobs: steamify="$(gh release view -R theupriser/steamify-cachyos --json tagName -q .tagName)" steamify="${steamify#v}" stamp="$(date -u +%Y.%m.%d-%H%M)" - if [ "${{ github.ref }}" = refs/heads/master ]; then + kind="${{ inputs.kind }}" + if [ -z "$kind" ] || [ "$kind" = auto ]; then + if [ "${{ github.ref }}" = refs/heads/master ]; then kind=release; else kind=dev; fi + fi + if [ "$kind" = release ]; then tag="v$steamify-$stamp" pre="" name="CachyOS with Steamify Live ISO $steamify ($stamp UTC)" else tag="v$steamify-dev.$stamp" pre="--prerelease" name="CachyOS with Steamify Live ISO $steamify (test build $stamp UTC)" diff --git a/AGENTS.md b/AGENTS.md index 3f11d4c..2b390e9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -117,7 +117,7 @@ Full write-up, with every trap: `steamify-iso-release` in steamify-cachyos-dev. - `.github/workflows/iso-1-github-tag.yml` (GitHub, `workflow_dispatch` only: by hand, or started by a new Steamify release, whose `bundle.yml` needs the secret `ISO_DISPATCH_TOKEN`) names the release: the Steamify version plus GitHub's UTC time, `vX.Y.Z-[dev.]YYYY.MM.DD-HHMM`, an **annotated** tag and a release with notes and the direct - download link. `feat/steamify` makes a `dev.` pre-release, `master` a real one. + download link. The workflow always runs from `master`; its input `kind` (`release`, `dev`, or `auto`: release on master, dev on other branches) decides: `release` is a real release, `dev` a `dev.` pre-release. steamify-cachyos' bundle.yml passes it: a version published from main -> `release`, a version on a release branch -> `dev`. - `.github/workflows/iso-2-gitea-build.yml` (Gitea, `on: push: tags: v*`, skipped on GitHub) runs two jobs for that tag: build the ISO, then attach it to the mirror's release. It is not tested there (the runner is small): the VM tests run on GitHub, in steamify-cachyos `vmtest.yml`, on the newest ISO release of the mirror. The tag decides everything: Steamify version (`steamify-prepare.sh` From 588627a14d061fcd1faf58de3d477112dd682712 Mon Sep 17 00:00:00 2001 From: Rick Peters Date: Wed, 30 Sep 2026 11:35:22 +0200 Subject: [PATCH 2/2] feat(ci): ISOs are named by the day like CachyOS (steamify-cachyos--), one per day and kind, at most 10 dev and 10 real releases kept --- .github/workflows/iso-1-github-tag.yml | 35 ++++++++++++++++++++++--- .github/workflows/iso-2-gitea-build.yml | 27 +++++++++---------- AGENTS.md | 10 ++++--- archiso/profiledef.sh | 6 ++--- 4 files changed, 53 insertions(+), 25 deletions(-) diff --git a/.github/workflows/iso-1-github-tag.yml b/.github/workflows/iso-1-github-tag.yml index 53b53b9..d34ac48 100644 --- a/.github/workflows/iso-1-github-tag.yml +++ b/.github/workflows/iso-1-github-tag.yml @@ -4,7 +4,9 @@ # GitHub releases take at most 2 GB per file, the ISO is bigger, and a release on the mirror only survives its # syncs when its tag comes from here. # Name: the Steamify version the ISO gets (its newest release) plus the time (UTC, GitHub's clock when the release starts): -# kind release: v--, a real release; kind dev: a pre-release v-dev.<...>. +# kind release: v-, a real release; kind dev: a pre-release v-dev- (like CachyOS: the +# day, no time). One ISO per day and kind: the newest build of a day replaces the earlier one. At most 10 dev and 10 real +# releases are kept (the oldest go, here and, with the mirror's next sync, there). name: ISO 1/2 · Tag and release (GitHub) # Always run from master. Started by steamify-cachyos (its bundle.yml, secret ISO_DISPATCH_TOKEN), which says what to @@ -36,18 +38,19 @@ jobs: - name: Tag and release env: GH_TOKEN: ${{ github.token }} + GIT_UPRISER_TOKEN: ${{ secrets.GIT_UPRISER_TOKEN }} run: | steamify="$(gh release view -R theupriser/steamify-cachyos --json tagName -q .tagName)" steamify="${steamify#v}" - stamp="$(date -u +%Y.%m.%d-%H%M)" + stamp="$(date -u +%y%m%d)" day="$(date -u +%Y.%m.%d)" kind="${{ inputs.kind }}" if [ -z "$kind" ] || [ "$kind" = auto ]; then if [ "${{ github.ref }}" = refs/heads/master ]; then kind=release; else kind=dev; fi fi if [ "$kind" = release ]; then - tag="v$steamify-$stamp" pre="" name="CachyOS with Steamify Live ISO $steamify ($stamp UTC)" + tag="v$steamify-$stamp" pre="" name="CachyOS with Steamify Live ISO $steamify ($day UTC)" else - tag="v$steamify-dev.$stamp" pre="--prerelease" name="CachyOS with Steamify Live ISO $steamify (test build $stamp UTC)" + tag="v$steamify-dev-$stamp" pre="--prerelease" name="CachyOS with Steamify Live ISO $steamify (test build $day UTC)" fi # The ISO's file on the mirror is named after the tag (iso-2-gitea-build.yml, profiledef.sh), so its # download link is known now. @@ -61,6 +64,21 @@ jobs: Sources: [Steamify](https://github.com/theupriser/steamify-cachyos/tree/v$steamify), this repository at this tag, and the packages' sources at [CachyOS](https://github.com/CachyOS) and [Arch Linux](https://archlinux.org/packages/). $section" + # One ISO per day and kind: the newest build of a day replaces the earlier one (space), here and on the mirror. + mirror_sync() { [ -n "$GIT_UPRISER_TOKEN" ] && curl -fsS --retry 5 --retry-delay 10 --retry-all-errors -X POST -H "Authorization: token $GIT_UPRISER_TOKEN" "$GITEA_URL/api/v1/repos/$GITEA_REPO/mirror-sync" > /dev/null; } + if git ls-remote --exit-code --tags origin "refs/tags/$tag" > /dev/null 2>&1 || gh release view "$tag" > /dev/null 2>&1; then + echo "$tag exists: replacing it" + gh release delete "$tag" --cleanup-tag -y 2> /dev/null || true + git push origin ":refs/tags/$tag" 2> /dev/null || true + # The mirror has to see the tag go (it drops the tag and its release with the ISO); otherwise the new tag is only an + # update there and starts no build. + if mirror_sync; then + for _ in $(seq 36); do + [ "$(curl -s -o /dev/null -w '%{http_code}' "$GITEA_URL/api/v1/repos/$GITEA_REPO/tags/$tag")" = 404 ] && break + sleep 5 + done + fi + fi # An annotated tag: its own date, so the mirror sorts releases on the same commit in order (a # lightweight tag only has its commit's date). git config user.name "github-actions[bot]" @@ -69,6 +87,15 @@ jobs: gh release create "$tag" --verify-tag $pre --title "$name" --notes "$notes" echo "Released $tag" + # Retention (space): at most 10 dev and 10 real releases; the oldest go, release and tag. The mirror drops the + # tag with its release and ISO at its next sync (the step below). + keep=10 + for pre_flag in true false; do + gh release list -L 200 --json tagName,isPrerelease,createdAt \ + -q "[.[] | select(.isPrerelease == $pre_flag)] | sort_by(.createdAt) | reverse | .[$keep:][] | .tagName" | + while read -r old; do echo "Retention: removing $old"; gh release delete "$old" --cleanup-tag -y; done + done + # Optional: sync the mirror at once instead of at its interval (a Gitea token with repository write # access as the secret GIT_UPRISER_TOKEN). - name: Sync the Gitea mirror diff --git a/.github/workflows/iso-2-gitea-build.yml b/.github/workflows/iso-2-gitea-build.yml index f91e15a..f0ffdc5 100644 --- a/.github/workflows/iso-2-gitea-build.yml +++ b/.github/workflows/iso-2-gitea-build.yml @@ -60,13 +60,13 @@ jobs: id: tag run: | tag="${{ github.ref_name }}" - [[ "$tag" =~ ^v([0-9]+\.[0-9]+\.[0-9]+)-(dev\.)?([0-9]{4})\.([0-9]{2})\.([0-9]{2})-([0-9]{4})$ ]] || - { echo "::error::$tag is not a Steamify ISO release tag (v-[dev.]-)"; exit 1; } + [[ "$tag" =~ ^v([0-9]+\.[0-9]+\.[0-9]+)-(dev-)?([0-9]{6})$ ]] || + { echo "::error::$tag is not a Steamify ISO release tag (v-[dev-])"; exit 1; } m=("${BASH_REMATCH[@]}") { echo "STEAMIFY_VERSION=${m[1]}" - # One moment for the ISO's label (profiledef.sh) and the release's name: the tag's (UTC). - echo "STEAMIFY_BUILD_STAMP=${m[3]}${m[4]}${m[5]}_${m[6]}" + # The day (YYMMDD, UTC) for the ISO's label (profiledef.sh) and the release's name: the tag's. + echo "STEAMIFY_BUILD_STAMP=${m[3]}" echo "ISO_PRERELEASE=$([ -n "${m[2]}" ] && echo true || echo false)" # The ISO's file name: the tag without its v (profiledef.sh). echo "STEAMIFY_ISO_VERSION=${tag#v}" @@ -119,13 +119,13 @@ jobs: - name: Version and time from the tag run: | tag="${{ github.ref_name }}" - [[ "$tag" =~ ^v([0-9]+\.[0-9]+\.[0-9]+)-(dev\.)?([0-9]{4})\.([0-9]{2})\.([0-9]{2})-([0-9]{4})$ ]] || - { echo "::error::$tag is not a Steamify ISO release tag (v-[dev.]-)"; exit 1; } + [[ "$tag" =~ ^v([0-9]+\.[0-9]+\.[0-9]+)-(dev-)?([0-9]{6})$ ]] || + { echo "::error::$tag is not a Steamify ISO release tag (v-[dev-])"; exit 1; } m=("${BASH_REMATCH[@]}") { echo "STEAMIFY_VERSION=${m[1]}" - # One moment for the ISO's label (profiledef.sh) and the release's name: the tag's (UTC). - echo "STEAMIFY_BUILD_STAMP=${m[3]}${m[4]}${m[5]}_${m[6]}" + # The day (YYMMDD, UTC) for the ISO's label (profiledef.sh) and the release's name: the tag's. + echo "STEAMIFY_BUILD_STAMP=${m[3]}" echo "ISO_PRERELEASE=$([ -n "${m[2]}" ] && echo true || echo false)" # The ISO's file name: the tag without its v (profiledef.sh). echo "STEAMIFY_ISO_VERSION=${tag#v}" @@ -145,18 +145,17 @@ jobs: iso="$(ls out/desktop/*.iso | head -n 1)" [ -f "$iso" ] || { echo "::error::no ISO in out/desktop"; exit 1; } tag="${{ github.ref_name }}" b="$STEAMIFY_BUILD_STAMP" - stamp="${b:0:4}.${b:4:2}.${b:6:2}-${b:9:4}" + stamp="20${b:0:2}.${b:2:2}.${b:4:2}" if [ "$ISO_PRERELEASE" = true ]; then name="CachyOS with Steamify Live ISO $STEAMIFY_VERSION (test build $stamp UTC)" else name="CachyOS with Steamify Live ISO $STEAMIFY_VERSION ($stamp UTC)" fi - # Never overwritten: a release for this tag that already has the ISO is left alone. + # One ISO per day and kind: a release of this tag from an earlier build of the day is replaced. api="${{ github.server_url }}/api/v1/repos/${{ github.repository }}" - if curl -fsS -H "Authorization: token $TOKEN" "$api/releases/tags/$tag" -o /tmp/rel.json && - [ "$(jq '[.assets[] | select(.name | endswith(".iso"))] | length' /tmp/rel.json)" -gt 0 ]; then - echo "::warning::$tag already has its ISO and won't be overwritten." - echo "publish=false" >> "$GITHUB_OUTPUT"; exit 0 + if curl -fsS -H "Authorization: token $TOKEN" "$api/releases/tags/$tag" -o /tmp/rel.json; then + echo "Replacing the earlier release of $tag" + curl -fsS -X DELETE -H "Authorization: token $TOKEN" "$api/releases/$(jq -r .id /tmp/rel.json)" || true fi { echo "publish=true"; echo "name=$name"; } >> "$GITHUB_OUTPUT" section="$(awk '/^## CachyOS with Steamify Live ISO/ {found = 1; next} found && /^#/ {exit} found' CHANGELOG.md)" diff --git a/AGENTS.md b/AGENTS.md index 2b390e9..7224a8c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -115,14 +115,16 @@ failure, and retry the copy (it's idempotent) rather than the whole build. Full write-up, with every trap: `steamify-iso-release` in steamify-cachyos-dev. What matters here: - `.github/workflows/iso-1-github-tag.yml` (GitHub, `workflow_dispatch` only: by hand, or started by a new Steamify - release, whose `bundle.yml` needs the secret `ISO_DISPATCH_TOKEN`) names the release: the Steamify version plus - GitHub's UTC time, `vX.Y.Z-[dev.]YYYY.MM.DD-HHMM`, an **annotated** tag and a release with notes and the direct - download link. The workflow always runs from `master`; its input `kind` (`release`, `dev`, or `auto`: release on master, dev on other branches) decides: `release` is a real release, `dev` a `dev.` pre-release. steamify-cachyos' bundle.yml passes it: a version published from main -> `release`, a version on a release branch -> `dev`. + release, whose `bundle.yml` needs the secret `ISO_DISPATCH_TOKEN`) names the release like CachyOS does, by the day (GitHub's UTC + date, no time): `vX.Y.Z-YYMMDD` (real) or `vX.Y.Z-dev-YYMMDD` (dev), file `steamify-cachyos-X.Y.Z-[dev-]YYMMDD-x86_64.iso`, an **annotated** tag and a + release with notes and the direct download link. One ISO per day and kind: a second build the same day deletes + the earlier release and tag (GitHub), waits until the mirror has dropped them, then tags again. Retention: at most 10 dev and 10 real + releases are kept (space); the oldest are deleted with `gh release delete --cleanup-tag`, and the mirror drops their tags, releases and ISOs at its next sync. The workflow always runs from `master`; its input `kind` (`release`, `dev`, or `auto`: release on master, dev on other branches) decides: `release` is a real release, `dev` a `dev-` pre-release. steamify-cachyos' bundle.yml passes it: a version published from main -> `release`, a version on a release branch -> `dev`. - `.github/workflows/iso-2-gitea-build.yml` (Gitea, `on: push: tags: v*`, skipped on GitHub) runs two jobs for that tag: build the ISO, then attach it to the mirror's release. It is not tested there (the runner is small): the VM tests run on GitHub, in steamify-cachyos `vmtest.yml`, on the newest ISO release of the mirror. The tag decides everything: Steamify version (`steamify-prepare.sh` takes `STEAMIFY_VERSION`), file name (`STEAMIFY_ISO_VERSION` -> `iso_version` in `profiledef.sh`), label - (`STEAMIFY_BUILD_STAMP`), boot menu and `/etc/steammachine-iso-build`. The build reads no clock; without a + (`STEAMIFY_BUILD_STAMP`, YYMMDD), boot menu and `/etc/steammachine-iso-build`. The build reads no clock; without a tag everything says `local`. - `util-iso.sh` runs `sudo mkarchiso`: sudo drops the environment, so `--preserve-env=STEAMIFY_ISO_VERSION, STEAMIFY_BUILD_STAMP` is required (without it the ISO comes out `-local-`). diff --git a/archiso/profiledef.sh b/archiso/profiledef.sh index d862a52..cdcb62e 100644 --- a/archiso/profiledef.sh +++ b/archiso/profiledef.sh @@ -3,8 +3,8 @@ iso_name="steamify-cachyos" # The label follows the release tag, which GitHub names (iso-1-github-tag.yml): the Steamify version on the ISO -# (steamify-prepare.sh put it there) plus the tag's date and time, e.g. STEAMIFY_2_9_3_20260929_2030 (ISO 9660: -# at most 32 characters, A-Z 0-9 _). STEAMIFY_BUILD_STAMP (YYYYMMDD_HHMM, UTC) comes from the tag; the build +# (steamify-prepare.sh put it there) plus the tag's day, e.g. STEAMIFY_2_9_3_260929 (ISO 9660: +# at most 32 characters, A-Z 0-9 _). STEAMIFY_BUILD_STAMP (YYMMDD, UTC) comes from the tag; the build # reads no clock of its own. Built by hand, without a tag: LOCAL. _steamify="$(sed -n 's/^VERSION=//p' "${BASH_SOURCE[0]%/*}/airootfs/usr/local/share/steamify/steamify.sh" 2>/dev/null | head -n 1)" _stamp="${STEAMIFY_BUILD_STAMP:-LOCAL}" @@ -13,7 +13,7 @@ iso_label="${iso_label:0:32}" iso_publisher="CachyOS " iso_application="Steamify CachyOS Live (based on CachyOS)" # The file name (--x86_64.iso) is the release tag without its v (STEAMIFY_ISO_VERSION -# from the tag: steamify-cachyos-2.9.3-dev.2026.09.29-2030-x86_64.iso). Built by hand, without a tag: local. +# from the tag: steamify-cachyos-2.9.6-dev-260930-x86_64.iso). Built by hand, without a tag: local. iso_version="${STEAMIFY_ISO_VERSION:-local}" install_dir="arch" buildmodes=('iso')