* ci(release): build FreeBSD aarch64 archives by default
app packaging/freebsd/package.sh scripts now honor CARGO_BUILD_TARGET, so
aarch64 is no longer opt-in: freebsd-architectures defaults to
'["x86_64","aarch64"]'. Callers can still narrow it to x86_64 only.
* docs(readme): FreeBSD aarch64 is in the default freebsd-architectures
* Provision coverage fonts for archive builds and test runners
* Keep coverage fonts optional unless a pin is requested
* Pin nested actions used by coverage jobs
* Export the font path only after a requested checkout
* Measure optional Python tooling coverage in Sonar
* ci: restore craft-common-actions tracking main
* ci: share Python coverage configuration with XML reporting
* sonarcloud: optionally shard Rust coverage across parallel nextest partitions
* sonarcloud: default coverage to four shards
* sonarcloud: build the instrumented archive once and shard tests from it
* sonarcloud: skip sharded scan steps when the plan job produces no shards
* sonarcloud: validate shard count, pin the archive's toolchain, merge only test binaries
- Preserve full SPDX expression as a single shell-quoted array element in PKGBUILD license
- Use cp --remove-destination to replace any destination symlink when copying license files
- Update test_pkgbuild_and_makepkg_conf_safe_serialization test assertion to expect preserved expression
- Restrict SonarCloud project-key grammar to start with an alphanumeric/underscore, reject leading hyphens/flags, and enforce 400 character length limit (F-006)
- Verify Nix extra-args argument parsing without eval (F-005)
- Verify Windows ARM64 PowerShell environment variable passing and path component sanitization (F-007)
- Add unit test verifying FreeBSD workflow architecture pre-validation rejects invalid architectures and injection payloads prior to VM execution (F-009)
- Verify FreeBSD custom-test-command environment data passing (F-010)
- Disallow carriage returns and newlines upfront in resolve-version to prevent GitHub output-file protocol forgery
- Strictly validate generated build date against YYYY-MM-DD format
- Sanitize APP_TITLE against carriage returns and newlines in step summary
- Validate package tokens in install-apt-packages, rejecting options, path traversals, and injection payloads
- Terminate options with -- when invoking apt-get install
- Add unit tests for install-apt-packages validation and new test suite for resolve-version
* ast-grep: a reusable structural-lint workflow driven by the app's sgconfig.yml
A reusable workflow and composite action that install a pinned
ast-grep (checked against a SHA256 kept in the action, since its
releases publish none), run the caller's rule tests when its
sgconfig.yml has testConfigs, then scan with that config. CI and a
local `ast-grep scan` read the same rules. Findings become PR
annotations; a failing scan also lists every finding as file:line,
since GitHub shows only the first few annotations of a step.
No rules ship here. test-ast-grep.yml checks the action against a
fixture: clean code passes, a finding fails, a path outside the
repository is refused.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* ast-grep: run the action from the workflow's own commit; always run rule tests
ast-grep.yml loads the action with `$/actions/ast-grep`, GitHub's
self-repository syntax, so a caller that pins the workflow to a commit,
or calls a fork's, runs the action from the same commit rather than
whatever is on main.
The rule tests no longer depend on grepping for `testConfigs:`, which
missed valid YAML such as `testConfigs :`. ast-grep test runs every
time; with no testConfigs it has no tests to run.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Remove i18n-gate.yml, the skip-i18n-gate input and the i18n-gate jobs in
release.yml and freebsd.yml. Keep checkout-craft-i18n and the
CRAFT_I18N_DIR / CRAFT_I18N_REQUIRED=1 env, which fail only on a
missing or invalid craft-i18n checkout.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* ci: add a shared CI workflow for the craft apps
A reusable workflow that runs each app's quality gate (by default
`cargo xtask ci`) on one or more runners, with the suite's shared setup:
cached apt packages, the repository's toolchain, prebuilt cargo tools,
the craft-fonts and craft-i18n checkouts, and a dependency cache that only
the default branch writes.
The rust-cache key includes the root Cargo.toml: rust-cache hashes only
the workspace members' manifests, so a [profile] change in a virtual root
manifest would otherwise keep restoring the old cache.
Part of #60.
* tests: cover the shared CI workflow
test_ci.py checks that third-party actions are pinned to commits, that
only non-PR runs on the default branch save the cache, that caller input
reaches run blocks only through env, and runs the caller-environment
script under bash to check it writes valid KEY=VALUE lines and rejects
others. The craft-fonts test also holds ci.yml to the suite's pin, and the
test workflow runs on changes to ci.yml.
* docs: document the shared CI workflow
Describes what ci.yml sets up and runs, with a plain caller and one that
adds a Windows leg on main, manual runs and full-ci pull requests.
* actions: don't persist credentials in the craft-fonts and craft-i18n checkouts
Both repositories are public and nothing later in a job runs git in them,
so the token has no use there.
* ci: name the shared workflow gate.yml
The repository's own CI proposed in #21 takes .github/workflows/ci.yml.
Callers use .github/workflows/gate.yml@main; the job still reports as
gate (<os>).
* sonarcloud: measure Rust coverage with cargo-llvm-cov and import the LCOV report
The scan never imported coverage, so SonarCloud counted only the few Python and
PowerShell files as coverable and showed 0.0% for every project.
* sonarcloud: --ignore-run-fail keeps coverage after a red test; drop persisted checkout credentials before running Cargo
GitHub's Ubuntu mirror is sometimes slow: a FilmCraft CI run spent 5 min 42 s downloading
21 MB of build dependencies (storytold/filmcraft#2860), and FigCraft's apt step took 4 to 9
minutes in two of eight runs (craft-common-actions#60). The action points apt's archive
directory at a cached folder, so a run with a warm cache downloads only the package lists.
* fix(release): give riscv64 packages the glibc floor of their runner
The riscv64 job builds on ubuntu-24.04, so its GUI binaries need GLIBC_2.39,
but the apps' nfpm.yaml declares the 2.35 floor of the other Linux jobs.
Ubuntu 22.04 can't host the cross build: jammy's ncurses security update was
never built for riscv64, so libtinfo6 can't match across architectures.
Export GLIBC_FLOOR=2.39 on the riscv64 job for each app's package.sh and
nfpm.yaml to declare. Refs #44.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* docs(release): say GLIBC_FLOOR reaches the packages only through the app's nfpm.yaml
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The identity step hashed the caller's source hash into the cache prefix and
restore-keys only matched that prefix, so any revision that touched Cargo.toml,
Cargo.lock, build.rs, .cargo, rust-toolchain or packaging/ started from an
empty cache. Releases bump a version in the manifests, and PRs that change a
dependency do too. Issue #33 records the consequence on the aarch64 leg:
compiling ~260 dependency crates takes 4h32m under emulation, and 93 PR runs
were cancelled at the 300-minute limit with 0 passing.
Keep the exact key as the primary match and add a second restore key that pins
only what decides artifact compatibility: the guest toolchain and packages, the
serialized configuration and the profile. Cargo's fingerprints still reject
stale artifacts, so restoring a previous revision only saves work.
Bump the cache namespace to v3 because the key layout changed. Existing v2
entries are not readable through the new prefixes, so the first run after this
merge starts cold, as for any namespace bump.
Refs #33, #39
* ci(release): cross-compile FreeBSD aarch64 in the x86_64 VM
The FreeBSD release job built aarch64 in an emulated aarch64 guest, which
ran into the 300-minute limit for most apps, so aarch64 was dropped from
releases (#39). #49 fixed the same problem in freebsd.yml by cross-compiling
in the KVM-accelerated x86_64 guest; this ports that setup to release.yml.
The release VM is now always x86_64. For aarch64 it pins the quarterly
package branch, installs lang/rust and runs the same "Set up the aarch64
cross toolchain" step as freebsd.yml; a test keeps the two copies identical.
The build step sets the version before loading the cross environment, so
xtask still builds for the guest. The built-in packager reads binaries from
target/<triple>/release, and the job fails if an archive does not match the
matrix architecture.
aarch64 stays opt-in through the new freebsd-architectures input (default
'["x86_64"]'): each app's package.sh still reads target/release and names
its archive after uname -m, so it must honor CARGO_BUILD_TARGET first.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* ci(release): reject unsupported FreeBSD architectures before the VM starts
The guest is always x86_64 and only aarch64 is cross-compiled, so any other
freebsd-architectures value would build x86_64 binaries and the fallback
packager would label them with that value. Check matrix.arch on the host
first and fail for anything but x86_64 and aarch64.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
An app's craft-i18n.rev switches craft-i18n on. checkout-craft-i18n reads
it (trimmed: Windows checks it out with CRLF), checks out that SHA and sets
CRAFT_I18N_DIR and CRAFT_I18N_REQUIRED=1 for every build job in release.yml,
freebsd.yml and windows-arm64.yml; the FreeBSD guests export them too.
The i18n-gate workflow runs cargo test --workspace i18n_gate with
CRAFT_I18N_GATE=1 and fails if no test ran. Release packaging needs it,
skip-i18n-gate skips it on workflow_dispatch only, and freebsd.yml runs it
on pull requests that touch craft-i18n.rev. Without the pin file every
step is a no-op and the gate succeeds.
Claude-Session: https://claude.ai/code/session_01B5qjdPbdcyPMikAqXxAAdW
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The VM's shell is root, and root is never refused a file: a test that
locks a file and expects "permission denied" fails, and a permission bug
root would hide goes unseen. Default and custom tests now run as
craft-test through su; the build and packaging stay root.
Human-reviewed, AI-assisted, nobody's mailbox harmed.
The aarch64 leg booted an emulated aarch64 guest, which compiles about 23
times slower than the KVM-accelerated x86_64 one, so it ran into the
300-minute limit in every caller (#33). GitHub's Arm runners cannot help:
none of them exposes nested virtualization.
Both legs now boot an x86_64 guest. The aarch64 leg pins the quarterly
package branch, installs lang/rust, copies the aarch64 standard library from
the aarch64 lang/rust package of the same version, builds an arm64 sysroot
from base.txz and the GUI library packages, and builds the workspace with
CARGO_BUILD_TARGET=aarch64-unknown-freebsd through base clang and lld. Stable
Rust, no -Zbuild-std.
The cross leg compiles every target, including the tests, but cannot run them
or package.sh, which builds for the guest's own architecture.
release.yml and freebsd.yml default craft-fonts-ref to craft-fonts main
(bfcbf71: Japanese, Chinese, Arabic, Thai, Georgian), so one bump here
reaches every app's next release; an app sets the input only to pin
another commit. A test checks the two defaults agree and that every
release job that checks out craft-fonts builds with CRAFT_FONTS_REQUIRED=1.
Refs storytold/craft-libs#197
The emulated aarch64 VM takes over 5 hours for most apps, so the job is
cancelled at the limit. GitHub's ARM64 runners have no KVM, so they do not
help. Releases ship FreeBSD x86_64 only until #39 is fixed.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
aarch64-unknown-freebsd is a tier-3 Rust target, so rustup publishes no
toolchain for it: since the multiarch matrix (#17), every FreeBSD aarch64
job stopped in `prepare` with "installer for platform
'aarch64-unknown-freebsd' not found". The aarch64 leg now installs
FreeBSD's lang/rust package (1.98.1 on FreeBSD 15) and x86_64 keeps
rustup; `$HOME/.cargo/env`, which only rustup writes, is sourced only
when present. Applied to both freebsd.yml and release.yml.
Tests check that each architecture picks its installer and that the run
step works without a rustup environment; both fail on the previous
workflows.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* feat(release): build Arch Linux package (.pkg.tar.zst) via makepkg from release tarball
* fix(arch-package): fix PKGBUILD provides quotes, add podman fallback and e2e test