Skip to content

Build linux release binaries on a dedicated ubuntu runner - #116

Closed
Bencheng21 wants to merge 1 commit into
mainfrom
split-linux-builds-to-ubuntu
Closed

Bencheng21 wants to merge 1 commit into
mainfrom
split-linux-builds-to-ubuntu

Conversation

@Bencheng21

Copy link
Copy Markdown
Contributor

Why

The release critical path is the macOS binaries job, and almost all of it is a single step. On baton-okta v0.5.36 (12m07s end to end), goreleaser-binaries took 9m15s, of which Run GoReleaser was 447s — and the compile phase alone was 6m45s:

18:15:20  3 builds start (linux/amd64, linux/arm64, darwin/amd64)
18:20:51  darwin/arm64 finally starts   <- waited 5m31s for a CPU slot
18:22:05  compile phase done (6m45s)

macos-latest has 3 vCPUs and GoReleaser builds one target per core, so the fourth target sat idle for over five minutes. Notarization is not the bottleneck — the gon hooks took 9s. Only the darwin targets need codesign/notarize, so the linux builds were occupying the scarcest runner in the matrix for no reason.

Measured job durations today (two recent releases):

Job baton-okta baton-aws
goreleaser-binaries (macOS) 9.25m ← critical path 7.05m
goreleaser-windows 4.63m 4.88m
goreleaser-docker 3.93m 4.10m

What this changes

  • Adds a goreleaser-linux job on ubuntu-latest that builds linux amd64/arm64, signs, generates SBOMs and provenance, uploads to S3, and emits a manifest fragment plus its checksums. It mirrors the existing goreleaser-windows job and runs with --skip=publish; the darwin job still owns the GitHub release and changelog.
  • Narrows .goreleaser-binaries-template.yaml.tmpl to the darwin targets; adds .goreleaser-linux-template.yaml.tmpl.
  • merge-manifests gains -linux-manifest and merges its assets map (skipping the partial checksums entry that publish-release-manifest recomputes).
  • publish-release-manifest concatenates the linux checksums into the unified file.

Two contracts that a single GoReleaser run used to cover implicitly

These would have regressed silently, so they are now handled explicitly:

The Homebrew formula. GoReleaser can only build formula entries from artifacts in its own run, so a darwin-only run would drop the tap's on_linux blocks and break brew install on Linux for every connector. The formula is now rendered by cmd/generate-brew-formula from the merged manifest — where every platform is present — and pushed to the tap by publish-release-manifest. Rendering it against baton-okta v0.5.36 reproduces the published formula exactly apart from the header comment and the URLs:

-# This file was generated by GoReleaser. DO NOT EDIT.
+# This file was generated by ConductorOne/github-workflows. DO NOT EDIT.
-      url "https://github.com/ConductorOne/baton-okta/releases/download/v0.5.36/...-darwin-amd64.zip"
+      url "https://dist.conductorone.com/releases/ConductorOne/baton-okta/v0.5.36/...-darwin-amd64.zip"

Class name, version, SHAs, CPU conditions, ordering and the test do block are byte-identical.

The GitHub release contents. GoReleaser now attaches only the darwin archives and a darwin-only checksums file. publish-release-manifest re-attaches the linux archives and --clobbers the checksums file with the unified one, so the release keeps the same five assets it has today.

Behaviour change to be aware of

Formula download URLs move from GitHub release assets to dist.conductorone.com. That is already the canonical distribution channel and what the connector registry records, but it is user-visible. Previously published formulae are untouched.

Expected saving

Estimated ~3–4 minutes off the critical path (12m → ~8–9m): the macOS job drops to 2 targets with no queueing, and linux compiles concurrently on a 4-vCPU runner. This is a projection from the measured timings above, not a measurement — see below.

Validation

  • go build ./..., go vet ./..., go test ./cmd/... — new tests cover formula class naming, incomplete-asset skipping, and per-OS block rendering.
  • scripts/test-release-config-templates.py, extended to assert the binaries and linux templates build disjoint GOOS sets (two jobs writing the same archive to the same immutable S3 key would fail the release) and that no template still declares brews:.
  • scripts/test-release-workflow-tag-pin.sh, extended to cover the new GoReleaser invocation. yq was unavailable locally, so the invariant was verified directly against the parsed YAML.
  • scripts/test-normalize-release-options.sh, scripts/test-derive-iam-role-name.sh.
  • Parsed .github/workflows/release.yaml with PyYAML.

Not validated: the workflow has not been executed. Job-level behaviour can only be confirmed by a real release, which is why this is a draft. Suggested rollout: cut a prerelease tag on one connector against this branch, then confirm (a) the tap commit diff is URL-only, (b) the GitHub release still has all five assets, (c) the unified checksums file covers all four archives, and (d) verify-release passes.

Since callers pin @v4, this ships only when the v4 tag moves or v5 is cut.

🤖 Generated with Claude Code

**Why**

The release critical path is the macOS binaries job, and almost all of it is one
step. On baton-okta v0.5.36 (12m07s end to end), goreleaser-binaries took 9m15s,
of which "Run GoReleaser" was 447s and the compile phase alone was 6m45s:

    18:15:20  3 builds start (linux/amd64, linux/arm64, darwin/amd64)
    18:20:51  darwin/arm64 finally starts   <- waited 5m31s for a CPU slot
    18:22:05  compile phase done (6m45s)

macos-latest has 3 vCPUs and GoReleaser builds one target per core, so the fourth
target sat idle for over five minutes. Notarization is not the bottleneck: the gon
hooks took 9s. Only the darwin targets need codesign/notarize, so the linux builds
were occupying the scarcest runner in the matrix for no reason.

**What this changes**

- Adds a goreleaser-linux job on ubuntu-latest that builds linux amd64/arm64,
  signs, generates SBOMs and provenance, uploads to S3, and emits a manifest
  fragment plus its checksums. It mirrors the existing goreleaser-windows job and
  runs with --skip=publish; the darwin job still owns the GitHub release.
- Narrows the binaries template to the darwin targets and adds a linux template.
- merge-manifests takes -linux-manifest and merges its assets map, skipping the
  partial checksums entry that publish-release-manifest recomputes.
- publish-release-manifest concatenates the linux checksums into the unified file.

Two contracts a single GoReleaser run used to cover implicitly, now handled
explicitly so they do not silently regress:

- The Homebrew formula. GoReleaser can only build formula entries from artifacts
  in its own run, so a darwin-only run would drop the tap's on_linux blocks. The
  formula is now rendered by cmd/generate-brew-formula from the merged manifest,
  where every platform is present, and pushed to the tap by publish-release-manifest.
  Rendering it against baton-okta v0.5.36 reproduces the published formula exactly
  apart from the header comment and the CDN URLs.
- The GitHub release contents. GoReleaser now attaches only the darwin archives and
  a darwin-only checksums file, so publish-release-manifest re-attaches the linux
  archives and clobbers the checksums file with the unified one. The release keeps
  the same five assets it has today.

Formula download URLs move from GitHub release assets to dist.conductorone.com,
which is already the canonical distribution channel and what the registry records.

**Validation**

- go build ./... , go vet ./... , go test ./cmd/... (new tests cover formula class
  naming, incomplete-asset skipping, and per-OS block rendering).
- scripts/test-release-config-templates.py, extended to assert the binaries and
  linux templates build disjoint GOOS sets and that no template declares brews.
- scripts/test-release-workflow-tag-pin.sh, extended to cover the new GoReleaser
  invocation; the invariant was verified directly since yq was unavailable locally.
- scripts/test-normalize-release-options.sh, scripts/test-derive-iam-role-name.sh.
- Parsed .github/workflows/release.yaml with PyYAML.

Not validated: the workflow has not been executed. Job-level behaviour needs a real
release, so this should go out on a prerelease tag against one connector first.

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