Skip to content

feat(go)!: the traust package: typed calls, iterators, ProcessExecutor, traustest, a user guide - #48

Merged
smith-xyz merged 2 commits into
developmentfrom
feat/sdk-developer-experience
Oct 30, 2026
Merged

smith-xyz merged 2 commits into
developmentfrom
feat/sdk-developer-experience

Conversation

@smith-xyz

@smith-xyz smith-xyz commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

What

The SDK does the work for the user, on par with cloud SDKs: every operation is a typed call, every listing an iterator, the run lifecycle includes a ready executor, there's a fake core for tests, and the README is a guide.

import "github.com/traust-security/traust-sdk/go/v1/traust"

client, err := traust.NewClient(traust.Config{BaseURL: url, Tokens: traust.FileTokenSource{Path: p}})
repo, err := client.GetRepository(ctx, id)                      // (*traust.Repository, error)
for f, err := range client.ListFindingsAll(ctx, repoID, nil) {} // every page
var problem *traust.ProblemError                                 // core's refusal, typed
Before After
package api: api.NewBoundClient(api.ClientConfig{…}) → *api.BoundClient package traust (go/v1/traust): traust.NewClient(traust.Config{…}) → *traust.Client; traust.Repository, traust.ProblemError, …; the raw generated client is RawClient / NewRawClient (oapi-codegen client-type-name + the calls generator)
resp, err := c.GetRepositoryWithResponse(…), then traust.Decode(resp.HTTPResponse, resp.Body, resp.JSON200) on every call c.GetRepository(ctx, id) → (*traust.Repository, error): one generated call per operation (calls.gen.go, 69 ops). Lists return []T; artifact content returns the bytes; Claim returns nil, nil on 204
cursor loops by hand; two hand-written EachProduct / EachRepository an …All iterator for every paged listing (12, including ReadFeedAll), Go 1.23 range-over-func
every consumer writes its own process-group executor (ours was hidden in the e2e test) ops.ProcessExecutor: no shell, own process group, killed with its descendants on lease loss; stdout is the command's JobResult JSON, or set Outcome
Pack didn't expose its digest, so nobody outside ops could Submit a runnable order Pack.Pin() → the traust.ConfigDigest for Submit; Pack.Assets()
no test double: consumers wrote their own httptest traustest: New(t)/Start(), Handle("GET /v1/…", apitest.JSON/Problem/Status), Client(), Requests(); unknown routes are a 404 problem naming them
only FileTokenSource plus traust.StaticToken for scripts, local dev and tests
runnable examples only for the retired ledger/skills go doc examples for traust (lookup by URL, iterate pages, summarize findings, problem errors) and ops (LoadPack, ProcessExecutor, Runner)
go/README.md opened with storage/*sql.DB, ledger URLs and review-status prose a guide: quickstart → connect → read → keep inventory → errors → run work → test your code; the retired packages moved under "Legacy packages"
root README: "point it at a database and a ledger URL" core's REST API, what the SDK owns, what the consumer keeps

The calls and iterators are generated from the oapi-codegen client (internal/generate/calls, run by make api-generate, covered by check-drift), so there's no hand-written method per endpoint; AGENTS.md now says a better call shape is a generator change. ops uses the new calls internally (lease, recording run, record, runner), which removes the …WithResponse + Decode pairs there.

Not in this PR: release tags (paused), and a local traust-server quickstart plus a token command in core.

Breaking

The client package is traust (was api), with NewClient / Client / Config (were NewBoundClient / BoundClient / ClientConfig) and traustest (was apitest). The generated calls take the operation names, shadowing the raw *http.Response methods (still reachable as client.ClientWithResponses.ClientInterface.X); …WithResponse keeps the full response. EachProduct / EachRepository → ListProductsAll / ListRepositoriesAll. SCI's internal/traustcore and the worker change the import to go/v1/traust and EachProduct/EachRepository to the iterators on their next bump.

Changed tests

Test Old → new Why
v1/traust/client_test.go (was v1/api): TestFileTokenSourceRotates, TestEachProductFollowsCursorAndReportsProblems → TestListProductsAllFollowsCursorAndReportsProblems, TestProblemErrorCarriesTheDocument, TestRetryAbsorbsTransientFailures, TestCallerHTTPClientPolicySurvives client.EachProduct(ctx, n, fn) → a products(client, n) helper over ListProductsAll; assertions unchanged EachProduct is replaced by the generated iterator
v1/traust/e2e_test.go::TestE2EPagingDecodesAnEmptyInventory EachRepository → ListRepositoriesAll same
v1/ops/runner_e2e_test.go::TestE2ERunnerRunsASubmittedPackCommand SubmitWithResponse + Decode + hand-built ConfigDigest → Submit + pack.Pin(); private executor → ProcessExecutor; testdata/local-command.yaml prints a JobResult JSON dogfoods the new API
every moved test in v1/api → v1/traust package api → traust, BoundClient/NewBoundClient/ClientConfig → Client/NewClient/Config, error prefixes api: → traust:; assertions unchanged the rename
TestE2ERunnerLocalCommandStopsDescendantsOnLeaseLoss → v1/ops/runner_unix_test.go::TestRunnerStopsACommandsDescendantsOnLeaseLoss python + a test-only process helper, e2e-only → ProcessExecutor with sh, a "started" marker proving the command ran, lease lost on the 6th heartbeat; same assertions (no complete, descendant dead) now runs in the unit suite, against the real executor
  • A person reviewed each changed test and the behaviour change is intended

New: an traustest test, TestProcessExecutorTurnsOutputIntoTheResult, TestProcessExecutorStopsDescendantsOnCancel, TestPackPinsOrdersAndListsItsAssets, examples with // Output:.

Checks

  • make lint test security guardrails, go test -race ./v1/traust/... ./v1/ops
  • make -C go check-drift; GOOS=windows go build ./v1/ops ./v1/traust/... (non-unix ProcessExecutor stub)
  • make e2e against core b72bf77 (6 passed)

@smith-xyz
smith-xyz requested a review from chubirka as a code owner October 30, 2026 03:58
@openshift-ci

openshift-ci Bot commented Oct 30, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: smith-xyz

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ci openshift-ci Bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Oct 30, 2026
@smith-xyz smith-xyz changed the title feat(go)!: an SDK that does the work: typed calls, iterators, ProcessExecutor, apitest, a user guide feat(go)!: the traust package: typed calls, iterators, ProcessExecutor, traustest, a user guide Oct 30, 2026
@smith-xyz
smith-xyz merged commit 8cd131a into development Oct 30, 2026
7 of 8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant