When the workflow runs make deb twice in a row (amd64, then
arm64), the second rm -f ../postit_*1.0.6-1*.deb glob matched
BOTH postit_1.0.6-1_amd64.deb (produced by the first build)
and postit_1.0.6-1_arm64.deb (about to be produced). So the
second rm was erasing the first build's output before its own
make ran.
Result: postit_1.0.6-1_amd64.deb was created and then deleted
by the arm64 build, leaving only the .deb arm64 in /src/. The
'Localiser les .deb produits' step failed with amd64=''.
Scope the rm to this build's arch tag via DPKG_HOST:
rm -f ../postit_<TAG>-1_${DPKG_HOST}.deb \
../postit_<TAG>-1_${DPKG_HOST}.buildinfo \
../postit_<TAG>-1_${DPKG_HOST}.changes
DPKG_HOST is already computed in the recipe for the dpkg-
buildpackage call — we just reuse it for the rm.
crossbuild-essential-arm64 only pulls gcc-aarch64-linux-gnu +
binutils-aarch64-linux-gnu + dpkg-cross. It does NOT transitively
pull libc6:arm64 (verified on the Debian package index: the Depends
chain stops at gcc-12-aarch64-linux-gnu, which doesn't depend on
libc6:arm64). So dh_makeshlibs works (binutils is there), but
dh_shlibdeps has no arm64 shlibs cache to read from.
Add explicit runtime libs after a dpkg --add-architecture arm64 +
apt-get update:
dpkg --add-architecture arm64
apt-get update
apt-get install -y --no-install-recommends \
build-essential debhelper imagemagick librsvg2-bin \
ca-certificates crossbuild-essential-arm64 \
libc6:arm64 libstdc++6:arm64 \
libfontconfig1:arm64 libfreetype6:arm64 \
libgtk-3-0:arm64
If the arm64 packages are unavailable from the runner's
configured apt sources, this run will fail with 'Unable to
locate package libc6:arm64' — but that was a real failure
mode already explored in earlier runs (run #31, before we had
dpkg --add-architecture arm64 in place).
crossbuild-essential-arm64 transitively installs libc6:arm64 et
al. (Depends chain: crossbuild-essential-arm64 → gcc-aarch64-
linux-gnu → libc6-dev:arm64 → libc6:arm64), placing the runtime
.so files in /usr/lib/aarch64-linux-gnu/. The non-dev libs
(libc.so.6, libm.so.6, etc.) live there, not in /usr/lib/
which is what dh_shlibdeps searches by default.
dpkg-shlibdeps accepts -l<dir> for additional library search
paths. We compute the right path via dpkg-architecture -aarm64
-q DEB_HOST_MULTIARCH (which returns 'aarch64-linux-gnu'
without choking on the CC mismatch warning, since it's a pure
query), and pass /usr/lib/$HOST_MULTIARCH/ as -l. Falls back
to a literal aarch64-linux-gnu if dpkg-architecture isn't
installed (it always is on a Debian build host).
The previous two attempts (wget + dpkg-deb -x from packages.debian.org,
apt-get install libc6:arm64) were wrong approaches. The
Debian-recommended way is the crossbuild-essential-<ARCH> meta-package,
which pulls in:
- gcc-aarch64-linux-gnu / g++-aarch64-linux-gnu (cross-gcc, unused
for our managed-only build but installed for consistency)
- binutils-aarch64-linux-gnu (provides aarch64-linux-gnu-objdump
needed by dh_makeshlibs — replaces the previous binutils-only
install)
- libc6-dev:arm64, linux-libc-dev:arm64 (cross-libc for dpkg-shlibdeps
to resolve the arm64 binaries' NEEDED entries against the system
shlibs cache)
- dpkg-cross CONFIG_SITE files under /etc/dpkg-cross/
With crossbuild-essential-arm64 installed:
- dh_makeshlibs finds its aarch64-linux-gnu-objdump ✓
- dh_shlibdeps can resolve arm64 binaries against the on-host shlibs
cache ✓
- dpkg-buildpackage -Pcross,nocheck runs cleanly
The environment for the arm64 make deb is now:
CONFIG_SITE=/etc/dpkg-cross/cross-config.arm64 \
DEB_HOST_ARCH=arm64 DEB_BUILD_ARCH=amd64 \
DEB_BUILD_OPTIONS=nocheck \
make deb POSTIT_GIT_TAG= POSTIT_RUNTIME=linux-arm64
which matches the Debian cross-build recipe documented at
https://wiki.debian.org/CrossCompiling (Build without a build
environment section).
debian/rules also drops the now-unused override_dh_shlibdeps
override (the stage dir hack) — standard dh_shlibdeps now
works because the cross-libc is installed.
The earlier apt-get install libc6:arm64 ... failed because
the packages aren't listed in the runner image's configured
apt sources (dpkg-checkbuilddeps came back with
'Unmet build dependencies').
Switch to direct download from packages.debian.org, where
the URL is resolved dynamically from the package index
(no hardcoded versions — glibc patches frequently, and a
hardcoded libc6 URL would break the workflow the moment
Debian uploads a security update).
This is the same approach as sbuild/pbuilder: build a
cross-arch stage dir without installing arm64 on the host,
then point dpkg-shlibdeps at it via -l.
Verified locally: deb.debian.org returns the right
.libc6_2.36-9+deb12u14_arm64.deb URL for arm64.
dpkg-shlibdeps refuses to build if it cannot resolve ELF
NEEDED entries from the cross-arch binaries (libc.so.6,
libstdc++.so.6, etc.). The clean answer is dpkg-shlibdeps
-l<dir>, which adds extra library search paths.
We download the .deb for the libs we know the binaries
need (libc6, libstdc++6, libfontconfig1, libfreetype6,
libgtk-3-0) from deb.debian.org, extract them into
/tmp/arm64-stage/ via 'dpkg-deb -x', and pass ARM64_STAGE
through to debian/rules. override_dh_shlibdeps then
forwards -l<stage>/usr/lib/aarch64-linux-gnu/ to
dpkg-shlibdeps.
This mirrors what sbuild/pbuilder do internally: a
chroot-less cross-arch stage dir that dh_shlibdeps
resolves against. No host installation of arm64 needed
(the runner image stays slim, ~50 Mo for binutils-aarch64
and the few libs we extract).
dh_makeshlibs is invoked by debhelper during cross-builds to
produce the .shlibs cache, and it calls arch-specific objdump
to read ELF symbols of the bin we're shipping. For arm64 that
means aarch64-linux-gnu-objdump, which is provided by
binutils-aarch64-linux-gnu (~50 Mo in apt).
binutils-aarch64-linux-gnu ships the cross-binutils without
the cross-gcc; we don't need gcc-arm64 because there's no C
code in this package (the build is pure dotnet publish).
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.
For arm64 cross-build, dpkg-checkbuilddeps fails with:
Unmet build dependencies: dotnet-sdk-10.0 (>= 10.0.0)
because it looks for dotnet-sdk-10.0:arm64 (the arm64 build of
the SDK), which we don't install. But we don't need it — the
dotnet publish --runtime linux-arm64 invocation runs natively
on the amd64 host. The Build-Depends entry is only meaningful
for native amd64 builds.
-d skips the build-deps check entirely. Safe for this package:
dh_shlibdeps picks up real runtime deps from the .so files
(libfontconfig, libgtk-3, etc.), so the resulting .deb has
correct Depends: regardless.
The previous commit set up -aarm64 + DEB_HOST_ARCH=arm64
without going through dpkg-architecture. Add a comment
explaining why that's safe for this particular package:
- No C compilation (only dotnet publish --runtime linux-arm64)
- dh_strip disabled (no arch-specific objcopy needed)
- dh_shlibdeps works because we install arm64 libs via multi-arch
This is the standard pattern for mono-managed / Java / Go
Debian packages that cross-build without a C toolchain.
The case ... esac block was split across multiple recipe lines,
and make runs each line in its own shell — so dash saw 'esac'
as a stray keyword and bailed with 'unexpected end of file
(expecting ")")'. Same shell-line gotcha as the earlier
if/then/fi.
Collapse into one line: DPKG_HOST=$(case ... echo arm64 ... esac)
&& DEB_HOST_ARCH=$DPKG_HOST ... dpkg-buildpackage -a$DPKG_HOST.
All in one shell, one set of variable assignments.
The 'dpkg-architecture -c' approach is meant for proper cross
builds, but dpkg-architecture refuses to set up the env when
the CC system type doesn't match the target. For arm64 cross
builds that means CC=aarch64-linux-gnu-gcc is required. We
don't have that — we cross-publish .NET binaries with
`dotnet publish --runtime linux-arm64`, no C compilation.
So skip dpkg-architecture entirely:
- DPKG_HOST = 'arm64' or 'amd64' from a small case (no shell
substitution issues, single line)
- DEB_HOST_ARCH, DEB_BUILD_ARCH, DEB_HOST_GNU_TYPE,
DEB_BUILD_GNU_TYPE set in env directly
- -a$DPKG_HOST passed to dpkg-buildpackage, which now
builds and names the .deb for the target arch
dpkg-buildpackage doesn't actually need arm64 tools if the
rules don't use them: dh_strip is disabled (cross-strip
issues with ARM64 .so), and dh_shlibdeps works with the
multi-arch arm64 libs we install in the runner.
dpkg-architecture -aarm64 sets DEB_HOST_ARCH=arm64 but does
not propagate the env to the calling shell. The 'eval
$(dpkg-architecture $DPKG_ARCH_ARGS)' pattern tried to bridge
that but failed: the command substitution was parsed by make
before bash ever ran, with $DPKG_ARCH_ARGS still empty at make
parse time.
dpkg-architecture's -c flag runs a command with the proper env
set up: 'dpkg-architecture -aarm64 -c "..."' exports all the
DEB_* vars and then execs the command, all in one process.
The 'CC system type mismatch' warning on stderr is informational
(it complains that the C compiler isn't cross, but we don't
compile C — we only cross-publish .NET binaries). Verified
locally: 'dpkg-architecture -aarm64 -c "echo \$DEB_HOST_ARCH"'
prints 'arm64'.
The previous version had $(dpkg-architecture $DPKG_ARCH_ARGS).
make expands $(...) at parse time, BEFORE any bash command runs.
So make called 'dpkg-architecture' (no -a flag, because
$DPKG_ARCH_ARGS is empty at parse time) and substituted the
amd64 DEB_* vars into the recipe line. Bash then ran with the
amd64 vars baked in, regardless of what the runtime DPKG_ARCH_ARGS
ended up being.
Escape both dollars: $$ -> $, so make sees '$$' and passes
'$$' to bash, which sees '$(...)' and evaluates it at runtime
after DPKG_ARCH_ARGS is set on the previous command.
The '&&' between 'eval' and 'dpkg-buildpackage' was eating the
arguments to eval because make's substitution of the
$(dpkg-architecture $DPKG_ARCH_ARGS) command substitution
is visible only at runtime, not in the make pre-processing.
Bash then saw 'eval' followed by '&&' (with the substituted
command becoming a separate statement) and ran eval with no
arguments, leaving DEB_HOST_ARCH unset.
With ';', the three commands are one statement and bash
evaluates them left-to-right: assign DPKG_ARCH_ARGS, eval
the dpkg-architecture output to populate DEB_HOST_ARCH etc.,
then call dpkg-buildpackage which inherits the DEB_* vars.
Verified locally that 'eval $(dpkg-architecture -aarm64)'
sets DEB_HOST_ARCH=arm64 in the current shell.
The previous version put DPKG_ARCH_ARGS on its own logical line
and 'eval ... && dpkg-buildpackage' on the next, but each make
recipe line runs in its own shell. So when 'eval' ran, the
$DPKG_ARCH_ARGS variable from the previous shell was already
gone — 'eval' saw an empty argument and produced no DEB_HOST_ARCH
override, which made dpkg-buildpackage default to amd64.
Collapse everything onto one logical line (one shell invocation),
joining with '&&' so a failure in 'case' or 'dpkg-architecture'
short-circuits before dpkg-buildpackage runs.
make runs each recipe line in its own shell (typically /bin/sh
= dash on Debian). A multi-line case/esac block — same gotcha
as the earlier 'if/then/fi' — gets split across shells, and
the second shell sees 'esac' as a stray keyword and bails:
/bin/sh: 1: Syntax error: end of file unexpected (expecting ")")
Collapse the case into a single line with a command substitution,
then assign the result to DPKG_ARCH_ARGS. Same pattern as the
single-line if/then/fi used later.
dpkg-buildpackage doesn't know about dotnet's --runtime flag.
It uses the host arch by default, so the cross-build for arm64
produced postit_*_amd64.deb (filename mismatch with the actual
arm64 .so content inside).
The Debian-blessed way to do cross-builds is to set DEB_HOST_ARCH
via dpkg-architecture. With -aarm64, dpkg-buildpackage will:
- name the .deb postit_*_arm64.deb
- accept the amd64 host as the build environment
- run debhelper rules with the right DEB_* variables exported
The vars from dpkg-architecture have to be exported into the
same shell as dpkg-buildpackage (each make recipe line runs in
its own shell), so eval and dpkg-buildpackage are chained with
'\\' continuation.
The warning 'GNU system type aarch64-linux-gnu does not match
the CC system type x86_64-linux-gnu' is informational — it just
acknowledges that we're cross-compiling. We don't compile C, so
no CC toolchain is needed.
dpkg-buildpackage -b uses the host architecture for both naming
and packaging, ignoring dotnet's --runtime flag. The cross-build
for arm64 therefore produced postit_*_amd64.deb (with arm64 .so
inside, weird) instead of postit_*_arm64.deb. Worse, the
subsequent arm64 step then saw no arm64 .deb in /src/ and failed
with 'Missing .deb files'.
Pass -aarm64 (or -aamd64) to dpkg-buildpackage so it tags the
.deb with the right Debian architecture. Linux-arm64 dotnet
runtime publishes binaries for arm64 already; we just need the
.deb filename to match.
Caveat: this also tells dpkg-buildpackage that the build is
targeting arm64, which may cause it to look for arm64-specific
tooling. In practice for this packaging the only tool is
objcopy/strip (both disabled for the cross-build) and dpkg-shlibdeps
(which we already enable with multi-arch libc:arm64 etc.).
dpkg-buildpackage -b writes the produced .deb to the parent of
the source directory (/src/_src/.. = /src/, which is
by default). The 'mv ../postit_*.deb $POSTIT_OUT_DIR/' step was
therefore trying to move a file onto itself — every build
exited with 'mv: ... are the same file' and make exited 1.
The earlier 'rm -f ../postit_*.deb' cleanup never had anything
to remove (dpkg-deb hadn't run yet), and even if it had, the
build step would have produced a new .deb at the same path
before the mv had a chance to act.
Drop the mv entirely. Just verify the .deb is there after
dpkg-buildpackage; fail loudly if not.
The 'mv' step after dpkg-buildpackage was failing with
`mv: '../postit_1.0.6-1_amd64.deb' and '/src/_src/../postit_1.0.6-1_amd64.deb' are the same file`
because the amd64 .deb from previous CI runs (or from a partial
arm64 build that copied the leftover amd64 .deb) was still in
the source directory.
Remove any postit_*<TAG>-1*.deb, *.buildinfo, and *.changes files
before invoking dpkg-buildpackage. The fresh build then writes
to ../ without colliding with stale output.
The previous commit put an 'if ... then ... fi' block across
multiple physical lines in the Makefile recipe. make runs each
recipe line in its own shell, so the 'fi' never matched its
'if', and the parser (dash on Debian) reported
`Syntax error: end of file unexpected (expecting "fi")`.
Collapse the if/then/fi into a single line joined with ';',
joined to the surrounding lines with '\\' continuation so make
treats them as one shell command.
The previous 'mv ... || mv ... || true' chain silently masked
real build failures: when dpkg-buildpackage for arm64 aborted,
the fallback 'mv ../postit_*.deb' matched the previous run's
leftover amd64 .deb (same filename prefix 'postit_*-1') and
returned 0. The CI step exited cleanly with no arm64 .deb.
Replace the silent fallback with an explicit check that errors
out if no .deb was produced. Two-stage pattern (specific version
first, any .deb second) keeps the same matching flexibility for
both stable and pre-release tags, but the absence of a .deb now
fails the build step.
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.
Previous fix (--exclude=*.so --no-automatic-dbgsym) failed because
dh_strip does MULTIPLE strip passes (--strip-unneeded,
--strip-debug, --only-keep-debug), and objcopy's ARM64-format
recognition issue surfaces again in a pass that does not honour
--exclude:
strip: Unable to recognise the format of the input file
`debian/postit/usr/lib/postit/libHarfBuzzSharp.so'
dh_strip: error: strip --remove-section=.comment ... returned exit code 1
The cleanest fix is to skip dh_strip entirely. The cost is minor
for our use case (no strip on managed DLLs saves a few KB; no
postit-dbg auto-package — the debug info in libHarfBuzzSharp /
libSkiaSharp is from Avalonia deps, not our code).
If stripping becomes important, the proper fix is binutils-multiarch
(multi-arch objcopy) in the container runner. Out of scope here.
Discovered via Forgejo Actions run #9 on tag 1.0.6.
Previous fix (--exclude=*.so) only affects the strip phase. dh_strip
also runs a separate --only-keep-debug pass for the automatic dbgsym
package, and that pass does not honour --exclude. So objcopy still
trips on the ARM64 .so files and the build aborts.
Add --no-automatic-dbgsym to skip the dbgsym pass entirely. Trade-
off: no separate postit-dbg package is generated. That's fine for
this upstream — the only debug info in the .so files is from Avalonia
deps (HarfBuzz, Skia) and is not useful for debugging PostIt itself.
Discovered via the Forgejo Actions run #8 on tag 1.0.6.
When cross-compiling PostIt.Desktop for linux-arm64 from an amd64
runner container, the .deb ends up shipping HarfBuzzSharp.so and
SkiaSharp.so in ARM64 format. dh_strip invokes objcopy on every
file under debian/postit/, and objcopy on the amd64 host cannot
parse ARM64 .so files:
objcopy: Unable to recognise the format of the input file
`debian/postit/usr/lib/postit/libHarfBuzzSharp.so'
dh_strip: error: ... returned exit code 1
dh_strip: error: Aborting due to earlier error
Add --exclude=*.so to override_dh_strip. The upstream .so ships
with its own debug symbols; we don't lose anything by skipping
the strip on them.
Also drop the diag.yml workflow that diagnosed the initial
tag-not-found failure — no longer needed.
Discovered via the Forgejo Actions release.yml run 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.
Probes the runner setup so we can tell whether the release.yml
failure is upstream of our workflow (no runner, no network, no
tooling) or specific to a step.
- Container reachable + image usable
- DNS + outbound HTTPS to forgejo.pschneider.fr and hub.docker.com
- Required tooling available (git, dotnet, jq, curl, make, debhelper,
imagemagick)
- forgejo.* context populated
- secrets.GITHUB_TOKEN resolves (FORGEJO_TOKEN populated)
- API call with the token returns 200 on /repos/<repo>
Will be removed once we've confirmed the runner is healthy.
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.
- Makefile + debian/rules: POSTIT_GIT_TAG default 1.0.0 → 1.0.6.
- CHANGELOG.md: document the 1.0.6 release (workflow rollout).
- .github/workflows/build-and-release-deb.yml: GitHub Actions workflow
that builds postit_*.deb (matrix amd64+arm64 via cross-RID .NET on
amd64 runners) from a tag pushed to this repo, validates the tag
(semver, patch-parity channel, CHANGELOG section), and publishes a
GitHub Release with both .deb as assets. Refuses to re-tag an
existing release by default (re-tag = le mal); opt-in via
workflow_dispatch + force_republish=true.
Pattern mirrors yavsc/.github/workflows/docker-publish-android.yml.
The previous override_dh_auto_build used 'git clone --branch <tag>'
which only accepts branch names. Feeding it 1.0.1-rc02 (a tag,
not yet pushed to origin) failed with 'La branche distante
1.0.1-rc02 n'a pas ete trouvee' and aborted the package build.
Switch to a 'git init + remote add + fetch --depth=1 origin <ref>
+ checkout FETCH_HEAD' sequence, which works for both branches
and tags, including local-only tags fetched via file:// (the
typical RC-staging workflow: 'git tag' on the local checkout,
then 'make POSTIT_GIT_URL=file:///... POSTIT_GIT_TAG=<tag>'
before pushing).
The fetch error now carries a diagnostic that points the operator
at the file:// override and at the missing 'git push'.
Adds tests/test-fetch-upstream.sh with four cases against the
real pazof/yavsc remote and a synthetic local repo: tag on
GitHub, branch on GitHub, .git reuse, and tag via file://.
Wired into 'make test'.
The .deb was hardcoded to 1.0.0-1 in debian/changelog regardless of
POSTIT_GIT_TAG, so 'make POSTIT_GIT_TAG=1.0.1-rc01' produced a
postit_1.0.0-1_amd64.deb (wrong name) and the trailing mv into
POSTIT_OUT_DIR silently fell back to the same wrong-name glob.
Move the version into a @VERSION@-bearing debian/changelog.in
template and render debian/changelog from it in the Makefile, before
dpkg-buildpackage runs (it reads debian/changelog during
dpkg-source --before-build, before any rule fires). debian/changelog
is .gitignored; the template is the source of truth.
debian/rules re-renders the changelog as a safety net for direct
dpkg-buildpackage invocations and adds it to override_dh_auto_clean
so partial builds leave no stale rendered file behind.
Default stays linux-x64 (the only architecture built today).
Override on the command line to target arm64 (or any other
RID supported by Avalonia 12 on Linux):
make deb POSTIT_RUNTIME=linux-arm64
The Makefile passes POSTIT_RUNTIME through to dpkg-buildpackage,
which propagates it to debian/rules, which threads it into
dotnet publish --runtime.
Note: the .deb is mono-architecture. For a multi-arch .deb,
the build must run twice (once per RID) and the resulting .debs
can be combined with dpkg-gencontrol / Multi-Arch: same.
Update README accordingly.
dh_install, by default, only installs files explicitly listed
in debian/<package>.install. Without that file, it silently
installs nothing — the build pipeline ends with an empty .deb
of just changelog and copyright.
List:
/usr/lib/postit/* (managed assemblies)
/usr/bin/postit (wrapper script)
/usr/share/applications/postit.desktop
/usr/share/icons/hicolor/{16,32,48,64,128,256}x{...}/apps/postit.png
/usr/share/icons/hicolor/scalable/apps/postit.svg
The original .gitignore caught *.deb in the source tree but not:
- debian/.debhelper/ (cache populated by every dh_* helper)
- debian/debhelper-build-stamp, debian/postit.debhelper.log,
debian/postit.substvars (regenerated per build)
- debian/tmp/ (staging tree for dh_install)
- postit_*.{deb,changes,buildinfo} in the parent workspace
(where dpkg-buildpackage drops artifacts)
Initial debian/rules wrote into debian/postit/usr/... directly.
That fails: dh_clean (run between override_dh_auto_build and
dh_install) wipes the package staging dir, and dh_install needs
debian/tmp/ as the canonical staging area to install files
into debian/postit/.
Move the cp-and-convert block out of override_dh_auto_build into
a 'stage_postit' make define, and call it from override_dh_install
right before dh_install moves files into the package tree.
Also: simplify POSTIT_* path variables — drop the duplicated
POSTIT_APP_DIR / POSTIT_LIB_DIR / POSTIT_BIN_DIR / POSTIT_SHARE_DIR
set (they all derived from debian/postit which is now what dh_install
populates).