Skip to content

go: fix Go self-call schema names and cover dependency use#2

Open
grouville wants to merge 2 commits into
eunomie:workspace-go-no-codegen-at-runtimefrom
grouville:go-self-call-introspection-fix
Open

go: fix Go self-call schema names and cover dependency use#2
grouville wants to merge 2 commits into
eunomie:workspace-go-no-codegen-at-runtimefrom
grouville:go-self-call-introspection-fix

Conversation

@grouville
Copy link
Copy Markdown

This PR is a follow-up against eunomie's workspace-go-no-codegen-at-runtime branch.

That branch changes how Go modules with SELF_CALLS provide the schema used to generate their own Go client. My manual review led to those findings:

  • Go self-calls worked when called directly.
  • Go self-calls also worked when the SELF_CALLS module was used as a dependency.
  • They also worked through a transitive dependency.
  • A separate real issue showed up with exported fields.

The actual bug

A Go module can expose fields on its root object:

type Test struct {
  Message string
}

The engine exposes that field as message, but the new self-call schema emitter was exposing it as Message.

That mismatch can break the generated Go client. In the failing case, generated code could end up with a field and a method using the same Go name, so the code would not compile.

What changed:

  • Emit exported fields in the self-call schema with the same name the engine uses --> Message now becomes message.
  • Extend the existing Go self-call fixture with a Message field.
  • Add an integration test that queries message, proving the generated client compiles and the schema name is correct.
  • Add dependency fixtures to lock down the review concern even though the exact reported failure did not reproduce.

Dependency coverage added:

  • caller -> dep
  • caller -> middle -> dep

The dep module is the only module with SELF_CALLS enabled. That keeps the tests focused on the case from the review.

The new tests verify:

  • a dependency function can self-call through dag.Dep()
  • a container returned by a dependency self-call is still usable by the caller
  • self-calls also work from a secondary object
  • self-calls work when the SELF_CALLS module is a transitive dependency

Tested with:

dagger call engine-dev test --pkg ./core/integration --run '^TestModule/TestSelfCalls' --test-verbose --count=1 --failfast --timeout=20m

Result: 17 passed.

grouville added 2 commits May 26, 2026 16:16
Problem:
When a Go module has SELF_CALLS enabled, the Go SDK writes the module schema
that is then used to generate the module's own Go client.

For exported fields, that schema was using the Go name directly. So a field
called Message was exposed as Message.

The engine exposes that same field as message. Because of that mismatch, the
generated Go client could be wrong. In the case we found, it could generate a
field and a method with the same Go name, which does not compile.

Fix:
Emit exported fields with the same name the engine uses. Message now becomes
message.

Test:
Add a Message field to the existing Go self-call fixture and query message from
the integration test. This proves the generated Go code compiles and the field
is available under the expected name.

Signed-off-by: Guillaume de Rouville <guillaume@dagger.io>
Problem:
The review raised a possible failure where a Go module with SELF_CALLS works
when called directly, but breaks when another module depends on it.

If that happened, the caller could still generate Go code for dag.Dep(), but
the running engine would not expose dep at runtime. The user would only see the
failure when calling the function.

We did not reproduce that exact failure, but this is still a useful area to
cover because it is easy to miss. A module dependency is loaded through a
different path than the module being called directly.

Solution:
Add two small Go fixtures:

- self-calls-as-dep: caller -> dep
- self-calls-transitive: caller -> middle -> dep

Only dep has SELF_CALLS enabled. That keeps the tests focused on the dependency
case from the review.

Tests:
viaDep calls a dependency function that calls itself through dag.Dep().

viaDepContainer returns a container created by that self-call and then reads
stdout from the caller. This proves the returned object is still usable.

viaDepWorker calls back into dag.Dep() from a secondary object. This proves the
case is not limited to methods on the root object.

viaTransitiveDep calls through caller -> middle -> dep. This proves the
self-call still works when the module with SELF_CALLS is a dependency of a
dependency.

Signed-off-by: Guillaume de Rouville <guillaume@dagger.io>
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