mirror of
https://github.com/storytold/wordcraft.git
synced 2026-10-29 12:57:09 -06:00
The MSI had no UI, so Windows Installer showed only its basic progress box and closed without saying the install had worked. It now uses WiX's standard WixUI_Minimal dialogs: a welcome page with the license (MIT or Apache-2.0, built at packaging time from LICENSE-MIT and LICENSE-APACHE), progress, and a finish page with an optional "Launch WordCraft" checkbox. The release and Windows ARM64 workflows install the WiX UI and Util extensions the build now needs. UpgradeCode and component GUIDs are unchanged. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
427 lines
18 KiB
YAML
427 lines
18 KiB
YAML
# Release pipeline: every push to `release` builds signed installers for every platform and
|
|
# creates or updates the draft GitHub Release "WordCraft v<version>". See docs/releasing.md.
|
|
#
|
|
# The signing jobs (macOS, Windows) and the job that drafts the release 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, Flatpak, FreeBSD, web) get no environment and no
|
|
# secrets, so a `workflow_dispatch` on any branch dry-runs them; there the signing jobs are refused
|
|
# by the environment's branch rule, and the release job, which needs them, is skipped.
|
|
name: Release
|
|
|
|
on:
|
|
push:
|
|
branches: [release]
|
|
workflow_dispatch:
|
|
inputs:
|
|
version:
|
|
description: "Version override for a test run, e.g. 0.2.0-rc.1 (empty: Cargo.toml)"
|
|
type: string
|
|
default: ""
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
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
|
|
WORDCRAFT_BUILD_SHA: ${{ github.sha }}
|
|
INPUT_VERSION: ${{ inputs.version }}
|
|
|
|
jobs:
|
|
version:
|
|
name: version
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
version: ${{ steps.v.outputs.version }}
|
|
date: ${{ steps.v.outputs.date }}
|
|
prerelease: ${{ steps.v.outputs.prerelease }}
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- 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 "### WordCraft v$v (\`${GITHUB_SHA::9}\`)" >>"$GITHUB_STEP_SUMMARY"
|
|
|
|
macos:
|
|
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:
|
|
WORDCRAFT_VERSION: ${{ needs.version.outputs.version }}
|
|
WORDCRAFT_BUILD_DATE: ${{ needs.version.outputs.date }}
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
# Optional font input (docs: storytold/craft-fonts docs/integration.md); releases always embed it.
|
|
- name: Check out craft-fonts
|
|
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
|
with:
|
|
repository: storytold/craft-fonts
|
|
ref: abb83316d96aa59c1cf64784289e378fe9fa5695
|
|
path: craft-fonts
|
|
- uses: dtolnay/rust-toolchain@stable
|
|
with:
|
|
targets: aarch64-apple-darwin, x86_64-apple-darwin
|
|
- uses: Swatinem/rust-cache@v2
|
|
with:
|
|
key: release-macos-universal
|
|
- name: Apply version override
|
|
if: inputs.version != ''
|
|
run: cargo xtask version set "$WORDCRAFT_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@v4
|
|
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:
|
|
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:
|
|
WORDCRAFT_VERSION: ${{ needs.version.outputs.version }}
|
|
WORDCRAFT_BUILD_DATE: ${{ needs.version.outputs.date }}
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
# Optional font input (docs: storytold/craft-fonts docs/integration.md); releases always embed it.
|
|
- name: Check out craft-fonts
|
|
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
|
with:
|
|
repository: storytold/craft-fonts
|
|
ref: abb83316d96aa59c1cf64784289e378fe9fa5695
|
|
path: craft-fonts
|
|
- uses: dtolnay/rust-toolchain@stable
|
|
with:
|
|
targets: ${{ matrix.target }}
|
|
- uses: Swatinem/rust-cache@v2
|
|
with:
|
|
key: release-windows-${{ matrix.arch }}
|
|
- name: Apply version override
|
|
if: inputs.version != ''
|
|
shell: bash
|
|
run: cargo xtask version set "$WORDCRAFT_VERSION"
|
|
- name: Install WiX
|
|
shell: pwsh
|
|
run: |
|
|
$PSNativeCommandUseErrorActionPreference = $true # stop at the first failing command
|
|
dotnet tool install --global wix --version 5.0.2
|
|
# Installer dialogs and the finish page's "Launch WordCraft" (packaging/windows/wordcraft.wxs).
|
|
wix extension add -g WixToolset.UI.wixext/5.0.2
|
|
wix extension add -g WixToolset.Util.wixext/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@v4
|
|
with:
|
|
name: release-windows-${{ matrix.arch }}
|
|
path: dist/release/*
|
|
if-no-files-found: error
|
|
|
|
linux:
|
|
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 }}
|
|
# No `environment: release`: this job signs nothing, so it gets no release secrets.
|
|
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:
|
|
WORDCRAFT_VERSION: ${{ needs.version.outputs.version }}
|
|
WORDCRAFT_BUILD_DATE: ${{ needs.version.outputs.date }}
|
|
NFPM_VERSION: 2.47.0
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
# Optional font input (docs: storytold/craft-fonts docs/integration.md); releases always embed it.
|
|
- name: Check out craft-fonts
|
|
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
|
with:
|
|
repository: storytold/craft-fonts
|
|
ref: abb83316d96aa59c1cf64784289e378fe9fa5695
|
|
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@stable
|
|
- uses: Swatinem/rust-cache@v2
|
|
with:
|
|
key: release-linux-${{ matrix.arch }}
|
|
- name: Apply version override
|
|
if: inputs.version != ''
|
|
run: cargo xtask version set "$WORDCRAFT_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/wordcraft$|metainfo|applications'
|
|
ldd target/release/wordcraft
|
|
python3 -c "import yaml; m = yaml.safe_load(open('packaging/linux/flatpak/ai.storyteller.wordcraft.yml')); assert m['id'] == 'ai.storyteller.wordcraft'"
|
|
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@v4
|
|
with:
|
|
name: release-linux-${{ matrix.arch }}
|
|
path: dist/release/*
|
|
if-no-files-found: error
|
|
|
|
flatpak:
|
|
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 }}
|
|
# No `environment: release`: this job signs nothing, so it gets no release secrets.
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- { arch: x86_64, runner: ubuntu-24.04 }
|
|
- { arch: aarch64, runner: ubuntu-24.04-arm }
|
|
env:
|
|
WORDCRAFT_VERSION: ${{ needs.version.outputs.version }}
|
|
WORDCRAFT_BUILD_DATE: ${{ needs.version.outputs.date }}
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: actions/download-artifact@v4
|
|
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/wordcraft-$WORDCRAFT_VERSION-linux-${{ matrix.arch }}.tar.gz"
|
|
- uses: actions/upload-artifact@v4
|
|
with:
|
|
name: release-flatpak-${{ matrix.arch }}
|
|
path: dist/release/*.flatpak
|
|
if-no-files-found: error
|
|
|
|
web:
|
|
name: Web (wasm)
|
|
needs: version
|
|
runs-on: ubuntu-latest
|
|
# No `environment: release`: this job signs nothing, so it gets no release secrets.
|
|
env:
|
|
WORDCRAFT_VERSION: ${{ needs.version.outputs.version }}
|
|
WORDCRAFT_BUILD_DATE: ${{ needs.version.outputs.date }}
|
|
TRUNK_VERSION: 0.21.14
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
# Optional font input (docs: storytold/craft-fonts docs/integration.md); releases always embed it.
|
|
- name: Check out craft-fonts
|
|
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
|
with:
|
|
repository: storytold/craft-fonts
|
|
ref: abb83316d96aa59c1cf64784289e378fe9fa5695
|
|
path: craft-fonts
|
|
- uses: dtolnay/rust-toolchain@stable
|
|
with:
|
|
targets: wasm32-unknown-unknown
|
|
- uses: Swatinem/rust-cache@v2
|
|
with:
|
|
key: release-web
|
|
- name: Install trunk
|
|
run: |
|
|
curl -fsSL "https://github.com/trunk-rs/trunk/releases/download/v${TRUNK_VERSION}/trunk-x86_64-unknown-linux-gnu.tar.gz" \
|
|
| tar -xz -C "$HOME/.cargo/bin" trunk
|
|
trunk --version
|
|
- name: Apply version override
|
|
if: inputs.version != ''
|
|
run: cargo xtask version set "$WORDCRAFT_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@v4
|
|
with:
|
|
name: release-web
|
|
path: dist/release/*
|
|
if-no-files-found: error
|
|
|
|
freebsd:
|
|
name: FreeBSD x86_64
|
|
needs: version
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 300
|
|
# No `environment: release`: this job signs nothing, so it gets no release secrets.
|
|
env:
|
|
WORDCRAFT_VERSION: ${{ needs.version.outputs.version }}
|
|
WORDCRAFT_BUILD_DATE: ${{ needs.version.outputs.date }}
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
# Optional font input (docs: storytold/craft-fonts docs/integration.md); releases always embed it.
|
|
# Synced into the VM with the sources.
|
|
- name: Check out craft-fonts
|
|
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
|
with:
|
|
repository: storytold/craft-fonts
|
|
ref: abb83316d96aa59c1cf64784289e378fe9fa5695
|
|
path: craft-fonts
|
|
- name: Build in a FreeBSD VM
|
|
uses: vmactions/freebsd-vm@c46abacb49f09938ca4e1702d15d836285d694cc # v1
|
|
with:
|
|
release: "14.3"
|
|
usesh: true
|
|
cpu: 4
|
|
mem: 12288
|
|
envs: "WORDCRAFT_VERSION WORDCRAFT_BUILD_DATE WORDCRAFT_BUILD_SHA"
|
|
copyback: true
|
|
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"
|
|
git config --global --add safe.directory "*"
|
|
cargo run -q -p xtask -- version set "$WORDCRAFT_VERSION"
|
|
CRAFT_FONTS_DIR="$PWD/craft-fonts" CRAFT_FONTS_REQUIRED=1 cargo build --release --locked -p wordcraft -p wordcraft-cli
|
|
name="wordcraft-$WORDCRAFT_VERSION-freebsd-x86_64"
|
|
mkdir -p "dist/release/$name"
|
|
cp target/release/wordcraft target/release/wordcraft-cli LICENSE-MIT LICENSE-APACHE NOTICE ATTRIBUTION.md README.md "dist/release/$name/"
|
|
cp packaging/linux/ai.storyteller.wordcraft.desktop "dist/release/$name/"
|
|
cp assets/app-icon/hicolor/256x256/apps/ai.storyteller.wordcraft.png "dist/release/$name/"
|
|
# The embedded craft-fonts' licences, as in the Linux packages (OFL-<family>.txt).
|
|
for f in craft-fonts/fonts/*/OFL.txt; do cp "$f" "dist/release/$name/OFL-$(basename "$(dirname "$f")").txt"; done
|
|
tar -C dist/release -czf "dist/release/$name.tar.gz" "$name"
|
|
rm -rf "dist/release/$name" target
|
|
- uses: actions/upload-artifact@v4
|
|
with:
|
|
name: release-freebsd
|
|
path: dist/release/*.tar.gz
|
|
if-no-files-found: error
|
|
|
|
release:
|
|
name: Draft GitHub Release
|
|
# Needs the signing jobs, which only run on `release`: a branch dry run never gets here.
|
|
needs: [version, macos, windows, linux, 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@v4
|
|
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="WordCraft 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"
|