Compare commits

...
Author SHA1 Message Date
yonlu d414fdb2b6 docs: a CI-only change is ci:, not fix(ci):
CI / check (push) Skipped
CI / e2e (push) Skipped
CI / check (pull_request) Successful in 2m25s
CI / e2e (pull_request) Successful in 6m24s
The commit-analyzer reads the type and ignores the scope, so `fix` is a
patch whatever sits in the brackets. Two commits touching nothing but
.gitea/workflows/unclaim.yml were written `fix(ci):` and cut v0.2.1 and
v0.2.2 -- real releases, published to Arch, Homebrew and the APK
registry, containing no user-facing change.

CLAUDE.md already warned that a mistyped feat ships a minor version.
That was not enough, because this was not a mistyped type: `fix` was
chosen deliberately, in the belief that the (ci) scope qualified it.

The version bump is the small half, which is why this gets a paragraph
rather than a clause. A merge to main starts two workflows; if
release.yml then pushes a tag, that tag push starts four more --
arch-package, homebrew-formula, android-apk and desktop-assets -- on a
runner with capacity 1, where the APK build alone is tens of minutes
and publishes a signed artifact to a public registry. So a mistyped
type is six workflow runs, not an odd-looking changelog.

`make release-dry` answers this before the merge instead of after, and
is cheaper than any one of those runs.

The two releases are staying: they are already published, and a version
that vanishes is worse for whoever pulled it than one that turns out to
be empty.

Closes #111
2026-08-18 19:40:43 -04:00
yonlu bf0a53e64c Merge pull request 'Give the unclaim step a CA bundle' (#110) from fix/unclaim-ca-certs into main
Release / release (push) Successful in 31s
CI / e2e (push) Successful in 6m3s
CI / check (push) Successful in 2m22s
Build & publish the Android APK / apk (push) Successful in 1m24s
Build & publish Arch package / arch-package (push) Successful in 2m29s
Attach the desktop build to the release / linux (push) Successful in 52s
Sync Homebrew formula / sync-formula (push) Successful in 6s
Reviewed-on: #110
2026-08-18 23:30:31 +00:00
yonlu 7be4a02e31 fix(ci): give the unclaim step a CA bundle
CI / check (push) Skipped
CI / e2e (push) Skipped
CI / check (pull_request) Successful in 2m27s
CI / e2e (pull_request) Successful in 6m14s
Second defect in the same workflow. The shell fix took -- the step ran
under `bash --noprofile --norc -e -o pipefail` -- and got one layer
further before failing:

  curl: (77) error setting certificate file: /etc/ssl/certs/ca-certificates.crt

ubuntu:24.04 ships no CA bundle, and --no-install-recommends skips the
ca-certificates that curl recommends, so curl came up unable to verify
TLS against our own Gitea.

This was avoidable by reading the repo rather than reasoning about it:
ci.yml (twice), desktop-assets.yml, android-apk.yml and release.yml all
spell out `ca-certificates curl ... jq` for exactly this reason. The
convention was written down five times already.

Validated in the real image this time rather than by extracting the
script and running it on the host, which is what missed this: the step
now succeeds inside `docker run ubuntu:24.04` against a scratch issue --
label present, 204, label gone -- and the previous version reproduces
`curl: (77)` in the same image. Both checked, then the scratch issue was
deleted.

The DELETE also keeps its response body now and prints it on a non-204.
Whether the automatic token carries issue-write scope is still unproven,
because both failures happened before the API call, and "403" without
Gitea's own sentence would cost another merge to interpret.

Closes #102
2026-08-18 19:08:50 -04:00
yonlu ad9c25a5a2 Merge pull request 'Run the unclaim step under bash' (#108) from fix/unclaim-shell into main
Release / release (push) Successful in 32s
CI / e2e (push) Successful in 6m10s
CI / check (push) Successful in 2m23s
Build & publish the Android APK / apk (push) Successful in 1m23s
Build & publish Arch package / arch-package (push) Successful in 2m37s
Attach the desktop build to the release / linux (push) Successful in 52s
Sync Homebrew formula / sync-formula (push) Successful in 7s
Reviewed-on: #108
2026-08-18 23:05:38 +00:00
yonlu a83a127e31 fix(ci): run the unclaim step under bash
CI / check (push) Skipped
CI / e2e (push) Skipped
CI / check (pull_request) Successful in 2m28s
CI / e2e (pull_request) Successful in 6m14s
The workflow shipped in #103 and failed on every close, on its second
line, before reaching the API:

  shell: sh -e {0}
  /var/run/act/workflow/0.sh: 2: set: Illegal option -o pipefail

Inside `container:` the act runner selects sh, not bash, and
`set -o pipefail` is a bashism. homebrew-formula.yml carries the same
line without trouble because it runs with no container, on the host
image where bash is the default -- so "another workflow does it" was
not the evidence it looked like, and the comment now says so where the
next person will read it.

pipefail is kept rather than dropped for POSIX's sake: the lookup is
`curl -sSf ... | jq`, so without it an API error yields empty output,
an empty label id, and a cheerful "nothing to do" on every close. A
silent no-op is the one outcome worse than a failing job here.

Validated end to end against scratch issues rather than by reading it:
with the label present the step returns 204 and the label is gone, and
against an issue that never carried it the step also returns 204 and
exits 0 -- which is what makes it safe to run on every close rather
than only claimed ones.

Still untested: whether secrets.GITEA_TOKEN carries issue-write scope.
The old run never got far enough to find out. If it 403s, the fix is
one line -- secrets.PACKAGE_TOKEN, which is a user PAT.

Closes #102
2026-08-18 18:54:39 -04:00
yonlu e049a71458 Merge pull request 'Drop the claim label when an issue closes' (#103) from ci/unclaim-on-close into main
CI / check (push) Successful in 2m30s
Release / release (push) Successful in 31s
CI / e2e (push) Successful in 6m6s
Reviewed-on: #103
2026-08-18 21:57:11 +00:00
2 changed files with 40 additions and 2 deletions
+23 -2
View File
@@ -42,6 +42,13 @@ jobs:
steps:
- name: Drop the claim label
# **Inside a container the act runner selects `sh`, not bash**, so
# `set -o pipefail` fails the job on its second line with "Illegal
# option" and the step never reaches the API. `homebrew-formula.yml`
# carries the same `set -euo pipefail` without trouble because it
# runs with **no container**, on the host image where bash is the
# default — so "another workflow does it" is not evidence here.
shell: bash
env:
# The automatic Actions token, as release.yml uses for the
# floor tag. It needs no more than write access to this repo.
@@ -51,8 +58,15 @@ jobs:
run: |
set -euo pipefail
# `ca-certificates` is named because `--no-install-recommends`
# skips it, and `ubuntu:24.04` ships no CA bundle of its own —
# so curl comes up unable to verify TLS against our own Gitea
# and fails with "error setting certificate file" (exit 77).
# Every other containerised workflow here spells it out for the
# same reason; this one did not, and cost a release cycle.
apt-get update -qq
apt-get install -y -qq --no-install-recommends curl jq >/dev/null
apt-get install -y -qq --no-install-recommends \
ca-certificates curl jq >/dev/null
label_id=$(
curl -sSf -H "Authorization: token $TOKEN" "$API/labels?limit=100" |
@@ -71,8 +85,14 @@ jobs:
# label answers the same as one that did, which is what makes
# this safe to run on *every* close rather than only the ones
# that were claimed.
# The body is captured, not discarded, so a refusal is
# diagnosable from this log alone. Whether the automatic
# token carries issue-write scope is still unproven, and
# "DELETE returned 403" without Gitea's own sentence costs
# another merge to find out which of the two it is.
body=$(mktemp)
code=$(
curl -sS -o /dev/null -w '%{http_code}' -X DELETE \
curl -sS -o "$body" -w '%{http_code}' -X DELETE \
-H "Authorization: token $TOKEN" \
"$API/issues/$ISSUE/labels/$label_id"
)
@@ -81,6 +101,7 @@ jobs:
204) echo "unclaim: #$ISSUE is closed and unclaimed" ;;
*)
echo "unclaim: DELETE returned $code for #$ISSUE" >&2
cat "$body" >&2
exit 1
;;
esac
+17
View File
@@ -2138,6 +2138,23 @@ Pre-commit hooks verify generated code is fresh — always run `make generate` a
mistyped `feat` ships a minor version. `make release-dry` answers "what
would this merge release" without pushing.
**The analyzer reads the type and ignores the scope, so a CI-only change
is `ci:` and never `fix(ci):`.** The scope is decoration; `fix` is a
patch whatever is in the brackets. Two commits touching nothing but
`.gitea/workflows/unclaim.yml` were written `fix(ci):` and cut `v0.2.1`
and `v0.2.2` — real releases, published to Arch, Homebrew and the APK
registry, containing no user-facing change. They were left in place
rather than deleted, because a version that vanishes is worse for
whoever pulled it than one that turns out to be empty.
**The blast radius is bigger than the version number**, which is what
makes this worth a paragraph. A merge to `main` starts two workflows;
if `release.yml` then pushes a tag, that tag push starts **four more**
(`arch-package`, `homebrew-formula`, `android-apk`, `desktop-assets`) —
on a runner with capacity 1, where the APK build alone is tens of
minutes. `make release-dry` before merging is how you find out, and it
is cheaper than every one of those.
**`@semantic-release/github` is not in that config and must not be.**
Gitea's API is `/api/v1` and is not GitHub's surface, so
`@semantic-release/exec` calls `scripts/gitea-release.sh` instead — one