mirror of
https://github.com/storytold/photocraft.git
synced 2026-10-26 19:07:06 -06:00
* Linux: AppImage update information and a .zsync beside it (#349) appimagetool now embeds gh-releases-zsync|<owner>|<repo>|latest|photocraft-*-linux-<arch>.AppImage.zsync (owner/repo from GITHUB_REPOSITORY, default storytold/photocraft), so AppImageUpdate, AppImageLauncher and the like can find the next release and fetch only the blocks that changed. With zsyncmake on the host it also writes the .zsync, into its working directory, so the script runs it in $DIST and warns when the file is missing. The release job installs zsync and checks the embedded string and the .zsync before uploading; docs/releasing.md lists the new asset. Verified locally with packaging/linux/package.sh --skip-build --formats appimage and zsyncmake from Ubuntu 22.04: --appimage-updateinformation prints the string, and the .zsync's URL is the AppImage's basename. * package.sh: absolute AppDir and appimagetool paths before running in $DIST appimagetool now runs inside $DIST so zsyncmake writes the .zsync there, which broke relative CARGO_TARGET_DIR / APPIMAGETOOL paths (AGENTS.md suggests target/agent-<name>). Resolve both first, remove a stale .zsync so a missing zsyncmake is reported, and list the .zsync and zsyncmake in the header. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: bflatastic <bombayapirat@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
422 lines
17 KiB
YAML
422 lines
17 KiB
YAML
# Release pipeline: every push to `release` builds signed installers for every platform and
|
||
# creates or updates the draft GitHub Release "PhotoCraft v<version>". See docs/releasing.md.
|
||
#
|
||
# All jobs 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.
|
||
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
|
||
PHOTOCRAFT_BUILD_SHA: ${{ github.sha }}
|
||
INPUT_VERSION: ${{ inputs.version }}
|
||
# craft-fonts (https://github.com/storytold/craft-fonts): shared font assets every release
|
||
# embeds (today the Japanese fonts). An optional build input for everyone else; see
|
||
# docs/development.md › Fonts. Bump deliberately; every desktop build job checks out this commit (not web).
|
||
CRAFT_FONTS_REF: abb83316d96aa59c1cf64784289e378fe9fa5695
|
||
|
||
jobs:
|
||
version:
|
||
name: version
|
||
runs-on: ubuntu-latest
|
||
environment: release
|
||
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 "### PhotoCraft v$v (\`${GITHUB_SHA::9}\`)" >>"$GITHUB_STEP_SUMMARY"
|
||
|
||
macos:
|
||
name: macOS universal
|
||
needs: version
|
||
runs-on: macos-15
|
||
environment: release
|
||
env:
|
||
PHOTOCRAFT_VERSION: ${{ needs.version.outputs.version }}
|
||
PHOTOCRAFT_BUILD_DATE: ${{ needs.version.outputs.date }}
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
- name: Check out craft-fonts (embedded fonts)
|
||
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
||
with:
|
||
repository: storytold/craft-fonts
|
||
ref: ${{ env.CRAFT_FONTS_REF }}
|
||
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 "$PHOTOCRAFT_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:
|
||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||
CRAFT_FONTS_DIR: ${{ github.workspace }}/craft-fonts
|
||
CRAFT_FONTS_REQUIRED: "1"
|
||
run: packaging/macos/package.sh --arch universal
|
||
# Unpacks the CLI zip and checks the binary users get: Developer ID, hardened runtime,
|
||
# timestamp, and Gatekeeper's notarization lookup (codesign --verify, spctl). Also the DMG's
|
||
# stapled ticket. Strict when the signing secrets are present, warnings otherwise.
|
||
- name: Verify signatures and notarization
|
||
env:
|
||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||
run: packaging/macos/verify.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:
|
||
PHOTOCRAFT_VERSION: ${{ needs.version.outputs.version }}
|
||
PHOTOCRAFT_BUILD_DATE: ${{ needs.version.outputs.date }}
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
- name: Check out craft-fonts (embedded fonts)
|
||
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
||
with:
|
||
repository: storytold/craft-fonts
|
||
ref: ${{ env.CRAFT_FONTS_REF }}
|
||
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 "$PHOTOCRAFT_VERSION"
|
||
- name: Install WiX
|
||
shell: pwsh
|
||
run: dotnet tool install --global wix --version 5.0.2
|
||
- name: Build, sign, package
|
||
shell: pwsh
|
||
env:
|
||
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 }}
|
||
CRAFT_FONTS_DIR: ${{ github.workspace }}/craft-fonts
|
||
CRAFT_FONTS_REQUIRED: "1"
|
||
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 }}
|
||
environment: release
|
||
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:
|
||
PHOTOCRAFT_VERSION: ${{ needs.version.outputs.version }}
|
||
PHOTOCRAFT_BUILD_DATE: ${{ needs.version.outputs.date }}
|
||
NFPM_VERSION: 2.47.0
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
- name: Check out craft-fonts (embedded fonts)
|
||
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
||
with:
|
||
repository: storytold/craft-fonts
|
||
ref: ${{ env.CRAFT_FONTS_REF }}
|
||
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 "$PHOTOCRAFT_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/photocraft$|metainfo|applications'
|
||
ldd target/release/photocraft
|
||
python3 -c "import yaml; m = yaml.safe_load(open('packaging/linux/flatpak/ai.storyteller.photocraft.yml')); assert m['id'] == 'ai.storyteller.photocraft'"
|
||
for appimage in dist/release/*.AppImage; do
|
||
chmod +x "$appimage"
|
||
APPIMAGE_EXTRACT_AND_RUN=1 "$appimage" --version
|
||
# Update information (#349) 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:
|
||
PHOTOCRAFT_VERSION: ${{ needs.version.outputs.version }}
|
||
PHOTOCRAFT_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/photocraft-$PHOTOCRAFT_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
|
||
|
||
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
|
||
# No `environment: release`: this job signs nothing, so it gets no release secrets.
|
||
env:
|
||
PHOTOCRAFT_VERSION: ${{ needs.version.outputs.version }}
|
||
PHOTOCRAFT_BUILD_DATE: ${{ needs.version.outputs.date }}
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
- name: Check out craft-fonts (embedded fonts)
|
||
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
||
with:
|
||
repository: storytold/craft-fonts
|
||
ref: ${{ env.CRAFT_FONTS_REF }}
|
||
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
|
||
with:
|
||
release: "14.3"
|
||
usesh: true
|
||
cpu: 4
|
||
mem: 12288
|
||
# Passed into the VM by name; never templated into the script below.
|
||
envs: "PHOTOCRAFT_VERSION PHOTOCRAFT_BUILD_DATE PHOTOCRAFT_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/photocraft-target CARGO_BUILD_JOBS=4
|
||
if [ -n "$INPUT_VERSION" ]; then cargo xtask version set "$PHOTOCRAFT_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/photocraft-*-freebsd-x86_64.tar.gz \
|
||
| grep -E '/bin/photocraft$|/bin/photocraft-cli$|/share/applications/ai\.storyteller\.photocraft\.desktop$|/share/doc/photocraft/LICENSE-MIT$'
|
||
- uses: actions/upload-artifact@v4
|
||
with:
|
||
name: release-freebsd-x86_64
|
||
path: dist/release/*
|
||
if-no-files-found: error
|
||
|
||
web:
|
||
name: Web (wasm)
|
||
needs: version
|
||
runs-on: ubuntu-latest
|
||
environment: release
|
||
env:
|
||
PHOTOCRAFT_VERSION: ${{ needs.version.outputs.version }}
|
||
PHOTOCRAFT_BUILD_DATE: ${{ needs.version.outputs.date }}
|
||
TRUNK_VERSION: 0.21.14
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
- 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 "$PHOTOCRAFT_VERSION"
|
||
# No craft-fonts here: the wasm32 build never embeds it (crates/text/build.rs), since even
|
||
# one Japanese font takes the wasm over packaging/web/package.sh's 24 MiB gate.
|
||
- name: Build and zip
|
||
run: packaging/web/package.sh
|
||
- uses: actions/upload-artifact@v4
|
||
with:
|
||
name: release-web
|
||
path: dist/release/*
|
||
if-no-files-found: error
|
||
|
||
release:
|
||
name: Draft GitHub Release
|
||
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="PhotoCraft 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"
|