diff --git a/.dagger/modules/e2e/util.dang b/.dagger/modules/e2e/util.dang index af705b5..64585e9 100644 --- a/.dagger/modules/e2e/util.dang +++ b/.dagger/modules/e2e/util.dang @@ -152,6 +152,16 @@ type Fixtures { that depends on the runtime, deno.json instead of package.json + tsconfig. """ let denoModule: String! = root + "/generate-deno/app" + + """ + The runtime execution fixtures, managed so `dagger generate` keeps them + current, and registered by the engine when the runtimes module loads them. + """ + let runtimeFixtures: [String!]! = [ + ".dagger/modules/runtimes/fixtures/bun", + ".dagger/modules/runtimes/fixtures/deno", + ".dagger/modules/runtimes/fixtures/node", + ] let configBareModule: String! = root + "/config/bare" # A client binds to client/app (which depends on client/dep) and is generated @@ -193,5 +203,5 @@ type Fixtures { depLibModule, denoModule, parentSourceModule, - ] + ] + runtimeFixtures } diff --git a/.dagger/modules/runtimes/dagger.json b/.dagger/modules/runtimes/dagger.json new file mode 100644 index 0000000..1962b85 --- /dev/null +++ b/.dagger/modules/runtimes/dagger.json @@ -0,0 +1,17 @@ +{ + "name": "runtimes", + "engineVersion": "v1.0.0-0", + "sdk": { + "source": "dang" + }, + "dependencies": [ + { + "name": "typescript-sdk", + "source": "../../../" + }, + { + "name": "sdk-sdk", + "source": "github.com/dagger/sdk-sdk" + } + ] +} diff --git a/.dagger/modules/runtimes/fixtures/bun/.gitignore b/.dagger/modules/runtimes/fixtures/bun/.gitignore new file mode 100644 index 0000000..5b4e8f0 --- /dev/null +++ b/.dagger/modules/runtimes/fixtures/bun/.gitignore @@ -0,0 +1,5 @@ +/sdk +/__dagger.entrypoint.ts +/tsconfig.json +/**/node_modules/** +/.env diff --git a/.dagger/modules/runtimes/fixtures/bun/bun.lock b/.dagger/modules/runtimes/fixtures/bun/bun.lock new file mode 100644 index 0000000..e69de29 diff --git a/.dagger/modules/runtimes/fixtures/bun/dagger-module.toml b/.dagger/modules/runtimes/fixtures/bun/dagger-module.toml new file mode 100644 index 0000000..6448a29 --- /dev/null +++ b/.dagger/modules/runtimes/fixtures/bun/dagger-module.toml @@ -0,0 +1,5 @@ +name = "runtime-bun" +engineVersion = "v1.0.0-beta.9" + +[runtime] + source = "typescript" diff --git a/.dagger/modules/runtimes/fixtures/bun/package.json b/.dagger/modules/runtimes/fixtures/bun/package.json new file mode 100644 index 0000000..3dbc1ca --- /dev/null +++ b/.dagger/modules/runtimes/fixtures/bun/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} diff --git a/.dagger/modules/runtimes/fixtures/bun/src/index.ts b/.dagger/modules/runtimes/fixtures/bun/src/index.ts new file mode 100644 index 0000000..e3e7fc4 --- /dev/null +++ b/.dagger/modules/runtimes/fixtures/bun/src/index.ts @@ -0,0 +1,9 @@ +import { object, func } from "@dagger.io/dagger" + +@object() +export class RuntimeBun { + @func() + greet(name: string): string { + return "hello " + name + " from bun" + } +} diff --git a/.dagger/modules/runtimes/fixtures/deno/.gitignore b/.dagger/modules/runtimes/fixtures/deno/.gitignore new file mode 100644 index 0000000..5b4e8f0 --- /dev/null +++ b/.dagger/modules/runtimes/fixtures/deno/.gitignore @@ -0,0 +1,5 @@ +/sdk +/__dagger.entrypoint.ts +/tsconfig.json +/**/node_modules/** +/.env diff --git a/.dagger/modules/runtimes/fixtures/deno/dagger-module.toml b/.dagger/modules/runtimes/fixtures/deno/dagger-module.toml new file mode 100644 index 0000000..66e89a1 --- /dev/null +++ b/.dagger/modules/runtimes/fixtures/deno/dagger-module.toml @@ -0,0 +1,5 @@ +name = "runtime-deno" +engineVersion = "v1.0.0-beta.9" + +[runtime] + source = "typescript" diff --git a/.dagger/modules/runtimes/fixtures/deno/deno.json b/.dagger/modules/runtimes/fixtures/deno/deno.json new file mode 100644 index 0000000..256151a --- /dev/null +++ b/.dagger/modules/runtimes/fixtures/deno/deno.json @@ -0,0 +1,20 @@ +{ + "tasks": { + "dev": "deno run main.ts" + }, + "imports": { + "typescript": "npm:typescript@5.9.3", + "@dagger.io/dagger": "./sdk/index.ts", + "@dagger.io/dagger/telemetry": "./sdk/telemetry.ts" + }, + "nodeModulesDir": "auto", + "unstable": [ + "bare-node-builtins", + "sloppy-imports", + "node-globals", + "byonm" + ], + "compilerOptions": { + "experimentalDecorators": true + } +} diff --git a/.dagger/modules/runtimes/fixtures/deno/src/index.ts b/.dagger/modules/runtimes/fixtures/deno/src/index.ts new file mode 100644 index 0000000..64c3e92 --- /dev/null +++ b/.dagger/modules/runtimes/fixtures/deno/src/index.ts @@ -0,0 +1,9 @@ +import { object, func } from "@dagger.io/dagger" + +@object() +export class RuntimeDeno { + @func() + greet(name: string): string { + return "hello " + name + " from deno" + } +} diff --git a/.dagger/modules/runtimes/fixtures/node/.gitignore b/.dagger/modules/runtimes/fixtures/node/.gitignore new file mode 100644 index 0000000..5b4e8f0 --- /dev/null +++ b/.dagger/modules/runtimes/fixtures/node/.gitignore @@ -0,0 +1,5 @@ +/sdk +/__dagger.entrypoint.ts +/tsconfig.json +/**/node_modules/** +/.env diff --git a/.dagger/modules/runtimes/fixtures/node/dagger-module.toml b/.dagger/modules/runtimes/fixtures/node/dagger-module.toml new file mode 100644 index 0000000..ab4c158 --- /dev/null +++ b/.dagger/modules/runtimes/fixtures/node/dagger-module.toml @@ -0,0 +1,5 @@ +name = "runtime-node" +engineVersion = "v1.0.0-beta.9" + +[runtime] + source = "typescript" diff --git a/.dagger/modules/runtimes/fixtures/node/package.json b/.dagger/modules/runtimes/fixtures/node/package.json new file mode 100644 index 0000000..3dbc1ca --- /dev/null +++ b/.dagger/modules/runtimes/fixtures/node/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} diff --git a/.dagger/modules/runtimes/fixtures/node/src/index.ts b/.dagger/modules/runtimes/fixtures/node/src/index.ts new file mode 100644 index 0000000..37fcba5 --- /dev/null +++ b/.dagger/modules/runtimes/fixtures/node/src/index.ts @@ -0,0 +1,9 @@ +import { object, func } from "@dagger.io/dagger" + +@object() +export class RuntimeNode { + @func() + greet(name: string): string { + return "hello " + name + " from node" + } +} diff --git a/.dagger/modules/runtimes/main.dang b/.dagger/modules/runtimes/main.dang new file mode 100644 index 0000000..df7c929 --- /dev/null +++ b/.dagger/modules/runtimes/main.dang @@ -0,0 +1,103 @@ +""" +Execution checks: does a module this SDK generates actually run? + +Every other check in this repo inspects what generation *writes*. These load +the result, which for a dagger-module.toml module means the engine builds the +runtime container and executes the generated entrypoint to discover its types — +there is no codegen at call time to paper over a bad tree. + +That makes them the only checks covering the contract the whole move rests on: +sdk/ resolving as @dagger.io/dagger, the bundle it holds being loadable, the +entrypoint importing the user's classes and registering them. A break here is a +module that generates cleanly and then fails at `dagger call`. + +One fixture per TypeScript runtime, because this is where they stop being +interchangeable: generation differs only in which config file it writes, but +node, bun and deno each run the entrypoint in a different container, with a +different interpreter, resolving @dagger.io/dagger a different way. +""" +type Runtimes { + let fixtures: String! = ".dagger/modules/runtimes/fixtures" + + """ + A node module should generate and run. + """ + nodeRunsCheck(ws: Workspace!): Void @check { + assertRuns(ws, "node", "RuntimeNode") + } + + """ + A bun module should generate and run. + + The engine picks bun over node by finding a lockfile next to package.json, so + the generated tree is byte-identical to the node one — only the container it + is executed in differs. Nothing else in this repo has ever run bun. + """ + bunRunsCheck(ws: Workspace!): Void @check { + assertRuns(ws, "bun", "RuntimeBun") + } + + """ + A deno module should generate and run. + + Deno has no node_modules to fall back on: if generation writes the wrong + config, the runtime cannot resolve @dagger.io/dagger at all. + """ + denoRunsCheck(ws: Workspace!): Void @check { + assertRuns(ws, "deno", "RuntimeDeno") + } + + """ + A scaffolded module should answer a function call. + + Loading a module proves the entrypoint registers its types; this proves the + other half of the dispatcher — that a call reaches the user's code and its + return value comes back. It goes through sdk-sdk's harness, which builds a + scratch workspace and drives a release CLI the way a user would: `sdk + install`, `module init`, `generate`, then the call. + + The function is the default template's `baseImageAddress`, whose value is set + by the module's constructor, so a passing call also means the constructor ran + and its default was applied. + """ + invokesFunctionCheck(ws: Workspace!): Void @check { + let target = sdkSdk.target(ws.directory("/"), ".") + let run = target.run(["call", target.moduleName, "base-image-address"]) + + run.assertSuccess + + if (run.stdout.contains("alpine") == false) { + raise "calling a scaffolded module returned unexpected output: " + run.stdout + } + + null + } + + """ + Generate a fixture, stage the result, and load the module from it. + + Staging through withChanges rather than writing to disk keeps the generated + tree out of the fixture entirely — which also sidesteps the trap that a + module whose .gitignore covers sdk/ hides its own generated files from the + engine's module context. + + Reading the module's objects is what forces execution: the TypeScript SDK has + no ModuleTypes function, so type discovery goes through the runtime and the + committed entrypoint's register(). + """ + let assertRuns(ws: Workspace!, runtime: String!, objectName: String!): Void { + let path = fixtures + "/" + runtime + let staged = ws.withChanges(typescriptSdk.mod(ws, path: path).generate(ws)) + + let objects = staged.moduleSource("/" + path).asModule.objects + .{{ asObject.{{ name }} }} + .map { o => o.asObject.name } + + if (objects.filter { name => name == objectName }.length == 0) { + raise runtime + " module did not load: expected object " + objectName + + ", got [" + objects.join(", ") + "]" + } + + null + } +} diff --git a/dagger.toml b/dagger.toml index 7924f2a..1a39d26 100644 --- a/dagger.toml +++ b/dagger.toml @@ -12,6 +12,9 @@ source = ".dagger/modules/e2e" [modules.packager] source = ".dagger/modules/packager" +[modules.runtimes] +source = ".dagger/modules/runtimes" + [modules.typescript-sdk] source = "." check.skip = ["*"] @@ -51,6 +54,15 @@ 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.modules]] +path = ".dagger/modules/runtimes/fixtures/bun" + +[[modules.typescript-sdk.as-sdk.modules]] +path = ".dagger/modules/runtimes/fixtures/deno" + +[[modules.typescript-sdk.as-sdk.modules]] +path = ".dagger/modules/runtimes/fixtures/node" + [[modules.typescript-sdk.as-sdk.clients]] path = ".dagger/modules/e2e/fixtures/client/out" module = ".dagger/modules/e2e/fixtures/client/app"