ci(bar): auto-build and publish CCS Bar on main via self-hosted macOS runner

Releasing the macOS bar app was fully manual (run package_app.sh on a Mac, then
gh release upload --clobber), so Swift-side changes never reached users until
someone remembered to rebuild and re-upload the floating ccs-bar-latest asset.

Add a tightly-scoped Bar Release workflow that does it automatically:
- triggers ONLY on push to main touching macos-bar/**, or manual dispatch, so
  regular PRs, dev pushes, and non-bar changes never start it
- runs ONLY on the dedicated self-hosted macOS runner (label ccs-bar); the Linux
  CI runners never match it and it never competes for them
- least-privilege contents:write, single-flight via concurrency

Version is sourced from a new macos-bar/VERSION single-line file (the workflow
reads it; the asset is always the latest build regardless). package_app.sh now
defaults to that file when no version arg is passed, so the local manual path and
CI share one source of truth. Documents the release process in docs/ccs-bar.md.
This commit is contained in:
Tam Nhu Tran committed 2026-06-20 22:17:40 -04:00
1 parent a843709135
commit b3a9abffbc
4 files changed
+108 -1

No files matched your search

+74
View File
@@ -0,0 +1,74 @@
name: Bar Release
# Build and publish the macOS CCS Bar app to the floating `ccs-bar-latest`
# release asset (what `ccs bar install` downloads).
#
# Scoped deliberately so it NEVER burdens other PRs or CI:
# - Triggers ONLY on push to `main` that changes `macos-bar/**`, or a manual
# run. Regular PRs, dev pushes, and non-bar changes do not start it.
# - Runs ONLY on the dedicated self-hosted macOS runner (label `ccs-bar` on
# kai-minim4). The Linux CI runners never match this job, and this job never
# competes for them.
#
# Version source: `macos-bar/VERSION` (single line semver). Bump it in the same
# PR when you want a new number; the asset is always "latest" regardless.
on:
push:
branches: [main]
paths:
- 'macos-bar/**'
workflow_dispatch:
# Least privilege: only the release-asset write the publish step needs.
permissions:
contents: write
# One release at a time; never cancel a publish midway.
concurrency:
group: bar-release
cancel-in-progress: false
jobs:
release:
name: Build and publish CCS Bar
runs-on: [self-hosted, macos, ccs-bar]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Read bar version
id: ver
run: |
version="$(tr -d '[:space:]' < macos-bar/VERSION)"
if [ -z "$version" ]; then
echo "::error file=macos-bar/VERSION::macos-bar/VERSION is empty"
exit 1
fi
echo "version=$version" >> "$GITHUB_OUTPUT"
echo "[i] CCS Bar version: $version"
- name: Build and package CCS Bar.app
working-directory: macos-bar
run: ./Scripts/package_app.sh "${{ steps.ver.outputs.version }}"
- name: Publish to ccs-bar-latest
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VERSION: ${{ steps.ver.outputs.version }}
run: |
# gh lives in Homebrew on the self-hosted runner; ensure it is on PATH.
export PATH="/opt/homebrew/bin:/usr/local/bin:$PATH"
gh release upload ccs-bar-latest macos-bar/dist/CCS-Bar.app.zip --clobber
notes="$(mktemp)"
cat > "$notes" <<NOTES
CCS Bar v${VERSION}
Install or update: ccs bar install
Ad-hoc signed: first launch may need right-click then Open.
Built automatically from main when macos-bar changes.
NOTES
gh release edit ccs-bar-latest --title "CCS Bar v${VERSION}" --notes-file "$notes"
rm -f "$notes"
echo "[OK] Published CCS Bar v${VERSION} to ccs-bar-latest"
+25
View File
@@ -117,3 +117,28 @@ The source lives in `macos-bar/`. Contributors can build and run the logic check
swift build # build all targets, including the app
swift run ccs-bar-check # run the logic tests
```
## Releasing
The app ships as a single floating GitHub release asset, `CCS-Bar.app.zip` under the `ccs-bar-latest` tag, which is what `ccs bar install` downloads. The version comes from one file: `macos-bar/VERSION` (a single line of semver). Bump it in the same PR when you want a new number; the asset is always the latest build regardless.
### Automatic (preferred)
The `Bar Release` workflow (`.github/workflows/bar-release.yml`) builds and publishes the asset automatically. It is scoped tightly so it never affects other PRs or CI:
- It runs only on a push to `main` that touches `macos-bar/**`, or a manual run from the Actions tab (`workflow_dispatch`).
- It runs only on the dedicated self-hosted macOS runner (label `ccs-bar`); the Linux CI runners never pick it up and it never competes for them.
So bar changes reach users when they land on `main` (the stable cadence). To cut a release without a code change, or to re-publish, trigger the workflow manually.
### Manual fallback
From a macOS machine with the Swift toolchain and `gh`:
```bash
cd macos-bar
./Scripts/package_app.sh # uses macos-bar/VERSION; pass a version to override
gh release upload ccs-bar-latest dist/CCS-Bar.app.zip --clobber
```
Builds are ad-hoc signed by default. Developer ID notarization (for the no-prompt public install) is available via `CCS_BAR_SIGNING=developer-id` once a paid Apple cert is configured.
+8 -1
View File
@@ -13,12 +13,19 @@
# CCS_BAR_SIGNING=developer-id CCS_BAR_SIGN_IDENTITY="Developer ID Application: ..." ./Scripts/package_app.sh 0.1.0
set -euo pipefail
VERSION="${1:-0.0.0}"
SIGNING="${CCS_BAR_SIGNING:-adhoc}"
APP_NAME="CCS Bar"
EXEC_NAME="CCSBar"
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
# Version precedence: explicit arg, else the committed `VERSION` file (the single
# source of truth shared with the Bar Release CI workflow), else 0.0.0.
VERSION="${1:-}"
if [[ -z "$VERSION" && -f "$ROOT/VERSION" ]]; then
VERSION="$(tr -d '[:space:]' < "$ROOT/VERSION")"
fi
VERSION="${VERSION:-0.0.0}"
DIST="$ROOT/dist"
APP="$DIST/$APP_NAME.app"
+1
View File
@@ -0,0 +1 @@
1.7.0