47 Commits
Author SHA1 Message Date
FredApps 2b3fb9dc05 ci: preserve dependency cache reuse across root manifest updates (#108)
* ci: preserve dependency cache reuse across root manifest updates

* docs: align gate cache overview with profile keys and dispatch opt-in
2026-10-30 07:47:08 -04:00
Dylan M. Taylor fa5ef18780 Build FreeBSD aarch64 release archives by default (#104)
* 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
2026-10-28 15:49:07 -04:00
Jeremy 8bb4e5ab95 fix(ci): separate ARM64 install paths and scope (#100)
* fix(ci): separate ARM64 install paths and installation scope

* test(ci): exercise ARM64 installer contract fixtures on Windows
2026-10-27 15:45:39 -04:00
skylar b97d27ffa3 test: cover nextest installer aliases
Reject affected alias installers and duplicate selectors in the pin inventory.
2026-10-26 16:20:05 -06:00
skylar d07589f3fe ci: pin nextest to 0.9.150
Use the fixed release for GHSA-vc25-qg58-w57c.
2026-10-26 14:13:00 -06:00
skylar taylor-barrick be6bbf3ca5 Provision fonts and optional Python coverage for Sonar (#88)
* 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
2026-10-25 23:37:12 -04:00
Dylan M. Taylor 452c0b419f sonarcloud: exclude i18n tables from duplication checks (#91) 2026-10-25 21:20:17 -04:00
Dylan M. Taylor e3814b3ec2 sonarcloud: fail a shard that cannot run its tests instead of scanning empty coverage (#89) 2026-10-25 19:49:55 -04:00
Dylan M. Taylor 395270d2c0 sonarcloud: optionally shard Rust coverage across parallel nextest partitions (#87)
* 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
2026-10-25 18:54:59 -04:00
Dylan M. Taylor 2b4eb2c6d9 Merge pull request #83 from dylanmtaylor/security/workflows-hardening
security(workflows): strengthen SonarCloud project-key and FreeBSD architecture input validation (F-005, F-006, F-007, F-009, F-010)
2026-10-24 22:48:54 -04:00
Dylan M. Taylor e84f24ac6b Merge pull request #82 from dylanmtaylor/security/action-inputs-validation
security(actions): harden apt package and resolve-version input handling (F-003, F-004)
2026-10-24 22:48:50 -04:00
Dylan M. Taylor 7b38cb1f31 test(resolve-version): add test for CRLF sanitization in app-title 2026-10-24 22:46:59 -04:00
Dylan M. Taylor 85f7365b1c fix(arch-package): preserve SPDX license expression and copy licenses with --remove-destination
- 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
2026-10-24 22:45:48 -04:00
Dylan M. Taylor 58e2894843 security(workflows): strengthen SonarCloud project-key and FreeBSD architecture input validation (F-005, F-006, F-007, F-009, F-010)
- 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)
2026-10-24 22:44:09 -04:00
Dylan M. Taylor 80908a10c8 security(actions): harden apt package and resolve-version input handling (F-003, F-004)
- 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
2026-10-24 22:42:14 -04:00
Dylan M. Taylor 55fde55771 security(arch-package): harden metadata serialization and license path validation (F-001, F-002, F-008)
- Consistently serialize all PKGBUILD variables (pkgver, pkgrel, arch, license, provides, conflicts) using shell-safe quoting
- Validate SPDX license expression syntax, rejecting invalid characters, pipes, and dangling or consecutive operators
- Enforce boundary validation on the license destination using realpath in package() to prevent path traversal outside pkgdir
- Reject symlinks in license path and ensure cp does not dereference symlinks (-P)
- Add comprehensive unit tests covering arch allowlist, version validation, pkgrel validation, license expression validation, PKGBUILD serialization, and license directory symlink rejection
2026-10-24 22:39:25 -04:00
IvanandClaude Opus 5.5 13491f8120 ast-grep: a reusable structural-lint workflow driven by the app's sgconfig.yml (#80)
* 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>
2026-10-24 19:29:21 -04:00
Martin BallandClaude Opus 5.5 9e42334399 fix(release): drop the i18n gate; missing translations never block (storytold/craft-i18n#20) (#79)
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>
2026-10-23 17:06:45 -04:00
aiongg 9587bf235d Shared quality-gate workflow for the craft apps (gate.yml) (#69)
* 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>).
2026-10-23 12:15:49 -04:00
Dylan M. Taylor 5d4336931c sonarcloud: measure Rust coverage with cargo-llvm-cov and import the LCOV report (#74)
* 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
2026-10-23 12:06:24 -04:00
aiongg 13f14e1788 install-apt-packages: cache downloaded .deb files between runs (#67)
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.
2026-10-22 22:46:37 -04:00
Martin BallandClaude Opus 5.5 879a6a1684 fix(release): give riscv64 packages the glibc floor of their runner (#62)
* 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>
2026-10-22 17:10:08 -04:00
ihsan mokhlisse 67a331f56f fix(ci): keep release artifacts separate until names are checked (#31)
Download each release artifact into its own directory and fail when two
files share a name, before the SBOM or the signatures are created.
2026-10-22 17:09:19 -04:00
coderabbitai[bot] ea8c0f57af security(windows-arm64): prevent PowerShell injection through installer inputs (#58)
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-10-22 15:23:56 -04:00
coderabbitai[bot] 82b63dea97 security(freebsd): prevent shell injection through Cargo extra arguments (#56)
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-10-22 15:10:38 -04:00
Saadi 11013609b1 fix(freebsd-cache): fall back to a compatible cache when manifests move (#41)
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
2026-10-22 15:00:48 -04:00
ZuujisandClaude Opus 5.5 50a6bc4968 ci(release): cross-compile FreeBSD aarch64 in the x86_64 VM (#50)
* 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>
2026-10-22 14:58:24 -04:00
Martin BallandClaude Opus 5.5 b4bfe83b30 feat(release): build with the app's craft-i18n pin and gate packaging on it (#51)
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>
2026-10-22 14:54:21 -04:00
coderabbitai[bot] d6d11df79e security(release): prevent app-name shell injection in FreeBSD and server jobs (#55)
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-10-22 14:47:14 -04:00
Martin Ball 5140aec7d3 fix(freebsd): run the tests as an ordinary user, not root (#54)
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.
2026-10-22 14:43:28 -04:00
coderabbitai[bot] 58d4f80bc3 security(nix): prevent shell injection through extra-args (#52)
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-10-22 14:37:49 -04:00
coderabbitai[bot] 3fcccd5573 security(sonar): prevent project key injection into scanner arguments (#53)
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-10-22 14:37:31 -04:00
Zuujis a4acae8e3a ci(freebsd): cross-compile aarch64 in the x86_64 VM instead of emulating it (#49)
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.
2026-10-22 01:44:01 -04:00
aiongg d664cb69f4 feat(release): default craft-fonts-ref to the suite's craft-fonts pin (#38)
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
2026-10-21 09:31:29 -04:00
Brandon ThomasandClaude Opus 5.5 39962f26c3 Release: drop FreeBSD aarch64 until it can be built in time (#40)
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>
2026-10-21 08:41:36 -04:00
skylar taylor-barrick f49277a3d0 fix(ci): let GitHub manage FreeBSD cache storage limits 2026-10-18 22:46:35 -06:00
skylar taylor-barrick 2f3b0f5e83 test(ci): make empty-profile regression independent of filesystem 2026-10-18 22:07:57 -06:00
skylar taylor-barrick c347980c9f fix(ci): retain untouched profiles and isolate workflow fixtures 2026-10-18 22:01:49 -06:00
skylar taylor-barrick f3d700ca15 fix(ci): support FreeBSD login shells and prioritize artifact delivery 2026-10-18 21:45:22 -06:00
skylar taylor-barrick 0efb69ba2a fix(ci): preserve bounded caches and tolerate absent Cargo sources 2026-10-18 21:30:50 -06:00
skylar taylor-barrick 69e4aa9c2c fix(ci): bound FreeBSD caches and recover from cache failures 2026-10-18 21:25:32 -06:00
skylar taylor-barrick 0156a114e8 fix(freebsd): keep cache-save failures from blocking package delivery 2026-10-18 19:10:19 -06:00
skylar taylor-barrick adcd8bf032 ci(freebsd): keep ARM coverage outside default PR checks and cache Cargo builds 2026-10-18 18:59:20 -06:00
lukasholdandClaude Opus 5.5 1f5d516959 fix(freebsd): install Rust from packages on aarch64 (#24)
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>
2026-10-18 13:15:23 -04:00
Dylan M. Taylor bb838fd857 feat(freebsd): add matrix strategy for multiarch support (x86_64 and aarch64) (#17) 2026-10-18 00:39:48 -04:00
Dylan M. Taylor 31e08e5e14 feat(release): build Arch Linux package (.pkg.tar.zst) natively via makepkg from release tarball (#16)
* 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
2026-10-18 00:37:20 -04:00
skylartaylor e10d698d5c ci: bound shared jobs and preserve caller validation 2026-10-16 04:55:29 +02:00