You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[Build Data] Run Go build functions through a module-local, cancellable protocol
Priority
High — correctness, portability, and compiler execution safety
Context
GOWDK supports imported, same-package, and inline Go functions as a source of deterministic build-time page data. The current runner in internal/buildgen/build_data_runner.go generates a temporary Go source file under the operating-system temporary directory and executes it with:
exec.Command("go", "run", path)
The result is read as JSON from stdout. The command inherits the calling process environment and has no context, deadline, output ceiling, or shared process-lifecycle policy.
This path is distinct from generated application compilation. Issue #663 addresses generated apps being placed in a nested gowdk-generated-app module, but build-data helper execution has its own module-boundary and process-management problems.
Problem
The current runner has several failure modes:
Application internal/ packages may be inaccessible. The generated helper source lives outside the application module tree. A build-data function imported from example.com/site/internal/content can therefore fail Go's internal visibility rule even when the .gwdk source and application package are valid members of example.com/site.
A build function can block the compiler indefinitely. A function that deadlocks, waits forever, starts a long network request, or launches a child process has no deadline or cancellation path.
User stdout conflicts with the result protocol. The runner expects stdout to contain one JSON object. Logging with fmt.Println, a library writing to stdout, or noisy initialization can make a valid function result undecodable.
Output is unbounded. A function can emit arbitrarily large stdout or stderr and exhaust memory in the compiler process.
Execution provenance is unclear. The effective module, workspace, vendoring, build tags, environment, and GOWDK version are not represented as an explicit execution contract.
Process cleanup is platform-dependent. Cancelling only the immediate go run process is insufficient when the Go tool or user code creates descendants.
These are compiler correctness issues, not merely performance improvements.
Goal
Introduce a module-local, versioned, cancellable build-data execution protocol that preserves ordinary Go module semantics and keeps user logging separate from machine-readable results.
Proposed direction
1. Execute from a generated helper package inside the application module
Create or stage the helper beneath a compiler-owned directory inside the project module, for example:
.gowdk/tmp/builddata/<digest>/main.go
The helper should be built and run using the application's normal module context. It must honor:
application internal/ visibility;
replace and exclude directives;
vendoring mode;
the selected go.work policy;
build tags, GOOS, GOARCH, and toolchain selection;
the local-versus-released GOWDK runtime resolution policy.
If temporary source cannot safely live under the project tree, use a temporary workspace/module arrangement that preserves the original module's import semantics explicitly.
2. Replace stdout JSON with a dedicated result channel
The helper should write its response envelope to one of:
a compiler-created result file opened with restrictive permissions;
a dedicated inherited file descriptor/handle; or
a framed local IPC channel.
Stdout and stderr should remain user-observable logs and must not be parsed as the result payload.
A versioned response envelope should include at least:
The protocol should reject unknown incompatible versions with a specific diagnostic.
3. Add context, timeout, and process-tree termination
All build-data calls should accept context.Context and run through the shared subprocess supervisor proposed separately for compiler-owned external commands.
Provide a conservative default timeout and an explicit configuration/CLI override. On cancellation or timeout, terminate the complete process tree on supported Unix and Windows environments and perform deterministic temporary-file cleanup.
4. Bound every channel
Define limits for:
result payload bytes;
captured stdout bytes;
captured stderr bytes;
helper source/package size where relevant;
total execution duration.
When logs exceed their limit, retain a bounded prefix/suffix and report truncation rather than allocating indefinitely.
5. Make execution visible
Build reports and --timings output should record:
function identity and source location;
module/package used;
protocol version;
cache hit/miss when caching is introduced;
duration;
timeout/cancellation status;
bounded log metadata without exposing secrets.
Human diagnostics should distinguish compilation failure, unsupported function signature, helper protocol failure, timeout, cancellation, malformed result, and user-returned error.
6. Avoid signature probing through repeated go run
The current implementation attempts multiple candidate signatures. Prefer resolving supported signatures through the existing Go binding/type inspection phase, then generate and execute exactly one helper invocation. Repeated compilation should not be the signature-discovery mechanism.
[Build Data] Run Go build functions through a module-local, cancellable protocol
Priority
High — correctness, portability, and compiler execution safety
Context
GOWDK supports imported, same-package, and inline Go functions as a source of deterministic build-time page data. The current runner in
internal/buildgen/build_data_runner.gogenerates a temporary Go source file under the operating-system temporary directory and executes it with:The result is read as JSON from stdout. The command inherits the calling process environment and has no context, deadline, output ceiling, or shared process-lifecycle policy.
This path is distinct from generated application compilation. Issue #663 addresses generated apps being placed in a nested
gowdk-generated-appmodule, but build-data helper execution has its own module-boundary and process-management problems.Problem
The current runner has several failure modes:
internal/packages may be inaccessible. The generated helper source lives outside the application module tree. A build-data function imported fromexample.com/site/internal/contentcan therefore fail Go'sinternalvisibility rule even when the.gwdksource and application package are valid members ofexample.com/site.fmt.Println, a library writing to stdout, or noisy initialization can make a valid function result undecodable.go runprocess is insufficient when the Go tool or user code creates descendants.These are compiler correctness issues, not merely performance improvements.
Goal
Introduce a module-local, versioned, cancellable build-data execution protocol that preserves ordinary Go module semantics and keeps user logging separate from machine-readable results.
Proposed direction
1. Execute from a generated helper package inside the application module
Create or stage the helper beneath a compiler-owned directory inside the project module, for example:
The helper should be built and run using the application's normal module context. It must honor:
internal/visibility;replaceandexcludedirectives;go.workpolicy;GOOS,GOARCH, and toolchain selection;If temporary source cannot safely live under the project tree, use a temporary workspace/module arrangement that preserves the original module's import semantics explicitly.
2. Replace stdout JSON with a dedicated result channel
The helper should write its response envelope to one of:
Stdout and stderr should remain user-observable logs and must not be parsed as the result payload.
A versioned response envelope should include at least:
{ "protocolVersion": 1, "status": "ok", "value": {}, "error": null }The protocol should reject unknown incompatible versions with a specific diagnostic.
3. Add context, timeout, and process-tree termination
All build-data calls should accept
context.Contextand run through the shared subprocess supervisor proposed separately for compiler-owned external commands.Provide a conservative default timeout and an explicit configuration/CLI override. On cancellation or timeout, terminate the complete process tree on supported Unix and Windows environments and perform deterministic temporary-file cleanup.
4. Bound every channel
Define limits for:
When logs exceed their limit, retain a bounded prefix/suffix and report truncation rather than allocating indefinitely.
5. Make execution visible
Build reports and
--timingsoutput should record:Human diagnostics should distinguish compilation failure, unsupported function signature, helper protocol failure, timeout, cancellation, malformed result, and user-returned error.
6. Avoid signature probing through repeated
go runThe current implementation attempts multiple candidate signatures. Prefer resolving supported signatures through the existing Go binding/type inspection phase, then generate and execute exactly one helper invocation. Repeated compilation should not be the signature-discovery mechanism.
Suggested public behavior
Possible configuration shape:
Possible CLI override:
Exact public naming should follow the configuration-boundary decision from #677 and the shared execution model from #665/#670.
Non-goals
The initial goal is bounded, correct local execution. Stronger sandboxing may remain an explicit hosted/playground concern.
Test plan
Add focused integration tests covering:
internal/package;replacedirective resolution;go.workfile and the documentedGOWORKpolicy;Acceptance criteria
internal/packages.replace, vendoring, workspace, build-tag, and toolchain policies are honored consistently.Related
gowdk-generated-appmodule #663 — generated application module placementgowdk.config.goloading under one explicit execution model #665 — authoritative config execution model