Commit Graph
64 Commits
Author SHA1 Message Date
Dylan M. Taylor bfec7e2a70 security: fetch amd64 security updates over HTTPS (SonarCloud S5332) (#68)
The amd64 security source added in #61 used http://security.ubuntu.com,
reintroducing the clear-text apt source that #26 removed for
ports.ubuntu.com. security.ubuntu.com serves the same archive over HTTPS.
2026-10-22 22:46:49 -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 ffe9bd01ad Revert "Install target libdbus headers for Linux portal builds (#57)" (#65)
This reverts commit b86cdd3. Wayland drag-and-drop waits for winit 0.31,
and Flatpak portal drops will be resolved with zbus (already in every
app via rfd) rather than libdbus. Records the decision as ADR 0001;
tracking in #64.

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 17:48:22 -04:00
Mocky Digital b86cdd3d0e Install target libdbus headers for Linux portal builds (#57) 2026-10-22 17:10:28 -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
Brandon ThomasandClaude Opus 5.5 cfdd61fda0 riscv64 cross: read amd64 security updates from the primary archive (#61)
Ubuntu published a zlib security update (1:1.3.dfsg-3.1ubuntu2.3) at 18:50
UTC on 2026-10-22. ports.ubuntu.com had the riscv64 build at once, but the
runner's amd64 mirror (azure.archive.ubuntu.com) still served 2.2. zlib1g is
Multi-Arch: same, so apt could not pair the versions and every release's
Linux riscv64 job failed with "held broken packages".

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 16:21:49 -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
Dylan M. Taylor 2446e3bd4f security(actions): stop evaluating tool-version inputs as shell/pwsh source (#45)
* security(actions): stop evaluating tool-version inputs as shell/pwsh source

The install-wix, install-nfpm, install-trunk, and packaging-lint composite
actions interpolated their *-version (and deb-arch) inputs directly into
Bash/PowerShell source when constructing download URLs. A command substitution
or subexpression in those values executed on the runner before any checksum
verification (CWE-78).

install-trunk additionally placed the version into a URL path component, so
path-traversal segments could point both the archive and its fallback checksum
at an attacker-controlled repository (CWE-23).

Fixes:
- Pass every version/arch input through the step `env:` and read it with quoted
  variable expansions instead of GitHub `${{ }}` interpolation in script bodies.
- Validate a strict version grammar (and an allow-list for deb-arch) before the
  value is used, which also excludes slashes and other URL path-traversal
  characters so the download publisher stays fixed.

No change to default versions, pinned checksums, or install behavior.

* security(actions): anchor version validation against the whole value

Address review feedback:
- The bash version checks used `printf %s | grep -Eq '^...$'`, which matches
  line-by-line. A value like `2.49.0\ninvalid` passed and then flowed into the
  download URL. Switch to a bash `[[ =~ ^...$ ]]` test, which anchors the entire
  string and rejects any value containing a newline. Applied to install-nfpm,
  install-trunk, and packaging-lint.
- The install-wix PowerShell check used `$`, which in .NET also matches before a
  trailing newline. Use `\z` (absolute end of input) so a trailing-newline value
  cannot bypass validation.
2026-10-22 11:40:53 -04:00
Dylan M. Taylor 3a54b3bbb8 security(arch-package): close symlink-flatten, su -c, and PKGBUILD injection (#46)
* security(arch-package): close symlink-flatten, su -c, and PKGBUILD injection

Three issues in the Arch packaging action, all reachable from the public
tarball input:

- arch-package.sh flatten step (CWE-61): the single-root-directory check used
  `-d`, which follows symlinks. An archive could ship a symlink whose target is
  an external directory; the subsequent `mv` moved external files into disposable
  staging, and the EXIT trap then deleted them. The wrapper is now rejected when
  it is a symlink, and its resolved path must stay inside staging before any
  child is moved.

- arch-package.sh PKGBUILD (CWE-78): pkgname (from an archive-controlled
  .desktop basename) and the app name were written into PKGBUILD unescaped, and
  makepkg evaluates PKGBUILD as Bash. Both values are now validated against safe
  identifier grammars and serialized with `printf %q`, matching the existing
  handling of pkgdesc/url.

- docker-entrypoint.sh (CWE-78): input values were interpolated into a
  single-quoted program passed to `su - builder -c`, so an embedded quote in a
  value (e.g. the tarball path) could run commands as builder. The user switch
  now uses a fixed argument vector via `runuser`/`su` with values passed through
  `env` as data; HOME is set explicitly to the builder's home.

Verified with `bash -n` and shellcheck (clean), plus functional checks that the
validators reject injection payloads and accept legitimate names.

* security(arch-package): harden symlink/pkgname handling per review

Address review feedback:
- A sole symlinked wrapper entry skipped the flatten branch entirely and was
  left in staging, where package() could copy it and later directory creation
  could follow it outside $pkgdir. Reject a symlinked wrapper outright before
  the flatten logic.
- The pkgname validator rejected uppercase letters, which broke legitimate
  freedesktop IDs such as org.gnome.Nautilus (and the ai.storyteller.$app
  fallback when $app had uppercase). makepkg accepts mixed case; uppercase does
  not weaken the injection fix (the character set still excludes shell
  metacharacters and printf %q escapes the value). Allow A-Z.
- docker-entrypoint referenced optional inputs bare under `set -u`, which could
  fail if OUTPUT_DIR (or another optional) was unset. Resolve all inputs to
  defaults once and pass the resolved values to the builder.
2026-10-22 11:40:44 -04:00
Dylan M. Taylor d04f2a42da security(release): keep app-title and version metadata out of shell source (#47)
draft-release and resolve-version interpolated caller-provided metadata directly
into Bash source (CWE-78):

- draft-release embedded app-title into the `title=` assignment in the step that
  has GH_TOKEN available.
- resolve-version embedded input-version into a `v=` assignment before it was
  validated, and app-title into the job-summary `echo`.

Both inputs now travel through the step `env:` and are read with quoted variable
expansions; the version is validated before it is used to produce outputs, and
the summary is written with a fixed `printf` format. No change to outputs or
release behavior.
2026-10-22 11:40:30 -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
Dylan M. Taylor fc9e47894e ci(sonar): skip SonarCloud scan on fork pull requests (#48)
GitHub does not expose repository secrets (including SONAR_TOKEN) to pull
requests opened from forks. Because the reusable sonarcloud.yml declared
SONAR_TOKEN as a required secret and sonar-scan.yml called it with
`secrets: inherit` on every pull_request, fork PRs failed immediately with:

  Error when evaluating 'secrets' ... Secret SONAR_TOKEN is required, but not
  provided while calling.

This made every fork PR show a failing "SonarCloud Scan" check unrelated to the
PR's contents. (The separate SonarCloud GitHub App check, "SonarCloud Code
Analysis", is unaffected and continues to run.)

Fixes:
- Gate the caller job so it only runs on pushes to main and on same-repo PRs;
  fork PRs now report a neutral skipped status instead of a hard failure.
- Make SONAR_TOKEN optional in the reusable workflow and guard the scan step on
  the token being present, so a missing token never fails the workflow.

Verified with actionlint.
2026-10-21 19:04:08 -04:00
Dylan M. Taylor ec92568a7a ci: exclude infra/glue code from SonarCloud coverage gate
craft-common-actions holds reusable GitHub Actions (YAML), composite actions,
shell, and Python helper/test scripts with no unit-test coverage surface.
Exclude actions/, tests/, .github/, and shell/python from the coverage gate so
the new-code coverage condition doesn't block the project.
2026-10-21 17:44:12 -04:00
Dylan M. Taylor 10bdd0e0e2 ci: re-trigger SonarCloud scan after disabling Automatic Analysis 2026-10-21 17:42:48 -04:00
Dylan M. Taylor cab3588fb6 ci: re-trigger SonarCloud scan 2026-10-21 16:04:50 -04:00
Dylan M. Taylor e245b3d9ea ci: add sonar-project.properties for CI-based analysis
Marks the project ineligible for SonarCloud Automatic Analysis, resolving the
CI-vs-automatic-analysis conflict so the SonarCloud workflow succeeds.
2026-10-21 16:00:24 -04:00
Dylan M. Taylor 7f2a5aabd6 ci: restore reusable SonarCloud workflow; add self-scan caller
Fixes an accidental overwrite where the reusable workflow_call definition was
replaced by a caller stub. The reusable workflow lives at sonarcloud.yml; this
repo's own scan now runs via sonar-scan.yml calling the local reusable workflow.
2026-10-21 15:48:48 -04:00
Dylan M. Taylor b8c9254bfd ci: add SonarCloud scan via shared reusable workflow
Calls storytold/craft-common-actions/.github/workflows/sonarcloud.yml@main
with secrets: inherit. Adds SonarCloud badge to README where missing and
removes the redundant sonar-project.properties (config now passed by the
reusable workflow).
2026-10-21 15:46:48 -04:00
Dylan M. Taylor cc230f9e93 ci: add reusable SonarCloud scan workflow (#43)
Reusable workflow_call entrypoint for SonarCloud analysis across craft repos.
Consumer repos call it via storytold/craft-common-actions/.github/workflows/sonarcloud.yml@main.
Derives project key as storytold_<repo> unless project-key input is provided.
2026-10-21 15:44:53 -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
9824a77554 fix(release): find the Linux tarball by version, not app-name (#37)
#35 matched <app-name>-<version>-linux-<arch>.tar.gz exactly. craft-launcher
passes app-name craft-launcher but ships artcraft-launcher-*, so its 0.6.1
Arch and Flatpak jobs found nothing. Match *-<version>-linux-<arch>.tar.gz,
skip -cli- and -server- companion tarballs, and require exactly one.

Co-authored-by: Brandon Thomas <brandon@storyteller.ai>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 03:42:19 -04:00
Brandon ThomasandClaude Opus 5.5 d18075e30c Skip CodeRabbit reviews on Dependabot PRs (#36)
Adds .coderabbit.yaml so CodeRabbit no longer reviews or comments on PRs opened by dependabot[bot].

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 11:42:28 -04:00
fd68caa519 fix(release): pick the app's own Linux tarball by exact name (#35)
* fix(release): pick the app's own Linux tarball by exact name

The Arch and Flatpak jobs globbed *-linux-<arch>.tar.gz. PDFCraft also ships
pdfcraft-cli-<ver>-linux-<arch>.tar.gz, so Arch saw two tarballs and failed,
and Flatpak aarch64 took the CLI tarball (no bin/pdfcraft). Select
<app-name>-<version>-linux-<arch>.tar.gz instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(release): pass tarball name parts through step env

Review (CodeRabbit, CWE-78): app-name is a free-form reusable-workflow
input, and interpolating it into run: puts caller text into the shell
source. The Flatpak and Arch steps now read APP_NAME, RELEASE_VERSION
and ARCH from env.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Brandon Thomas <brandon@storyteller.ai>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 11:15:03 -04:00
Torben d7bf8f06e1 Create dependabot.yml (#30)
copied from https://github.com/storytold/photocraft/blob/main/.github/dependabot.yml
2026-10-19 01:14:25 -04:00
skylar taylor-barrick c6f87c1cc0 Merge pull request #32 from storytold/codex/freebsd-ci-cargo-cache
ci(freebsd): keep ARM coverage outside default PR checks and cache Cargo builds
2026-10-18 22:48:01 -06: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
Dylan M. Taylor 7fe542f0e5 fix(arch-package): use [[ for test constructs and merge nested conditionals (#25) 2026-10-18 14:44:20 -04:00
Dylan M. Taylor c89691d3c0 security(ci): restrict top-level workflow permissions to read-only in release.yml (#27) 2026-10-18 14:43:57 -04:00
Dylan M. Taylor 98a5a2f7e7 security(actions): enforce HTTPS on curl redirects and repository URLs (#26) 2026-10-18 14:43:01 -04: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
ihsan mokhlisse 58076cf0ff docs: correct the release FreeBSD job limit (#22)
The release job timeout is 300 minutes so aarch64 can run under QEMU.
The limits table still listed the previous 180 minute value.
2026-10-18 07:57:19 -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