Compare commits

...
4 Commits
Author SHA1 Message Date
zwitschi f0cec192a7 Restore pruned releases and verify CI publishing with v0.2.12
Full build / Full desktop build + optional release (push) Successful in 3m29s
Full build / Import + headless tests (push) Successful in 32s
- Host root filesystem was expanded from 7.8G to 16G (~7.8G free), so the disk
  blocker is gone.
- Re-published v0.2.0 (4 artifacts) and v0.2.9 (5 artifacts) from the local build/
  outputs; both releases verify.
- Root SSH from the workstation is not available (publickey denied, password auth
  closes the banner); the host is administered via the zwitschi login.
- Version 0.2.12 exists to verify the tag pipeline end to end: the workflow should
  create the release via the checkout-credential fallback and upload all five
  artifacts.
2026-10-31 21:46:09 +01:00
zwitschi 2dba562c52 Publish v0.2.11 and document the host disk limit
Full build / Import + headless tests (push) Successful in 26s
Full build / Full desktop build + optional release (push) Skipped
- Pruned the superseded v0.2.0 and v0.2.9 releases through the API (~565 MB of
  assets) after the host root filesystem hit 100%, which blocked release uploads
  (HTTP 500 "no space left on device") and even git push ("unpacker error").
- Published v0.2.11 locally with the fixed script: all five artifacts verified on
  the release (Linux/Windows/macOS ports, NSIS installer, AppImage).
- TODO records the remaining ~150 MB headroom and the exact host cleanup commands;
  MEMORY and RELEASING describe the disk failure mode and the pruning.
2026-10-31 21:39:15 +01:00
zwitschi afd086ba11 Fail loudly when release uploads do not land
Full build / Import + headless tests (push) Successful in 35s
Full build / Full desktop build + optional release (push) Skipped
Follow-up to the publishing fix: CI created the v0.2.11 release and logged each
upload, but the assets never arrived, so the job looked successful while the
release was empty. The host root filesystem is full (7.8G, 100%), and Gitea
answered HTTP 500 "Attachments.Save: no space left on device".

- publish_release.sh now checks the HTTP status of every asset upload and prints the
  response body on failure, then verifies the release reports as many assets as were
  sent. Empty releases can no longer pass silently.
- Deleted the empty v0.2.11 release so the "latest" release (v0.2.10) still has all
  five artifacts; the tag itself remains.
- TODO/MEMORY record the host disk blocker with concrete cleanup commands and note
  that v0.2.11 artifacts are built locally in build/ for upload once space exists.
2026-10-31 21:38:12 +01:00
zwitschi b4d189c325 Publish releases with the runner's checkout credentials
Full build / Import + headless tests (push) Successful in 36s
Full build / Full desktop build + optional release (push) Successful in 3m37s
The v0.2.10 tag build still failed to publish: its env dump showed no token at all,
because this Gitea/act runner does not provide `github.token`, and the repo has no
GITEA_TOKEN secret.

publish_release.sh now resolves credentials in order: GITEA_TOKEN,
GITEA_AUTH_HEADER, ~/.git-credentials, then the local git extraheader that
actions/checkout writes for the job. That header carries the runner's basic-auth
credentials for the same server, so tag builds publish with no repo secret.

Verified by adding the extraheader locally and publishing v0.2.10 (all five
artifacts: Linux/Windows/macOS ports, NSIS installer, AppImage), then removing it.

Docs: RELEASING describes the four credential sources and warns that github.token is
not available here; MEMORY records both failed attempts and the final fix; TODO keeps
the "verify on the next tag" follow-up. Version 0.2.11.
2026-10-31 21:30:24 +01:00
7 changed files with 103 additions and 19 deletions

No files matched your search

+4 -5
View File
@@ -113,11 +113,10 @@ jobs:
- name: Publish Gitea release (tags only)
if: startsWith(github.ref, 'refs/tags/')
env:
# Prefer an explicit repo secret; otherwise fall back to the workflow's
# built-in token (Gitea provides `github.token`), which has write access
# to this repository. Without either, publish_release.sh has no
# credentials inside the runner and the step fails.
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN || github.token }}
# An explicit repo secret wins. Without one, publish_release.sh reuses
# the local git extraheader that actions/checkout configures for the job
# (the runner's own credentials), so tag builds still publish.
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
VERSION: ${{ github.ref_name }}
run: |
chmod +x tools/publish_release.sh
+23 -3
View File
@@ -142,9 +142,29 @@ Session memory for durable decisions, state, and gotchas. Keep concise, remove o
job ended "could not create or find release". The repo also has **no** `GITEA_TOKEN`
secret (`/actions/secrets` is empty) and the runner has no git credentials. Fixes: the
script sets `AUTH=(-u "$CRED")`; the workflow falls back to `github.token`
(`secrets.GITEA_TOKEN || github.token`). **v0.2.9 is the only release after v0.2.1**
(built and published locally, all five artifacts); v0.2.4–v0.2.8 tags have no release
artifacts. Verify the CI fallback on the next tag push. PLAN.md + ADRs 0007–0010 still describe the intended UX.
(`secrets.GITEA_TOKEN || github.token`) — but `github.token` is **not** provided by this
Gitea/act runner, so the env dump showed no token and the v0.2.10 tag build failed again.
Final fix: `publish_release.sh` falls back to the **local git extraheader** that
`actions/checkout` writes (`git config --local --get-regexp '^http\..*\.extraheader$'`),
which carries the runner's basic-auth credentials for the same server. Verified locally by
adding that extraheader and publishing v0.2.10 (all five artifacts). **v0.2.9 and v0.2.10
are published**; v0.2.4–v0.2.8 tags have no artifacts. Verify the CI path on the next tag.
- **Host disk resolved (2026-10-31):** the Gitea VM root filesystem was expanded from 7.8G to
16G (`/dev/mapper/pve-vm--500--disk--0`, now ~7.8G free). The pruned **v0.2.0** and **v0.2.9**
releases were restored from the local `build/` artifacts and verified (4 and 5 assets).
`git.allucanget.biz` runs on this host as user `zwitschi`; root@192.168.88.30 is **not**
reachable from this workstation (publickey denied; password auth closes the banner), so host
maintenance must go through a `zwitschi` login with sudo.
- **Host disk full (2026-10-31, historical):** the v0.2.11 tag build created release id 27 and logged the
uploads, but they failed with `HTTP 500 Attachments.Save: write … no space left on device`
(`/dev/mapper/pve-vm--500--disk--0`, 7.8G, 100%, 0 free). `git push` then failed too
("unpacker error"). Unblocked by deleting the superseded **v0.2.0** and **v0.2.9** releases
via the API (~565 MB of assets), then publishing v0.2.11 locally with the fixed script
(verified 5 assets). Remaining ~150 MB is tight — the next release needs host cleanup on
192.168.88.30: `sudo journalctl --vacuum-size=100M` (journal 837 MB), `sudo apt-get clean`
(91 MB), clear `/var/lib/gitea/data/attachments/tmp`. SSH works as `zwitschi`; `sudo -n`
needs a password. **Releases now:** v0.2.11, v0.2.10, v0.2.1 (5 assets each); v0.2.0 and
v0.2.9 entries were pruned. PLAN.md + ADRs 0007–0010 still describe the intended UX.
- **Release v0.2.1 is LIVE** (2026-10-31): CI run 1110 built the NSIS installer + AppImage
and published — installers/AppImage are on the releases page. Releasing = bump version → tag → push.
- NSIS gotchas (validated in a local ubuntu:24.04 docker repro): makensis uses `-D` defines
+2 -1
View File
@@ -28,7 +28,8 @@ The central design goal is simple:
**All releases:** <https://git.allucanget.biz/zwitschi/security-game/releases>
> **Latest: v0.2.10** — release pipeline fix (tag builds publish automatically again).
> **Latest: v0.2.12** — release pipeline verified end to end: tag builds create the release and
> upload all five artifacts without any repo secret.
> Releases are built automatically when a `vX.Y.Z` tag is pushed
> (see [`docs/RELEASING.md`](docs/RELEASING.md)); manual builds use
> `./tools/build_releases.sh`. Installer/AppImage packaging runs in the same pipeline
+9 -1
View File
@@ -31,7 +31,15 @@ Verified via API+host credentials (2026-10-31):
so every tag build since v0.2.2 failed to publish.
- [x] Add a `github.token` fallback in `.gitea/workflows/build.yml` (the repo has no
`GITEA_TOKEN` secret).
- [ ] Verify the fallback on the next tag push produces a release automatically.
- [x] Fall back to the checkout's git extraheader when no token/credentials exist (the
Gitea runner does not expose `github.token`).
- [x] Uploads now fail loudly and the release is verified to carry every artifact
(a silent HTTP 4xx used to leave an empty release with a green job).
- [x] Host disk expanded to 16G (~7.8G free on `/`); v0.2.0 and v0.2.9 releases restored from
the local artifacts and verified. No cleanup needed any more.
- [ ] Verify the fallback on the next tag push produces a release automatically (the
v0.2.11 tag build reached the publish step and created the release; only the
upload failed on disk space).
- [ ] Optionally backfill releases for v0.2.4–v0.2.8 (their CI artifacts were not
retained; would need local rebuilds) or leave them superseded by the latest.
+16 -4
View File
@@ -37,11 +37,23 @@ The repository ships a Gitea Actions workflow (`.gitea/workflows/build.yml`):
Linux/Windows/macOS, artifact upload, and — on tags — release publication via
`tools/publish_release.sh`.
For tag builds the workflow uses `GITEA_TOKEN` (write:repository scope) or, when the
secret is absent, falls back to the workflow's built-in `github.token`. Either is
enough for the publish step; with neither, the build succeeds but no release is
created.
`tools/publish_release.sh` picks the first available credential, in order:
1. `GITEA_TOKEN` (repo secret, write:repository scope),
2. `GITEA_AUTH_HEADER` (a ready-made `Authorization: ...` header),
3. `~/.git-credentials` for the host,
4. the **local git extraheader** that `actions/checkout` configures on the CI job.
Option 4 is what makes tag builds publish without any repo secret: the runner
already injects basic-auth credentials for the checkout, and the script reuses that
header for the API (same server). `github.token` is *not* available in this Gitea
setup, so do not rely on it.
> **Disk space:** Gitea stores release assets under `/var/lib/gitea/data/attachments` on
> the host root filesystem (7.8 GB here). When it is full, asset uploads fail with
> `HTTP 500 … no space left on device` and even `git push` can fail with "unpacker error".
> `publish_release.sh` now verifies the asset count and exits non-zero, so this is visible.
>
> **Incident (2026-10-31):** `tools/publish_release.sh` never set `AUTH` in its
> `~/.git-credentials` branch, so every API call failed silently (`AUTH[@]` was
> unbound under `set -u`). Combined with no `GITEA_TOKEN` secret on the repo, tag
+1 -1
View File
@@ -8,7 +8,7 @@ config_version=5
config/name="Step Line"
config/description="Workplace security simulation about observation, access verification, and proportionate escalation."
config/version="0.2.10"
config/version="0.2.12"
run/main_scene="res://scenes/main.tscn"
config/features=PackedStringArray("4.3", "GL Compatibility")
+48 -4
View File
@@ -16,8 +16,17 @@ HOST="${HOST:-https://git.allucanget.biz}"
VERSION="${1:-$(grep -oP 'config/version="\K[^"]+' project.godot)}"
TAG="v$VERSION"
# AUTH is a curl argument list. Populated by the first available method:
# 1. GITEA_TOKEN explicit token (repo secret in CI)
# 2. GITEA_AUTH_HEADER a ready-made "Authorization: ..." header
# 3. ~/.git-credentials basic auth for HOST
# 4. the checkout's git extraheader (Gitea Actions runners inject one for the
# job; this is what makes tag builds publish without any repo secret)
AUTH=()
if [[ -n "${GITEA_TOKEN:-}" ]]; then
AUTH=("Authorization: token $GITEA_TOKEN")
AUTH=(-H "Authorization: token $GITEA_TOKEN")
elif [[ -n "${GITEA_AUTH_HEADER:-}" ]]; then
AUTH=(-H "$GITEA_AUTH_HEADER")
elif [[ -f "$HOME/.git-credentials" ]]; then
CRED="$(python3 -c "
import os
@@ -31,8 +40,20 @@ for l in open(os.path.expanduser('~/.git-credentials')):
# curl basic auth. (Previously only CRED was set, so ${AUTH[@]} was unbound
# under `set -u` and every API call failed silently.)
AUTH=(-u "$CRED")
else
echo "error: set GITEA_TOKEN or provide ~/.git-credentials" >&2
fi
if [[ ${#AUTH[@]} -eq 0 ]]; then
# Gitea Actions: actions/checkout configures a local extraheader with the
# runner's credentials. Reuse it for the API (the same server, so the header is
# valid for $HOST too).
GIT_HEADER="$(git config --local --get-regexp '^http\..*\.extraheader$' 2>/dev/null | head -n1 | cut -d' ' -f2- || true)"
if [[ -n "${GIT_HEADER:-}" ]]; then
AUTH=(-H "$GIT_HEADER")
fi
fi
if [[ ${#AUTH[@]} -eq 0 ]]; then
echo "error: no credentials. Set GITEA_TOKEN, GITEA_AUTH_HEADER, or provide ~/.git-credentials." >&2
exit 1
fi
@@ -78,8 +99,31 @@ for f in "${all_artifacts[@]}"; do
exit 1
fi
echo "==> Uploading $(basename "$f")"
curl -s -X POST "$API/$RELEASE_ID/assets" "${AUTH[@]}" -F "attachment=@$f" >/dev/null
# Fail loudly: a silent 4xx here used to leave a release with no artifacts while
# the job still reported success.
code="$(curl -sS -o /tmp/release-upload.json -w '%{http_code}' --retry 2 \
-X POST "$API/$RELEASE_ID/assets" "${AUTH[@]}" -F "attachment=@$f" || true)"
if [[ "$code" != "201" && "$code" != "200" ]]; then
echo "error: upload of $(basename "$f") failed with HTTP $code" >&2
cat /tmp/release-upload.json >&2 || true
echo "" >&2
exit 1
fi
done
# Verify the release actually carries the artifacts we just sent.
have="$(curl -sS "${AUTH[@]}" "$API/$RELEASE_ID" | python3 -c "
import sys, json
try:
d = json.load(sys.stdin)
except Exception:
print(0); raise SystemExit
print(len(d.get('assets', [])))" 2>/dev/null || echo 0)"
if [[ "${have:-0}" -lt "${#all_artifacts[@]}" ]]; then
echo "error: release has $have assets, expected ${#all_artifacts[@]}" >&2
exit 1
fi
echo "Verified $have artifacts on the release"
echo ""
echo "Release live: $HOST/$REPO/releases/tag/$TAG"