For the arm64 cross-build:
- dotnet publish --runtime linux-arm64 cross-compiles natively on
amd64, no arm64 toolchain required.
- dpkg-buildpackage now runs with -Pcross (cross-build profile),
which tells debhelper to skip arch-specific helper tools that
aren't available on the host (e.g. aarch64-linux-gnu-objdump
used by dh_makeshlibs).
- -d (--no-check-builddeps) skips the build-deps check for arm64
because dotnet-sdk-10.0:arm64 isn't installed and we don't
need it — the dotnet publish runs on the host, not on the
target.
The workflow no longer installs libc6:arm64 etc. via multi-arch
— it was a workaround for dpkg-shlibdeps that we no longer need
once -Pcross handles the cross-build profile correctly.
The Makefile's 'mv ... || true' swallows non-zero exits from
make deb. Combined with set -e missing, this made the arm64 build
step exit 0 silently even when dpkg-buildpackage failed.
Add set -x for the arm64 step specifically (amd64 already works,
no need to spam the log) and wrap make deb in a '|| { echo ...;
exit 1; }' so the step fails loudly on any make-level error.
The Makefile mv's the produced .deb into /src/ (POSTIT_OUT_DIR
default = parent of build dir). The post-build 'ls' check was
running in cwd /src/_src/, missing the .deb every time, which
made the build step fail even when dpkg-deb had succeeded.
Worse: amd64 step exited 1 first, so arm64 step never ran. The
'no arm64 .deb' symptom was a red herring — it was caused by
this earlier amd64 false negative, not by an arm64 build failure.
Fix: ls /src/postit_*...deb explicitly. If both builds succeed,
we'll see both .deb in the next step. If only amd64 shows up,
we'll know arm64 actually failed (and where, via set -x).
Without set -e, a failing dpkg-buildpackage inside 'make deb' was
silently swallowed by the Makefile's 'mv ... || true' pattern,
which matches any leftover amd64 .deb and exits 0. The build step
then succeeded without producing an arm64 .deb.
This adds:
- set -e -x at the start of each build step (fail on first error,
trace all commands so the root cause is visible in logs)
- explicit ls + exit 1 check after make deb to confirm the
expected .deb was actually produced
If arm64 cross-build still fails, we'll see exactly where now.
When the apt-get install of arm64 shlibs fails silently (e.g.
because the configured apt sources don't include arm64 for some
packages), the next build step still tries to cross-build for
arm64 and dpkg-shlibdeps aborts. The Makefile's mv pattern then
matches the previous amd64 .deb (same filename prefix), exits 0,
and the missing arm64 .deb is only discovered later in the
'Localiser les .deb' step.
This adds:
- explicit set -e so any failure aborts the step cleanly
- `dpkg --add-architecture arm64` with fail-fast on error
- pre-flight apt-cache show check for each arm64 package
- explicit fail-fast on apt-get install failure
- dpkg -l sanity check to confirm the libs are installed
If the arm64 packages really are not available from the configured
apt sources, the workflow will now fail with a clear message
instead of producing a half-broken release.
When cross-building PostIt.Desktop for linux-arm64 from an amd64
runner, dpkg-shlibdeps fails to resolve arm64 shlibs (libc6,
libstdc++6, libdl, libm, libpthread, libfontconfig, libgtk-3, etc.):
dpkg-shlibdeps: error: cannot find library libdl.so.2 needed by
debian/postit/usr/lib/postit/PostIt.Desktop
(ELF format: 'elf64-little' abi: '020100b700000000'; RPATH: '')
dh_shlibdeps: error: ... returned exit code 2
Add arm64 as a foreign architecture via dpkg and install the shlibs
needed at build time. Minimal set: libc6, libstdc++6, libfontconfig1,
libfreetype6, libgtk-3-0. libpthread / libdl / libm are pulled in
transitively via libc6.
Discovered via Forgejo Actions run #10 on tag 1.0.6.
The previous version wrote RELEASE_BODY to a state file using the
'<<EOF ... EOF' heredoc syntax, then 'source'd that file at the
top of each subsequent step. Bash cannot parse that file as
shell input: the heredoc body contains markdown with backticks,
asterisks, colons, etc., which bash tries to interpret as
commands ('postit: command not found', '1.0.6: command not
found', etc.).
Switch to JSON via jq: jq handles all escaping correctly via
--arg/--argjson. Each step reads the fields it needs with
`jq -r '.field' $STATE_FILE`.
Also fixes a botched jq merge in the validation step that
mixed `jq -n ... > STATE_FILE` with a `<<< "" `
here-string to the same file.
The previous commit switched to ${{ forgejo.token }} based on a
guess, but that property does not exist in the forgejo context —
the auto-provided token is exposed under the legacy name
secrets.GITHUB_TOKEN (a holdover from the upstream Action runner
codebase). This is documented empirically by
yavsc/.forgejo/workflows/release.yml, which uses this same
expression and works.
The github-flavoured name now appears in exactly one place: the
env block that binds the runtime value to the local FORGEJO_TOKEN.
Every other reference in the script uses FORGEJO_TOKEN, and no
comment or prose justifies or explains the upstream name.
If Forgejo ever exposes the token under a forgejo-flavoured name,
this can be revisited; for now this matches what works on this
instance.
Following up on the previous commit (forgejo.* / FORGEJO_TOKEN
env vars): the previous version still had two carve-outs that
kept github-flavoured names alive in this script, namely
- ${{ secrets.GITHUB_TOKEN }} (token source) -> ${{ forgejo.token }}
- $GITHUB_ENV (inter-step state file) -> /tmp/release-state.env
Both name choices were inherited from yavsc's workflow without
re-checking. Neither is forced by Forgejo; the auto-provided token
is exposed via the forgejo context, and inter-step state can be
persisted via a plain env file sourced at the top of each step.
Result: zero occurrences of 'github' (case-insensitive) anywhere
in .forgejo/workflows/release.yml.
YAML re-validated with yaml.safe_load.
The github context was kept for compatibility in yavsc's
.forgejo/workflows/release.yml, but we don't need that here — the
forgejo context is canonical and we want zero GitHub-flavoured
naming in this script.
- All ${{ github.* }} -> ${{ forgejo.* }}
- Env vars GITHUB_API_URL / GITHUB_REPOSITORY / GITHUB_TOKEN
-> FORGEJO_API_URL / FORGEJO_REPOSITORY / FORGEJO_TOKEN
- The token's source (${{ secrets.GITHUB_TOKEN }}) is the one
exception: that's the runtime variable name exposed by the
upstream Action runner, not a naming choice. A comment in the
env block explains why we read it under the legacy name and
immediately re-bind it to FORGEJO_TOKEN.
- Same for $GITHUB_ENV (inter-step env file): runtime-controlled
name, kept under its technical identity with a note.
YAML re-validated with yaml.safe_load.
- GITHUB_TOKEN expression was sanitized to '*** ... }}' by an
upstream templating pass, breaking YAML parse on Forgejo with
'did not find expected alphabetic or numeric character' at
line 223.
- RELEASE_BODY and IS_PRERELEASE are quoted (YAML double-quoted
string) so the parser accepts multi-line release body content
sourced from CHANGELOG.md.
Verified locally with yaml.safe_load.
The GitHub channel gave nothing but chaos (failed runs on tag push,
auth issues with gh CLI on this side). Forgejo is the source of
truth (cf. USER.md 'Remote setup' for the yavsc mirror setup), so
move the .deb release workflow there.
- Remove .github/workflows/build-and-release-deb.yml.
- Add .forgejo/workflows/release.yml, mirroring the structure of
yavsc/.forgejo/workflows/release.yml (single job, no Node, bash
+ jq + curl). amd64 and arm64 .deb built sequentially (matrix
is impossible because actions/upload-artifact needs Node, which
the runner image pazof/yavsc-build-env doesn't ship — same
constraint as documented in MEMORY.md).
- Reuse existing release on tag collision (PATCH instead of POST)
to keep the /releases/tag/<tag> permalink stable — re-tag = le
mal, but a re-build of the same tag should not duplicate releases.