diff --git a/.github/workflows/freebsd.yml b/.github/workflows/freebsd.yml
index b10e65a75..bdfdc21c1 100644
--- a/.github/workflows/freebsd.yml
+++ b/.github/workflows/freebsd.yml
@@ -1,16 +1,23 @@
-# Builds and tests the workspace on FreeBSD, inside a FreeBSD VM on a Linux runner
-# (GitHub has no FreeBSD runners). GPU tests skip themselves when the VM has no adapter.
+# FreeBSD build and package check in a QEMU VM.
name: FreeBSD
on:
push:
branches: [main]
- pull_request:
paths:
- - Cargo.toml
- - Cargo.lock
- - "**/Cargo.toml"
- - .github/workflows/freebsd.yml
+ - ".github/workflows/freebsd.yml"
+ - "packaging/freebsd/**"
+ - "Cargo.toml"
+ - "Cargo.lock"
+ - "crates/**"
+ pull_request:
+ branches: [main]
+ paths:
+ - ".github/workflows/freebsd.yml"
+ - "packaging/freebsd/**"
+ - "Cargo.toml"
+ - "Cargo.lock"
+ - "crates/**"
workflow_dispatch:
concurrency:
@@ -22,34 +29,7 @@ permissions:
jobs:
freebsd:
- name: FreeBSD 14 (build + test)
- runs-on: ubuntu-latest
- timeout-minutes: 300
- steps:
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- # Optional font build input, so the Japanese font tests run (they skip without it).
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- with:
- repository: storytold/craft-fonts
- ref: 3d9ab3a132ed0eff457e424226a435a901da30d3
- path: craft-fonts
- - name: Build and test in a FreeBSD VM
- uses: vmactions/freebsd-vm@c46abacb49f09938ca4e1702d15d836285d694cc # v1.5.9
- with:
- release: "14.3"
- usesh: true
- cpu: 4
- mem: 12288
- # Nothing to bring back; copying the target dir out of the VM fails on large builds.
- copyback: false
- prepare: |
- pkg install -y git curl pkgconf cmake python3 libxkbcommon wayland libX11 libXcursor libXrandr libXi libxcb mesa-libs vulkan-loader gtk3 fontconfig freetype2 alsa-lib
- curl -fsSL https://sh.rustup.rs | sh -s -- -y --profile minimal
- run: |
- . "$HOME/.cargo/env"
- # The checkout is synced in under another owner; let tests that call git read it.
- git config --global --add safe.directory "*"
- rustc --version
- export CRAFT_FONTS_DIR="$PWD/craft-fonts" CRAFT_FONTS_REQUIRED=1
- CARGO_PROFILE_DEV_DEBUG=0 cargo build --workspace --all-targets --locked
- CARGO_PROFILE_DEV_DEBUG=0 cargo test --workspace --locked
+ # Intentionally tracked at @main (not SHA-pinned) so org-wide CI improvements, security fixes, and build optimizations propagate without PR churn across all craftapp repos.
+ uses: storytold/craft-common-actions/.github/workflows/freebsd.yml@main
+ with:
+ craft-fonts-ref: 3d9ab3a132ed0eff457e424226a435a901da30d3
diff --git a/.github/workflows/packaging-lint.yml b/.github/workflows/packaging-lint.yml
index 7ec855992..f54b0538b 100644
--- a/.github/workflows/packaging-lint.yml
+++ b/.github/workflows/packaging-lint.yml
@@ -1,7 +1,5 @@
-# Quick checks for the release pipeline (no Rust build): workflow syntax, packaging
-# shell/PowerShell scripts, and the Linux desktop/AppStream/Flatpak metadata (seconds), plus a
-# Windows job that builds and installs throwaway per-user MSI probes for the install-folder
-# searches (a few minutes).
+# Quick static checks for the release pipeline (seconds, no Rust build): workflow syntax,
+# packaging shell/PowerShell scripts, and the Linux desktop/AppStream/Flatpak metadata.
name: Packaging lint
on:
@@ -21,75 +19,5 @@ concurrency:
jobs:
lint:
- timeout-minutes: 15
- runs-on: ubuntu-latest
- env:
- ACTIONLINT_VERSION: 1.7.12
- steps:
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- - name: Install tools
- run: |
- sudo apt-get update
- sudo apt-get install -y --no-install-recommends shellcheck desktop-file-utils appstream libxml2-utils python3-yaml
- curl -fsSL "https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz" \
- | tar -xz -C "$RUNNER_TEMP" actionlint
- - name: actionlint
- run: |
- "$RUNNER_TEMP/actionlint" -color
- - name: shellcheck
- run: shellcheck packaging/*.sh packaging/*/*.sh
- - name: PowerShell
- shell: pwsh
- run: |
- foreach ($f in Get-ChildItem packaging/windows/*.ps1) {
- $errors = $null
- [System.Management.Automation.Language.Parser]::ParseFile($f.FullName, [ref]$null, [ref]$errors) | Out-Null
- if ($errors) { $errors | ForEach-Object { Write-Output "::error file=$($f.Name)::$_" }; exit 1 }
- Write-Output "ok $($f.Name)"
- }
- - name: XML, desktop entry, AppStream, Flatpak manifest
- run: |
- xmllint --noout packaging/windows/lightcraft.wxs packaging/macos/*.plist* packaging/linux/*.xml*
- # Windows Installer needs Icon ids ending in .ico or .exe, or shortcuts show a blank icon.
- if grep -oE '' 'Value="InstallDirDlg"' '' \
- 'Key="Software\LightCraft" Name="InstallFolder"' 'Value="LaunchLightcraft"'; do
- grep -qF "$want" packaging/windows/lightcraft.wxs || { echo "::error::lightcraft.wxs lost: $want"; exit 1; }
- done
- # The completion page the Publish events point at must actually exist: a Publish alone
- # does not pull the dialog fragment into the MSI, and without the DialogRef the
- # installer finishes silently (issues #790, #892).
- grep -qF '' packaging/windows/lightcraft.wxs \
- || { echo "::error::ExitDialog is not referenced; installs and removes end without a completion dialog"; exit 1; }
- desktop-file-validate packaging/linux/ai.storyteller.lightcraft.desktop
- sed -e 's/@VERSION@/0.0.0/' -e 's/@DATE@/2026-01-01/' packaging/linux/ai.storyteller.lightcraft.metainfo.xml.in >"$RUNNER_TEMP/ai.storyteller.lightcraft.metainfo.xml"
- appstreamcli validate --no-net --explain "$RUNNER_TEMP/ai.storyteller.lightcraft.metainfo.xml"
- python3 - <<'EOF'
- import yaml
- src = yaml.safe_load(open("packaging/linux/flatpak/ai.storyteller.lightcraft.yml"))
- bundle = yaml.safe_load(open("packaging/linux/flatpak/ai.storyteller.lightcraft.bundle.yml"))
- for m in (src, bundle):
- assert m["id"] == "ai.storyteller.lightcraft" and m["command"] == "lightcraft", m["id"]
- for key in ("runtime", "runtime-version", "sdk", "finish-args"):
- assert src[key] == bundle[key], f"Flatpak manifests disagree on {key}: {src[key]} vs {bundle[key]}"
- print("Flatpak manifests ok")
- EOF
- - name: FreeBSD tarball layout (stub binaries)
- run: bash packaging/freebsd/package.sh --dry-run
-
- windows-install-folder:
- runs-on: windows-latest
- timeout-minutes: 10
- steps:
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- - name: Install WiX
- shell: pwsh
- run: dotnet tool install --global wix --version 5.0.2
- - name: Install-folder fallback and precedence (real MSI AppSearch)
- shell: pwsh
- run: |
- ./packaging/windows/test-install-folder.ps1 -Arch x64
- ./packaging/windows/test-install-folder.ps1 -Arch x86
+ # Intentionally tracked at @main (not SHA-pinned) so org-wide CI improvements, security fixes, and build optimizations propagate without PR churn across all craftapp repos.
+ uses: storytold/craft-common-actions/.github/workflows/packaging-lint.yml@main
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 392ee4eb7..e24229356 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -1,13 +1,5 @@
-# Release pipeline: every push to `release` builds platform artifacts (signed where credentials
-# are configured) and creates or updates the draft GitHub Release "LightCraft v".
-# See docs/releasing.md.
-#
-# The signing jobs (macOS, Windows) and the draft-release job run in the `release` environment
-# (restricted to the `release` branch), which holds the signing secrets. Every secret is optional:
-# missing ones produce unsigned artifacts and a warning, never a failed build. The jobs that sign
-# nothing (version, Linux, Linux riscv64, Flatpak, FreeBSD, web) run without it, so a dispatch on
-# any branch is a dry run of them; the release job needs the signed jobs, so a branch run never
-# drafts a release.
+# Release pipeline: every push to `release` builds signed installers for every platform and
+# creates or updates the draft GitHub Release "LightCraft v". See docs/releasing.md.
name: Release
on:
@@ -21,479 +13,33 @@ on:
default: ""
permissions:
- contents: read
+ contents: write
concurrency:
group: release-${{ github.ref }}
- # Never cancel a run mid-notarization or mid-upload; a newer push waits its turn.
cancel-in-progress: false
-env:
- CARGO_TERM_COLOR: always
- CARGO_INCREMENTAL: 0
- RUST_BACKTRACE: 1
- LIGHTCRAFT_BUILD_SHA: ${{ github.sha }}
- INPUT_VERSION: ${{ inputs.version }}
-
jobs:
- version:
- timeout-minutes: 10
- name: version
- runs-on: ubuntu-latest
- # No `environment: release` here or on the other jobs that sign nothing: no release secrets.
- outputs:
- version: ${{ steps.v.outputs.version }}
- date: ${{ steps.v.outputs.date }}
- prerelease: ${{ steps.v.outputs.prerelease }}
- steps:
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- - id: v
- name: Resolve version
- run: |
- . packaging/env.sh
- v="${INPUT_VERSION#v}"
- v="${v:-$VERSION}"
- if ! printf '%s' "$v" | grep -Eq '^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$'; then
- echo "::error::'$v' is not a version like 1.2.3 or 1.2.3-rc.1"
- exit 1
- fi
- case "$v" in *-*) pre=true ;; *) pre=false ;; esac
- {
- echo "version=$v"
- echo "date=$(date -u +%Y-%m-%d)"
- echo "prerelease=$pre"
- } >>"$GITHUB_OUTPUT"
- echo "### LightCraft v$v (\`${GITHUB_SHA::9}\`)" >>"$GITHUB_STEP_SUMMARY"
-
- macos:
- timeout-minutes: 90
- name: macOS universal
- needs: version
- runs-on: macos-15-xlarge # M2 Pro larger runner; use only in release.yml (actions_macos_xl budget caps spend)
- environment: release
- env:
- LIGHTCRAFT_VERSION: ${{ needs.version.outputs.version }}
- LIGHTCRAFT_BUILD_DATE: ${{ needs.version.outputs.date }}
- steps:
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- # Fonts (optional build input; required for releases): see craftrules standards/fonts.md.
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- with:
- repository: storytold/craft-fonts
- ref: 3d9ab3a132ed0eff457e424226a435a901da30d3 # bump deliberately
- path: craft-fonts
- - uses: dtolnay/rust-toolchain@686976e191b89faba57d3206551f0f330d8cb249 # stable
- with:
- targets: aarch64-apple-darwin, x86_64-apple-darwin
- - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
- with:
- key: release-macos-universal
- - name: Apply version override
- if: inputs.version != ''
- run: cargo xtask version set "$LIGHTCRAFT_VERSION"
- - name: Import signing certificate
- env:
- APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
- APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
- KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
- run: packaging/macos/import-cert.sh
- - name: Build, sign, notarize
- env:
- CRAFT_FONTS_DIR: ${{ github.workspace }}/craft-fonts
- CRAFT_FONTS_REQUIRED: "1"
- APPLE_ID: ${{ secrets.APPLE_ID }}
- APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
- APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
- run: packaging/macos/package.sh --arch universal
- - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
- with:
- name: release-macos
- path: dist/release/*
- if-no-files-found: error
- - name: Remove temporary keychain
- if: always()
- run: |
- if [ -n "${MACOS_KEYCHAIN:-}" ]; then security delete-keychain "$MACOS_KEYCHAIN" || true; fi
-
- windows:
- timeout-minutes: 90
- name: Windows ${{ matrix.arch }}
- needs: version
- runs-on: windows-latest
- environment: release
- strategy:
- fail-fast: false
- matrix:
- include:
- - { arch: x64, target: x86_64-pc-windows-msvc }
- - { arch: x86, target: i686-pc-windows-msvc }
- # Windows on ARM: cross-compiled on the x64 runner, so signing and WiX run as above.
- # .github/workflows/windows-arm64.yml installs and runs it on ARM64 hardware.
- - { arch: arm64, target: aarch64-pc-windows-msvc }
- env:
- LIGHTCRAFT_VERSION: ${{ needs.version.outputs.version }}
- LIGHTCRAFT_BUILD_DATE: ${{ needs.version.outputs.date }}
- steps:
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- # Fonts (optional build input; required for releases): see craftrules standards/fonts.md.
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- with:
- repository: storytold/craft-fonts
- ref: 3d9ab3a132ed0eff457e424226a435a901da30d3 # bump deliberately
- path: craft-fonts
- - uses: dtolnay/rust-toolchain@686976e191b89faba57d3206551f0f330d8cb249 # stable
- with:
- targets: ${{ matrix.target }}
- - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
- with:
- key: release-windows-${{ matrix.arch }}
- - name: Apply version override
- if: inputs.version != ''
- shell: bash
- run: cargo xtask version set "$LIGHTCRAFT_VERSION"
- # package.ps1 adds the matching WixToolset.UI/Util.wixext (5.0.2) to the global extension cache.
- - name: Install WiX
- shell: pwsh
- run: dotnet tool install --global wix --version 5.0.2
- - name: Build, sign, package
- shell: pwsh
- env:
- CRAFT_FONTS_DIR: ${{ github.workspace }}/craft-fonts
- CRAFT_FONTS_REQUIRED: "1"
- WINDOWS_CERTIFICATE: ${{ secrets.WINDOWS_CERTIFICATE }}
- WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
- AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
- AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
- AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
- AZURE_SIGNING_ENDPOINT: ${{ secrets.AZURE_SIGNING_ENDPOINT }}
- AZURE_SIGNING_ACCOUNT: ${{ secrets.AZURE_SIGNING_ACCOUNT }}
- AZURE_CERT_PROFILE: ${{ secrets.AZURE_CERT_PROFILE }}
- run: ./packaging/windows/package.ps1 -Arch ${{ matrix.arch }}
- - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
- with:
- name: release-windows-${{ matrix.arch }}
- path: dist/release/*
- if-no-files-found: error
-
- linux:
- timeout-minutes: 90
- name: Linux ${{ matrix.arch }}
- needs: version
- # The oldest GitHub-hosted image, so the binaries only need glibc >= 2.35 (Ubuntu 22.04+,
- # Debian 12+, Fedora 36+, RHEL 10). See docs/releasing.md.
- runs-on: ${{ matrix.runner }}
- strategy:
- fail-fast: false
- matrix:
- include:
- - { arch: x86_64, runner: ubuntu-22.04, deb: amd64 }
- - { arch: aarch64, runner: ubuntu-22.04-arm, deb: arm64 }
- env:
- LIGHTCRAFT_VERSION: ${{ needs.version.outputs.version }}
- LIGHTCRAFT_BUILD_DATE: ${{ needs.version.outputs.date }}
- NFPM_VERSION: 2.47.0
- steps:
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- # Fonts (optional build input; required for releases): see craftrules standards/fonts.md.
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- with:
- repository: storytold/craft-fonts
- ref: 3d9ab3a132ed0eff457e424226a435a901da30d3 # bump deliberately
- path: craft-fonts
- - name: Install build and packaging tools
- run: |
- sudo apt-get update
- sudo apt-get install -y --no-install-recommends \
- libxkbcommon-dev libwayland-dev libx11-dev libxrandr-dev libxi-dev libgl1-mesa-dev \
- desktop-file-utils appstream python3-yaml zsync
- curl -fsSL -o "$RUNNER_TEMP/nfpm.deb" \
- "https://github.com/goreleaser/nfpm/releases/download/v${NFPM_VERSION}/nfpm_${NFPM_VERSION}_${{ matrix.deb }}.deb"
- sudo dpkg -i "$RUNNER_TEMP/nfpm.deb"
- - uses: dtolnay/rust-toolchain@686976e191b89faba57d3206551f0f330d8cb249 # stable
- - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
- with:
- key: release-linux-${{ matrix.arch }}
- - name: Apply version override
- if: inputs.version != ''
- run: cargo xtask version set "$LIGHTCRAFT_VERSION"
- - name: Build and package (AppImage, deb, rpm, tar.gz)
- env:
- CRAFT_FONTS_DIR: ${{ github.workspace }}/craft-fonts
- CRAFT_FONTS_REQUIRED: "1"
- run: packaging/linux/package.sh
- - name: Check the packages
- run: |
- dpkg-deb --info dist/release/*.deb
- dpkg-deb --contents dist/release/*.deb | grep -E 'usr/bin/lightcraft$|metainfo|applications'
- ldd target/release/lightcraft
- python3 -c "import yaml; m = yaml.safe_load(open('packaging/linux/flatpak/ai.storyteller.lightcraft.yml')); assert m['id'] == 'ai.storyteller.lightcraft'"
- for appimage in dist/release/*.AppImage; do
- chmod +x "$appimage"
- APPIMAGE_EXTRACT_AND_RUN=1 "$appimage" --version
- # Update information is embedded, and the .zsync for delta updates sits beside it.
- "$appimage" --appimage-updateinformation | grep -F 'gh-releases-zsync|'
- test -s "$appimage.zsync"
- done
- - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
- with:
- name: release-linux-${{ matrix.arch }}
- path: dist/release/*
- if-no-files-found: error
-
- flatpak:
- timeout-minutes: 90
- name: Flatpak ${{ matrix.arch }}
- needs: [version, linux]
- # Repackages the Linux job's tarball (the same binaries as the AppImage/deb/rpm); no Rust
- # build here. 24.04 for a newer flatpak-builder (1.4) than 22.04 ships.
- runs-on: ${{ matrix.runner }}
- strategy:
- fail-fast: false
- matrix:
- include:
- - { arch: x86_64, runner: ubuntu-24.04 }
- - { arch: aarch64, runner: ubuntu-24.04-arm }
- env:
- LIGHTCRAFT_VERSION: ${{ needs.version.outputs.version }}
- LIGHTCRAFT_BUILD_DATE: ${{ needs.version.outputs.date }}
- steps:
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- - uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
- with:
- name: release-linux-${{ matrix.arch }}
- path: dist/linux
- - name: Install flatpak-builder
- run: |
- sudo apt-get update
- # librsvg2-common: the SVG pixbuf loader. flatpak-builder runs the host's
- # `appstreamcli compose`, which renders the scalable icon and fails without it.
- sudo apt-get install -y --no-install-recommends flatpak flatpak-builder librsvg2-common
- # Ubuntu 24.04's AppArmor blocks the unprivileged user namespaces bubblewrap needs.
- sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
- - name: Build the Flatpak bundle
- run: packaging/linux/flatpak-bundle.sh "dist/linux/lightcraft-$LIGHTCRAFT_VERSION-linux-${{ matrix.arch }}.tar.gz"
- - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
- with:
- name: release-flatpak-${{ matrix.arch }}
- path: dist/release/*.flatpak
- if-no-files-found: error
-
- linux-riscv64:
- timeout-minutes: 90
- name: Linux riscv64
- needs: version
- # Cross-compiled on x86_64 (no hosted RISC-V runners) against the runner's own Ubuntu release
- # from ports.ubuntu.com, so the binaries need that release's glibc (24.04: 2.39). 22.04's
- # riscv64 ports lag its amd64 updates, which breaks the multiarch install. tar.gz only; the
- # CLI is smoke-tested under QEMU.
- runs-on: ubuntu-24.04
- # No `environment: release`: this job signs nothing, so it gets no release secrets.
- env:
- LIGHTCRAFT_VERSION: ${{ needs.version.outputs.version }}
- LIGHTCRAFT_BUILD_DATE: ${{ needs.version.outputs.date }}
- steps:
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- # Fonts (optional build input; required for releases): see craftrules standards/fonts.md.
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- with:
- repository: storytold/craft-fonts
- ref: 3d9ab3a132ed0eff457e424226a435a901da30d3 # bump deliberately
- path: craft-fonts
- - uses: dtolnay/rust-toolchain@686976e191b89faba57d3206551f0f330d8cb249 # stable
- with:
- targets: riscv64gc-unknown-linux-gnu
- - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
- with:
- key: release-linux-riscv64
- - name: Apply version override
- if: inputs.version != ''
- run: cargo xtask version set "$LIGHTCRAFT_VERSION"
- - name: Install cross-compilation dependencies
- run: |
- . /etc/os-release
- sudo dpkg --add-architecture riscv64
- # Pin the existing (archive.ubuntu.com) sources to amd64; riscv64 comes from ports.
- if [ -f /etc/apt/sources.list.d/ubuntu.sources ]; then
- sudo sed -i '/^Types: deb/a Architectures: amd64' /etc/apt/sources.list.d/ubuntu.sources
- fi
- if [ -f /etc/apt/sources.list ]; then
- sudo sed -i 's/^deb /deb [arch=amd64] /g' /etc/apt/sources.list
- sudo sed -i 's/^deb-src /deb-src [arch=amd64] /g' /etc/apt/sources.list
- fi
- for suite in "$VERSION_CODENAME" "$VERSION_CODENAME-updates" "$VERSION_CODENAME-security"; do
- echo "deb [arch=riscv64] http://ports.ubuntu.com/ubuntu-ports $suite main universe restricted multiverse"
- done | sudo tee /etc/apt/sources.list.d/riscv64.list
- sudo apt-get update
- sudo apt-get install -y --no-install-recommends \
- gcc-riscv64-linux-gnu binutils-riscv64-linux-gnu libc6-dev-riscv64-cross libc6-dev:riscv64 qemu-user \
- libxkbcommon-dev:riscv64 libwayland-dev:riscv64 libx11-dev:riscv64 libxrandr-dev:riscv64 \
- libxi-dev:riscv64 libgl1-mesa-dev:riscv64
- - name: Build and package (tar.gz)
- env:
- CRAFT_FONTS_DIR: ${{ github.workspace }}/craft-fonts
- CRAFT_FONTS_REQUIRED: "1"
- CARGO_TARGET_RISCV64GC_UNKNOWN_LINUX_GNU_LINKER: riscv64-linux-gnu-gcc
- PKG_CONFIG_SYSROOT_DIR: /
- PKG_CONFIG_PATH: /usr/lib/riscv64-linux-gnu/pkgconfig:/usr/share/pkgconfig
- PKG_CONFIG_ALLOW_CROSS: "1"
- CROSS_ARCH: riscv64
- CROSS_TARGET: riscv64gc-unknown-linux-gnu
- CROSS_COMPILE: riscv64-linux-gnu-
- EMULATOR: qemu-riscv64
- QEMU_LD_PREFIX: /usr/riscv64-linux-gnu
- run: packaging/linux/package.sh --formats tar
- - name: Check the tarball
- run: |
- tar -tzf dist/release/lightcraft-*-linux-riscv64.tar.gz \
- | grep -E '/bin/lightcraft$|/bin/lightcraft-cli$|/share/applications/ai\.storyteller\.lightcraft\.desktop$'
- - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
- with:
- name: release-linux-riscv64
- path: dist/release/*.tar.gz
- if-no-files-found: error
-
- freebsd:
- name: FreeBSD x86_64
- needs: version
- # GitHub has no FreeBSD runners: build in a FreeBSD 14.3 VM, the same image and packages as
- # .github/workflows/freebsd.yml.
- runs-on: ubuntu-latest
- timeout-minutes: 180
- env:
- LIGHTCRAFT_VERSION: ${{ needs.version.outputs.version }}
- LIGHTCRAFT_BUILD_DATE: ${{ needs.version.outputs.date }}
- steps:
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- # Fonts (optional build input; required for releases): see craftrules standards/fonts.md.
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- with:
- repository: storytold/craft-fonts
- ref: 3d9ab3a132ed0eff457e424226a435a901da30d3 # bump deliberately
- path: craft-fonts
- - name: Build and package in a FreeBSD VM (tar.gz)
- # vmactions/freebsd-vm v1.5.9, pinned by commit (it runs our build).
- uses: vmactions/freebsd-vm@c46abacb49f09938ca4e1702d15d836285d694cc # v1.5.9
- with:
- release: "14.3"
- usesh: true
- cpu: 4
- mem: 12288
- # Passed into the VM by name; never templated into the script below.
- envs: "LIGHTCRAFT_VERSION LIGHTCRAFT_BUILD_DATE LIGHTCRAFT_BUILD_SHA INPUT_VERSION"
- # The FreeBSD CI packages, plus bash for the packaging script.
- prepare: |
- pkg install -y bash git curl pkgconf cmake python3 libxkbcommon wayland libX11 libXcursor libXrandr libXi libxcb mesa-libs vulkan-loader gtk3 fontconfig freetype2 alsa-lib
- curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain stable
- run: |
- set -e
- . "$HOME/.cargo/env"
- git config --global --add safe.directory "$PWD"
- # Build outside the checkout so only dist/ is copied back out of the VM (copying the
- # target dir back fails on large builds). 4 jobs: more runs the VM out of memory.
- export CARGO_TARGET_DIR=/var/tmp/lightcraft-target CARGO_BUILD_JOBS=4
- if [ -n "$INPUT_VERSION" ]; then cargo xtask version set "$LIGHTCRAFT_VERSION"; fi
- # craft-fonts was checked out next to the sources and synced into the VM with them.
- export CRAFT_FONTS_DIR="$PWD/craft-fonts" CRAFT_FONTS_REQUIRED=1
- bash packaging/freebsd/package.sh
- tar -tzf dist/release/lightcraft-*-freebsd-x86_64.tar.gz \
- | grep -E '/bin/lightcraft$|/bin/lightcraft-cli$|/share/applications/ai\.storyteller\.lightcraft\.desktop$|/share/doc/lightcraft/LICENSE-MIT$'
- - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
- with:
- name: release-freebsd-x86_64
- path: dist/release/*
- if-no-files-found: error
-
- web:
- timeout-minutes: 90
- name: Web (wasm)
- needs: version
- runs-on: ubuntu-latest
- env:
- LIGHTCRAFT_VERSION: ${{ needs.version.outputs.version }}
- LIGHTCRAFT_BUILD_DATE: ${{ needs.version.outputs.date }}
- WASM_BINDGEN_VERSION: 0.2.129
- steps:
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- # Fonts (optional build input; required for releases): see craftrules standards/fonts.md.
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- with:
- repository: storytold/craft-fonts
- ref: 3d9ab3a132ed0eff457e424226a435a901da30d3 # bump deliberately
- path: craft-fonts
- - uses: dtolnay/rust-toolchain@686976e191b89faba57d3206551f0f330d8cb249 # stable
- with:
- targets: wasm32-unknown-unknown
- - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
- with:
- key: release-web
- - name: Install wasm-bindgen CLI
- run: |
- # Must match the wasm-bindgen version in Cargo.lock exactly (cargo xtask web checks it).
- if ! { command -v wasm-bindgen >/dev/null && wasm-bindgen --version | grep -q "$WASM_BINDGEN_VERSION"; }; then
- cargo install wasm-bindgen-cli --version "$WASM_BINDGEN_VERSION" --locked
- fi
- wasm-bindgen --version
- - name: Apply version override
- if: inputs.version != ''
- run: cargo xtask version set "$LIGHTCRAFT_VERSION"
- - name: Build and zip
- env:
- CRAFT_FONTS_DIR: ${{ github.workspace }}/craft-fonts
- CRAFT_FONTS_REQUIRED: "1"
- run: packaging/web/package.sh
- - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
- with:
- name: release-web
- path: dist/release/*
- if-no-files-found: error
-
release:
- timeout-minutes: 10
- name: Draft GitHub Release
- needs: [version, macos, windows, linux, linux-riscv64, flatpak, freebsd, web]
- runs-on: ubuntu-latest
- environment: release
- permissions:
- contents: write
- env:
- VERSION: ${{ needs.version.outputs.version }}
- PRERELEASE: ${{ needs.version.outputs.prerelease }}
- GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- GH_REPO: ${{ github.repository }}
- steps:
- - uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
- with:
- pattern: release-*
- path: artifacts
- merge-multiple: true
- - name: Checksums
- working-directory: artifacts
- run: |
- sha256sum -- * | tee SHA256SUMS.txt
- - name: Create or update the draft release
- working-directory: artifacts
- run: |
- tag="v$VERSION"
- title="LightCraft v$VERSION"
- pre=()
- if [ "$PRERELEASE" = true ]; then pre=(--prerelease); fi
- if draft="$(gh release view "$tag" --json isDraft --jq .isDraft 2>/dev/null)"; then
- if [ "$draft" != "true" ]; then
- echo "::error::Release $tag is already published. Bump the version (cargo xtask version set) before pushing to release."
- exit 1
- fi
- echo "Updating draft $tag: replacing its assets"
- gh release view "$tag" --json assets --jq '.assets[].name' | while read -r asset; do
- gh release delete-asset "$tag" "$asset" --yes
- done
- gh release edit "$tag" --draft --title "$title" --target "$GITHUB_SHA" --prerelease="$PRERELEASE"
- gh release upload "$tag" --clobber -- *
- else
- echo "Creating draft $tag"
- gh release create "$tag" --draft --title "$title" --target "$GITHUB_SHA" --generate-notes "${pre[@]}" -- *
- fi
- {
- echo "Draft release **$title** at \`${GITHUB_SHA::9}\`:"
- echo
- sed 's/^/ /' SHA256SUMS.txt
- } >>"$GITHUB_STEP_SUMMARY"
+ # Intentionally tracked at @main (not SHA-pinned) so org-wide CI improvements, security fixes, and build optimizations propagate without PR churn across all craftapp repos.
+ uses: storytold/craft-common-actions/.github/workflows/release.yml@main
+ with:
+ app-name: lightcraft
+ app-title: LightCraft
+ craft-fonts-ref: 3d9ab3a132ed0eff457e424226a435a901da30d3
+ version: ${{ inputs.version }}
+ secrets:
+ APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
+ APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
+ KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
+ APPLE_ID: ${{ secrets.APPLE_ID }}
+ APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
+ APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
+ WINDOWS_CERTIFICATE: ${{ secrets.WINDOWS_CERTIFICATE }}
+ WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
+ AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
+ AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
+ AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
+ AZURE_SIGNING_ENDPOINT: ${{ secrets.AZURE_SIGNING_ENDPOINT }}
+ AZURE_SIGNING_ACCOUNT: ${{ secrets.AZURE_SIGNING_ACCOUNT }}
+ AZURE_CERT_PROFILE: ${{ secrets.AZURE_CERT_PROFILE }}
diff --git a/.github/workflows/windows-arm64.yml b/.github/workflows/windows-arm64.yml
index 1cccd858b..0033346dc 100644
--- a/.github/workflows/windows-arm64.yml
+++ b/.github/workflows/windows-arm64.yml
@@ -1,22 +1,27 @@
-# Windows on ARM: package the ARM64 release the way release.yml does (cross-compiled on an x64
-# runner, unsigned here), then install that MSI on a Windows 11 ARM64 runner, check the installed
-# binaries are ARM64, run the CLI natively, and uninstall. (A native ARM64 `cargo test` job is left
-# out for now: PdfCraft saw an intermittent access violation in egui UI tests there.)
+# Windows ARM64 build and smoke test.
+#
+# Release packages on arm64-windows are produced on x64 runners via cross-compilation
+# (target: aarch64-pc-windows-msvc) using MSVC's ARM64 cross tools, then installed and run
+# on GitHub Actions' native windows-11-arm runner.
name: Windows ARM64
on:
push:
branches: [main]
- pull_request:
paths:
- - Cargo.toml
- - Cargo.lock
- - "**/Cargo.toml"
- - "**/build.rs"
- - .github/workflows/windows-arm64.yml
- - packaging/windows/**
- schedule:
- - cron: "41 4 * * 1" # weekly, so drift in the runner images shows up
+ - ".github/workflows/windows-arm64.yml"
+ - "packaging/windows/**"
+ - "Cargo.toml"
+ - "Cargo.lock"
+ - "crates/**"
+ pull_request:
+ branches: [main]
+ paths:
+ - ".github/workflows/windows-arm64.yml"
+ - "packaging/windows/**"
+ - "Cargo.toml"
+ - "Cargo.lock"
+ - "crates/**"
workflow_dispatch:
concurrency:
@@ -26,70 +31,10 @@ concurrency:
permissions:
contents: read
-env:
- CARGO_TERM_COLOR: always
- CARGO_INCREMENTAL: 0
-
jobs:
- package:
- name: package (cross-compiled on x64)
- runs-on: windows-latest
- timeout-minutes: 90
- steps:
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- - uses: dtolnay/rust-toolchain@686976e191b89faba57d3206551f0f330d8cb249 # stable
- with:
- targets: aarch64-pc-windows-msvc
- - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
- with:
- key: windows-arm64-package
- - name: Install WiX
- shell: pwsh
- run: dotnet tool install --global wix --version 5.0.2
- - name: Build and package (unsigned)
- shell: pwsh
- env:
- DIST: ${{ github.workspace }}\dist\arm64
- run: ./packaging/windows/package.ps1 -Arch arm64
- - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
- with:
- name: lightcraft-windows-arm64
- path: dist/arm64/*
- if-no-files-found: error
- retention-days: 7
-
- install:
- name: install and run on ARM64
- needs: package
- runs-on: windows-11-arm
- timeout-minutes: 30
- steps:
- - uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
- with:
- name: lightcraft-windows-arm64
- path: dist
- - name: Install the MSI, check it, run it, uninstall it
- shell: pwsh
- run: |
- $ErrorActionPreference = 'Stop'
- $msi = (Get-ChildItem dist/*.msi | Select-Object -First 1).FullName
- $p = Start-Process msiexec.exe -ArgumentList '/i', "`"$msi`"", '/qn', '/l*v', 'install.log' -Wait -PassThru
- if ($p.ExitCode -ne 0) { Get-Content install.log -Tail 60; throw "msiexec /i exited $($p.ExitCode)" }
- $dir = Join-Path $env:ProgramFiles 'LightCraft'
- foreach ($exe in 'lightcraft.exe', 'lightcraft-cli.exe') {
- $path = Join-Path $dir $exe
- if (-not (Test-Path $path)) { throw "$exe not installed in $dir" }
- $bytes = [System.IO.File]::ReadAllBytes($path)
- $machine = [BitConverter]::ToUInt16($bytes, [BitConverter]::ToInt32($bytes, 0x3C) + 4)
- if ($machine -ne 0xAA64) { throw "$exe is for machine 0x$('{0:X}' -f $machine), not ARM64" }
- Write-Output "ok $path (ARM64)"
- }
- # The installed CLI runs natively on ARM64.
- $cli = Join-Path $dir 'lightcraft-cli.exe'
- $out = & $cli --version 2>&1 | Out-String
- if ($LASTEXITCODE -ne 0) { Write-Output $out; throw "lightcraft-cli --version exited $LASTEXITCODE" }
- Write-Output "ok lightcraft-cli --version: $($out.Trim())"
- $p = Start-Process msiexec.exe -ArgumentList '/x', "`"$msi`"", '/qn' -Wait -PassThru
- if ($p.ExitCode -ne 0) { throw "msiexec /x exited $($p.ExitCode)" }
- if (Test-Path (Join-Path $dir 'lightcraft.exe')) { throw 'uninstall left lightcraft.exe behind' }
- Write-Output 'ok uninstalled'
+ windows-arm64:
+ # Intentionally tracked at @main (not SHA-pinned) so org-wide CI improvements, security fixes, and build optimizations propagate without PR churn across all craftapp repos.
+ uses: storytold/craft-common-actions/.github/workflows/windows-arm64.yml@main
+ with:
+ app-name: lightcraft
+ app-dir: LightCraft