FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

sdk/go: make core types library-friendly (marshal seam, json/v2 marshalers, selection getter) by idlsoft · Pull Request #13842 · dagger/dagger · GitHub

/ dagger Public

sdk/go: make core types library-friendly (marshal seam, json/v2 marshalers, selection getter) - #13842

Draft
idlsoft wants to merge 4 commits into
dagger:mainfrom
idlsoft:portable-go/jsonv2-marshalers
Draft

sdk/go: make core types library-friendly (marshal seam, json/v2 marshalers, selection getter)#13842
idlsoft wants to merge 4 commits into
dagger:mainfrom
idlsoft:portable-go/jsonv2-marshalers

Conversation

idlsoft commented Aug 5, 2026
edited
Loading

Copy link
Copy Markdown
Contributor

What

Three commits that make the core types library-friendly: after this PR, anything with an engine connection can marshal, unmarshal, and build on Dagger objects through the published dagger.io/dagger API alone — no generated package-local state required. This is the foundation for the Go codegen v2 proposal (related: #11417, #11547): sharing core types via dagger.io/dagger instead of regenerating them under internal/dagger in every module. Behavior of existing modules is unchanged.

Commit 1 — explicit marshal/unmarshal seam

Generated code currently resolves objects to IDs (and back) through package-level globals — marshalCtx and dag — set once per dispatch via SetMarshalContext. This commit makes the dependencies explicit, in all codegen modes (library, module, standalone client):

  • IDClient — the minimal client surface needed to reconstruct objects from IDs (QueryBuilder(), GraphQLClient()). *Client already satisfies it in all three modes.
  • MarshalJSON(ctx, v) — marshals v, scoping the ID-resolution context to the single call (mutex-guarded swap of marshalCtx).
  • UnmarshalJSON(c, data, v) (module mode) — unmarshals data, reconstructing objects through the given client (mutex-guarded swap of dag).

The generated module dispatch now calls these with its ctx/dag explicitly: parent state and input args go through dagger.UnmarshalJSON(dag, ...), function results through dagger.MarshalJSON(ctx, ...). SetMarshalContext remains (deprecated) so user code that calls json.Marshal directly on Dagger objects keeps resolving IDs with the dispatch context.

Commit 2 — json/v2 Marshalers/Unmarshalers

Builds on IDClient to generate json/v2 marshaler/unmarshaler sets that carry their context/client in closures:

data, err := json.Marshal(v, json.WithMarshalers(dagger.Marshalers(ctx)))
err = json.Unmarshal(data, &v, json.WithUnmarshalers(dagger.Unmarshalers(dag)))

Unlike the v1 per-type methods, these need no methods on the types and no globals, are safe with multiple clients in one process, and bring ID-based unmarshaling to the library and standalone-client modes for the first time (previously module-only).

Two implementations rendered from one template body, selected by the goexperiment.jsonv2 build tag:

file build tag json/v2 package
jsonv2.gen.go goexperiment.jsonv2 stdlib encoding/json/v2 (default on Go 1.27+, opt-in on 1.25/1.26)
jsonv2_compat.gen.go !goexperiment.jsonv2 github.com/go-json-experiment/json (tracks the stdlib API)

Once the SDK's minimum Go version reaches 1.27, the compat file and the go-json-experiment dependency can be deleted with no API change.

Nothing generated calls these yet; migrating dispatch onto them — and then deleting the per-type UnmarshalJSON methods and the marshalCtx/dag globals, which is what makes internal/dagger's core types byte-identical to the published SDK — is the follow-up PR.

Commit 3 — GraphQLQuery selection getter

Every generated object/interface type gains GraphQLQuery() *querybuilder.Selection, pairing with the existing WithGraphQLQuery setter. Together they form the public query seam: code outside the generated package (dependency bindings in portable mode, third-party integrations) can read an object's selection and build on it without network ID round-trips or access to the unexported query field. Purely additive.

Notes for reviewers

  • New dependency (commit 2 only): github.com/go-json-experiment/json, pseudo-version pinned, compiled only under !goexperiment.jsonv2. It reaches module go.mods through the existing sdk-go.mod merge in bootstrapMod. If this needs separate discussion, commit 2 splits out cleanly — commit 1 stands on its own.
  • Marshalers are registered on value types, unmarshalers on pointer types, mirroring v1 method semantics (nil pointers marshal as null, pointer fields are allocated on unmarshal). Enums/scalars are untouched; json/v2 falls through to their existing context-free v1 methods.
  • IDClient reuses the already-exported QueryBuilder()/GraphQLClient() accessors, so any future wrapper client type satisfies it by embedding.

Test plan

  • sdk/go builds under both configs (default and GOEXPERIMENT=jsonv2, Go 1.26); regenerated files match template output byte-for-byte.
  • Module-mode output (all dispatch call sites + helpers) compiles against a generated-module fixture.
  • Needs a maintainer/CI pass for the dagger generate round-trip and integration suite (module codegen requires an engine connection).

idlsoft changed the title sdk/go: add explicit marshal/unmarshal seam (IDClient, MarshalJSON, UnmarshalJSON) sdk/go: decouple object (un)marshaling from global state; add json/v2 marshalers Aug 5, 2026
idlsoft changed the title sdk/go: decouple object (un)marshaling from global state; add json/v2 marshalers sdk/go: make core types library-friendly (marshal seam, json/v2 marshalers, selection getter) Aug 6, 2026
idlsoft force-pushed the portable-go/jsonv2-marshalers branch from 6e6eb48 to e535f82 Compare August 6, 2026 15:06
idlsoft added 4 commits August 6, 2026 13:12
…nmarshalJSON)

Generated code currently relies on package-level globals (marshalCtx, dag)
for (un)marshaling Dagger objects, set once per dispatch via
SetMarshalContext. This couples core types to module-specific global state
and is one of the blockers for sharing core types via dagger.io/dagger
instead of regenerating them under internal/dagger per module.

Introduce an explicit seam, in all codegen modes:

- IDClient: minimal client surface (QueryBuilder, GraphQLClient) needed to
  reconstruct objects from IDs; *Client already implements it in library,
  module, and standalone-client modes.
- MarshalJSON(ctx, v): marshals v, scoping the ID-resolution context to
  this call (mutex-guarded swap of marshalCtx).
- UnmarshalJSON(c, data, v) (module mode): unmarshals data, reconstructing
  objects through the given client (mutex-guarded swap of dag).

Rewire the generated module dispatch to use them: parent state and input
args now unmarshal via dagger.UnmarshalJSON(dag, ...), and the function
result marshals via dagger.MarshalJSON(ctx, ...). SetMarshalContext remains
(deprecated) so user code that json.Marshal's Dagger objects directly keeps
working; it can be dropped once per-type UnmarshalJSON methods go away.

Behavior is unchanged: the helpers swap the same globals the per-type
methods read today. The point is that generated code now expresses its
ctx/client dependencies explicitly, so the globals can later be removed
(e.g. via json/v2 marshalers) without touching call sites again.

Signed-off-by: Sandu Turcan <idlsoft@gmail.com>
Build on the IDClient seam by generating json/v2 marshaler/unmarshaler sets
that carry their ctx/client in closures instead of package-level globals:

  data, err := json.Marshal(v, json.WithMarshalers(dagger.Marshalers(ctx)))
  err = json.Unmarshal(data, &v, json.WithUnmarshalers(dagger.Unmarshalers(dag)))

Unlike the v1 MarshalJSON/UnmarshalJSON methods, these need no methods on
the types at all, no globals, and are safe for multiple clients in one
process. They also bring ID-based unmarshaling to the library and
standalone-client modes for the first time (previously module-only).

Two implementations, selected by the goexperiment.jsonv2 build tag:

- jsonv2.gen.go (//go:build goexperiment.jsonv2): stdlib encoding/json/v2.
  Selected by default on Go 1.27+, opt-in on 1.25/1.26.
- jsonv2_compat.gen.go (//go:build !goexperiment.jsonv2):
  github.com/go-json-experiment/json, which tracks the stdlib API.

Both render from the same template body, so they only differ in the import.
Once the SDK's minimum Go version reaches 1.27 the compat file and the
go-json-experiment dependency can be deleted with no API change.

Nothing generated calls these yet; migrating the module dispatch onto them
(and then dropping the per-type UnmarshalJSON methods and the marshalCtx/dag
globals) is a follow-up.

Signed-off-by: Sandu Turcan <idlsoft@gmail.com>
Every generated object and interface client type gains

    func (r *T) GraphQLQuery() *querybuilder.Selection

pairing with the existing WithGraphQLQuery setter. This is the read half of
the public query seam: dependency bindings generated outside the core
package (portable API mode) need to build selections on top of an object's
current query, which today is only possible by reading the unexported query
field from within the same package.

Purely additive; no behavior change.

Signed-off-by: Sandu Turcan <idlsoft@gmail.com>
Document why the json/v2 marshaler support is generated in two
build-tag-selected variants, and the mechanical steps to collapse it onto
stdlib encoding/json/v2 (and drop the go-json-experiment dependency) once
the repository's minimum Go version reaches 1.27. The compat templates
carry TODO(go1.27) pointers to the doc.

Signed-off-by: Sandu Turcan <idlsoft@gmail.com>
idlsoft force-pushed the portable-go/jsonv2-marshalers branch from 18f5d47 to 588869b Compare August 6, 2026 17:20
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
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.

1 participant


Back | FazBrowse Home | New Git URL