From 084ecc6bfe4e30f12fb535f53370760d6bc961b5 Mon Sep 17 00:00:00 2001 From: Tom Chauveau Date: Fri, 7 Aug 2026 18:19:04 +0200 Subject: [PATCH] WIP test(e2e): move fixture SDK registration into its own workspace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ports dagger/java-sdk#13: the repository's dagger.toml registered this SDK against the e2e fixture modules and the fixture client, so the real workspace advertised test fixtures as managed. That registration moves to a test-only dagger.toml under the fixtures directory, reached through Fixtures.workspace. Incomplete — pushed for discussion, not for merge. discovery:* passes. generate:* and client:* cannot work as written, because codegen needs the session's client-backed workspace and the fixtures config can only be selected by a synthetic one: - asWorkspace(cwd:) re-runs config detection, so the nested dagger.toml is selected, but the workspace carries no client ID and ModuleSource.generateLocalDependencies (mod.dang:97, added in #12) fails in withWorkspaceClientContext. - Workspace.withWorkdir keeps the client but only clones and sets Cwd, so ConfigFile stays the repository's dagger.toml and currentModule.asSDK(ws) reports the SDK is not installed. Codegen then resolves "typescript" to the builtin SDK rather than this one, which passes for the wrong reason. java-sdk does not hit this: its Mod.generate reads only introspectionSchemaJSON and builds the generated files in its own container, so a synthetic workspace is enough. Unblocking here needs an engine change — withWorkdir re-detecting config, or generateLocalDependencies working on a synthetic workspace. Signed-off-by: Tom Chauveau --- .dagger/modules/e2e/discovery.dang | 15 +++++---- .dagger/modules/e2e/fixtures/dagger.toml | 43 ++++++++++++++++++++++++ .dagger/modules/e2e/generate.dang | 43 +++++++++++++----------- .dagger/modules/e2e/util.dang | 39 ++++++++++++++++----- dagger.toml | 39 +++------------------ 5 files changed, 110 insertions(+), 69 deletions(-) create mode 100644 .dagger/modules/e2e/fixtures/dagger.toml diff --git a/.dagger/modules/e2e/discovery.dang b/.dagger/modules/e2e/discovery.dang index c1aa098..9bed36e 100644 --- a/.dagger/modules/e2e/discovery.dang +++ b/.dagger/modules/e2e/discovery.dang @@ -37,11 +37,12 @@ type DiscoveryChecks { nothing that isn't managed by this SDK (e.g. a sibling module using another SDK). - Runs against the real workspace, so it also catches a dagger.toml - [[...as-sdk.modules]] entry that no longer matches the fixture tree. + Runs against the whole fixture workspace rather than a discovery snapshot, so + it also catches a fixtures dagger.toml [[...as-sdk.modules]] entry that no + longer matches the fixture tree. """ modulesCheck(ws: Workspace!): Void @check { - let roots = typescriptSdk.modules(ws).{{rootPath}}.map { mod => mod.rootPath } + let roots = typescriptSdk.modules(fixtures.workspace(ws)).{{rootPath}}.map { mod => mod.rootPath } fixtures.managedModules.each { want => Asserts.assert(Asserts.contains(roots, want), "managed module should be listed: " + want) @@ -84,9 +85,11 @@ type DiscoveryChecks { "cwd=lookup/app/nested (cwd-relative)", ) - # Root cwd: the whole workspace is in scope, so exactly the managed modules - # are discovered, whether marked by dagger.json or dagger-module.toml. - Asserts.paths(discovered(ws, "/"), fixtures.managedModules, "cwd=/") + # Fixture root: every fixture is in scope, so exactly the managed modules are + # discovered, whether marked by dagger.json or dagger-module.toml. This is + # the widest cwd they can be seen from — the dagger.toml declaring them lives + # here. + Asserts.paths(discovered(ws, fixtures.root), fixtures.managedModules, "cwd=fixtures") null } diff --git a/.dagger/modules/e2e/fixtures/dagger.toml b/.dagger/modules/e2e/fixtures/dagger.toml new file mode 100644 index 0000000..ee2fe2e --- /dev/null +++ b/.dagger/modules/e2e/fixtures/dagger.toml @@ -0,0 +1,43 @@ +# Test-only workspace: registers this SDK against the e2e fixtures, so the +# repository's own workspace never advertises test fixtures as managed modules. +# +# The workspace root is the git root and dagger.toml is only the nearest config +# found walking up from the cwd, so a config nested here selects configuration +# without moving the root: `source` resolves against this file, while as-sdk +# paths stay relative to the repository root. Checks reach it through +# `Fixtures.workspace`, which anchors a workspace cwd at this directory. + +[modules.typescript-sdk] +source = "../../../.." +check.skip = ["*"] + +[modules.typescript-sdk.as-sdk] +name = "typescript" + +[[modules.typescript-sdk.as-sdk.modules]] +path = ".dagger/modules/e2e/fixtures/generate/app" + +[[modules.typescript-sdk.as-sdk.modules]] +path = ".dagger/modules/e2e/fixtures/lookup/app" + +[[modules.typescript-sdk.as-sdk.modules]] +path = ".dagger/modules/e2e/fixtures/deps/app" + +[[modules.typescript-sdk.as-sdk.modules]] +path = ".dagger/modules/e2e/fixtures/skip/app" + +[[modules.typescript-sdk.as-sdk.modules]] +path = ".dagger/modules/e2e/fixtures/managed-toml/app" + +[[modules.typescript-sdk.as-sdk.modules]] +path = ".dagger/modules/e2e/fixtures/generate-deps/app" + +[[modules.typescript-sdk.as-sdk.modules]] +path = ".dagger/modules/e2e/fixtures/generate-deps/dep" + +[[modules.typescript-sdk.as-sdk.modules]] +path = ".dagger/modules/e2e/fixtures/parent-source/.dagger/modules/app" + +[[modules.typescript-sdk.as-sdk.clients]] +path = ".dagger/modules/e2e/fixtures/client/out" +module = ".dagger/modules/e2e/fixtures/client/app" diff --git a/.dagger/modules/e2e/generate.dang b/.dagger/modules/e2e/generate.dang index ea71c02..789219c 100644 --- a/.dagger/modules/e2e/generate.dang +++ b/.dagger/modules/e2e/generate.dang @@ -13,9 +13,10 @@ type GenerateChecks { module without touching unrelated paths. """ generateCheck(ws: Workspace!): Void @check { - let changes = typescriptSdk.mod(ws, path: fixtures.generateModule).generate(ws) + let tws = fixtures.workspace(ws) + let changes = typescriptSdk.mod(tws, path: "/" + fixtures.generateModule).generate(tws) - Asserts.generated(changes, fixtures.generateModule + "/sdk/index.ts") + Asserts.generated(changes, fixtures.changesetPath(fixtures.generateModule) + "/sdk/index.ts") null } @@ -30,16 +31,17 @@ type GenerateChecks { failed with "dir module source does not contain a dagger config file". """ generateDependencyCheck(ws: Workspace!): Void @check { - let changes = typescriptSdk.mod(ws, path: fixtures.depAppModule).generate(ws) + let tws = fixtures.workspace(ws) + let changes = typescriptSdk.mod(tws, path: "/" + fixtures.depAppModule).generate(tws) - Asserts.generated(changes, fixtures.depAppModule + "/sdk/index.ts") - Asserts.generated(changes, fixtures.depAppModule + "/sdk/gendep.gen.ts") + Asserts.generated(changes, fixtures.changesetPath(fixtures.depAppModule) + "/sdk/index.ts") + Asserts.generated(changes, fixtures.changesetPath(fixtures.depAppModule) + "/sdk/gendep.gen.ts") # Generating the dependent stages its dependency's codegen only to feed its # own; that staging is ephemeral and must not leak into the changeset. Asserts.notAdded( changes, - fixtures.depLibModule + "/sdk/index.ts", + fixtures.changesetPath(fixtures.depLibModule) + "/sdk/index.ts", "generating a dependent should not also stage its dependency's own files", ) @@ -56,45 +58,45 @@ type GenerateChecks { than conflict over the files the closure staging touched. """ generateAllCheck(ws: Workspace!): Void @check { - let changes = typescriptSdk.generateAllModule(ws) + let changes = typescriptSdk.generateAllModule(fixtures.workspace(ws)) - Asserts.generated(changes, fixtures.generateModule + "/sdk/index.ts") + Asserts.generated(changes, fixtures.changesetPath(fixtures.generateModule) + "/sdk/index.ts") # Both ends of the dependency edge, plus the dependency's bindings inside # the dependent. - Asserts.generated(changes, fixtures.depAppModule + "/sdk/index.ts") - Asserts.generated(changes, fixtures.depAppModule + "/sdk/gendep.gen.ts") - Asserts.generated(changes, fixtures.depLibModule + "/sdk/index.ts") + Asserts.generated(changes, fixtures.changesetPath(fixtures.depAppModule) + "/sdk/index.ts") + Asserts.generated(changes, fixtures.changesetPath(fixtures.depAppModule) + "/sdk/gendep.gen.ts") + Asserts.generated(changes, fixtures.changesetPath(fixtures.depLibModule) + "/sdk/index.ts") # Skip-marked and unmanaged modules stay untouched. Asserts.notAdded( changes, - fixtures.lookupModule + "/sdk/index.ts", + fixtures.changesetPath(fixtures.lookupModule) + "/sdk/index.ts", "generate-all generated a lookup fixture that should be skipped", ) Asserts.notAdded( changes, - fixtures.depsModule + "/sdk/index.ts", + fixtures.changesetPath(fixtures.depsModule) + "/sdk/index.ts", "generate-all generated dependency fixtures that should be skipped", ) Asserts.notAdded( changes, - fixtures.skipModule + "/sdk/index.ts", + fixtures.changesetPath(fixtures.skipModule) + "/sdk/index.ts", "generate-all generated a skipped module", ) Asserts.notAdded( changes, - fixtures.managedTomlModule + "/sdk/index.ts", + fixtures.changesetPath(fixtures.managedTomlModule) + "/sdk/index.ts", "generate-all generated a skipped dagger-module.toml module", ) Asserts.notAdded( changes, - fixtures.parentSourceCode + "/sdk/index.ts", + fixtures.changesetPath(fixtures.parentSourceCode) + "/sdk/index.ts", "generate-all generated a skipped relocated-source module", ) Asserts.notAdded( changes, - fixtures.nonTsModule + "/sdk/index.ts", + fixtures.changesetPath(fixtures.nonTsModule) + "/sdk/index.ts", "generate-all generated a non-TypeScript SDK module", ) @@ -133,10 +135,11 @@ type GenerateChecks { A skip marker above a module should make generate return an empty changeset. """ skipGenerateCheck(ws: Workspace!): Void @check { - let mod = typescriptSdk.mod(ws, path: fixtures.skipModule) - let changes = mod.generate(ws) + let tws = fixtures.workspace(ws) + let mod = typescriptSdk.mod(tws, path: "/" + fixtures.skipModule) + let changes = mod.generate(tws) - Asserts.assert(mod.skipGenerate(ws), "skip marker was not detected") + Asserts.assert(mod.skipGenerate(tws), "skip marker was not detected") Asserts.assert(changes.isEmpty, "generate returned changes for a skipped module") null diff --git a/.dagger/modules/e2e/util.dang b/.dagger/modules/e2e/util.dang index 523fbbe..71b574c 100644 --- a/.dagger/modules/e2e/util.dang +++ b/.dagger/modules/e2e/util.dang @@ -158,17 +158,40 @@ type Fixtures { let clientExisting: String! = root + "/client/existing" """ - A snapshot of `ws` holding just the files module discovery reads, re-anchored - at `cwd` so a check can stand anywhere in the tree. + `ws` re-anchored at the fixture root, where the test-only dagger.toml registers + the fixture modules and client as SDK-managed. - Keeps the workspace config (dagger.toml, whose [modules..as-sdk] list is - what discovery resolves the re-anchored workspace against) and every module - config, in both filenames. `extra` adds paths a check needs to exist for the - cwd itself to be a directory. + Everything the SDK resolves from workspace config — the managed module list, + the registered clients, the "typescript" SDK itself — has to be read from here + rather than from the repository's own dagger.toml, which deliberately knows + nothing about the fixtures. Dropping that config from the snapshot keeps it out + of reach, so a check cannot silently fall back to the real workspace. + """ + let workspace(ws: Workspace!): Workspace! { + ws.directory("/").withoutFile("dagger.toml").asWorkspace(cwd: root) + } + + """ + A path as it appears in a changeset produced in the fixture workspace. A + returned changeset is applied relative to the caller's cwd, which `workspace` + anchors at the fixture root, so generated paths drop that prefix. + """ + let changesetPath(path: String!): String! { + path.trimPrefix(root + "/") + } + + """ + A snapshot of the fixture workspace holding just the files module discovery + reads, re-anchored at `cwd` so a check can stand anywhere in the tree. + + Keeps the workspace config (the fixtures dagger.toml, whose + [modules..as-sdk] list is what discovery resolves the re-anchored + workspace against) and every module config, in both filenames. `extra` adds + paths a check needs to exist for the cwd itself to be a directory. """ let discoverySnapshot(ws: Workspace!, cwd: String!, extra: [String!]! = []): Workspace! { - ws - .directory("/", include: ["dagger.toml", "**/dagger.json", "**/dagger-module.toml"] + extra) + workspace(ws) + .directory("/", include: ["**/dagger.toml", "**/dagger.json", "**/dagger-module.toml"] + extra) .asWorkspace(cwd: cwd) } diff --git a/dagger.toml b/dagger.toml index 09cbb79..db8d7e7 100644 --- a/dagger.toml +++ b/dagger.toml @@ -6,43 +6,12 @@ # Marker filename that skips generate when found at or above a TypeScript SDK module root. # settings.skipGenerateFilename = "" +# This SDK is registered against the e2e fixture modules in +# .dagger/modules/e2e/fixtures/dagger.toml, not here, so this workspace stays +# free of test fixtures. + [modules.e2e] source = ".dagger/modules/e2e" -[modules.typescript-sdk] -source = "." -check.skip = ["*"] - [modules.sdk-sdk] source = "github.com/dagger/sdk-sdk" - -[modules.typescript-sdk.as-sdk] -name = "typescript" - -[[modules.typescript-sdk.as-sdk.modules]] -path = ".dagger/modules/e2e/fixtures/generate/app" - -[[modules.typescript-sdk.as-sdk.modules]] -path = ".dagger/modules/e2e/fixtures/lookup/app" - -[[modules.typescript-sdk.as-sdk.modules]] -path = ".dagger/modules/e2e/fixtures/deps/app" - -[[modules.typescript-sdk.as-sdk.modules]] -path = ".dagger/modules/e2e/fixtures/skip/app" - -[[modules.typescript-sdk.as-sdk.modules]] -path = ".dagger/modules/e2e/fixtures/managed-toml/app" - -[[modules.typescript-sdk.as-sdk.modules]] -path = ".dagger/modules/e2e/fixtures/generate-deps/app" - -[[modules.typescript-sdk.as-sdk.modules]] -path = ".dagger/modules/e2e/fixtures/generate-deps/dep" - -[[modules.typescript-sdk.as-sdk.modules]] -path = ".dagger/modules/e2e/fixtures/parent-source/.dagger/modules/app" - -[[modules.typescript-sdk.as-sdk.clients]] -path = ".dagger/modules/e2e/fixtures/client/out" -module = ".dagger/modules/e2e/fixtures/client/app"