Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 66 additions & 1 deletion .github/workflows/mac-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,73 @@ concurrency:
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
await-swift-ci:
if: github.event.repository.fork == false && github.event_name == 'push'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kody code-review Bug high

Manual workflow_dispatch of the mac-release workflow publishes a Sparkle release without any Swift CI compile check because await-swift-ci is gated to github.event_name == 'push' only, causing await-swift-ci.result == 'skipped' to be treated as a green light and defeating the CC-19 gate. Loosen the await-swift-ci if to just github.event.repository.fork == false so release gates on result == 'success' for all triggers.

await-swift-ci:
  if: github.event.repository.fork == false
Prompt for LLM

File .github/workflows/mac-release.yml:

Line 59:

Manual `workflow_dispatch` of the `mac-release` workflow publishes a Sparkle release without any Swift CI compile check because `await-swift-ci` is gated to `github.event_name == 'push'` only, causing `await-swift-ci.result == 'skipped'` to be treated as a green light and defeating the CC-19 gate. Loosen the `await-swift-ci` if to just `github.event.repository.fork == false` so `release` gates on `result == 'success'` for all triggers.

Suggested Code:

  await-swift-ci:
    if: github.event.repository.fork == false

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

​

​

runs-on: ubuntu-latest
timeout-minutes: 90
permissions:
checks: read
contents: read
steps:
- name: Wait for Swift CI compile checks on this commit
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
SHA: ${{ github.sha }}
run: |
set -euo pipefail
required_checks=("Mac package build and test" "iOS companion compile")
deadline=$(( $(date +%s) + 5100 ))
while [ "$(date +%s)" -lt "$deadline" ]; do
payload="$(gh api "repos/${REPO}/commits/${SHA}/check-runs?per_page=100")"
Comment thread
jaywedgeworth22 marked this conversation as resolved.
Comment on lines +75 to +76

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kody code-review Bug high

The check-runs poll in await-swift-ci fetches ?per_page=100 once per iteration with no server-side name filter or Link header pagination, so required checks like Mac package build and test and iOS companion compile on page 2+ of a mature repo's check-runs list are never detected, keeping pending at 1 until the 5100s deadline. Filter the gh api call with an encoded &check_name= parameter per required check to ensure the gate sees all required entries.

while [ "$(date +%s)" -lt "$deadline" ]; do
  pending=0
  failed=0
  for check_name in "${required_checks[@]}"; do
    enc_name=$(printf '%s' "$check_name" | python3 -c 'import sys,urllib.parse;print(urllib.parse.quote(sys.stdin.read(),safe=""))')
    payload="$(gh api "repos/${REPO}/commits/${SHA}/check-runs?per_page=100&check_name=${enc_name}")"
Prompt for LLM

File .github/workflows/mac-release.yml:

Line 75 to 76:

The check-runs poll in `await-swift-ci` fetches `?per_page=100` once per iteration with no server-side name filter or `Link` header pagination, so required checks like `Mac package build and test` and `iOS companion compile` on page 2+ of a mature repo's check-runs list are never detected, keeping `pending` at 1 until the 5100s deadline. Filter the `gh api` call with an encoded `&check_name=` parameter per required check to ensure the gate sees all required entries.

Suggested Code:

          while [ "$(date +%s)" -lt "$deadline" ]; do
            pending=0
            failed=0
            for check_name in "${required_checks[@]}"; do
              enc_name=$(printf '%s' "$check_name" | python3 -c 'import sys,urllib.parse;print(urllib.parse.quote(sys.stdin.read(),safe=""))')
              payload="$(gh api "repos/${REPO}/commits/${SHA}/check-runs?per_page=100&check_name=${enc_name}")"

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

​

​

pending=0
failed=0
for check_name in "${required_checks[@]}"; do
conclusion="$(printf '%s' "$payload" | python3 -c '
import json, sys
name = sys.argv[1]
data = json.load(sys.stdin)
matches = [r for r in data.get("check_runs", []) if r.get("name") == name]
if not matches:
print("missing")
else:
print(matches[0].get("conclusion") or "pending")
' "$check_name")"
case "$conclusion" in
success) ;;
failure|cancelled|timed_out|action_required|stale)
echo "::error::Swift CI check '${check_name}' concluded ${conclusion} on ${SHA}"
failed=1
;;
missing|pending|""|null)
pending=1
;;
*)
pending=1
;;
esac
done
if [ "$failed" -eq 1 ]; then
exit 1
fi
if [ "$pending" -eq 0 ]; then
echo "Swift CI compile checks succeeded on ${SHA}"
exit 0
fi
echo "Waiting for Swift CI on ${SHA}..."
sleep 30
done
echo "::error::Timed out waiting for Swift CI compile checks on ${SHA}"
exit 1

release:
if: github.event.repository.fork == false
needs: await-swift-ci
# always() is required so release still runs when await-swift-ci is skipped
# (pull_request / workflow_dispatch); without it GitHub treats skipped needs as failure.
if: >-
always() &&
github.event.repository.fork == false &&
(needs.await-swift-ci.result == 'success' || needs.await-swift-ci.result == 'skipped')
Comment thread
jaywedgeworth22 marked this conversation as resolved.
runs-on: macos-latest
timeout-minutes: 60
env:
Expand Down
20 changes: 15 additions & 5 deletions .github/workflows/swift-ci.yml
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
name: Swift CI

# Validates the SwiftPM Mac package (Sources/Tests at repo root).
# The iOS companion job's SCHEME is a best guess: the checked-in
# CodeCapsCompanion.xcodeproj has no shared schemes
# (no xcshareddata/xcschemes), so verify the scheme name on first run.
# Hosted macOS compile gate for CodeCaps (CC-19). Validates the SwiftPM Mac
# package at the repo root and the iOS companion Xcode project. Job names
# below are the GitHub status contexts Jay should require on ruleset 24433457;
# see docs/rollouts/2026-10-15-cc19-compile-gate.md.
#
# The companion SCHEME is a best guess: the checked-in CodeCapsCompanion.xcodeproj
# has no shared schemes (no xcshareddata/xcschemes), so verify the scheme name
# on first run.

on:
push:
branches: [main]
pull_request:
workflow_dispatch:

concurrency:
group: swift-ci-${{ github.ref }}
cancel-in-progress: false
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

permissions:
contents: read
Expand All @@ -23,7 +28,9 @@ env:

jobs:
package:
name: Mac package build and test
runs-on: macos-latest
timeout-minutes: 45
steps:
- uses: actions/checkout@v7
- name: Swift build
Expand All @@ -41,7 +48,10 @@ jobs:
if-no-files-found: warn

companion:
name: iOS companion compile
needs: package
runs-on: macos-latest
timeout-minutes: 45
steps:
- uses: actions/checkout@v7
- name: Install XcodeGen and regenerate companion project
Expand Down
4 changes: 4 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ swift build
swift test
```

Hosted compile gate: `.github/workflows/swift-ci.yml` and
`docs/rollouts/2026-10-15-cc19-compile-gate.md` (required check names for
ruleset `default-main-protection`).

The `script/build_and_run.sh` script is the canonical release pipeline — it
bundles, codesigns, notarizes, staples, builds a DMG, and writes a ZIP + sha.
Run it once to read the contract; it is also the only path that knows the
Expand Down
77 changes: 77 additions & 0 deletions docs/rollouts/2026-10-15-cc19-compile-gate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# 2026-10-15 — CC-19: Swift compile gate (hosted macOS CI + ruleset handoff)

## Summary

CodeCaps had hosted Swift CI (`.github/workflows/swift-ci.yml`) but ruleset
`default-main-protection` (id `24433457`) did not require any status checks,
so merges and Sparkle releases could proceed while compile jobs were still
queued. CC-19 wires the lane for a real gate: one macOS runner per pull
request, Mac Release waits for the same compile checks on `main`, and this
document gives Jay the exact check names to add in GitHub (admin-only).

## What changed

- **Swift CI** — stay on `macos-latest` (Swift 6 toolchain required by
`sentry-cocoa` 9.30.x), cancel stale pull-request runs, run `companion`
only after `package` (`needs:`) so a PR never holds two macOS runners at
once. Explicit job `name:` values match the contexts GitHub reports for
required checks.
- **Mac Release (Sparkle)** — new `await-swift-ci` job on `ubuntu-latest`
polls until `package` and `companion` succeed on the merge commit (or fails
closed if either fails, is cancelled, or times out). The release job
`needs: await-swift-ci`, closing the race where Sparkle published while
companion was still running.
- **This rollout** — owner steps for ruleset required checks (not applied in
CI; Jay has the admin grant).

## Verification (agents)

```bash
python3 -c "import yaml; yaml.safe_load(open('.github/workflows/swift-ci.yml'))"
python3 -c "import yaml; yaml.safe_load(open('.github/workflows/mac-release.yml'))"
```

Hosted macOS compile is validated on the pull request’s Swift CI run after push.

## Required status checks (Jay — after this is on `main`)

Ruleset: **Settings → Rules → Rulesets → `default-main-protection`**
(`24433457`) → add a **Require status checks to pass** rule.

Require these **job** contexts (not the workflow title `Swift CI`):

| Context | Workflow | What it proves |
| --- | --- | --- |
| `Mac package build and test` | Swift CI | `swift build`, `swift test`, native UI fixture render |
| `iOS companion compile` | Swift CI | Companion XcodeGen build, simulator smoke, Mac widget target |
| `gitleaks` | Secret scanning | Secret scan on the PR commit range |

**Order of operations:** land this PR and confirm at least one green Swift CI
on `main` **before** ticking the two Swift contexts required. If macOS hosted
runners are backlogged, a required check that has not started yet blocks every
pull request — fix queue health first, then enable the Swift contexts.

Optional strictness: leave **Require branches to be up to date before merging**
off unless you want merge-queue semantics; Swift CI already runs on the head
commit.

Confirm live contexts:

```bash
gh api repos/Simple-With-Us/CodeCaps/rulesets/24433457 \
--jq '.rules[] | select(.type=="required_status_checks")'
```

After you add the rule, the same command should list the three contexts above
under `parameters.required_status_checks` (or `contexts`, depending on API
shape).

Do **not** require workflow names (`Swift CI`, `Secret scanning`). Require the
**job** names exactly as in the table.

## Deferred

- Ruleset edit itself (Jay admin).
- Moving compile to self-hosted macOS runners (fleet BotFleet pinning work).
- Requiring Mac Release or TestFlight workflows as merge gates (ship lanes, not
compile).
Loading