Skip to content

Pin code generation, split it into its own workflow, add renovate.json - #124

Open
biglittlebigben wants to merge 2 commits into
mainfrom
renovate/pin-generators-and-baseline
Open

Pin code generation, split it into its own workflow, add renovate.json#124
biglittlebigben wants to merge 2 commits into
mainfrom
renovate/pin-generators-and-baseline

Conversation

@biglittlebigben

@biglittlebigben biglittlebigben commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Third of three, after livekit/protocol#1733 and livekit/cloud-protocol#1305. This repo needed the most work and is the only one that could not land as a no-op — details below.

Why

The generate path pinned almost nothing, and four separate versions of the same thing disagreed:

version
CI installed protoc-gen-go at v1.28.1
go.mod carried protobuf at v1.36.11
the committed files were actually generated by v1.36.4
setup-protoc was asked for 3.x

setup-go was also on go-version: '>=1.22' against a go.mod requiring 1.25.5, and mage came from mage-action with version: latest.

On top of that the magefile passed the short --plugin= form:

--plugin=go=/path/to/protoc-gen-go              # silently ignored -> plugin taken from PATH
--plugin=protoc-gen-go=/path/to/protoc-gen-go   # honoured

protoc only honours the full-executable-name form, so generation ran whatever protoc-gen-go was on PATH rather than the resolved one. The drift shows up in the tree itself: internal.pb.go was written by protoc v5.28.2 while everything else used v4.23.4.

Pinning

  • protoc-gen-go and mage become tool directives in go.mod, resolved with go tool -n.
  • The --plugin= flag uses the form protoc honours.
  • protoc is pinned to 23.4, the release that generated most of the tree, instead of a 3.x range.
  • CI reads go-version-file: go.mod, so one pin covers build and test.

protoc-gen-psrpc is deliberately not pinned. It's this repo's own command, built from the tree by go install ./protoc-gen-psrpc — the fixtures exist to exercise the plugin as it currently is, so pinning it to a release would defeat the point.

That has a consequence worth flagging: the fixtures under internal/test/ invoke protoc directly via go:generate, so they take plugins off PATH. Generate() now puts the pinned protoc-gen-go on PATH for them. Without that, dropping the CI go install protoc-gen-go@v1.28.1 step would have left no protoc-gen-go available at all.

Workflow split

Generation of the committed output moves into a new generate.yaml, modelled on protocol's: runs on branch pushes, regenerates with the pinned toolchain, commits the result.

test.yaml keeps its own generation, because it has to. internal/test/.gitignore contains **/*.psrpc.go, so the psrpc half of every fixture is a build artifact that does not exist in a fresh checkout — the tests reference symbols from it (NewMyServiceServer and friends), so mage testall has to generate before anything compiles. What did change there: the four @latest installs and mage-action are gone, protoc is pinned, and Go comes from go.mod.

generate.yaml replaces an add-and-commit step that was dead for its entire life. It was copied from protocol in 4e4425e, the commit that first added protoc-gen-psrpc, together with protocol's add: livekit. livekit/ is correct in protocol — it holds that repo's generated Go code — but searching every commit here for an addition under livekit/ returns zero. The directory has never existed in psrpc; committed generated code lives in internal/, testutils/ and protoc-gen-psrpc/options/. add-and-commit warns and commits nothing when its path matches nothing, and doesn't fail the job, which is why it went unnoticed for ~3 years.

Why this one isn't a no-op

The other two PRs verified with "regenerate → empty diff". That's impossible here: protoc-gen-go and the protobuf runtime are the same module, so pinning the plugin to the v1.36.4 that wrote these files would mean downgrading the runtime library to v1.36.4 as well. Pinning to go.mod's existing v1.36.11 and regenerating is the smaller, safer change, so this PR carries that regeneration.

It is representational only. Across all 21 committed generated files the entire non-header diff is protobuf-go changing how it emits the descriptor:

-var file_internal_proto_rawDesc = string([]byte{
-	0x0a, 0x0e, 0x69, 0x6e, 0x74, 0x65, ...
-})
+const file_internal_proto_rawDesc = "" +
+	"\n" +
+	"\x0einternal.proto\x12\binternal..."

Verified mechanically — filtering the diff down to lines that are neither descriptor payload nor version headers leaves exactly that one construct in each file and nothing else. No struct, getter, or field changed.

go build ./... passes, and the full suite passes against the regenerated tree — including from a state with the gitignored *.psrpc.go fixtures deleted, which is what a CI checkout actually looks like.

renovate.json

New file — this repo had none. The org baseline for a library: third-party modules ungrouped so a bad bump reverts alone, livekit deps grouped and exempt from quarantine, a vulnerability fast path, and both the go and toolchain deptypes disabled, since this module's go directive is the minimum we ask of consumers.

No pion group (no pion dependencies) and no GOPRIVATE (all dependencies public). Since helpers:pinGitHubActionDigests is included, Renovate will pin the remaining floating action tags in slack-notifier.yaml on its first run.

🤖 Generated with Claude Code

Comment thread .github/workflows/generate.yaml Fixed
biglittlebigben and others added 2 commits August 25, 2026 10:02
The generate path here pinned almost nothing. CI installed protoc-gen-go
at v1.28.1 while go.mod carried protobuf v1.36.11, asked
arduino/setup-protoc for `3.x`, ran mage from `mage-action` with
`version: latest`, and set up Go with `>=1.22` against a go.mod
requiring 1.25.5. The magefile also passed `--plugin=go=`, the short
form protoc silently ignores, so generation ran whatever protoc-gen-go
happened to be on PATH rather than the resolved one.

The committed output showed the drift: every generated file was written
by protoc-gen-go v1.36.4 - matching neither go.mod nor CI - and
internal.pb.go by protoc v5.28.2 where the rest used v4.23.4.

Pin what writes the code:

  - protoc-gen-go and mage become `tool` directives in go.mod, resolved
    at generation time with `go tool -n`
  - the `--plugin=` flag uses the full-executable-name form protoc
    honours
  - protoc is pinned to the release that generated most of the tree,
    rather than a `3.x` range
  - CI reads go-version-file: go.mod, so one pin covers build and test

protoc-gen-psrpc is deliberately NOT pinned: it is this repo's own
command, built from the tree by `go install ./protoc-gen-psrpc` so the
fixtures exercise the plugin as it currently is. Because the fixtures
invoke protoc directly through go:generate, Generate() now puts the
pinned protoc-gen-go on PATH for them - without it, dropping the CI
install step would leave no protoc-gen-go at all.

Generation of the committed output moves into generate.yaml: it runs on
branch pushes, regenerates with the pinned toolchain and commits the
result. test.yaml keeps its own generation because it has to -
internal/test gitignores **/*.psrpc.go, so the fixtures the tests
compile against do not exist in a fresh checkout and `mage testall` must
build them before anything compiles.

generate.yaml replaces an add-and-commit step that arrived, copied in
from elsewhere, with the commit that first added protoc-gen-psrpc -
complete with an `add: livekit` path. That directory has never existed
here, so the step silently committed nothing for its whole life.

This could not land as a no-op. protoc-gen-go and the protobuf runtime
are one module, so pinning the plugin to the v1.36.4 that wrote these
files would mean downgrading the library too. Pinning to go.mod's
v1.36.11 and regenerating is the smaller change, so this commit carries
that regeneration. It is representational only: across all 21 committed
generated files the sole difference is protobuf-go emitting the
descriptor as `const x = "" + "..."` instead of
`var x = string([]byte{0x0a, ...})`, plus version headers. No struct,
getter or field changed. The full suite passes against the regenerated
tree, including from a state with the gitignored fixtures deleted.

renovate.json is new: the org baseline for a library, with third-party
modules ungrouped so a bad bump reverts alone, livekit deps grouped and
exempt from quarantine, a vulnerability fast path, and both the go and
toolchain directives disabled, since this module's go directive is the
minimum we ask of consumers.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CodeQL flagged generate.yaml for relying on the default GITHUB_TOKEN
permissions. Its suggested starting point of `contents: read` would not
work here - add-and-commit pushes the regenerated output, so the job
needs `contents: write`. test.yaml gets `contents: read`, which is all
it uses: setup-protoc's repo-token only reads release metadata.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@biglittlebigben
biglittlebigben force-pushed the renovate/pin-generators-and-baseline branch from 49e948c to 62b440d Compare August 25, 2026 17:03
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.

2 participants