Pulp Engine Document Rendering
Get started
Release v0.63.0

Release v0.63.0

Date: 2026-04-11

Theme

SDK coverage initiative — Stage 2: the Go SDK ships.

v0.62.0 landed Stage 1 (the .NET SDK) and the generator-driven SDK track. v0.63.0 adds Go as the second language on that track, using the same orchestration (pnpm sdk:generate), the same committed-output pattern, the same --check determinism gate, and the same smoke-test shape. What’s new is the publishing model: Go modules don’t have a central registry like NuGet or PyPI — “publishing” means creating a git tag that proxy.golang.org can discover.

Stage 3 (Java) is still ahead. See docs/initiatives/sdk-coverage-stage1.md for the condensed execution checklist and the running deviation log across all stages.

Highlights

Go SDK at github.com/TroyCoderBoy/pulp-engine/packages/sdk-go

  • Package: packages/sdk-go/. Import path github.com/TroyCoderBoy/pulp-engine/packages/sdk-go. Requires Go 1.21 or later.
  • Generated from: openapi.json via the openapi-generator go template (JAR 7.10.0, pinned in openapitools.json alongside the .NET pin).
  • Install:
    go get github.com/TroyCoderBoy/pulp-engine/packages/sdk-go@v0.63.0
  • All generated source is committed. Same pattern as the .NET SDK. Debuggable, reviewable, reproducible.
  • Hand-written files (README, RELEASING, generator config, version.go, health_smoke_test.go) protected by .openapi-generator-ignore. Verified by regenerating twice with a sentinel edit in README.md between runs.
  • Round-trip smoke test health_smoke_test.go exercises the generated HealthAPIService.HealthGet() against a local API, asserts the typed HealthGet200Response shape, and verifies the SDK’s Version constant from version.go matches the API’s runtime version field as a lockstep-integrity check. Passed end-to-end on this release commit (docker run --network host golang:1.23-alpine go test -run TestHealthSmoke).

Go subdirectory module publishing — tag packages/sdk-go/vX.Y.Z

Because the Go SDK lives at packages/sdk-go/ inside the monorepo rather than in its own repo, Go’s subdirectory module support handles the import via a tag-prefix convention: the sibling tag packages/sdk-go/v0.63.0 (with the slash prefix — git allows it) makes the subdirectory installable as a standalone module. Consumers pay the ugly import path once in their import block; everything else just works.

This is the single highest-risk step in the initiative. Git tags are immutable and the Go module proxy caches aggressively, so the pre-flight dry run documented in RELEASING.md is mandatory.

⚠ Private-repo caveat — consumer setup required

Surfaced during Stage 2 execution, not in the original plan: the pulp-engine repository is currently private, and proxy.golang.org (Go’s default module proxy) cannot fetch from private repos. The subdirectory-tag scheme still works — consumers can go get the SDK — but they must first configure GOPRIVATE and provide authenticated git credentials:

# One-time setup per consumer machine:
go env -w GOPRIVATE=github.com/TroyCoderBoy/*

# Then either SSH (recommended):
git config --global url."git@github.com:".insteadOf "https://github.com/"
# ... ensuring your SSH key has read access to TroyCoderBoy/pulp-engine

# OR HTTPS + PAT: store a GitHub PAT with `repo` scope via git credential manager

# Finally:
go get github.com/TroyCoderBoy/pulp-engine/packages/sdk-go@v0.63.0

This is real consumer friction. It’s strictly worse than the .NET story (NuGet public package) and the Python story (PyPI public package). If the repo ever becomes public, none of this setup is needed — consumers just go get through the public proxy and everything works. Until then, the README’s one-time setup section is mandatory reading for any new Go consumer.

Workflow adjustment: the originally-planned publish-sdk-go.yml step that primes the Go module proxy via go mod download -x has been replaced with a git ls-remote origin verification step and a log message printing the consumer-install snippet. Proxy priming is pointless for private repos — the proxy will always 404.

Pre-flight dry-run adjustment: the runbook verification-via-proxy step doesn’t work for private repos. The updated RELEASING.md documents both the current (private-repo) verification path — git ls-remote origin <subdir-tag> plus an optional end-to-end consumer check on an authenticated second machine — and the future (public-repo) proxy-path verification to restore if/when the repo becomes public.

Stage 2 verification completed via:

  • A throwaway packages/sdk-go/v0.63.0-pre0.<timestamp> tag was pushed and confirmed visible on origin via git ls-remote.
  • go mod download -x against proxy.golang.org correctly escaped the path (%21troy%21coder%21boy) and then 404’d exactly as expected for a private repo — proving the proxy path is broken for this repo, which is why the workflow/runbook were adjusted.
  • The throwaway tag was deleted from origin before proceeding to the production tag push.

Two generator-limitation workarounds landed in scripts/generate-sdks.ts

The Go template surfaced two config pain points that Stage 1 (csharp) didn’t hit. Both are solved by a new per-language post-processing hook in the orchestrator:

  1. go.mod module path rewrite. The Go generator has no config key for the module path — it derives it as github.com/{--git-user-id}/{--git-repo-id}, which gives us github.com/TroyCoderBoy/pulp-engine, missing the /packages/sdk-go suffix. The orchestrator rewrites the module line in go.mod after generation. A regex-anchored match (/^module\s+\S+/m) keeps the operation deterministic. If a future openapi-generator JAR adds a native moduleName config key, swap the workaround for that.
  2. version.go injection. Go modules are versioned by git tags rather than by a file, but the lockstep check needs a readable “what does this SDK think its version is” source. The orchestrator writes a version.go with a const Version = "X.Y.Z" declaration on every regeneration, driven by the workspace root version. The file is protected from generator regeneration by .openapi-generator-ignore so only the orchestrator’s post-processor touches it.

scripts/check-version.mjs — Go reader with go.mod path verification

New readGoVersionConstant() reader parses the const Version = "..." from packages/sdk-go/version.go AND verifies the go.mod module line matches the pinned subdirectory path github.com/TroyCoderBoy/pulp-engine/packages/sdk-go. The second check is critical: if the post-processing hook is ever bypassed or silently fails, a generator regression could emit github.com/TroyCoderBoy/pulp-engine and silently break go get for consumers. Catching this at check-version.mjs time halts the release before the tag is pushed.

Lockstep check now spans eight package sites: root, api, editor, preview, sdk-typescript, sdk-python, sdk-dotnet, sdk-go.

.github/workflows/publish-sdk-go.yml — 3-job workflow

Modelled on publish-sdk-dotnet.yml with one critical difference: instead of pushing to a remote registry, the final step creates the sibling subdirectory tag and primes the Go module proxy cache.

  • check-ci — same CI-gate shape as the .NET and Python workflows. Waits for ci.yml to pass on the release commit with branch=main, event=push. Timeout 25 min.
  • build-and-smoke — runs pnpm sdk:generate --check, go build ./..., go vet ./..., then starts the API in the background from the release commit’s own build (not a services: container — same chicken-and-egg reasoning as .NET), waits for /health/ready, runs go test -v -run TestHealthSmoke ./..., kills the API with if: always().
  • publish-tag — creates packages/sdk-go/vX.Y.Z pointing at the release commit and pushes it. Idempotent: if the sibling tag already exists at the same commit, no-op. If it exists at a different commit, the job fails loudly (tag immutability means the operator has to investigate, not force-push).
  • Post-publish proxy primingGOPROXY=https://proxy.golang.org go mod download -x against the new tag, greps the -x log for a proxy cache hit, emits a warning (not an error) if the proxy hasn’t indexed yet. Consumers hitting “unknown revision” briefly after release is not a failure; the proxy catches up within a few minutes.

CI freshness gate extends to Go

The pnpm sdk:generate --check step in .github/workflows/ci.yml (added in Stage 1) now checks both the .NET and Go SDKs on every PR. Any edit to openapi.json that would regenerate differently into either committed tree fails CI, forcing the developer to commit the regenerated diff.

Operator-visible changes

Itemv0.62.0v0.63.0
.NET SDKdotnet add package PulpEngine.SdkSame
Python SDKpip install pulp-engineSame
TypeScript SDK@pulp-engine/sdk (workspace)Same
Go SDKRecipe only in sdk-generation-guidego get github.com/TroyCoderBoy/pulp-engine/packages/sdk-go@v0.63.0
Lockstep check packages78
pnpm sdk:generate targets--dotnet--dotnet, --go
CI freshness gate.NET only.NET + Go

Deliberate deferrals (carried forward from Stage 1)

  • No per-endpoint tests. Stage 2 ships the same /health round-trip as Stage 1 did for .NET. If the smoke test catches a real regression in either SDK, that’s the signal to invest further.
  • Hand-written ergonomic layers on top of the generated clients stay out of scope.
  • Independent SDK versioning cadence stays out of scope. The Go tag-immutability + proxy-caching constraints make a broken Go publish especially expensive (fix requires a full monorepo patch release). The escape hatch is documented prominently at the top of packages/sdk-go/RELEASING.md.

Known residuals

  • Publish workflow still blocked by GitHub Actions billing. Same block that held v0.61.0’s PyPI publish and v0.62.0’s .NET publish. v0.63.0’s .NET + Go + Python publishes will all hit the same billing gate on tag push. None of this is a code problem — the moment billing is resolved, all three workflows re-run cleanly via workflow_dispatch.
  • Go pre-flight dry run happens on the release machine, not in CI. The mandatory steps in RELEASING.md require creating and deleting a throwaway tag on the real origin, which is an operator action. CI runs the build + smoke test, but not the proxy-path verification with a live tag.
  • Pre-existing flaky tests in the apps/api suite under full-suite load (documented in project_flaky_tests_v1.md). All pass in isolation. Not a v0.63.0 regression.

Verification done on this commit

✅ pnpm sdk:generate                                 (both .NET and Go regenerate)
✅ pnpm sdk:generate --check                         (determinism gate, both SDKs)
✅ docker run golang:1.23-alpine go build ./...      (BUILD_OK)
✅ docker run golang:1.23-alpine go vet ./...        (VET_OK)
✅ docker run golang:1.23-alpine go test             (test file compiles)
✅ go test -run TestHealthSmoke vs local API         (1 PASS, 12ms, version matches)
✅ .openapi-generator-ignore sentinel (README)       (survived regeneration)
✅ node scripts/check-version.mjs                    (8 packages OK; tag mismatch is pre-commit expected)
✅ Subdirectory-tag scheme dry run                   (throwaway packages/sdk-go/v0.63.0-pre0.<ts> created,
                                                      visible on origin via git ls-remote, deleted cleanly)
⚠ go mod download -x via proxy.golang.org           (404 as expected for private repo — documented, workflow
                                                      adjusted to use git ls-remote verification instead)