Skip to content

Run the macOS binaries job on a larger Apple Silicon runner - #117

Draft
Bencheng21 wants to merge 1 commit into
mainfrom
macos-xlarge-runner
Draft

Bencheng21 wants to merge 1 commit into
mainfrom
macos-xlarge-runner

Conversation

@Bencheng21

Copy link
Copy Markdown
Contributor

Why

The release critical path is goreleaser-binaries, and its dominant cost is Go compilation. macos-latest has 3 vCPUs and GoReleaser builds one target per core, so with four targets the fourth queues behind the first three.

This is measurable and universal. Across the most recent successful release of nine connectors, exactly three targets start together and macos-arm64 waits:

Connector 4th target starts macos-amd64 post-hook fires delta
baton-hubspot 108s 108s 0.1s
baton-snowflake 117s 118s 0.8s
baton-slack 160s 162s 2.9s
baton-salesforce 162s 165s 2.4s
baton-zoom 169s 170s 1.0s
baton-github 176s 177s 0.9s
baton-jira 192s 194s 1.9s
baton-aws 200s 201s 1.1s
baton-okta 331s 335s 3.7s

The gon post-hook fires the instant a build finishes, so the fourth target provably starts when the first frees its slot — it is queued, not running concurrently with delayed logging.

For scale: on baton-github the compile phase was 3m49s of a 9m27s release (~40%).

What this changes

  • Adds a macos_runner input, defaulting to macos-latest-xlarge (Apple Silicon M2, 5 vCPU, 14 GB). Five slots for four targets removes the queueing entirely.
  • validate-inputs restricts the value to GitHub-hosted Apple Silicon labels. This job holds APPLE_SIGNING_KEY_P12 and assumes the artifacts AWS role, so the runner it lands on is a trust decision — a free-form label would let a caller point it at a runner they control.

Intel labels are deliberately excluded. macos-latest-large is the 12-vCPU option, but it would change the codesigning host architecture and move the Homebrew prefix from /opt/homebrew to /usr/local, which the brew install conductorone/gon/gon step depends on. If the 12-core throughput turns out to be worth chasing, that should be its own change with the Homebrew prefix handled explicitly.

What this does not claim

The saving is not predicted here. The logs show when each build starts but not when the two linux builds finish, so per-core scaling cannot be derived from them. The only defensible claim is that the queueing is real and that five slots removes it. This PR is the experiment that measures the rest.

Suggested rollout: point one connector at this branch, cut a prerelease tag, and compare the building binaries phase duration against its current baseline (the table above gives you nine baselines to pick from). If the gain is small, the 5 vCPU ceiling is the reason and the structural fix in #116 is the better lever.

Cost

Larger runners bill at a higher per-minute rate, and this job runs on every connector release. The job should get shorter, but shorter × pricier is not automatically cheaper. The default should be reviewed against actual spend once the compile phase has been measured — if it is not clearly worth it, flip the default back to macos-latest and let individual connectors opt in.

Validation

  • Parsed .github/workflows/release.yaml with PyYAML; confirmed the input default and that the job resolves runs-on from it.
  • Exercised the allowlist: macos-latest, macos-latest-xlarge, macos-15-xlarge accepted; macos-latest-large, macos-13, self-hosted, and a shell-injection attempt rejected.
  • scripts/test-normalize-release-options.sh, scripts/test-derive-iam-role-name.sh, scripts/test-release-config-templates.py, go build ./....

Not validated: no release has been run on the new runner. Hence draft.

Relationship to #116

Independent and complementary. #116 removes two of the four targets from the macOS runner entirely; this widens the runner. Either alone helps; together the macOS job would build 2 targets with 5 slots. I would land this one first — it is one input and a validation check, versus #116's 850 lines.

🤖 Generated with Claude Code

**Why**

The release critical path is goreleaser-binaries, and its dominant cost is Go
compilation. macos-latest has 3 vCPUs and GoReleaser builds one target per core,
so with four targets the fourth queues behind the first three.

This is measurable and universal. Across the most recent successful release of
nine connectors, exactly three targets start together and macos-arm64 waits:

    connector          4th target starts   macos-amd64 post-hook   delta
    baton-hubspot                   108s                    108s    0.1s
    baton-snowflake                 117s                    118s    0.8s
    baton-slack                     160s                    162s    2.9s
    baton-salesforce                162s                    165s    2.4s
    baton-zoom                      169s                    170s    1.0s
    baton-github                    176s                    177s    0.9s
    baton-jira                      192s                    194s    1.9s
    baton-aws                       200s                    201s    1.1s
    baton-okta                      331s                    335s    3.7s

The gon post-hook fires the instant a build finishes, so the fourth target
provably starts when the first frees its slot rather than running concurrently.

**What this changes**

- Adds a macos_runner input, defaulting to macos-latest-xlarge (Apple Silicon M2,
  5 vCPU). Five slots for four targets removes the queueing entirely.
- validate-inputs restricts the value to GitHub-hosted Apple Silicon labels. This
  job holds the Apple signing certificate and assumes the artifacts AWS role, so
  the runner it lands on is a trust decision; a free-form label would let a caller
  point it at a runner they control.

Intel labels are deliberately excluded. macos-latest-large is the 12 vCPU option,
but it would change the codesigning host architecture and move the Homebrew prefix
from /opt/homebrew to /usr/local, which the gon install step depends on.

**What this does not claim**

The saving is not predicted here. The logs show when each build starts but not when
the two linux builds finish, so per-core scaling cannot be derived from them; the
only defensible claim is that the queueing is real and five slots removes it.
Treat this as the experiment that measures it.

Larger runners bill at a higher per-minute rate, and this job runs on every
connector release, so the default should be reviewed against actual spend once the
compile phase has been measured on the new runner.

**Validation**

- Parsed .github/workflows/release.yaml with PyYAML; confirmed the input default
  and that the job resolves runs-on from it.
- Exercised the allowlist against macos-latest, macos-latest-xlarge,
  macos-15-xlarge (accepted) and macos-latest-large, macos-13, self-hosted, and a
  shell-injection attempt (rejected).
- scripts/test-normalize-release-options.sh, scripts/test-derive-iam-role-name.sh,
  scripts/test-release-config-templates.py, go build ./...

Not validated: no release has been run on the new runner.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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