Skip to content

ci: conform CI/CD to the shared repo/CI-CD standard#151

Merged
rianjs merged 1 commit into
mainfrom
ci/148-conform-cicd-standard
May 30, 2026
Merged

ci: conform CI/CD to the shared repo/CI-CD standard#151
rianjs merged 1 commit into
mainfrom
ci/148-conform-cicd-standard

Conversation

@rianjs

@rianjs rianjs commented May 30, 2026

Copy link
Copy Markdown
Contributor

What

Brings google-readonly into conformance with the repo/CI-CD standard (flat-repo sibling of the merged slck#176 / nrq#116): CI on the shared composite actions, release/distribution on the reusable workflows (open-cli-collective/.github @v1), with identity declared once in packaging/identity.yml.

Published package identities are unchanged (acceptance): cask google-readonly, winget OpenCLICollective.google-readonly, chocolatey google-readonly, linux google-readonly — verified by identity-check.

Changes

  • packaging/identity.yml (new) — binary gro, goreleaser_config: .goreleaser.yaml, tag prefix v, the four channel identities above, and a JSON keychain_probe using gro's config show -j control-plane flag (the short JSON flag; not the global --output json). Faithful port of the INT-449 darwin gate (seeded config, asserts backend=keychain / backend_source=auto / credential_ref=google-readonly/default).
  • ci.yml on composites (ci.md §7): non-required build-platform OS matrix + required build aggregate; test (go-test@v1), lint (go-lint@v1), identity-check, pr-title. Preserved required gates: a tidy job (the old make tidy test build ran make tidy = go mod tidy && git diff --exit-code), the static-release-guard, and the coverage floor (make test-cover-check, ci.md §6 — kept blocking, not advisory).
  • auto-release / release callers replace the hand-rolled pipelines; the reusable owns the darwin keychain-probe gate and the homebrew/winget/chocolatey/linux fan-out.
  • .goreleaser.yaml: homebrew_casks add skip_upload: true + remove the tap token (the reusable homebrew step is the single tap writer); add release.replace_existing_artifacts: true; fix the mutating before-hook to fail on a dirty go.mod/go.sum (sh -c "go mod tidy && git diff --exit-code go.mod go.sum"). No url.template — flat v repo, no tag rename.
  • Remove snap/ (the disabled if: false job; distribution.md §7 — Linux ships via apt/rpm).
  • Demote chocolatey-publish.yml / winget-publish.yml to workflow_dispatch-only manual escape hatches (drop the release: published auto-trigger now that the reusable owns the common-path publish — avoids double-publish).

Out of scope

test-chocolatey.yml / test-winget.yml left as-is (post-merge packaging validation). The make test-cover-check target's output redirection is gro's pre-existing Makefile trait (the visible make test in the test job is the diagnostic signal).

Proof ladder (local)

  • goreleaser check + goreleaser release --snapshot → renders dist/homebrew/Casks/google-readonly.rb (binary gro, default v URL), skip_upload → no tap push.
  • identity-check validate — clean.
  • release-preflight check-config --homebrew --tag-prefix v — ✅.
  • darwin probe: config show -j in isolated HOME + seed → backend=keychain, source=auto, credential_ref=google-readonly/default.
  • make tidy (clean) + make lint (0 issues) + make test-cover-check (70.7% ≥ 60%) + make build.

Branch protection (post-merge): require build, tidy, test, lint, static-release-guard, coverage, identity-check, pr-title.

Closes #148

Migrate CI onto the shared composite actions and release/distribution onto
the reusable workflows (open-cli-collective/.github @v1), declaring identity
once in packaging/identity.yml. Published package identities (cask
google-readonly, winget/chocolatey/linux google-readonly) are unchanged.

- New packaging/identity.yml: binary gro, goreleaser_config .goreleaser.yaml,
  tag prefix v, canonical_cask google-readonly (no alias), winget/choco/linux
  google-readonly, and a JSON keychain_probe using gro's `config show -j`
  control-plane flag (verified backend=keychain/auto locally).
- ci.yml on composites: build-platform matrix + required build aggregate,
  go-test, go-lint, identity-check, pr-title; plus preserved required gates —
  a tidy job (go mod tidy + git diff --exit-code, from the old `make tidy test
  build`), the static-release build guard, and the make test-cover-check
  coverage floor (ci.md §6).
- auto-release/release callers replace the hand-rolled pipelines; the reusable
  owns the darwin keychain-probe gate and the homebrew/winget/chocolatey/linux
  fan-out.
- goreleaser homebrew_casks: skip_upload + tap token removed (the reusable
  homebrew step is the single tap writer); add release.replace_existing_artifacts;
  fix the mutating before-hook to fail on a dirty go.mod/go.sum.
- Remove the orphaned snap/ (disabled job; distribution.md §7 — Linux via apt/rpm).
- Demote chocolatey-publish/winget-publish to workflow_dispatch-only manual
  escape hatches (drop the release: published auto-trigger; the reusable now
  owns the common-path publish, avoiding double-publish).

Closes #148
@rianjs

rianjs commented May 30, 2026

Copy link
Copy Markdown
Contributor Author

Findings

Nit: PR body says the before-hook is sh -c "go mod tidy && rtk git diff --exit-code go.mod go.sum", but the actual hook correctly uses plain git diff in .goreleaser.yaml:8. Please update the PR body so it does not imply rtk is part of the release hook.

Review Notes

No code-level drift found. The implementation matches the converged plan:

  • tidy, static-release-guard, and coverage remain required custom gates.
  • Composite CI shape matches the flat-repo standard.
  • config show -j JSON keychain probe is correct for gro.
  • Cask stays google-readonly, with no alias and no url.template.
  • skip_upload, tap-token removal, fail-on-dirty before-hook, and replace_existing_artifacts are correct.
  • Snap is removed.
  • Choco/winget are manual-only escape hatches, avoiding double-publish.

@rianjs

rianjs commented May 30, 2026

Copy link
Copy Markdown
Contributor Author

Test Coverage Assessment

Change type: CI/CD migration — no Go production-code changes. Assessment is scoped to the verification surface that matters for workflow/packaging YAML.

Gates in place

Gate Mechanism Required check
Identity manifest validates identity-check@v1 composite yes
GoReleaser config + render goreleaser check + --snapshot (proof ladder, reusable release job) yes (via release job)
darwin Keychain probe keychain_probe in identity.yml → reusable release.yml darwin-gate yes (at release time)
Static CGO-off guard (Linux) static-release-guard job (preserved inline) yes
Coverage floor 60% make test-cover-check in coverage job (preserved) yes
Module tidy tidy job (preserved inline) yes
Build matrix + aggregate build-platform + build aggregate yes
Test suite go-test@v1 yes
Lint go-lint@v1 yes
PR title convention pr-title@v1 yes (on PRs)

Assessment

Coverage is adequate for this change type. Every preserved gate from the pre-migration CI is accounted for, and the new identity-check job directly validates the only new artifact introduced (packaging/identity.yml). The darwin Keychain probe logic is a faithful port of the INT-449 inline gate into the manifest — it runs under the reusable release workflow which has its own tests in open-cli-collective/.github. The proof ladder in the PR description (goreleaser check, snapshot, identity-check validate, release-preflight, local probe, tidy/lint/cover/build) covers the remaining manual verification.

No actionable gaps identified.

@monit-reviewer monit-reviewer left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Automated PR Review

Reviewed commit: b3d36aa

Summary

Reviewer Findings
harness-engineering:harness-architecture-reviewer 1
harness-engineering:harness-enforcement-reviewer 2
harness-engineering:harness-knowledge-reviewer 1
harness-engineering:harness-architecture-reviewer (1 findings)

💡 Suggestion - .github/workflows/ci.yml:42

The 'build' aggregate gate passes when all needs results are 'skipped'. While intentional for skipped matrix legs, if the entire build-platform matrix were somehow skipped (e.g., a future path filter added upstream), the required 'build' gate would pass vacuously. Consider documenting this invariant or adding an explicit check that at least one leg ran, if the standard mandates it.

harness-engineering:harness-enforcement-reviewer (2 findings)

💡 Suggestion - .github/workflows/ci.yml:101

The identity-check job calls open-cli-collective/.github/actions/identity-check@v1 with no preceding actions/checkout step. Every other job that reads repo files (tidy, static-release-guard, coverage) explicitly checks out first. If the composite action does not perform its own checkout internally, it will fail to find packaging/identity.yml. Consider adding - uses: actions/checkout@v4 as the first step unless the composite action's contract explicitly promises a self-checkout.

💡 Suggestion - .github/workflows/release.yml:16

The workflow_dispatch input was changed from a free-text tag to a boolean dry-run, removing the ability to manually re-trigger a release for an arbitrary existing tag. If a release ever needs to be re-cut from a historical tag (e.g. to recover a failed publish), the manual escape hatch no longer exists in this workflow. The chocolatey/winget dispatch files retain tag inputs as manual escape hatches, but the primary goreleaser build does not.

harness-engineering:harness-knowledge-reviewer (1 findings)

⚠️ Should Fix - packaging/identity.yml:8

archives.name_template declares 'gro_v{{ .Version }}{{ .Os }}{{ .Arch }}' but project_name in .goreleaser.yaml is 'google-readonly'. Unless .goreleaser.yaml already has a matching archives.name_template override, GoReleaser's default would produce 'google-readonly_{{ .Version }}...' — no 'gro' prefix and no 'v' infix. If the reusable release.yml@v1 uses this field to locate or verify published artifacts, the name mismatch will cause artifact lookup failures at release time.

2 info-level observations excluded. Run with --verbose to include.

2 PR discussion threads considered.


Completed in 1m 46s | $0.50 | sonnet | daemon 0.2.121 | Glorfindel
Field Value
Model sonnet
Reviewers hybrid-synthesis, harness-engineering:harness-architecture-reviewer, harness-engineering:harness-enforcement-reviewer, harness-engineering:harness-knowledge-reviewer
Engine claude · sonnet
Reviewed by pr-review-daemon · monit-pr-reviewer
Duration 1m 46s wall · 4m 02s compute (Reviewers: 1m 21s · Synthesis: 22s)
Cost $0.50
Tokens 94.7k in / 13.1k out
Turns 4

Per-workstream usage

Workstream Model In Out Cache read Cache create Cost
hybrid-synthesis sonnet 35.8k 1.5k 18.6k 17.2k (1h) $0.10
harness-engineering:harness-architecture-reviewer sonnet 19.6k 3.8k 2.1k 17.5k (1h) $0.13
harness-engineering:harness-enforcement-reviewer sonnet 19.6k 3.8k 2.1k 17.5k (1h) $0.13
harness-engineering:harness-knowledge-reviewer sonnet 19.6k 4.0k 2.1k 17.5k (1h) $0.14

Re-reviews only run when @monit-reviewer is re-requested as a reviewer — push as many commits as you need, then re-request when ready. PRs targeting branches other than main, master are skipped, even when @monit-reviewer is re-requested.

Comment thread packaging/identity.yml
tag:
prefix: v
version_scheme: major_minor_run_patch
archives:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Medium (harness-engineering:harness-knowledge-reviewer): archives.name_template declares 'gro_v{{ .Version }}{{ .Os }}{{ .Arch }}' but project_name in .goreleaser.yaml is 'google-readonly'. Unless .goreleaser.yaml already has a matching archives.name_template override, GoReleaser's default would produce 'google-readonly_{{ .Version }}...' — no 'gro' prefix and no 'v' infix. If the reusable release.yml@v1 uses this field to locate or verify published artifacts, the name mismatch will cause artifact lookup failures at release time.

Reply to this thread when addressed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Verified false positive: .goreleaser.yaml line 70 already declares archives.name_template: "gro_v{{ .Version }}_{{ .Os }}_{{ .Arch }}", identical to the manifest. identity-check enforces that the manifest template string-equals the goreleaser one and it passed in CI; the local goreleaser release --snapshot rendered gro_v* archives. No mismatch — no change.

Comment thread .github/workflows/ci.yml
# own required check so the floor stays blocking (not advisory).
coverage:
runs-on: ubuntu-latest
steps:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔵 Low (harness-engineering:harness-enforcement-reviewer): The identity-check job calls open-cli-collective/.github/actions/identity-check@v1 with no preceding actions/checkout step. Every other job that reads repo files (tidy, static-release-guard, coverage) explicitly checks out first. If the composite action does not perform its own checkout internally, it will fail to find packaging/identity.yml. Consider adding - uses: actions/checkout@v4 as the first step unless the composite action's contract explicitly promises a self-checkout.

Reply to this thread when addressed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

False positive: the job's first step is - uses: actions/checkout@v4 (ci.yml line 111), immediately before the composite. The identity-check job passed in CI, which it couldn't without the checkout finding packaging/identity.yml. No change.

- 'v*'
- "v*"
workflow_dispatch:
inputs:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔵 Low (harness-engineering:harness-enforcement-reviewer): The workflow_dispatch input was changed from a free-text tag to a boolean dry-run, removing the ability to manually re-trigger a release for an arbitrary existing tag. If a release ever needs to be re-cut from a historical tag (e.g. to recover a failed publish), the manual escape hatch no longer exists in this workflow. The chocolatey/winget dispatch files retain tag inputs as manual escape hatches, but the primary goreleaser build does not.

Reply to this thread when addressed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Intentional — matches the family standard (slck#176/nrq#116 release callers): the manual knob is the dry-run boolean, and re-cutting a release from a historical tag is done by re-pushing that tag (the tag-push path re-triggers the reusable, which is idempotent via replace_existing_artifacts). The free-text tag input was specific to the old inline goreleaser job that this caller replaces. No change.

Comment thread .github/workflows/ci.yml
# Preserve the pre-migration `make tidy test build` tidy gate: `make tidy` runs
# `go mod tidy && git diff --exit-code go.mod go.sum`, failing a PR whose module
# files aren't tidy. The go-build/go-test composites don't tidy, so this stays.
tidy:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔵 Low (harness-engineering:harness-architecture-reviewer): The 'build' aggregate gate passes when all needs results are 'skipped'. While intentional for skipped matrix legs, if the entire build-platform matrix were somehow skipped (e.g., a future path filter added upstream), the required 'build' gate would pass vacuously. Consider documenting this invariant or adding an explicit check that at least one leg ran, if the standard mandates it.

Reply to this thread when addressed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Intentional — this is the codified ci.md §7 'build aggregate' pattern (identical in slck/nrq). build-platform carries no gating if:/path-filter, so all three OS legs always run; the aggregate can't pass vacuously today. The skip-tolerance exists only so a genuinely failed/cancelled leg fails the gate while a skipped one doesn't. Guarding a hypothetical future path filter would be speculative. No change.

@rianjs rianjs merged commit 2e825ab into main May 30, 2026
11 checks passed
@rianjs rianjs deleted the ci/148-conform-cicd-standard branch May 30, 2026 13:16
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.

Conform CI/CD to repo/CI/CD standards

2 participants