Skip to content

ci(release): add workflow_dispatch to run Phase 2 manually #20

ci(release): add workflow_dispatch to run Phase 2 manually

ci(release): add workflow_dispatch to run Phase 2 manually #20

Workflow file for this run

name: Release (v2)
# Two-phase release flow.
#
# Phase 1 — automatic on git tag push (v*).
# Runs tests, regenerates the OpenAPI spec, builds the wheel/dist,
# and attaches them to a DRAFT GitHub Release. Nothing public yet.
#
# Phase 2 — fires when you click "Publish release" on that draft in
# the GitHub UI (release.published event).
# Publishes the wheel/sdist to PyPI, builds the macOS DMG and
# uploads it to the release, syncs the Homebrew tap, syncs AUR.
on:
push:
tags: [ "v*" ]
release:
types: [ published ]
workflow_dispatch:
inputs:
tag:
description: "Release tag to (re)publish, e.g. v3.0.0. Runs Phase 2 manually."
required: true
permissions:
contents: write
# Serialize all release work for a given version so a manual dispatch can't
# race a release-event run (would double-publish to PyPI / double-commit the tap).
concurrency:
group: release-${{ github.event.inputs.tag || github.ref_name }}
cancel-in-progress: false
env:
# release event → github.ref_name is the tag; workflow_dispatch → the input.
VERSION: ${{ github.event.inputs.tag || github.ref_name }}
jobs:
# ───────────────────────────────────────────────────────────────────
# PHASE 1 — automatic on tag push
# ───────────────────────────────────────────────────────────────────
test:
if: github.event_name == 'push'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: astral-sh/setup-uv@v8.2.0
with:
enable-cache: true
- run: uv python install 3.14
- run: uv sync --extra dev --extra tui
- run: uv run pytest -x -q -m "not gui"
stage:
if: github.event_name == 'push'
needs: [ test ]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: astral-sh/setup-uv@v8.2.0
with:
enable-cache: true
- run: uv python install 3.14
- name: Install dependencies for OpenAPI generation
run: uv sync --extra dev
- name: Regenerate OpenAPI spec
# Always regenerate at tag time so the artifact matches the
# released code, even if the committed docs/openapi.yaml drifted.
run: uv run python tools/gen_openapi.py
- run: uv build
- name: Create draft GitHub Release
uses: softprops/action-gh-release@v3
with:
draft: true
files: |
dist/*
docs/openapi.yaml
generate_release_notes: true
# ───────────────────────────────────────────────────────────────────
# PHASE 2 — fires when you click "Publish release" on the draft
# ───────────────────────────────────────────────────────────────────
publish-pypi:
if: github.event_name == 'release' || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
# Trusted-publisher OIDC: short-lived token minted per run, no secret.
permissions:
contents: write
id-token: write
steps:
- uses: actions/checkout@v6
with:
ref: ${{ github.event.inputs.tag || github.ref_name }}
- uses: astral-sh/setup-uv@v8.2.0
with:
enable-cache: true
- run: uv python install 3.14
- name: Install dependencies for OpenAPI generation
run: uv sync --extra dev
- name: Regenerate OpenAPI spec
# Rebuilding fresh in Phase 2 is faster than wiring up an
# artifact handoff from Phase 1's run.
run: uv run python tools/gen_openapi.py
- run: uv build
- run: uv publish
build-dmg:
if: github.event_name == 'release' || github.event_name == 'workflow_dispatch'
runs-on: macos-latest
steps:
- uses: actions/checkout@v6
with:
ref: ${{ github.event.inputs.tag || github.ref_name }}
- uses: actions/setup-python@v6
with:
python-version: "3.14"
- name: Install deps
run: pip install pyinstaller rumps pyobjc && pip install -e ".[tray-mac]"
- name: Build SusOps.app
run: pyinstaller packaging/macos/susops.spec --clean --noconfirm
- name: Create dmg
run: |
VER="${VERSION#v}"
hdiutil create -volname "SusOps" -srcfolder dist/SusOps.app -ov -format UDZO "SusOps-${VER}-arm64.dmg"
- name: Upload dmg to release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
VER="${VERSION#v}"
gh release upload "$VERSION" "SusOps-${VER}-arm64.dmg" --clobber
- uses: actions/upload-artifact@v7
with:
name: dmg
path: "SusOps-*-arm64.dmg"
retention-days: 1
update-tap:
if: github.event_name == 'release' || github.event_name == 'workflow_dispatch'
needs: [ publish-pypi, build-dmg ]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
ref: ${{ github.event.inputs.tag || github.ref_name }}
path: main
- uses: actions/checkout@v6
with:
repository: mashb1t/homebrew-susops
token: ${{ secrets.HOMEBREW_TAP_TOKEN }}
path: tap
- uses: actions/setup-python@v6
with:
python-version: "3.14"
- uses: actions/download-artifact@v8
with:
name: dmg
path: main
- name: Update formula and cask sha256s
working-directory: main
run: |
VER="${VERSION#v}"
# Install [tui] so compute_resource_shas.py sees the full
# runtime closure and writes accurate sha256s.
pip install -e ".[tui]"
python scripts/update_homebrew_sha.py "${VER}" --dmg "SusOps-${VER}-arm64.dmg"
- name: Copy to tap
run: |
cp main/packaging/homebrew/Formula/susops.rb tap/Formula/susops.rb
cp main/packaging/homebrew/Casks/susops.rb tap/Casks/susops.rb
- name: Commit and push
working-directory: tap
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add Formula/susops.rb Casks/susops.rb
git commit -m "chore: bump to ${VERSION}"
git push
# update-aur:
# if: github.event_name == 'release'
# needs: [publish-pypi]
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v6
# - uses: actions/setup-python@v6
# with:
# python-version: "3.14"
# - name: Update PKGBUILD
# run: |
# VER="${VERSION#v}"
# python scripts/update_aur_pkgver.py "${VER}"
# - name: Generate .SRCINFO via Arch Docker
# run: |
# docker run --rm \
# -v "${{ github.workspace }}/packaging/aur:/pkg" \
# archlinux:base-devel \
# sh -c "useradd -m builder && chown builder /pkg && su builder -c 'cd /pkg && makepkg --printsrcinfo > .SRCINFO'"
# - uses: webfactory/ssh-agent@v0.10.0
# with:
# ssh-private-key: ${{ secrets.AUR_SSH_KEY }}
# - name: Add AUR host key
# run: ssh-keyscan -t ed25519 aur.archlinux.org >> ~/.ssh/known_hosts
# - name: Clone and push AUR repo
# run: |
# git clone ssh://aur@aur.archlinux.org/susops.git aur-repo
# cp packaging/aur/PKGBUILD aur-repo/PKGBUILD
# cp packaging/aur/.SRCINFO aur-repo/.SRCINFO
# cd aur-repo
# git config user.name "github-actions[bot]"
# git config user.email "github-actions[bot]@users.noreply.github.com"
# git add PKGBUILD .SRCINFO
# git commit -m "chore: bump to ${VERSION}"
# git push