mirror of
https://github.com/storytold/photocraft.git
synced 2026-10-15 03:36:48 -04:00
419 lines
17 KiB
YAML
419 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
|
||
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
|
||
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"
|