Skip to content

fix(providers): inject shim version at build time#338

Merged
robert-cronin merged 3 commits into
kaito-project:mainfrom
surajssd:remove-hardcoded-provider-version
Jun 29, 2026
Merged

fix(providers): inject shim version at build time#338
robert-cronin merged 3 commits into
kaito-project:mainfrom
surajssd:remove-hardcoded-provider-version

Conversation

@surajssd

@surajssd surajssd commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Description

Each provider shim hard-coded its reported version as a Go const in config.go, which was written verbatim into InferenceProviderConfig.status.version (shown by kubectl, the Web UI, and the Headlamp plugin). The const was never bumped at release time, so the reported version lied.

This PR injects the shim version at build time from the release workflow's version input — the same value that tags the image — so status.version equals the image tag by construction. Non-release builds report a git-derived dev stamp (dev-<sha>, or dev-<sha>-dirty when the tree is dirty). The anti-pattern existed in all 5 shims: dynamo, kaito, kuberay, llmd, vllm.

Visual Representation of the change

https://suraj.io/share/prs/github.com/kaito-project/airunway/pull/338/dashboard.html

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 💥 Breaking change (fix or feature that would cause existing functionality to change)
  • 📚 Documentation update
  • 🎨 UI/UX improvement
  • ♻️ Refactoring (no functional changes)
  • 🧪 Test update
  • 🔧 Build/CI configuration

Related Issues

Fixes #309

Changes Made

Go source — providers/<p>/config.go (all 5)

  • Removed the hand-maintained ProviderVersion const (kept ProviderConfigName as a const).
  • Added var shimVersion = "dev" as the build-time -ldflags -X injection target, and derived var ProviderVersion = ProviderConfigName + "-provider:" + shimVersion.
  • Inject shimVersion (a constant-string initializer), never the composite ProviderVersion-X silently no-ops on a non-constant initializer. shimVersion stays unexported; -X resolves the linker symbol regardless of Go visibility.

Tests — providers/<p>/config_test.go (all 5)

  • Replaced exact-literal version assertions with strings.HasPrefix(ProviderVersion, "<name>-provider:") shape checks, so the test no longer needs lockstep edits.
  • dynamo/kaito/kuberay edited their existing TestProviderConstants in place; llmd/vllm gained a new TestProviderConstants (and a "strings" import) asserting both ProviderConfigName and the version shape.

providers/<p>/Makefile (all 5)

  • Resolve the module path with MODULE := $(shell go list -m) so the -X target can never drift from a hand-typed string.
  • Added a git-stamp default: SHIM_VERSION ?= dev-$(GIT_SHA)$(GIT_DIRTY) (bin/provider is gitignored, so building never dirties the tree).
  • Wired every -X flag through $(MODULE); dynamo/vllm retrofit their existing DynamoVersion/VLLMVersion flags onto it. --build-arg SHIM_VERSION=$(SHIM_VERSION) is now passed to every docker-build.

providers/<p>/Dockerfile (all 5)

  • Added ARG SHIM_VERSION with no default plus a RUN test -n "${SHIM_VERSION}" guard, mirroring the existing DYNAMO_VERSION/VLLM_VERSION treatment, so a hand-run docker build fails loud instead of silently shipping :dev.
  • The build RUN resolves MODULE=$(go list -m) and injects via ${MODULE}; dynamo/vllm retrofit their existing -X flags (preserving -a and vllm's -mod=readonly).

Release workflows — .github/workflows/release-<p>-provider.yml

  • dynamo/kaito/kuberay/llmd: pass SHIM_VERSION=${{ inputs.version }} as a build-arg (the same value that tags the image).
  • Added a new release-vllm-provider.yml mirroring release-dynamo-provider.yml, carrying both VLLM_VERSION (from versions.env) and SHIM_VERSION.

CI anti-regression guard — .github/workflows/test.yml

  • Added a step to the existing provider-go-checks matrix (between make build and make test) that reads the binary back with go version -m to assert both the git-stamp default and an explicit SHIM_VERSION override land — guarding against future wiring/symbol drift.

Testing

  • Unit tests pass (bun run test)
  • Manual testing performed
  • Tested with a Kubernetes cluster

This change is Go/build-tooling only and does not touch the frontend/backend TypeScript, so bun run test is not the relevant suite. Every check below is copy-pasteable from the repo root.

1. Go unit + shape testsmake -C providers/<p> test (vet + go test) passes for all 5, exercising the new TestProviderConstants shape assertion. The second loop proves the bare fallback (no -ldflags) resolves to exactly <name>-provider:dev:

# vet + unit tests for all 5 shims
for p in dynamo kaito kuberay llmd vllm; do make -C providers/$p test; done

# prove the in-source fallback is exactly "<name>-provider:dev" (self-cleaning)
for p in dynamo kaito kuberay llmd vllm; do
  cat > providers/$p/zz_fallback_test.go <<EOF
package $p
import "testing"
func TestZZFallback(t *testing.T){ if ProviderVersion != "$p-provider:dev" { t.Fatalf("got %s", ProviderVersion) } }
EOF
  ( cd providers/$p && go test -run TestZZFallback ./... ) || echo "$p"
  rm -f providers/$p/zz_fallback_test.go
done

2. Injection worksgo list -m makes the -X target correct-by-construction; this proves the symbol actually resolves so ProviderVersion becomes <name>-provider:v9.9.9 for all 5 (also confirms the shimVersionProviderVersion init-ordering):

for p in dynamo kaito kuberay llmd vllm; do
  ( cd providers/$p && mod=$(go list -m) && \
    go build -ldflags="-X $mod.shimVersion=v9.9.9" -o /tmp/$p-provider ./cmd/main.go && \
    go version -m /tmp/$p-provider | grep -F "shimVersion=v9.9.9" ) \
    && echo "$p" || { echo "❌ FAIL: $p"; break; }
done

3. Real image build (kuberay) — build the image with the release-style arg, then read the version baked into the binary inside the image. It must report shimVersion=v0.6.0, so status.version reads kuberay-provider:v0.6.0 — matching the image tag by construction:

make -C providers/kuberay docker-build SHIM_VERSION=v0.6.0 IMG=kuberay-provider:verify

cid=$(docker create kuberay-provider:verify)
docker cp "$cid":/provider /tmp/kuberay-fromimg && docker rm "$cid"
go version -m /tmp/kuberay-fromimg | grep -F "shimVersion=v0.6.0" \
  && echo "✅ image binary carries v0.6.0"

4. Negative guard checkmake docker-build always injects the arg, so the missing-arg case must be exercised with a raw docker build. With no SHIM_VERSION, the RUN test -n guard fails the build loud instead of silently shipping :dev:

docker build -f providers/kuberay/Dockerfile -t kuberay-provider:shouldfail . \
  && echo "❌ guard did NOT trip" \
  || echo "✅ guard tripped as expected (build failed without SHIM_VERSION)"

5. Repo guards — confirm no new version source was introduced; both still pass:

make verify-versions
make test-verify-versions

6. CI read-back step, run locally — mirrors the new provider-go-checks step for all 5: (a) the un-overridden make build used the Makefile git-stamp default, and (b) an explicit override injects. (CI checkout is clean, so the stamp is dev-<sha>; the regex also allows -dirty for local trees.)

for p in dynamo kaito kuberay llmd vllm; do
  make -C providers/$p build
  mod=$(cd providers/$p && go list -m)
  go version -m providers/$p/bin/provider \
    | command grep -Eq -- "-X ${mod}.shimVersion=dev-[0-9a-f]+(-dirty)?" \
    || { echo "❌ git-stamp default missing in $p"; break; }
  make -C providers/$p build SHIM_VERSION=v9.9.9-citest
  go version -m providers/$p/bin/provider \
    | command grep -F -- "-X ${mod}.shimVersion=v9.9.9-citest" \
    || { echo "❌ override injection missing in $p"; break; }
  echo "$p"
done

Checklist

  • My code follows the project's style guidelines
  • I have run bun run lint
  • I have added tests that prove my fix/feature works
  • New and existing unit tests pass locally
  • I have updated documentation if needed
  • My changes generate no new warnings

Additional Notes

  • Docs deliberately untouched. The illustrative dynamo-provider:v0.2.0 examples in docs/api.md / docs/crd-reference.md are shape examples, not assertions, and nothing tests them — any literal replacement would just re-stale, so they were left as-is.
  • No versions.env / verify-versions wiring for the shim version — there is no committed source of truth for it (unlike DYNAMO_VERSION/VLLM_VERSION); the release tag is the source of truth, injected at build time.
  • Follow-up (separate, needs human approval): file an issue to add actionlint to CI. Release workflows are workflow_dispatch-only and never lint in PR CI, so the hand-authored release-vllm-provider.yml currently relies on the Dockerfile test -n guard as the release-time backstop. actionlint validates syntax/shape, not semantic completeness (it won't catch a missing build-arg), so it complements rather than replaces the guard.

@surajssd surajssd requested a review from Copilot June 26, 2026 18:21
@surajssd surajssd requested a review from a team as a code owner June 26, 2026 18:21

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes issue #309, where each provider shim's reported version (InferenceProviderConfig.status.version, surfaced by kubectl, the Web UI, and Headlamp) was a hand-maintained Go const that drifted from the actually-published image tag. The fix replaces the const with a build-time -ldflags -X injection target so the reported version equals the image tag by construction, and falls back to a git-derived dev-<sha> stamp for non-release builds. The change is applied uniformly across all five in-tree shims (dynamo, kaito, kuberay, llmd, vllm) and fits the existing pattern already used for the pinned upstream versions (DynamoVersion, VLLMVersion).

Changes:

  • In each config.go, replace the ProviderVersion const with var shimVersion = "dev" (the -X injection target) and a derived var ProviderVersion = ProviderConfigName + "-provider:" + shimVersion; tests switch from exact-literal to HasPrefix shape checks.
  • Each Makefile/Dockerfile resolves the module path via go list -m, computes a git-stamp default SHIM_VERSION, wires it through -ldflags, and the Dockerfile adds a test -n "${SHIM_VERSION}" guard.
  • Release workflows pass SHIM_VERSION=${{ inputs.version }} (a new release-vllm-provider.yml mirrors the dynamo one), and test.yml gains a CI step asserting both the git-stamp default and an explicit override land in the binary.

Reviewed changes

Copilot reviewed 26 out of 26 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
providers/*/config.go (5) Convert ProviderVersion const to a var derived from build-injected shimVersion.
providers/*/config_test.go (5) Replace exact-version assertions with strings.HasPrefix shape checks.
providers/*/Makefile (5) Resolve MODULE from go.mod, add git-stamp SHIM_VERSION, wire -X flags and docker build-arg.
providers/*/Dockerfile (5) Add ARG SHIM_VERSION + test -n guard, resolve MODULE, inject via -ldflags.
.github/workflows/release-{dynamo,kaito,kuberay,llmd}-provider.yml Pass SHIM_VERSION build-arg from the release version input.
.github/workflows/release-vllm-provider.yml New workflow mirroring dynamo, carrying VLLM_VERSION + SHIM_VERSION.
.github/workflows/test.yml New CI guard asserting shimVersion wiring and git-stamp default.

The implementation is consistent across all providers: the -X flag correctly targets the unexported shimVersion (not the composite ProviderVersion), Go's init-order ensures ProviderVersion picks up the injected value, and all ProviderVersion usages are runtime contexts so the const→var change is safe. The new vLLM workflow matches the proven dynamo workflow and pinned action SHAs. The only observation is a minor documentation nit about opaque "(decision N)" references in the Makefile comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread providers/dynamo/Makefile Outdated
Each provider shim hard-coded its reported version as a Go `const` in
`config.go`, written verbatim into
`InferenceProviderConfig.status.version`. The const was never bumped at
release, so the reported version lied. Inject it at build time from the
release workflow `version` input — the same value that tags the image —
so `status.version` equals the image tag by construction. Non-release
builds report a git stamp (`dev-<sha>`, `dev-<sha>-dirty`).

Applied across all 5 shims (dynamo, kaito, kuberay, llmd, vllm):

- `config.go`: drop the `ProviderVersion` const; add `var shimVersion =
  "dev"` (the `-X` injection target) and derive `var ProviderVersion`
  from it. Inject `shimVersion`, never the composite `ProviderVersion`
  (a non-constant initializer that `-X` silently no-ops).
- `config_test.go`: replace exact-literal version assertions with
  `strings.HasPrefix` shape checks.
- `Makefile`: resolve the module path via `go list -m` so the `-X`
  target cannot drift from a hand-typed string; add a git-stamp
  `SHIM_VERSION` default; pass `--build-arg SHIM_VERSION` to
  `docker-build`. Retrofit dynamo/vllm `DynamoVersion`/`VLLMVersion`
  flags onto the computed `$(MODULE)`.
- `Dockerfile`: add `ARG SHIM_VERSION` (no default) plus a `test -n`
  guard so a bare `docker build` fails loud instead of shipping `:dev`;
  resolve `MODULE` via `go list -m` in the build `RUN`.

CI and release wiring:

- `release-*-provider.yml`: pass `SHIM_VERSION=${{ inputs.version }}` as
  a build-arg; add a new `release-vllm-provider.yml` mirroring the
  dynamo workflow.
- `test.yml`: assert in `provider-go-checks` that the git-stamp default
  and an explicit `SHIM_VERSION` override both land in the built binary.

Signed-off-by: Suraj Deshmukh <suraj.deshmukh@microsoft.com>
@surajssd surajssd force-pushed the remove-hardcoded-provider-version branch from 70fb715 to 4164b17 Compare June 26, 2026 18:34
The CI `go version -m` check only proves the `-ldflags -X` flag was
*passed* to `go build`, not that the linker *patched* the symbol. Making
shimVersion a const, renaming it, or retargeting `ProviderVersion` would
keep the flag present but silently no-op the injection, reverting
`status.version` to `<name>-provider:dev` with CI still green.

- add `TestShimVersionInjection` to all 5 shims: asserts the runtime
  value of `ProviderVersion` under injection, gated on
  `EXPECT_PROVIDER_VERSION` so a plain `go test` skips
- wire the test into the `provider-go-checks` matrix, built with
  `-ldflags`, so a silent `-X` no-op now fails CI
- harden the git-stamp regex in `test.yml` to tolerate `dev-unknown`
  (builds outside a git tree, e.g. source tarballs)
- document the `shimVersion` / `SHIM_VERSION` contract in
  `providers/README.md` so a new shim cannot reintroduce the const bug
- drop dead `(decision N)` references from all 5 Makefiles and clarify
  that the repo-wide `git status --porcelain` dirty check is a
  deliberate conservative superset

Signed-off-by: Suraj Deshmukh <suraj.deshmukh@microsoft.com>
Copilot AI review requested due to automatic review settings June 26, 2026 19:32

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 32 out of 32 changed files in this pull request and generated no new comments.

@robert-cronin robert-cronin added this to the 0.8.0 milestone Jun 29, 2026
@robert-cronin robert-cronin merged commit 04f5de5 into kaito-project:main Jun 29, 2026
13 checks passed
@surajssd surajssd deleted the remove-hardcoded-provider-version branch June 29, 2026 23:53
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.

ProviderVersion is a hand-maintained const in all four shims

3 participants