The release FreeBSD jobs keyed their Cargo cache on toJSON(inputs), and
contributors-artifact is contributor-snapshot-<run id>-<attempt>, so every run
wrote an entry no other run could restore: on 11 Nov every release built FreeBSD
cold. The key now holds only the inputs that change the FreeBSD build (app name,
architecture, craft-fonts ref, FreeBSD release); dry-run is left out too, so a dry
run warms the cache a release restores. A test makes every new input pick a side.
The aarch64 job cross-compiles, and Cargo writes that output to
target/aarch64-unknown-freebsd/release, which the cache action never saved: only
the host build scripts in target/release were kept. The action takes the triple as
a new target input and saves and restores that directory too.
Ubuntu's MOTD news and Ubuntu Pro (ESM) hooks reach motd.ubuntu.com and
esm.ubuntu.com; TLS revocation checks for Amazon-issued certificates go
to *.amazontrust.com over plain HTTP. Every Harden-Runner allow list now
names them.
Each coverage shard now merges its own raw profiles into one sparse
.profdata with the archive toolchain's llvm-profdata and uploads only
that. The scan job merges the shard profiles instead of downloading
every raw profile beside the test archive, which on 11 Nov 2026 filled
the runner for vectorcraft (four ~7 GB zipped profraw artifacts).
A raw profile llvm-profdata cannot read, such as one cut short by a
test process that crashed while writing it (lightcraft, 11 Nov 2026),
now fails its shard with an error naming the file, instead of being
uploaded and failing the scan's merge.
Single-shard callers keep the cargo llvm-cov path unchanged.
* release.yml: a dry-run input builds and checks without releasing
For a nightly run on main that finds release-only breakage before release day. On 11 Nov 2026
gridcraft's riscv64 build, materialcraft's private-repo FreeBSD jobs, wordcraft's Print job and
the riscv64 apt mirrors each surfaced only during the release, one fix PR and release run at a
time. With dry-run: no job enters the release environment (no signing secrets; the environment
admits only the release branch), the macOS job and the draft are skipped, and the Flatpak OCI
layouts are not pushed to GHCR or Docker Hub.
* tests: the Flatpak OCI push and the release environment also stay out of a dry run
* ci: use StepSecurity's maintained actions
Each third-party action that StepSecurity publishes a secure drop-in
replacement for now uses that replacement, pinned to a commit with its
version as a comment. Versions follow what we used: the newest
StepSecurity release in the same major, or the closest major they
publish. dtolnay/rust-toolchain refs that named a toolchain (stable,
nightly, 1.76) become the replacement's `toolchain` input.
* tests: find the FreeBSD VM, SonarCloud scan and install-action steps by their StepSecurity names
* sonarcloud: honour each repository's own sonar.cpd.exclusions
* sonarcloud: read sonar.cpd.exclusions with properties syntax and allow the ? wildcard
Block-mode Harden-Runner still flagged apt's lookups under these zones
(_https._tcp.mirrors.edge.kernel.org, _https._tcp.mirrors.dotsrc.org),
and apt failures are breaking CI across the org. Allow the whole zones on
443 next to the existing mirror hosts.
The release builds' Linux riscv64 job read amd64 from archive and
security.ubuntu.com and riscv64 from ports.ubuntu.com only. With no
ubuntu.com name resolving on the runners, apt spent 3m20s on failed
lookups and then found no libc6-dev:riscv64, so every app's release
failed there.
select-mirrors.sh now chooses one pair of mirrors for both architectures:
Ubuntu's own, then dotsrc (it carries both trees), then kernel.org with
TU Chemnitz's ports. It takes the first pair whose amd64 and riscv64
InRelease files answer for every suite with the same Date (Ubuntu signs
one Release for archive and ports), so Multi-Arch: same packages still
match. The image's own Ubuntu sources are disabled in that job: they
only added a second, lagging snapshot and, during the outage, the stall.
The riscv64 job runs Harden-Runner in audit mode, so release.yml needs
no allowlist change.
One run on 11 Nov 2026 found no candidate for tcsh or debsigs right after
an update that fetched every index from kernel.org; the runs before and
after it, under the same ubuntu.com outage, passed. Print apt-cache policy,
the architecture settings, the universe index files and whether the CPU
supports x86-64-v3, so the next failure shows the cause.
Harden-Runner blocked apt's SRV lookups (_https._tcp.mirrors.edge.kernel.org,
_https._tcp.mirrors.dotsrc.org): the allow lists name the hosts, not their
SRV records. configure-apt-mirrors now sets Acquire::EnableSrvRecords false;
apt connects to the hosts directly either way.
The FreeBSD workflow's VM action apt-installs QEMU on the host, which failed
during the ubuntu.com outage (sudo exit 100) and reached packages.microsoft.com.
Run configure-apt-mirrors first and drop the image's Microsoft source, which
the job never installs from.
On 11 Nov 2026 no ubuntu.com name resolved on the runners for about
half an hour. The images' mirror list (azure.archive, archive and
security.ubuntu.com) sits entirely in that zone, so every retry spent
its five minutes on 20-second lookups of the same dead names and the
SonarCloud archive builds failed with "downloading the packages failed
3 times".
configure-apt-mirrors points the image's Ubuntu sources at
/etc/apt/craft-apt-mirrors.txt: the image's mirrors in the image's
order, then mirrors.edge.kernel.org and mirrors.dotsrc.org (arm:
mirrors.dotsrc.org and ftp.tu-chemnitz.de). Each mirror is checked
first, in parallel; those that do not serve this release's InRelease
move to the end. install-apt-packages runs it before every network
attempt, and the plain apt-get steps (Flatpak, release, packaging lint,
RPM signing, Linux deps, FreeBSD test) run it before apt-get update.
Block-mode Harden-Runner lists allow the new hosts.
* ci: harden workflows with credential isolation, template injection defenses, and dependabot cooldown
- Add persist-credentials: false across all actions/checkout invocations
- Eliminate template injection by mapping matrix and input variables to env: in freebsd.yml, release.yml, and sonarcloud.yml
- Add 7-day cooldown to dependabot.yml to protect against supply-chain compromise of recently published releases
- Update test suite to validate hardened environment mappings
* ci: allow static.crates.io and registry endpoints in gate and windows-arm64 runners
* fix(freebsd): give the VM a swap file and cap the ZFS ARC
photocraft's FreeBSD job was SIGKILLed compiling photocraft-ui-egui's lib tests late in the run (release run 42609972735, dispatch 42805955303). Add an 8 GB swap file when the guest has none, cap the ARC at 2 GB when the image uses ZFS, and print disk, swap and memory so the next OOM is diagnosable.
* fix(freebsd): only touch swap and the ARC inside a FreeBSD guest
The workflow tests run the prepare script on Linux under sh -eu, where swapctl doesn't exist. Run the ARC cap, swap file and diagnostics only when uname is FreeBSD, and don't let the diagnostics fail the step.
* fix(freebsd): add the swap file only with room to spare, and remove a partial one
Per review: a dd that runs out of space must not leave a partial /var/swap0 that then starves the build. Require 16 GB free on /var first and delete the file if any step fails.
* fix(freebsd): keep only the ZFS ARC cap; drop the swap-file fallback
Per review: swap on a ZFS-backed file can hang FreeBSD under memory pressure, and today's image already has a swap partition, so the fallback never ran. The verified run passed on the ARC cap alone.
- Add allowed-endpoints to ast-grep.yml matching test-ast-grep.yml
- Add pypi.org and files.pythonhosted.org to sonarcloud.yml for Python coverage
- Add packages.microsoft.com:443 and https mirrors to apt allowed-endpoints
- Set unvalidated reusable workflows (release, docker-build, gate, etc.) to audit mode
Adds step-security/harden-runner (v2.22.4, SHA-pinned) as the first step of
every job in the reusable and repo workflows, in egress-policy: audit mode.
Audit mode only records network, file and process activity; it blocks nothing.
Unsupported environments (containers, FreeBSD VM guests, ubuntu-slim) are
skipped by the action itself. Dependabot's github-actions group keeps the pin
current.
buildx pushes a by-digest output to one repository per invocation, so with two
imagerepositories only the last one received the manifest and the merge job failed
with "not found". Loop over the image list and assert the digests agree.
Builds every platform in `platforms` by digest into each repository in `images`,
then joins those digests into one multi-arch manifest per repository, logging in
to DOCR, GHCR or dhi.io only when an image needs it. It carries the inputs both
craft web images need: a custom Dockerfile and stage, build args, a GHA layer
cache, preinstalled-SDK pruning, and a hook for preparing large build contexts
such as the font sources.
* release.yml: export each app's build variables from its env.sh, not a list
Eight release jobs set <APP>_VERSION, <APP>_BUILD_DATE and <APP>_BUILD_SHA for
every app by name (298 lines), and the FreeBSD VM step listed them all again,
so every new app meant editing this workflow. materialcraft and texturecraft
were never added: their packaging reads MATERIALCRAFT_* and TEXTURECRAFT_*,
so their release builds got no release version, date or commit, and a
dispatched run with a version override packaged them under Cargo.toml's.
actions/export-app-build-env reads the prefix the app's packaging/env.sh (or
packaging/common.sh) already declares in VERSION="${<PREFIX>_VERSION:-...}",
falling back to app-name in capitals, and writes the three variables to
GITHUB_ENV; its names output feeds the FreeBSD VM's envs. Each job runs it
right after checkout. On all 17 apps' env.sh it finds the prefix the old
list used.
Tests run the script on sample env.sh files and check release.yml sets no
app's variable by name, so the list can't come back.
* Export the build variables after the contributor snapshot, which must follow the checkout
test_contributor_refresh requires each build job to load the contributor snapshot directly after checkout, so the export step goes after it when a job has one. Also fix the line-break test passing a variable twice.
---------
Co-authored-by: nnaannoo <n4nosaur@gmail.com>
The writer and the checker share one normalization. A rewritten digest does not make an edit pass, an intact older copy warns only when main has moved on, and a source that cannot be read is reported as could not check.
Co-authored-by: Cursor <cursoragent@cursor.com>
Eight apps paste the same 25-line job into ci.yml, nix.yml or release.yml (ten
copies): Cargo.lock exists and is up to date, no .cargo/config(.toml)
replaces, patches or overrides a source, and every git dependency comes from
github.com/storytold. Apps' workflows are meant to only call workflows here.
actions/check-git-sources holds the check as a tested script, and
git-sources.yml runs it on a credential-less checkout, so an app swaps its
job for one `uses:` line and keeps `needs: git-sources`. It reports every
problem rather than the first, as GitHub error annotations, and treats
unreadable TOML as a failure. On all 18 apps' current Cargo.lock and Cargo
config it agrees with the inline copy.
Co-authored-by: Kristian Myram <kmy@oresundsbron.com>
The release job ran each app's packaging/linux/flatpak-oci.sh --push. Most
apps still carry the copy from before scripts/scaffold-flatpak-oci.sh learnt
to guard the stable tag, and it moves stable-<arch> on every push, so a
prerelease or a test run dispatched from any branch became what stable-<arch>
clients install. 15 of the 17 apps carry that copy.
Call the script without --push, which only builds the layout in every copy,
and push from the workflow: <version>-<arch> on every non-PR run, and
stable-<arch> only for a stable version built from the release branch, the
rule the scaffold template already uses. No app needs to change.
Co-authored-by: Kristian Myram <kmy@oresundsbron.com>
* Add changed-paths.yml: run a job only when its paths change or a label asks
* changed-paths: read paths NUL-separated, keep the reason on one line; README: what the checkout holds