Skip to content

Release

Release #12

Workflow file for this run

name: Release
on:
push:
tags:
- 'v*.*.*'
# Least-privilege default; jobs that need more elevate individually
# (build-macos: id-token/attestations, release: contents write).
permissions: read-all
jobs:
# Cheap fail-fast before spending time on a real build: the pushed tag
# must match build/config.yml's own version, so a build never gets
# released under the wrong version number. Precedent: wailsapp/wails's
# own release-v3.yml does the same check before its build matrix.
preflight:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Verify tag matches build/config.yml version
run: |
TAG_VERSION="${GITHUB_REF_NAME#v}"
# Must require leading whitespace: build/config.yml's line 4 is
# `version: '3'`, the Taskfile schema version at column 0, not
# the app version -- that one's nested under `info:` a few lines
# down. Confirmed by testing this exact command against the real
# file, not assumed (the unindented pattern silently matched the
# wrong line on the first attempt).
CONFIG_VERSION=$(grep -m1 '^[[:space:]]\+version:' build/config.yml | sed -E 's/.*version: *"?([0-9][0-9A-Za-z.+-]*)"?.*/\1/')
echo "Tag version: $TAG_VERSION"
echo "build/config.yml version: $CONFIG_VERSION"
if [ "$TAG_VERSION" != "$CONFIG_VERSION" ]; then
echo "::error::Tag v$TAG_VERSION does not match build/config.yml's version ($CONFIG_VERSION)"
exit 1
fi
# macOS only, deliberately -- see docs/adr/0002 and SPEC.md §1.3.
# Windows and Linux desktop builds are PARKED, not silently shipped
# unverified: Linux desktop needs GTK4/webkitgtk-6.0 system packages this
# workflow has never installed or tested, and Windows needs an entirely
# different toolchain with zero local verification possible from this
# macOS-only development environment. Shipping CI steps nobody has ever
# actually run is exactly the mistake build-go's ubuntu-latest entry made
# in Phase 1 (see that commit) -- not repeating it here for a release
# artifact users would actually download. macOS is also Mill's stated
# primary target (SPEC.md), so this covers the platform that matters most
# today, correctly, rather than four platforms unverifiedly.
build-macos:
needs: preflight
runs-on: macos-latest
# A wedged build must fail fast, not burn the 6h default (the first
# live run sat 63 minutes in a hung step before manual cancel).
timeout-minutes: 30
permissions:
id-token: write
attestations: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: '1.25'
cache: true
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: '22'
cache: 'npm'
cache-dependency-path: frontend/package-lock.json
# Task installed via go install, not arduino/setup-task: the tool
# itself is MIT but that ACTION wrapper is GPL-3.0, which trips
# dependency-review's deny-licenses on any PR touching this file.
- name: Install Task and wails3 CLI
run: |
go install github.com/go-task/task/v3/cmd/task@v3.52.0
go install github.com/wailsapp/wails/v3/cmd/wails3@v3.0.0-beta.6
# task build already chains: go mod tidy -> generate icons -> install
# frontend deps -> generate bindings -> frontend build -> go build.
# No GoReleaser (see ADR-0002 / wailsapp/wails#747, closed wont-fix) --
# this mirrors what the Wails team does for its own releases.
# MILL_SKIP_BINDINGS: bindings generation launches the real app to
# extract bindings and never exits on a headless runner (first live
# run of this workflow hung 63 minutes exactly there); the repo
# commits frontend/bindings, so a release build uses them as-is.
# task package (not task build): the raw binary is not a
# launchable macOS artifact -- v0.1.0 shipped it and double-click
# hit Finder's "no application set to open" dialog. The .app
# bundle zipped with ditto (preserves bundle metadata/resource
# forks the way plain zip may not) is what a person downloads.
- run: task package
env:
MILL_SKIP_BINDINGS: "1"
- name: Zip app bundle with platform/arch/version suffix
run: |
VERSION="${GITHUB_REF_NAME#v}"
ditto -c -k --keepParent bin/mill.app "bin/mill-${VERSION}-macos-$(uname -m).zip"
- uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2
with:
subject-path: bin/mill-*.zip
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: mill-macos
path: bin/mill-*.zip
retention-days: 7
release:
needs: build-macos
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: write
steps:
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: mill-macos
path: dist
- name: Generate checksums
working-directory: dist
run: sha256sum -- * > SHA256SUMS
- name: Create GitHub release
env:
GH_TOKEN: ${{ github.token }}
# --notes-file text is prepended to the generated notes; the app
# is ad-hoc signed (no Apple Developer ID), so first launch
# needs the standard right-click -> Open confirmation. Notes are
# built line-by-line into a file: every line of a run:| block
# must stay indented (an unindented continuation terminates the
# literal scalar -- the v0.2.0 first-run failure), and backticks
# must stay escaped inside double quotes (command substitution).
run: |
{
echo "## Install"
echo ""
echo "Download the \`.zip\`, unzip, and drag \`mill.app\` to Applications — no build needed. First launch: **right-click the app → Open → Open** (it is ad-hoc signed, not notarized — macOS asks once). Verify the download came from this repo's CI: \`gh attestation verify <the .zip> -R ${GITHUB_REPOSITORY}\`. Prefer building from source? \`git clone\` + the README's few commands work on any Mac."
} > /tmp/release-notes.md
gh release create "${GITHUB_REF_NAME}" \
--repo "${GITHUB_REPOSITORY}" \
--title "${GITHUB_REF_NAME}" \
--generate-notes \
--notes-file /tmp/release-notes.md \
dist/*