Skip to content

ci: run swift test + unsigned app compile on PRs - #129

Merged
tarakanof merged 2 commits into
overhaul/ui-ng-2026-09from
ci/128-swift-job
Sep 26, 2026
Merged

tarakanof merged 2 commits into
overhaul/ui-ng-2026-09from
ci/128-swift-job

Conversation

@tarakanof

Copy link
Copy Markdown
Owner

Closes #128

CI only ran the Go job, so EmberKit decode breakage (incl. the Go golden
files the Swift tests decode, added in #125) and app compile errors were
only caught locally.

What changed

  • New changes job (dorny/paths-filter) computes whether macos/**,
    cmd/ember/testdata/**, or the workflow file itself changed.
  • New macos job, gated on that filter, runs-on: macos-latest (currently
    macOS 26/Xcode 26 — matches project.yml's deploymentTarget.macOS: "26.0"
    and Package.swift's swift-tools-version: 6.0):
    • brew install xcodegen
    • swift test --package-path macos
    • xcodegen generate --spec macos/project.yml --project macos
    • unsigned xcodebuild -project macos/Ember.xcodeproj -scheme Ember -configuration Debug CODE_SIGNING_ALLOWED=NO build
    • SPM build cache keyed on Package.swift's hash (EmberKit has no external
      SPM dependencies, so this mostly saves recompiling stdlib-ish output on
      reruns of the same branch).
  • scripts/build-producers.sh's "Bundle & sign producer helpers" phase now
    skips itself under GITHUB_ACTIONS=true instead of running go build +
    codesign (there's no Developer ID on the runner, and go isn't
    guaranteed to be there either). Local dev/release builds are unaffected —
    that env var is unset outside GitHub Actions.
  • Documented the new job in docs/RUNBOOK.md.

Test evidence

  • swift test --package-path macos — 239 tests pass locally.
  • xcodegen generate --spec macos/project.yml --project macos +
    GITHUB_ACTIONS=true xcodebuild -project macos/Ember.xcodeproj -scheme Ember -configuration Debug CODE_SIGNING_ALLOWED=NO build — BUILD SUCCEEDED,
    with the sign phase printing its skip message instead of failing codesign.
  • GOTOOLCHAIN=go1.26.0 go fmt ./... clean, go vet ./...,
    go test ./... -race all green.
  • yamllint -d relaxed .github/workflows/ci.yml — only a pre-existing
    line-length warning on line 5, unrelated to this change.

@tarakanof tarakanof left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Review of #129. CI is green: changes 5s, macos 2m25s, go passed. No blockers. A few things are worth fixing before merge.

Should-fix

  1. The GITHUB_ACTIONS early exit in build-producers.sh skips more than signing, and it keys on the wrong signal. It exits before go build, lipo, codesign, and the LaunchAgent plist copy and plutil -lint. The comment in ci.yml says "skips itself" as if it only covers signing. Today only ci.yml and docker-publish.yml exist, and neither builds the app for release, so there is no current release impact. But a future release or notarize workflow on Actions would exit 0 and ship an app with no producer helpers and no LaunchAgents, and nothing would fail. It would also make scripts/build-producers_test.sh fail if that ever runs in CI. Suggestion: gate on CODE_SIGNING_ALLOWED=NO instead (Xcode exports build settings to script phases), and in that case skip only codesign, or sign ad-hoc with -. Note that CODE_SIGN_IDENTITY is still exported as "Developer ID Application" under CODE_SIGNING_ALLOWED=NO, so falling through as-is would fail. The Go build and plist copy/lint would still run. That needs actions/setup-go in the macos job, though macos-latest ships Go anyway. This also avoids coupling CI behaviour to the CI vendor.
  2. Pin dorny/paths-filter to a commit SHA. The repo pins everything by major tag, but those tags belong to GitHub or Docker orgs. paths-filter is a single-maintainer third-party action, so a moved v4 tag would run arbitrary code in CI. The token is contents: read and there are no secrets in this job, so the blast radius is small, but pinning costs nothing:
    uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3 (currently what v4 points to).
    Add a Dependabot github-actions entry if you want bumps.
  3. The path filter misses scripts/build-producers.sh, which is the script the Xcode post-compile phase runs. A change to it, including the new early exit itself, won't trigger the macos job. Add 'scripts/**', or at least scripts/build-producers.sh.

Nit

  1. The cache key is effectively frozen. hashFiles('macos/Package.swift') never changes when sources change. After the first save, every run gets an exact hit, and actions/cache doesn't re-save on an exact hit. So the cache stays pinned to the first .build forever, and macos-latest will change Xcode/Swift underneath it. SwiftPM will mostly just rebuild, but that makes the cache near-useless and a possible source of stale-module weirdness. Either include the toolchain and sources, e.g. ${{ runner.os }}-${{ env.ImageVersion }}-spm-${{ hashFiles('macos/Package.swift', 'macos/Sources/**', 'macos/Tests/**') }}, or drop the cache: there are no external deps and the job takes about 2.5 min.
  2. Wrong comment in ci.yml: "EmberKit's deployment target is macOS 26 (see macos/project.yml)". EmberKit's Package.swift says .macOS(.v14). macOS 26 is the app target's deploymentTarget.
  3. Skipped job vs required checks: nothing to fix today. The only ruleset has deletion and non_fast_forward rules and no required status checks, so a skipped macos job can't block merges. If macos becomes required later, a path-skipped job reports as "skipped", which GitHub counts as passing for required checks. That's fine, but note it in RUNBOOK if you add protection.
  4. The changes job works on PRs with only contents: read because the repo is public, so the PR-files API is readable. If the repo ever goes private, paths-filter needs pull-requests: read on that job. Consider adding it now at job level.

Verdict: fix 1–3 first. All three are small. The rest are optional.

CI only ran the Go job, so EmberKit decode breakage and app compile
errors were only caught locally. Add a path-filtered macos job
(swift test --package-path macos, xcodegen generate, unsigned
xcodebuild) gated by a paths-filter job so it only runs for
macos/**, cmd/ember/testdata/**, or workflow changes.

There's no Developer ID on the runner, so build-producers.sh (the
app's postCompileScripts sign phase) now skips itself under
GITHUB_ACTIONS instead of failing codesign — local dev/release
builds are unaffected since that env var is unset there.
- build-producers.sh no longer early-exits under GITHUB_ACTIONS; it now
  falls back to ad-hoc signing only when CODE_SIGNING_ALLOWED=NO or the
  configured identity isn't in the keychain, so go build/lipo/plist
  copy/plutil lint keep running and get exercised in CI. Verified this
  also matches the existing local (no Developer ID) fallback behavior.
- macos job gets actions/setup-go (go-version-file: go.mod) since
  build-producers.sh now actually runs `go build` there.
- Pin dorny/paths-filter to its v4.0.3 commit SHA instead of a
  mutable major-version tag.
- Add scripts/** to the path filter (build-producers.sh lives there).
- Drop the SPM build cache — EmberKit has no external dependencies,
  so it wasn't earning its complexity.
- Fix the deployment-target comment: the Ember app target is macOS 26
  (project.yml); EmberKit itself only needs macOS 14 (Package.swift).
- Grant the changes job pull-requests: read so paths-filter keeps
  working if the repo goes private.
@tarakanof

Copy link
Copy Markdown
Owner Author

Addressed review feedback (rebased onto latest `overhaul/ui-ng-2026-09`, force-pushed):

  1. `build-producers.sh` no longer early-exits under `GITHUB_ACTIONS`. It now falls back to ad-hoc signing only when `CODE_SIGNING_ALLOWED=NO` or the configured identity isn't in the keychain, so `go build`/`lipo`/plist copy/`plutil` lint keep running (verified locally with both the existing `build-producers_test.sh` smoke test and a manual `CODE_SIGN_IDENTITY="Developer ID Application" CODE_SIGNING_ALLOWED=NO` run, plus the full unsigned `xcodebuild`). Added `actions/setup-go` (`go-version-file: go.mod`) to the `macos` job since the script now actually builds Go binaries there.
  2. Pinned `dorny/paths-filter` to `ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3` — verified against the tag via `gh api repos/dorny/paths-filter/git/refs/tags/v4.0.3`.
  3. Added `scripts/**` to the path filter.
  4. Dropped the SPM cache (EmberKit has no external dependencies).
  5. Fixed the deployment-target comment: the Ember app target is macOS 26 (`project.yml`); EmberKit itself only needs macOS 14 (`Package.swift`).
  6. Added `pull-requests: read` to the `changes` job.

One rerun was needed: `swift test` failed once on `debouncedWriterRunsAfterQuietPeriod()`, a pre-existing timing-sensitive test (20ms debounce, 120ms wait) unrelated to this PR's changes — passed clean on rerun. Flagging in case it's worth widening that test's margin separately; left it untouched here since it's out of this ticket's scope.

@tarakanof
tarakanof merged commit d819bbe into overhaul/ui-ng-2026-09 Sep 26, 2026
5 of 6 checks passed
@tarakanof
tarakanof deleted the ci/128-swift-job branch September 26, 2026 09:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant