From b6b41024353cfb12924d36a499eee41d51fa1650 Mon Sep 17 00:00:00 2001 From: Tsiry Sandratraina Date: Sun, 19 Nov 2023 06:34:51 +0000 Subject: [PATCH] use generated client --- deno.lock | 2 + deps.ts | 28 +- example/.fluentci/deno.lock | 7 + example/.fluentci/deps.ts | 30 +- example/.fluentci/fixtures/.gitlab-ci.yml | 2 +- .../.fluentci/fixtures/azure-pipelines.yml | 2 +- example/.fluentci/fixtures/buildspec.yml | 2 +- example/.fluentci/fixtures/config.yml | 2 +- example/.fluentci/fixtures/workflow.yml | 12 +- example/.fluentci/gen/nexus.ts | 8 +- example/.fluentci/schema.graphql | 7 +- example/.fluentci/sdk/client.gen.ts | 5993 +++++++++++++++++ example/.fluentci/sdk/connect.ts | 89 + example/.fluentci/sdk/utils.ts | 251 + example/.fluentci/src/aws/config.ts | 2 +- example/.fluentci/src/azure/config.ts | 2 +- example/.fluentci/src/circleci/config.ts | 2 +- example/.fluentci/src/dagger/jobs.ts | 3 +- example/.fluentci/src/dagger/queries.ts | 4 +- example/.fluentci/src/dagger/runner.ts | 16 +- example/.fluentci/src/dagger/schema.ts | 10 +- example/.fluentci/src/github/config.ts | 19 +- example/.fluentci/src/gitlab/config.ts | 2 +- example/test.gql | 2 +- gen/nexus.ts | 8 +- schema.graphql | 4 +- sdk/client.gen.ts | 5993 +++++++++++++++++ sdk/connect.ts | 89 + sdk/utils.ts | 251 + src/dagger/jobs.ts | 3 +- src/dagger/queries.ts | 4 +- src/dagger/schema.ts | 10 +- 32 files changed, 12791 insertions(+), 68 deletions(-) create mode 100644 example/.fluentci/sdk/client.gen.ts create mode 100644 example/.fluentci/sdk/connect.ts create mode 100644 example/.fluentci/sdk/utils.ts create mode 100644 sdk/client.gen.ts create mode 100644 sdk/connect.ts create mode 100644 sdk/utils.ts diff --git a/deno.lock b/deno.lock index ffe8ecb..08c89cc 100644 --- a/deno.lock +++ b/deno.lock @@ -261,6 +261,8 @@ "https://esm.sh/v134/yallist@4.0.0/denonext/yallist.mjs": "61f180d807dda50bac17028eda05d5722a3fecef6e98a9064e2353ea6864fd82", "https://esm.sh/v134/yaml@2.3.1/denonext/yaml.mjs": "71f677b4bfc69271af9d98db5194e354f9a1863955e208e26d32a9ef78bd89f5", "https://esm.sh/yaml@v2.3.1": "9689357882f365e29af1f7b2015cd08c7e01fe5d0a8363132216b6b9b905a337", + "https://nix.fluentci.io/v0.5.3/deps.ts": "469c1f084eda8d2ee78135b0bf4f9490b80b36d8d0bdb88594167133a918da8e", + "https://nix.fluentci.io/v0.5.3/src/dagger/steps.ts": "b766f4fa9624a032e7af884a5ca47bc666a529c4a472d38b74b55ca0d63cf81d", "https://sdk.fluentci.io/v0.3.0/deps.ts": "3a145e76b4345a9a7888f09b1b48cb54523ebfa43247a1abebc40a9e82d555f4", "https://sdk.fluentci.io/v0.3.0/mod.ts": "261ba81a4728f5def4e327a5cd80664ea8449515a2f4eea5f3f416acae39a1fa", "https://sdk.fluentci.io/v0.3.0/src/client.ts": "7f1df4b1fee62dd6f946fa9d15d47a37b938ffb4ac91faf3d39b44b83d4f5921", diff --git a/deps.ts b/deps.ts index ebc34b8..cf89fc2 100644 --- a/deps.ts +++ b/deps.ts @@ -1,9 +1,10 @@ export { assertEquals } from "https://deno.land/std@0.191.0/testing/asserts.ts"; -import Client from "https://sdk.fluentci.io/v0.3.0/mod.ts"; +import { Client } from "./sdk/client.gen.ts"; export default Client; export { connect, uploadContext } from "https://sdk.fluentci.io/v0.3.0/mod.ts"; export { brightGreen } from "https://deno.land/std@0.191.0/fmt/colors.ts"; +export { withDevbox } from "https://nix.fluentci.io/v0.5.3/src/dagger/steps.ts"; export { stringifyTree } from "https://esm.sh/stringify-tree@1.1.1"; import gql from "https://esm.sh/graphql-tag@2.12.6"; export { gql }; @@ -11,6 +12,7 @@ export { arg, queryType, stringArg, + booleanArg, intArg, nonNull, makeSchema, @@ -23,6 +25,30 @@ export { export { parse } from "https://deno.land/std@0.205.0/flags/mod.ts"; export { snakeCase, camelCase } from "https://cdn.skypack.dev/lodash"; +export { + ClientError, + GraphQLClient, +} from "https://esm.sh/v128/graphql-request@6.1.0"; +export { + DaggerSDKError, + UnknownDaggerError, + DockerImageRefValidationError, + EngineSessionConnectParamsParseError, + ExecError, + GraphQLRequestError, + InitEngineSessionBinaryError, + TooManyNestedObjectsError, + EngineSessionError, + EngineSessionConnectionTimeoutError, + NotAwaitedRequestError, + ERROR_CODES, +} from "https://esm.sh/@dagger.io/dagger@0.9.3"; + +export type { + CallbackFct, + ConnectOpts, +} from "https://sdk.fluentci.io/v0.3.0/mod.ts"; + export * as FluentGitlabCI from "https://deno.land/x/fluent_gitlab_ci@v0.4.2/mod.ts"; export * as FluentGithubActions from "https://deno.land/x/fluent_github_actions@v0.2.1/mod.ts"; export * as FluentCircleCI from "https://deno.land/x/fluent_circleci@v0.2.5/mod.ts"; diff --git a/example/.fluentci/deno.lock b/example/.fluentci/deno.lock index 8445e0b..9bddc42 100644 --- a/example/.fluentci/deno.lock +++ b/example/.fluentci/deno.lock @@ -31,6 +31,8 @@ "https://cdn.jsdelivr.net/gh/tsirysndr/tar@v0.1.1/deps.ts": "096395daebc7ed8a18f0484e4ffcc3a7f70e50946735f7df9611a7fcfd8272cc", "https://cdn.jsdelivr.net/gh/tsirysndr/tar@v0.1.1/mod.ts": "e269d71c72ae68e82c1960e5db2a0c7419c97c9683ef717de0ab75d90f364713", "https://cdn.jsdelivr.net/gh/tsirysndr/tar@v0.1.1/src/tar.ts": "9b02eaaa784b225ad7a23d2769cd492adf113ea7c11c02e3646849e98f4ae43b", + "https://cdn.skypack.dev/-/lodash@v4.17.21-K6GEbP02mWFnLA45zAmi/dist=es2019,mode=imports/optimized/lodash.js": "10c4df47937ffc78548d136dd535a021df5f57182a653260d715c0690dd22978", + "https://cdn.skypack.dev/lodash": "8280de0b3efd87f06ea0eb330d15b8de32c059556023b8c6524e9eb9e4844dc0", "https://deno.land/std@0.129.0/_util/assert.ts": "e94f2eb37cebd7f199952e242c77654e43333c1ac4c5c700e929ea3aa5489f74", "https://deno.land/std@0.129.0/_util/os.ts": "49b92edea1e82ba295ec946de8ffd956ed123e2948d9bd1d3e901b04e4307617", "https://deno.land/std@0.129.0/archive/tar.ts": "35ea1baddec7988cc4034765a2cee7613bc8074bd40940d3f5e98f63070a716a", @@ -149,6 +151,9 @@ "https://deno.land/std@0.203.0/path/to_file_url.ts": "00e6322373dd51ad109956b775e4e72e5f9fa68ce2c6b04e4af2a6eed3825d31", "https://deno.land/std@0.203.0/path/to_namespaced_path.ts": "1b1db3055c343ab389901adfbda34e82b7386bcd1c744d54f9c1496ee0fd0c3d", "https://deno.land/std@0.203.0/path/win32.ts": "8b3f80ef7a462511d5e8020ff490edcaa0a0d118f1b1e9da50e2916bdd73f9dd", + "https://deno.land/std@0.205.0/assert/assert.ts": "9a97dad6d98c238938e7540736b826440ad8c1c1e54430ca4c4e623e585607ee", + "https://deno.land/std@0.205.0/assert/assertion_error.ts": "4d0bde9b374dfbcbe8ac23f54f567b77024fb67dbb1906a852d67fe050d42f56", + "https://deno.land/std@0.205.0/flags/mod.ts": "0948466fc437f017f00c0b972a422b3dc3317a790bcf326429d23182977eaf9f", "https://deno.land/std@0.52.0/fmt/colors.ts": "ec9d653672a9a3c7b6eafe53c5bc797364a2db2dcf766ab649c1155fea7a80b2", "https://deno.land/x/crc32@v0.2.0/mod.ts": "de7a3fa2d4ef24b96fc21e1cc4d2d65d1d2b1dcea92f63960e3e11bfa82df0fa", "https://deno.land/x/fluent_aws_codepipeline@v0.2.3/mod.ts": "79cc758901d20a3573d7e3cc2db9f0a5fe56833f4d9befcedc072b94d542eec7", @@ -244,6 +249,8 @@ "https://esm.sh/v134/yallist@4.0.0/denonext/yallist.mjs": "61f180d807dda50bac17028eda05d5722a3fecef6e98a9064e2353ea6864fd82", "https://esm.sh/v134/yaml@2.3.1/denonext/yaml.mjs": "71f677b4bfc69271af9d98db5194e354f9a1863955e208e26d32a9ef78bd89f5", "https://esm.sh/yaml@v2.3.1": "9689357882f365e29af1f7b2015cd08c7e01fe5d0a8363132216b6b9b905a337", + "https://nix.fluentci.io/v0.5.3/deps.ts": "469c1f084eda8d2ee78135b0bf4f9490b80b36d8d0bdb88594167133a918da8e", + "https://nix.fluentci.io/v0.5.3/src/dagger/steps.ts": "b766f4fa9624a032e7af884a5ca47bc666a529c4a472d38b74b55ca0d63cf81d", "https://sdk.fluentci.io/v0.3.0/deps.ts": "3a145e76b4345a9a7888f09b1b48cb54523ebfa43247a1abebc40a9e82d555f4", "https://sdk.fluentci.io/v0.3.0/mod.ts": "261ba81a4728f5def4e327a5cd80664ea8449515a2f4eea5f3f416acae39a1fa", "https://sdk.fluentci.io/v0.3.0/src/client.ts": "7f1df4b1fee62dd6f946fa9d15d47a37b938ffb4ac91faf3d39b44b83d4f5921", diff --git a/example/.fluentci/deps.ts b/example/.fluentci/deps.ts index 832af17..cf89fc2 100644 --- a/example/.fluentci/deps.ts +++ b/example/.fluentci/deps.ts @@ -1,9 +1,10 @@ export { assertEquals } from "https://deno.land/std@0.191.0/testing/asserts.ts"; -import Client from "https://sdk.fluentci.io/v0.3.0/mod.ts"; +import { Client } from "./sdk/client.gen.ts"; export default Client; export { connect, uploadContext } from "https://sdk.fluentci.io/v0.3.0/mod.ts"; export { brightGreen } from "https://deno.land/std@0.191.0/fmt/colors.ts"; +export { withDevbox } from "https://nix.fluentci.io/v0.5.3/src/dagger/steps.ts"; export { stringifyTree } from "https://esm.sh/stringify-tree@1.1.1"; import gql from "https://esm.sh/graphql-tag@2.12.6"; export { gql }; @@ -11,6 +12,7 @@ export { arg, queryType, stringArg, + booleanArg, intArg, nonNull, makeSchema, @@ -20,6 +22,32 @@ export { join, resolve, } from "https://deno.land/std@0.203.0/path/mod.ts"; +export { parse } from "https://deno.land/std@0.205.0/flags/mod.ts"; +export { snakeCase, camelCase } from "https://cdn.skypack.dev/lodash"; + +export { + ClientError, + GraphQLClient, +} from "https://esm.sh/v128/graphql-request@6.1.0"; +export { + DaggerSDKError, + UnknownDaggerError, + DockerImageRefValidationError, + EngineSessionConnectParamsParseError, + ExecError, + GraphQLRequestError, + InitEngineSessionBinaryError, + TooManyNestedObjectsError, + EngineSessionError, + EngineSessionConnectionTimeoutError, + NotAwaitedRequestError, + ERROR_CODES, +} from "https://esm.sh/@dagger.io/dagger@0.9.3"; + +export type { + CallbackFct, + ConnectOpts, +} from "https://sdk.fluentci.io/v0.3.0/mod.ts"; export * as FluentGitlabCI from "https://deno.land/x/fluent_gitlab_ci@v0.4.2/mod.ts"; export * as FluentGithubActions from "https://deno.land/x/fluent_github_actions@v0.2.1/mod.ts"; diff --git a/example/.fluentci/fixtures/.gitlab-ci.yml b/example/.fluentci/fixtures/.gitlab-ci.yml index fb21f9c..54df3fe 100644 --- a/example/.fluentci/fixtures/.gitlab-ci.yml +++ b/example/.fluentci/fixtures/.gitlab-ci.yml @@ -17,7 +17,7 @@ before_script: - apk add docker-cli curl unzip - deno install -A -r https://cli.fluentci.io -n fluentci - - curl -L https://dl.dagger.io/dagger/install.sh | DAGGER_VERSION=0.8.1 sh + - curl -L https://dl.dagger.io/dagger/install.sh | DAGGER_VERSION=0.9.3 sh - mv bin/dagger /usr/local/bin - dagger version diff --git a/example/.fluentci/fixtures/azure-pipelines.yml b/example/.fluentci/fixtures/azure-pipelines.yml index bacd688..e48ea67 100644 --- a/example/.fluentci/fixtures/azure-pipelines.yml +++ b/example/.fluentci/fixtures/azure-pipelines.yml @@ -14,7 +14,7 @@ steps: - script: deno install -A -r https://cli.fluentci.io -n fluentci displayName: Setup Fluent CI CLI - script: | - curl -L https://dl.dagger.io/dagger/install.sh | DAGGER_VERSION=0.8.1 sh + curl -L https://dl.dagger.io/dagger/install.sh | DAGGER_VERSION=0.9.3 sh sudo mv bin/dagger /usr/local/bin dagger version displayName: Setup Dagger diff --git a/example/.fluentci/fixtures/buildspec.yml b/example/.fluentci/fixtures/buildspec.yml index 0a07d18..4d8f94f 100644 --- a/example/.fluentci/fixtures/buildspec.yml +++ b/example/.fluentci/fixtures/buildspec.yml @@ -8,7 +8,7 @@ phases: - export DENO_INSTALL="$HOME/.deno" - export PATH="$DENO_INSTALL/bin:$PATH" - deno install -A -r https://cli.fluentci.io -n fluentci - - curl -L https://dl.dagger.io/dagger/install.sh | DAGGER_VERSION=0.8.1 sh + - curl -L https://dl.dagger.io/dagger/install.sh | DAGGER_VERSION=0.9.3 sh - mv bin/dagger /usr/local/bin - dagger version build: diff --git a/example/.fluentci/fixtures/config.yml b/example/.fluentci/fixtures/config.yml index efa2abb..86933ab 100644 --- a/example/.fluentci/fixtures/config.yml +++ b/example/.fluentci/fixtures/config.yml @@ -12,7 +12,7 @@ jobs: export PATH="$DENO_INSTALL/bin:$PATH" - run: deno install -A -r https://cli.fluentci.io -n fluentci - run: | - curl -L https://dl.dagger.io/dagger/install.sh | DAGGER_VERSION=0.8.1 sh + curl -L https://dl.dagger.io/dagger/install.sh | DAGGER_VERSION=0.9.3 sh sudo mv bin/dagger /usr/local/bin dagger version - run: diff --git a/example/.fluentci/fixtures/workflow.yml b/example/.fluentci/fixtures/workflow.yml index a70356a..8f734b4 100644 --- a/example/.fluentci/fixtures/workflow.yml +++ b/example/.fluentci/fixtures/workflow.yml @@ -10,15 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - uses: denoland/setup-deno@v1 - with: - deno-version: v1.37 - - name: Setup Fluent CI CLI - run: deno install -A -r https://cli.fluentci.io -n fluentci - - name: Setup Dagger - run: | - curl -L https://dl.dagger.io/dagger/install.sh | DAGGER_VERSION=0.8.1 sh - sudo mv bin/dagger /usr/local/bin - dagger version + - name: Setup Fluent CI + uses: fluentci-io/setup-fluentci@v2 - name: Run Dagger Pipelines run: fluentci run bun_pipeline diff --git a/example/.fluentci/gen/nexus.ts b/example/.fluentci/gen/nexus.ts index 9fac157..e3ec88c 100644 --- a/example/.fluentci/gen/nexus.ts +++ b/example/.fluentci/gen/nexus.ts @@ -58,13 +58,13 @@ export interface NexusGenFieldTypeNames { export interface NexusGenArgTypes { Query: { run: { // args - bunVersion: string; // String! + bunVersion?: string | null; // String command: string; // String! - src: string; // String! + src?: string | null; // String } test: { // args - bunVersion: string; // String! - src: string; // String! + bunVersion?: string | null; // String + src?: string | null; // String } } } diff --git a/example/.fluentci/schema.graphql b/example/.fluentci/schema.graphql index 07fea8c..882993d 100644 --- a/example/.fluentci/schema.graphql +++ b/example/.fluentci/schema.graphql @@ -1,7 +1,8 @@ ### This file was generated by Nexus Schema ### Do not make changes to this file directly + type Query { - run(bunVersion: String!, command: String!, src: String!): String - test(bunVersion: String!, src: String!): String -} + run(bunVersion: String, command: String!, src: String): String + test(bunVersion: String, src: String): String +} \ No newline at end of file diff --git a/example/.fluentci/sdk/client.gen.ts b/example/.fluentci/sdk/client.gen.ts new file mode 100644 index 0000000..d18ad57 --- /dev/null +++ b/example/.fluentci/sdk/client.gen.ts @@ -0,0 +1,5993 @@ +/** + * This file was auto-generated by `client-gen`. + * Do not make direct changes to the file. + */ +import { GraphQLClient } from "../deps.ts" + +import { computeQuery } from "./utils.ts" + +/** + * @hidden + */ +export type QueryTree = { + operation: string + args?: Record +} + +/** + * @hidden + */ +export type Metadata = { + [key: string]: { + is_enum?: boolean + } +} + +interface ClientConfig { + queryTree?: QueryTree[] + host?: string + sessionToken?: string +} + +class BaseClient { + protected _queryTree: QueryTree[] + protected client: GraphQLClient + /** + * @defaultValue `127.0.0.1:8080` + */ + public clientHost: string + public sessionToken: string + + /** + * @hidden + */ + constructor({ queryTree, host, sessionToken }: ClientConfig = {}) { + this._queryTree = queryTree || [] + this.clientHost = host || "127.0.0.1:8080" + this.sessionToken = sessionToken || "" + this.client = new GraphQLClient(`http://${host}/query`, { + headers: { + Authorization: + "Basic " + btoa(sessionToken + ":"), + }, + }) + } + + /** + * @hidden + */ + get queryTree() { + return this._queryTree + } +} + +export type BuildArg = { + /** + * The build argument name. + */ + name: string + + /** + * The build argument value. + */ + value: string +} + +/** + * Sharing mode of the cache volume. + */ +export enum CacheSharingMode { + + /** + * Shares the cache volume amongst many build pipelines, + * but will serialize the writes + */ + Locked = "LOCKED", + + /** + * Keeps a cache volume for a single build pipeline + */ + Private = "PRIVATE", + + /** + * Shares the cache volume amongst many build pipelines + */ + Shared = "SHARED", +} +/** + * A global cache volume identifier. + */ +export type CacheVolumeID = string & {__CacheVolumeID: never} + +export type ContainerAsTarballOpts = { + /** + * Identifiers for other platform specific containers. + * Used for multi-platform image. + */ + platformVariants?: Container[] + + /** + * Force each layer of the image to use the specified compression algorithm. + * If this is unset, then if a layer already has a compressed blob in the engine's + * cache, that will be used (this can result in a mix of compression algorithms for + * different layers). If this is unset and a layer has no compressed blob in the + * engine's cache, then it will be compressed using Gzip. + */ + forcedCompression?: ImageLayerCompression + + /** + * Use the specified media types for the image's layers. Defaults to OCI, which + * is largely compatible with most recent container runtimes, but Docker may be needed + * for older runtimes without OCI support. + */ + mediaTypes?: ImageMediaTypes +} + +export type ContainerBuildOpts = { + /** + * Path to the Dockerfile to use. + * + * Default: './Dockerfile'. + */ + dockerfile?: string + + /** + * Additional build arguments. + */ + buildArgs?: BuildArg[] + + /** + * Target build stage to build. + */ + target?: string + + /** + * Secrets to pass to the build. + * + * They will be mounted at /run/secrets/[secret-name] in the build container + * + * They can be accessed in the Dockerfile using the "secret" mount type + * and mount path /run/secrets/[secret-name] + * e.g. RUN --mount=type=secret,id=my-secret curl url?token=$(cat /run/secrets/my-secret)" + */ + secrets?: Secret[] +} + +export type ContainerExportOpts = { + /** + * Identifiers for other platform specific containers. + * Used for multi-platform image. + */ + platformVariants?: Container[] + + /** + * Force each layer of the exported image to use the specified compression algorithm. + * If this is unset, then if a layer already has a compressed blob in the engine's + * cache, that will be used (this can result in a mix of compression algorithms for + * different layers). If this is unset and a layer has no compressed blob in the + * engine's cache, then it will be compressed using Gzip. + */ + forcedCompression?: ImageLayerCompression + + /** + * Use the specified media types for the exported image's layers. Defaults to OCI, which + * is largely compatible with most recent container runtimes, but Docker may be needed + * for older runtimes without OCI support. + */ + mediaTypes?: ImageMediaTypes +} + +export type ContainerImportOpts = { + /** + * Identifies the tag to import from the archive, if the archive bundles + * multiple tags. + */ + tag?: string +} + +export type ContainerPipelineOpts = { + /** + * Pipeline description. + */ + description?: string + + /** + * Pipeline labels. + */ + labels?: PipelineLabel[] +} + +export type ContainerPublishOpts = { + /** + * Identifiers for other platform specific containers. + * Used for multi-platform image. + */ + platformVariants?: Container[] + + /** + * Force each layer of the published image to use the specified compression algorithm. + * If this is unset, then if a layer already has a compressed blob in the engine's + * cache, that will be used (this can result in a mix of compression algorithms for + * different layers). If this is unset and a layer has no compressed blob in the + * engine's cache, then it will be compressed using Gzip. + */ + forcedCompression?: ImageLayerCompression + + /** + * Use the specified media types for the published image's layers. Defaults to OCI, which + * is largely compatible with most recent registries, but Docker may be needed for older + * registries without OCI support. + */ + mediaTypes?: ImageMediaTypes +} + +export type ContainerWithDefaultArgsOpts = { + /** + * Arguments to prepend to future executions (e.g., ["-v", "--no-cache"]). + */ + args?: string[] +} + +export type ContainerWithDirectoryOpts = { + /** + * Patterns to exclude in the written directory (e.g., ["node_modules/**", ".gitignore", ".git/"]). + */ + exclude?: string[] + + /** + * Patterns to include in the written directory (e.g., ["*.go", "go.mod", "go.sum"]). + */ + include?: string[] + + /** + * A user:group to set for the directory and its contents. + * + * The user and group can either be an ID (1000:1000) or a name (foo:bar). + * + * If the group is omitted, it defaults to the same as the user. + */ + owner?: string +} + +export type ContainerWithEnvVariableOpts = { + /** + * Replace ${VAR} or $VAR in the value according to the current environment + * variables defined in the container (e.g., "/opt/bin:$PATH"). + */ + expand?: boolean +} + +export type ContainerWithExecOpts = { + /** + * If the container has an entrypoint, ignore it for args rather than using it to wrap them. + */ + skipEntrypoint?: boolean + + /** + * Content to write to the command's standard input before closing (e.g., "Hello world"). + */ + stdin?: string + + /** + * Redirect the command's standard output to a file in the container (e.g., "/tmp/stdout"). + */ + redirectStdout?: string + + /** + * Redirect the command's standard error to a file in the container (e.g., "/tmp/stderr"). + */ + redirectStderr?: string + + /** + * Provides dagger access to the executed command. + * + * Do not use this option unless you trust the command being executed. + * The command being executed WILL BE GRANTED FULL ACCESS TO YOUR HOST FILESYSTEM. + */ + experimentalPrivilegedNesting?: boolean + + /** + * Execute the command with all root capabilities. This is similar to running a command + * with "sudo" or executing `docker run` with the `--privileged` flag. Containerization + * does not provide any security guarantees when using this option. It should only be used + * when absolutely necessary and only with trusted commands. + */ + insecureRootCapabilities?: boolean +} + +export type ContainerWithExposedPortOpts = { + /** + * Transport layer network protocol + */ + protocol?: NetworkProtocol + + /** + * Optional port description + */ + description?: string +} + +export type ContainerWithFileOpts = { + /** + * Permission given to the copied file (e.g., 0600). + * + * Default: 0644. + */ + permissions?: number + + /** + * A user:group to set for the file. + * + * The user and group can either be an ID (1000:1000) or a name (foo:bar). + * + * If the group is omitted, it defaults to the same as the user. + */ + owner?: string +} + +export type ContainerWithMountedCacheOpts = { + /** + * Identifier of the directory to use as the cache volume's root. + */ + source?: Directory + + /** + * Sharing mode of the cache volume. + */ + sharing?: CacheSharingMode + + /** + * A user:group to set for the mounted cache directory. + * + * Note that this changes the ownership of the specified mount along with the + * initial filesystem provided by source (if any). It does not have any effect + * if/when the cache has already been created. + * + * The user and group can either be an ID (1000:1000) or a name (foo:bar). + * + * If the group is omitted, it defaults to the same as the user. + */ + owner?: string +} + +export type ContainerWithMountedDirectoryOpts = { + /** + * A user:group to set for the mounted directory and its contents. + * + * The user and group can either be an ID (1000:1000) or a name (foo:bar). + * + * If the group is omitted, it defaults to the same as the user. + */ + owner?: string +} + +export type ContainerWithMountedFileOpts = { + /** + * A user or user:group to set for the mounted file. + * + * The user and group can either be an ID (1000:1000) or a name (foo:bar). + * + * If the group is omitted, it defaults to the same as the user. + */ + owner?: string +} + +export type ContainerWithMountedSecretOpts = { + /** + * A user:group to set for the mounted secret. + * + * The user and group can either be an ID (1000:1000) or a name (foo:bar). + * + * If the group is omitted, it defaults to the same as the user. + */ + owner?: string + + /** + * Permission given to the mounted secret (e.g., 0600). + * This option requires an owner to be set to be active. + * + * Default: 0400. + */ + mode?: number +} + +export type ContainerWithNewFileOpts = { + /** + * Content of the file to write (e.g., "Hello world!"). + */ + contents?: string + + /** + * Permission given to the written file (e.g., 0600). + * + * Default: 0644. + */ + permissions?: number + + /** + * A user:group to set for the file. + * + * The user and group can either be an ID (1000:1000) or a name (foo:bar). + * + * If the group is omitted, it defaults to the same as the user. + */ + owner?: string +} + +export type ContainerWithUnixSocketOpts = { + /** + * A user:group to set for the mounted socket. + * + * The user and group can either be an ID (1000:1000) or a name (foo:bar). + * + * If the group is omitted, it defaults to the same as the user. + */ + owner?: string +} + +export type ContainerWithoutExposedPortOpts = { + /** + * Port protocol to unexpose + */ + protocol?: NetworkProtocol +} + +/** + * A unique container identifier. Null designates an empty container (scratch). + */ +export type ContainerID = string & {__ContainerID: never} + +/** + * The `DateTime` scalar type represents a DateTime. The DateTime is serialized as an RFC 3339 quoted string + */ +export type DateTime = string & {__DateTime: never} + +export type DirectoryAsModuleOpts = { + /** + * An optional subpath of the directory which contains the module's source + * code. + * + * This is needed when the module code is in a subdirectory but requires + * parent directories to be loaded in order to execute. For example, the + * module source code may need a go.mod, project.toml, package.json, etc. file + * from a parent directory. + * + * If not set, the module source code is loaded from the root of the + * directory. + */ + sourceSubpath?: string +} + +export type DirectoryDockerBuildOpts = { + /** + * Path to the Dockerfile to use (e.g., "frontend.Dockerfile"). + * + * Defaults: './Dockerfile'. + */ + dockerfile?: string + + /** + * The platform to build. + */ + platform?: Platform + + /** + * Build arguments to use in the build. + */ + buildArgs?: BuildArg[] + + /** + * Target build stage to build. + */ + target?: string + + /** + * Secrets to pass to the build. + * + * They will be mounted at /run/secrets/[secret-name]. + */ + secrets?: Secret[] +} + +export type DirectoryEntriesOpts = { + /** + * Location of the directory to look at (e.g., "/src"). + */ + path?: string +} + +export type DirectoryPipelineOpts = { + /** + * Pipeline description. + */ + description?: string + + /** + * Pipeline labels. + */ + labels?: PipelineLabel[] +} + +export type DirectoryWithDirectoryOpts = { + /** + * Exclude artifacts that match the given pattern (e.g., ["node_modules/", ".git*"]). + */ + exclude?: string[] + + /** + * Include only artifacts that match the given pattern (e.g., ["app/", "package.*"]). + */ + include?: string[] +} + +export type DirectoryWithFileOpts = { + /** + * Permission given to the copied file (e.g., 0600). + * + * Default: 0644. + */ + permissions?: number +} + +export type DirectoryWithNewDirectoryOpts = { + /** + * Permission granted to the created directory (e.g., 0777). + * + * Default: 0755. + */ + permissions?: number +} + +export type DirectoryWithNewFileOpts = { + /** + * Permission given to the copied file (e.g., 0600). + * + * Default: 0644. + */ + permissions?: number +} + +/** + * A content-addressed directory identifier. + */ +export type DirectoryID = string & {__DirectoryID: never} + +export type FileExportOpts = { + /** + * If allowParentDirPath is true, the path argument can be a directory path, in which case + * the file will be created in that directory. + */ + allowParentDirPath?: boolean +} + +/** + * A file identifier. + */ +export type FileID = string & {__FileID: never} + +export type FunctionWithArgOpts = { + /** + * A doc string for the argument, if any + */ + description?: string + + /** + * A default value to use for this argument if not explicitly set by the caller, if any + */ + defaultValue?: JSON +} + +/** + * A reference to a FunctionArg. + */ +export type FunctionArgID = string & {__FunctionArgID: never} + +/** + * A reference to a Function. + */ +export type FunctionID = string & {__FunctionID: never} + +/** + * A reference to GeneratedCode. + */ +export type GeneratedCodeID = string & {__GeneratedCodeID: never} + +export type GitRefTreeOpts = { + sshKnownHosts?: string + sshAuthSocket?: Socket +} + +export type HostDirectoryOpts = { + /** + * Exclude artifacts that match the given pattern (e.g., ["node_modules/", ".git*"]). + */ + exclude?: string[] + + /** + * Include only artifacts that match the given pattern (e.g., ["app/", "package.*"]). + */ + include?: string[] +} + +export type HostServiceOpts = { + /** + * Upstream host to forward traffic to. + */ + host?: string +} + +export type HostTunnelOpts = { + /** + * Map each service port to the same port on the host, as if the service were + * running natively. + * + * Note: enabling may result in port conflicts. + */ + native?: boolean + + /** + * Configure explicit port forwarding rules for the tunnel. + * + * If a port's frontend is unspecified or 0, a random port will be chosen by + * the host. + * + * If no ports are given, all of the service's ports are forwarded. If native + * is true, each port maps to the same port on the host. If native is false, + * each port maps to a random port chosen by the host. + * + * If ports are given and native is true, the ports are additive. + */ + ports?: PortForward[] +} + +/** + * The `ID` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `"4"`) or integer (such as `4`) input value will be accepted as an ID. + */ +export type ID = string & {__ID: never} + +/** + * Compression algorithm to use for image layers. + */ +export enum ImageLayerCompression { + Estargz = "EStarGZ", + Gzip = "Gzip", + Uncompressed = "Uncompressed", + Zstd = "Zstd", +} +/** + * Mediatypes to use in published or exported image metadata. + */ +export enum ImageMediaTypes { + Dockermediatypes = "DockerMediaTypes", + Ocimediatypes = "OCIMediaTypes", +} +/** + * An arbitrary JSON-encoded value. + */ +export type JSON = string & {__JSON: never} + +/** + * A reference to a Module. + */ +export type ModuleID = string & {__ModuleID: never} + +/** + * Transport layer network protocol associated to a port. + */ +export enum NetworkProtocol { + + /** + * TCP (Transmission Control Protocol) + */ + Tcp = "TCP", + + /** + * UDP (User Datagram Protocol) + */ + Udp = "UDP", +} +export type PipelineLabel = { + /** + * Label name. + */ + name: string + + /** + * Label value. + */ + value: string +} + +/** + * The platform config OS and architecture in a Container. + * + * The format is [os]/[platform]/[version] (e.g., "darwin/arm64/v7", "windows/amd64", "linux/arm64"). + */ +export type Platform = string & {__Platform: never} + +export type PortForward = { + /** + * Destination port for traffic. + */ + backend: number + + /** + * Port to expose to clients. If unspecified, a default will be chosen. + */ + frontend?: number + + /** + * Protocol to use for traffic. + */ + protocol?: NetworkProtocol +} + +export type ClientContainerOpts = { + id?: ContainerID + platform?: Platform +} + +export type ClientDirectoryOpts = { + id?: DirectoryID +} + +export type ClientGitOpts = { + /** + * Set to true to keep .git directory. + */ + keepGitDir?: boolean + + /** + * Set SSH known hosts + */ + sshKnownHosts?: string + + /** + * Set SSH auth socket + */ + sshAuthSocket?: Socket + + /** + * A service which must be started before the repo is fetched. + */ + experimentalServiceHost?: Service +} + +export type ClientHttpOpts = { + /** + * A service which must be started before the URL is fetched. + */ + experimentalServiceHost?: Service +} + +export type ClientModuleConfigOpts = { + subpath?: string +} + +export type ClientPipelineOpts = { + /** + * Pipeline description. + */ + description?: string + + /** + * Pipeline labels. + */ + labels?: PipelineLabel[] +} + +export type ClientSocketOpts = { + id?: SocketID +} + +/** + * A unique identifier for a secret. + */ +export type SecretID = string & {__SecretID: never} + +export type ServiceEndpointOpts = { + /** + * The exposed port number for the endpoint + */ + port?: number + + /** + * Return a URL with the given scheme, eg. http for http:// + */ + scheme?: string +} + +/** + * A unique service identifier. + */ +export type ServiceID = string & {__ServiceID: never} + +/** + * A content-addressed socket identifier. + */ +export type SocketID = string & {__SocketID: never} + +export type TypeDefWithFieldOpts = { + /** + * A doc string for the field, if any + */ + description?: string +} + +export type TypeDefWithObjectOpts = { + description?: string +} + +/** + * A reference to a TypeDef. + */ +export type TypeDefID = string & {__TypeDefID: never} + +/** + * Distinguishes the different kinds of TypeDefs. + */ +export enum TypeDefKind { + + /** + * A boolean value + */ + Booleankind = "BooleanKind", + + /** + * An integer value + */ + Integerkind = "IntegerKind", + + /** + * A list of values all having the same type. + * + * Always paired with a ListTypeDef. + */ + Listkind = "ListKind", + + /** + * A named type defined in the GraphQL schema, with fields and functions. + * + * Always paired with an ObjectTypeDef. + */ + Objectkind = "ObjectKind", + + /** + * A string value + */ + Stringkind = "StringKind", + + /** + * A special kind used to signify that no value is returned. + * + * This is used for functions that have no return value. The outer TypeDef + * specifying this Kind is always Optional, as the Void is never actually + * represented. + */ + Voidkind = "VoidKind", +} +/** + * The absense of a value. + * + * A Null Void is used as a placeholder for resolvers that do not return anything. + */ +export type Void = string & {__Void: never} + +export type __TypeEnumValuesOpts = { + includeDeprecated?: boolean +} + +export type __TypeFieldsOpts = { + includeDeprecated?: boolean +} + + + + + + + + +/** + * A directory whose contents persist across runs. + */ +export class CacheVolume extends BaseClient { + private readonly _id?: CacheVolumeID = undefined + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + _id?: CacheVolumeID, + ) { + super(parent) + + this._id = _id + } + async id(): Promise { + if (this._id) { + return this._id + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "id", + }, + ], + this.client + ) + + + return response + } +} + + + +/** + * An OCI-compatible container, also known as a docker container. + */ +export class Container extends BaseClient { + private readonly _id?: ContainerID = undefined + private readonly _envVariable?: string = undefined + private readonly _export?: boolean = undefined + private readonly _imageRef?: string = undefined + private readonly _label?: string = undefined + private readonly _platform?: Platform = undefined + private readonly _publish?: string = undefined + private readonly _shellEndpoint?: string = undefined + private readonly _stderr?: string = undefined + private readonly _stdout?: string = undefined + private readonly _sync?: ContainerID = undefined + private readonly _user?: string = undefined + private readonly _workdir?: string = undefined + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + _id?: ContainerID, + _envVariable?: string, + _export?: boolean, + _imageRef?: string, + _label?: string, + _platform?: Platform, + _publish?: string, + _shellEndpoint?: string, + _stderr?: string, + _stdout?: string, + _sync?: ContainerID, + _user?: string, + _workdir?: string, + ) { + super(parent) + + this._id = _id + this._envVariable = _envVariable + this._export = _export + this._imageRef = _imageRef + this._label = _label + this._platform = _platform + this._publish = _publish + this._shellEndpoint = _shellEndpoint + this._stderr = _stderr + this._stdout = _stdout + this._sync = _sync + this._user = _user + this._workdir = _workdir + } + + /** + * A unique identifier for this container. + */ + async id(): Promise { + if (this._id) { + return this._id + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "id", + }, + ], + this.client + ) + + + return response + } + + /** + * Turn the container into a Service. + * + * Be sure to set any exposed ports before this conversion. + */ + asService(): Service { + return new Service({ + queryTree: [ + ...this._queryTree, + { + operation: "asService", + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Returns a File representing the container serialized to a tarball. + * @param opts.platformVariants Identifiers for other platform specific containers. + * Used for multi-platform image. + * @param opts.forcedCompression Force each layer of the image to use the specified compression algorithm. + * If this is unset, then if a layer already has a compressed blob in the engine's + * cache, that will be used (this can result in a mix of compression algorithms for + * different layers). If this is unset and a layer has no compressed blob in the + * engine's cache, then it will be compressed using Gzip. + * @param opts.mediaTypes Use the specified media types for the image's layers. Defaults to OCI, which + * is largely compatible with most recent container runtimes, but Docker may be needed + * for older runtimes without OCI support. + */ + asTarball(opts?: ContainerAsTarballOpts): File { + const metadata: Metadata = { + forcedCompression: { is_enum: true }, + mediaTypes: { is_enum: true }, + } + + return new File({ + queryTree: [ + ...this._queryTree, + { + operation: "asTarball", + args: { ...opts, __metadata: metadata }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Initializes this container from a Dockerfile build. + * @param context Directory context used by the Dockerfile. + * @param opts.dockerfile Path to the Dockerfile to use. + * + * Default: './Dockerfile'. + * @param opts.buildArgs Additional build arguments. + * @param opts.target Target build stage to build. + * @param opts.secrets Secrets to pass to the build. + * + * They will be mounted at /run/secrets/[secret-name] in the build container + * + * They can be accessed in the Dockerfile using the "secret" mount type + * and mount path /run/secrets/[secret-name] + * e.g. RUN --mount=type=secret,id=my-secret curl url?token=$(cat /run/secrets/my-secret)" + */ + build(context: Directory, opts?: ContainerBuildOpts): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "build", + args: { context, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves default arguments for future commands. + */ + async defaultArgs(): Promise { + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "defaultArgs", + }, + ], + this.client + ) + + + return response + } + + /** + * Retrieves a directory at the given path. + * + * Mounts are included. + * @param path The path of the directory to retrieve (e.g., "./src"). + */ + directory(path: string): Directory { + return new Directory({ + queryTree: [ + ...this._queryTree, + { + operation: "directory", + args: { path }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves entrypoint to be prepended to the arguments of all commands. + */ + async entrypoint(): Promise { + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "entrypoint", + }, + ], + this.client + ) + + + return response + } + + /** + * Retrieves the value of the specified environment variable. + * @param name The name of the environment variable to retrieve (e.g., "PATH"). + */ + async envVariable(name: string): Promise { + if (this._envVariable) { + return this._envVariable + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "envVariable", + args: { name }, + }, + ], + this.client + ) + + + return response + } + + /** + * Retrieves the list of environment variables passed to commands. + */ + async envVariables(): Promise { + type envVariables = { + name: string + value: string + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "envVariables", + }, + { + operation: "name value" + }, + ], + this.client + ) + + + return response.map( + (r) => new EnvVariable( + { + queryTree: this.queryTree, + host: this.clientHost, + sessionToken: this.sessionToken, + }, + r.name, + r.value, + ) + ) + } + + /** + * EXPERIMENTAL API! Subject to change/removal at any time. + * + * experimentalWithAllGPUs configures all available GPUs on the host to be accessible to this container. + * This currently works for Nvidia devices only. + */ + experimentalWithAllGPUs(): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "experimentalWithAllGPUs", + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * EXPERIMENTAL API! Subject to change/removal at any time. + * + * experimentalWithGPU configures the provided list of devices to be accesible to this container. + * This currently works for Nvidia devices only. + */ + experimentalWithGPU(devices: string[]): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "experimentalWithGPU", + args: { devices }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Writes the container as an OCI tarball to the destination file path on the host for the specified platform variants. + * + * Return true on success. + * It can also publishes platform variants. + * @param path Host's destination path (e.g., "./tarball"). + * Path can be relative to the engine's workdir or absolute. + * @param opts.platformVariants Identifiers for other platform specific containers. + * Used for multi-platform image. + * @param opts.forcedCompression Force each layer of the exported image to use the specified compression algorithm. + * If this is unset, then if a layer already has a compressed blob in the engine's + * cache, that will be used (this can result in a mix of compression algorithms for + * different layers). If this is unset and a layer has no compressed blob in the + * engine's cache, then it will be compressed using Gzip. + * @param opts.mediaTypes Use the specified media types for the exported image's layers. Defaults to OCI, which + * is largely compatible with most recent container runtimes, but Docker may be needed + * for older runtimes without OCI support. + */ + async export(path: string, opts?: ContainerExportOpts): Promise { + if (this._export) { + return this._export + } + + const metadata: Metadata = { + forcedCompression: { is_enum: true }, + mediaTypes: { is_enum: true }, + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "export", + args: { path, ...opts, __metadata: metadata }, + }, + ], + this.client + ) + + + return response + } + + /** + * Retrieves the list of exposed ports. + * + * This includes ports already exposed by the image, even if not + * explicitly added with dagger. + */ + async exposedPorts(): Promise { + type exposedPorts = { + description: string + port: number + protocol: NetworkProtocol + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "exposedPorts", + }, + { + operation: "description port protocol" + }, + ], + this.client + ) + + + return response.map( + (r) => new Port( + { + queryTree: this.queryTree, + host: this.clientHost, + sessionToken: this.sessionToken, + }, + r.description, + r.port, + r.protocol, + ) + ) + } + + /** + * Retrieves a file at the given path. + * + * Mounts are included. + * @param path The path of the file to retrieve (e.g., "./README.md"). + */ + file(path: string): File { + return new File({ + queryTree: [ + ...this._queryTree, + { + operation: "file", + args: { path }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Initializes this container from a pulled base image. + * @param address Image's address from its registry. + * + * Formatted as [host]/[user]/[repo]:[tag] (e.g., "docker.io/dagger/dagger:main"). + */ + from(address: string): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "from", + args: { address }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * The unique image reference which can only be retrieved immediately after the 'Container.From' call. + */ + async imageRef(): Promise { + if (this._imageRef) { + return this._imageRef + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "imageRef", + }, + ], + this.client + ) + + + return response + } + + /** + * Reads the container from an OCI tarball. + * + * NOTE: this involves unpacking the tarball to an OCI store on the host at + * $XDG_CACHE_DIR/dagger/oci. This directory can be removed whenever you like. + * @param source File to read the container from. + * @param opts.tag Identifies the tag to import from the archive, if the archive bundles + * multiple tags. + */ + import_(source: File, opts?: ContainerImportOpts): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "import", + args: { source, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves the value of the specified label. + */ + async label(name: string): Promise { + if (this._label) { + return this._label + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "label", + args: { name }, + }, + ], + this.client + ) + + + return response + } + + /** + * Retrieves the list of labels passed to container. + */ + async labels(): Promise { + type labels = { + name: string + value: string + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "labels", + }, + { + operation: "name value" + }, + ], + this.client + ) + + + return response.map( + (r) => new Label( + { + queryTree: this.queryTree, + host: this.clientHost, + sessionToken: this.sessionToken, + }, + r.name, + r.value, + ) + ) + } + + /** + * Retrieves the list of paths where a directory is mounted. + */ + async mounts(): Promise { + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "mounts", + }, + ], + this.client + ) + + + return response + } + + /** + * Creates a named sub-pipeline + * @param name Pipeline name. + * @param opts.description Pipeline description. + * @param opts.labels Pipeline labels. + */ + pipeline(name: string, opts?: ContainerPipelineOpts): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "pipeline", + args: { name, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * The platform this container executes and publishes as. + */ + async platform(): Promise { + if (this._platform) { + return this._platform + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "platform", + }, + ], + this.client + ) + + + return response + } + + /** + * Publishes this container as a new image to the specified address. + * + * Publish returns a fully qualified ref. + * It can also publish platform variants. + * @param address Registry's address to publish the image to. + * + * Formatted as [host]/[user]/[repo]:[tag] (e.g. "docker.io/dagger/dagger:main"). + * @param opts.platformVariants Identifiers for other platform specific containers. + * Used for multi-platform image. + * @param opts.forcedCompression Force each layer of the published image to use the specified compression algorithm. + * If this is unset, then if a layer already has a compressed blob in the engine's + * cache, that will be used (this can result in a mix of compression algorithms for + * different layers). If this is unset and a layer has no compressed blob in the + * engine's cache, then it will be compressed using Gzip. + * @param opts.mediaTypes Use the specified media types for the published image's layers. Defaults to OCI, which + * is largely compatible with most recent registries, but Docker may be needed for older + * registries without OCI support. + */ + async publish(address: string, opts?: ContainerPublishOpts): Promise { + if (this._publish) { + return this._publish + } + + const metadata: Metadata = { + forcedCompression: { is_enum: true }, + mediaTypes: { is_enum: true }, + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "publish", + args: { address, ...opts, __metadata: metadata }, + }, + ], + this.client + ) + + + return response + } + + /** + * Retrieves this container's root filesystem. Mounts are not included. + */ + rootfs(): Directory { + return new Directory({ + queryTree: [ + ...this._queryTree, + { + operation: "rootfs", + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Return a websocket endpoint that, if connected to, will start the container with a TTY streamed + * over the websocket. + * + * Primarily intended for internal use with the dagger CLI. + */ + async shellEndpoint(): Promise { + if (this._shellEndpoint) { + return this._shellEndpoint + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "shellEndpoint", + }, + ], + this.client + ) + + + return response + } + + /** + * The error stream of the last executed command. + * + * Will execute default command if none is set, or error if there's no default. + */ + async stderr(): Promise { + if (this._stderr) { + return this._stderr + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "stderr", + }, + ], + this.client + ) + + + return response + } + + /** + * The output stream of the last executed command. + * + * Will execute default command if none is set, or error if there's no default. + */ + async stdout(): Promise { + if (this._stdout) { + return this._stdout + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "stdout", + }, + ], + this.client + ) + + + return response + } + + /** + * Forces evaluation of the pipeline in the engine. + * + * It doesn't run the default command if no exec has been set. + */ + async sync(): Promise { + await computeQuery( + [ + ...this._queryTree, + { + operation: "sync", + }, + ], + this.client + ) + + return this + } + + /** + * Retrieves the user to be set for all commands. + */ + async user(): Promise { + if (this._user) { + return this._user + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "user", + }, + ], + this.client + ) + + + return response + } + + /** + * Configures default arguments for future commands. + * @param opts.args Arguments to prepend to future executions (e.g., ["-v", "--no-cache"]). + */ + withDefaultArgs(opts?: ContainerWithDefaultArgsOpts): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withDefaultArgs", + args: { ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this container plus a directory written at the given path. + * @param path Location of the written directory (e.g., "/tmp/directory"). + * @param directory Identifier of the directory to write + * @param opts.exclude Patterns to exclude in the written directory (e.g., ["node_modules/**", ".gitignore", ".git/"]). + * @param opts.include Patterns to include in the written directory (e.g., ["*.go", "go.mod", "go.sum"]). + * @param opts.owner A user:group to set for the directory and its contents. + * + * The user and group can either be an ID (1000:1000) or a name (foo:bar). + * + * If the group is omitted, it defaults to the same as the user. + */ + withDirectory(path: string, directory: Directory, opts?: ContainerWithDirectoryOpts): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withDirectory", + args: { path, directory, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this container but with a different command entrypoint. + * @param args Entrypoint to use for future executions (e.g., ["go", "run"]). + */ + withEntrypoint(args: string[]): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withEntrypoint", + args: { args }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this container plus the given environment variable. + * @param name The name of the environment variable (e.g., "HOST"). + * @param value The value of the environment variable. (e.g., "localhost"). + * @param opts.expand Replace ${VAR} or $VAR in the value according to the current environment + * variables defined in the container (e.g., "/opt/bin:$PATH"). + */ + withEnvVariable(name: string, value: string, opts?: ContainerWithEnvVariableOpts): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withEnvVariable", + args: { name, value, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this container after executing the specified command inside it. + * @param args Command to run instead of the container's default command (e.g., ["run", "main.go"]). + * + * If empty, the container's default command is used. + * @param opts.skipEntrypoint If the container has an entrypoint, ignore it for args rather than using it to wrap them. + * @param opts.stdin Content to write to the command's standard input before closing (e.g., "Hello world"). + * @param opts.redirectStdout Redirect the command's standard output to a file in the container (e.g., "/tmp/stdout"). + * @param opts.redirectStderr Redirect the command's standard error to a file in the container (e.g., "/tmp/stderr"). + * @param opts.experimentalPrivilegedNesting Provides dagger access to the executed command. + * + * Do not use this option unless you trust the command being executed. + * The command being executed WILL BE GRANTED FULL ACCESS TO YOUR HOST FILESYSTEM. + * @param opts.insecureRootCapabilities Execute the command with all root capabilities. This is similar to running a command + * with "sudo" or executing `docker run` with the `--privileged` flag. Containerization + * does not provide any security guarantees when using this option. It should only be used + * when absolutely necessary and only with trusted commands. + */ + withExec(args: string[], opts?: ContainerWithExecOpts): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withExec", + args: { args, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Expose a network port. + * + * Exposed ports serve two purposes: + * - For health checks and introspection, when running services + * - For setting the EXPOSE OCI field when publishing the container + * @param port Port number to expose + * @param opts.protocol Transport layer network protocol + * @param opts.description Optional port description + */ + withExposedPort(port: number, opts?: ContainerWithExposedPortOpts): Container { + const metadata: Metadata = { + protocol: { is_enum: true }, + } + + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withExposedPort", + args: { port, ...opts, __metadata: metadata }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this container plus the contents of the given file copied to the given path. + * @param path Location of the copied file (e.g., "/tmp/file.txt"). + * @param source Identifier of the file to copy. + * @param opts.permissions Permission given to the copied file (e.g., 0600). + * + * Default: 0644. + * @param opts.owner A user:group to set for the file. + * + * The user and group can either be an ID (1000:1000) or a name (foo:bar). + * + * If the group is omitted, it defaults to the same as the user. + */ + withFile(path: string, source: File, opts?: ContainerWithFileOpts): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withFile", + args: { path, source, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Indicate that subsequent operations should be featured more prominently in + * the UI. + */ + withFocus(): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withFocus", + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this container plus the given label. + * @param name The name of the label (e.g., "org.opencontainers.artifact.created"). + * @param value The value of the label (e.g., "2023-01-01T00:00:00Z"). + */ + withLabel(name: string, value: string): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withLabel", + args: { name, value }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this container plus a cache volume mounted at the given path. + * @param path Location of the cache directory (e.g., "/cache/node_modules"). + * @param cache Identifier of the cache volume to mount. + * @param opts.source Identifier of the directory to use as the cache volume's root. + * @param opts.sharing Sharing mode of the cache volume. + * @param opts.owner A user:group to set for the mounted cache directory. + * + * Note that this changes the ownership of the specified mount along with the + * initial filesystem provided by source (if any). It does not have any effect + * if/when the cache has already been created. + * + * The user and group can either be an ID (1000:1000) or a name (foo:bar). + * + * If the group is omitted, it defaults to the same as the user. + */ + withMountedCache(path: string, cache: CacheVolume, opts?: ContainerWithMountedCacheOpts): Container { + const metadata: Metadata = { + sharing: { is_enum: true }, + } + + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withMountedCache", + args: { path, cache, ...opts, __metadata: metadata }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this container plus a directory mounted at the given path. + * @param path Location of the mounted directory (e.g., "/mnt/directory"). + * @param source Identifier of the mounted directory. + * @param opts.owner A user:group to set for the mounted directory and its contents. + * + * The user and group can either be an ID (1000:1000) or a name (foo:bar). + * + * If the group is omitted, it defaults to the same as the user. + */ + withMountedDirectory(path: string, source: Directory, opts?: ContainerWithMountedDirectoryOpts): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withMountedDirectory", + args: { path, source, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this container plus a file mounted at the given path. + * @param path Location of the mounted file (e.g., "/tmp/file.txt"). + * @param source Identifier of the mounted file. + * @param opts.owner A user or user:group to set for the mounted file. + * + * The user and group can either be an ID (1000:1000) or a name (foo:bar). + * + * If the group is omitted, it defaults to the same as the user. + */ + withMountedFile(path: string, source: File, opts?: ContainerWithMountedFileOpts): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withMountedFile", + args: { path, source, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this container plus a secret mounted into a file at the given path. + * @param path Location of the secret file (e.g., "/tmp/secret.txt"). + * @param source Identifier of the secret to mount. + * @param opts.owner A user:group to set for the mounted secret. + * + * The user and group can either be an ID (1000:1000) or a name (foo:bar). + * + * If the group is omitted, it defaults to the same as the user. + * @param opts.mode Permission given to the mounted secret (e.g., 0600). + * This option requires an owner to be set to be active. + * + * Default: 0400. + */ + withMountedSecret(path: string, source: Secret, opts?: ContainerWithMountedSecretOpts): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withMountedSecret", + args: { path, source, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this container plus a temporary directory mounted at the given path. + * @param path Location of the temporary directory (e.g., "/tmp/temp_dir"). + */ + withMountedTemp(path: string): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withMountedTemp", + args: { path }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this container plus a new file written at the given path. + * @param path Location of the written file (e.g., "/tmp/file.txt"). + * @param opts.contents Content of the file to write (e.g., "Hello world!"). + * @param opts.permissions Permission given to the written file (e.g., 0600). + * + * Default: 0644. + * @param opts.owner A user:group to set for the file. + * + * The user and group can either be an ID (1000:1000) or a name (foo:bar). + * + * If the group is omitted, it defaults to the same as the user. + */ + withNewFile(path: string, opts?: ContainerWithNewFileOpts): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withNewFile", + args: { path, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this container with a registry authentication for a given address. + * @param address Registry's address to bind the authentication to. + * Formatted as [host]/[user]/[repo]:[tag] (e.g. docker.io/dagger/dagger:main). + * @param username The username of the registry's account (e.g., "Dagger"). + * @param secret The API key, password or token to authenticate to this registry. + */ + withRegistryAuth(address: string, username: string, secret: Secret): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withRegistryAuth", + args: { address, username, secret }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Initializes this container from this DirectoryID. + */ + withRootfs(directory: Directory): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withRootfs", + args: { directory }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this container plus an env variable containing the given secret. + * @param name The name of the secret variable (e.g., "API_SECRET"). + * @param secret The identifier of the secret value. + */ + withSecretVariable(name: string, secret: Secret): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withSecretVariable", + args: { name, secret }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Establish a runtime dependency on a service. + * + * The service will be started automatically when needed and detached when it is + * no longer needed, executing the default command if none is set. + * + * The service will be reachable from the container via the provided hostname alias. + * + * The service dependency will also convey to any files or directories produced by the container. + * @param alias A name that can be used to reach the service from the container + * @param service Identifier of the service container + */ + withServiceBinding(alias: string, service: Service): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withServiceBinding", + args: { alias, service }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this container plus a socket forwarded to the given Unix socket path. + * @param path Location of the forwarded Unix socket (e.g., "/tmp/socket"). + * @param source Identifier of the socket to forward. + * @param opts.owner A user:group to set for the mounted socket. + * + * The user and group can either be an ID (1000:1000) or a name (foo:bar). + * + * If the group is omitted, it defaults to the same as the user. + */ + withUnixSocket(path: string, source: Socket, opts?: ContainerWithUnixSocketOpts): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withUnixSocket", + args: { path, source, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this container with a different command user. + * @param name The user to set (e.g., "root"). + */ + withUser(name: string): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withUser", + args: { name }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this container with a different working directory. + * @param path The path to set as the working directory (e.g., "/app"). + */ + withWorkdir(path: string): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withWorkdir", + args: { path }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this container minus the given environment variable. + * @param name The name of the environment variable (e.g., "HOST"). + */ + withoutEnvVariable(name: string): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withoutEnvVariable", + args: { name }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Unexpose a previously exposed port. + * @param port Port number to unexpose + * @param opts.protocol Port protocol to unexpose + */ + withoutExposedPort(port: number, opts?: ContainerWithoutExposedPortOpts): Container { + const metadata: Metadata = { + protocol: { is_enum: true }, + } + + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withoutExposedPort", + args: { port, ...opts, __metadata: metadata }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Indicate that subsequent operations should not be featured more prominently + * in the UI. + * + * This is the initial state of all containers. + */ + withoutFocus(): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withoutFocus", + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this container minus the given environment label. + * @param name The name of the label to remove (e.g., "org.opencontainers.artifact.created"). + */ + withoutLabel(name: string): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withoutLabel", + args: { name }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this container after unmounting everything at the given path. + * @param path Location of the cache directory (e.g., "/cache/node_modules"). + */ + withoutMount(path: string): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withoutMount", + args: { path }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this container without the registry authentication of a given address. + * @param address Registry's address to remove the authentication from. + * Formatted as [host]/[user]/[repo]:[tag] (e.g. docker.io/dagger/dagger:main). + */ + withoutRegistryAuth(address: string): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withoutRegistryAuth", + args: { address }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this container with a previously added Unix socket removed. + * @param path Location of the socket to remove (e.g., "/tmp/socket"). + */ + withoutUnixSocket(path: string): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withoutUnixSocket", + args: { path }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves the working directory for all commands. + */ + async workdir(): Promise { + if (this._workdir) { + return this._workdir + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "workdir", + }, + ], + this.client + ) + + + return response + } + + /** + * Call the provided function with current Container. + * + * This is useful for reusability and readability by not breaking the calling chain. + */ + with(arg: (param: Container) => Container) { + return arg(this) + } +} + + + + + +/** + * A directory. + */ +export class Directory extends BaseClient { + private readonly _id?: DirectoryID = undefined + private readonly _export?: boolean = undefined + private readonly _sync?: DirectoryID = undefined + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + _id?: DirectoryID, + _export?: boolean, + _sync?: DirectoryID, + ) { + super(parent) + + this._id = _id + this._export = _export + this._sync = _sync + } + + /** + * The content-addressed identifier of the directory. + */ + async id(): Promise { + if (this._id) { + return this._id + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "id", + }, + ], + this.client + ) + + + return response + } + + /** + * Load the directory as a Dagger module + * @param opts.sourceSubpath An optional subpath of the directory which contains the module's source + * code. + * + * This is needed when the module code is in a subdirectory but requires + * parent directories to be loaded in order to execute. For example, the + * module source code may need a go.mod, project.toml, package.json, etc. file + * from a parent directory. + * + * If not set, the module source code is loaded from the root of the + * directory. + */ + asModule(opts?: DirectoryAsModuleOpts): Module_ { + return new Module_({ + queryTree: [ + ...this._queryTree, + { + operation: "asModule", + args: { ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Gets the difference between this directory and an another directory. + * @param other Identifier of the directory to compare. + */ + diff(other: Directory): Directory { + return new Directory({ + queryTree: [ + ...this._queryTree, + { + operation: "diff", + args: { other }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves a directory at the given path. + * @param path Location of the directory to retrieve (e.g., "/src"). + */ + directory(path: string): Directory { + return new Directory({ + queryTree: [ + ...this._queryTree, + { + operation: "directory", + args: { path }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Builds a new Docker container from this directory. + * @param opts.dockerfile Path to the Dockerfile to use (e.g., "frontend.Dockerfile"). + * + * Defaults: './Dockerfile'. + * @param opts.platform The platform to build. + * @param opts.buildArgs Build arguments to use in the build. + * @param opts.target Target build stage to build. + * @param opts.secrets Secrets to pass to the build. + * + * They will be mounted at /run/secrets/[secret-name]. + */ + dockerBuild(opts?: DirectoryDockerBuildOpts): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "dockerBuild", + args: { ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Returns a list of files and directories at the given path. + * @param opts.path Location of the directory to look at (e.g., "/src"). + */ + async entries(opts?: DirectoryEntriesOpts): Promise { + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "entries", + args: { ...opts }, + }, + ], + this.client + ) + + + return response + } + + /** + * Writes the contents of the directory to a path on the host. + * @param path Location of the copied directory (e.g., "logs/"). + */ + async export(path: string): Promise { + if (this._export) { + return this._export + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "export", + args: { path }, + }, + ], + this.client + ) + + + return response + } + + /** + * Retrieves a file at the given path. + * @param path Location of the file to retrieve (e.g., "README.md"). + */ + file(path: string): File { + return new File({ + queryTree: [ + ...this._queryTree, + { + operation: "file", + args: { path }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Returns a list of files and directories that matche the given pattern. + * @param pattern Pattern to match (e.g., "*.md"). + */ + async glob(pattern: string): Promise { + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "glob", + args: { pattern }, + }, + ], + this.client + ) + + + return response + } + + /** + * Creates a named sub-pipeline + * @param name Pipeline name. + * @param opts.description Pipeline description. + * @param opts.labels Pipeline labels. + */ + pipeline(name: string, opts?: DirectoryPipelineOpts): Directory { + return new Directory({ + queryTree: [ + ...this._queryTree, + { + operation: "pipeline", + args: { name, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Force evaluation in the engine. + */ + async sync(): Promise { + await computeQuery( + [ + ...this._queryTree, + { + operation: "sync", + }, + ], + this.client + ) + + return this + } + + /** + * Retrieves this directory plus a directory written at the given path. + * @param path Location of the written directory (e.g., "/src/"). + * @param directory Identifier of the directory to copy. + * @param opts.exclude Exclude artifacts that match the given pattern (e.g., ["node_modules/", ".git*"]). + * @param opts.include Include only artifacts that match the given pattern (e.g., ["app/", "package.*"]). + */ + withDirectory(path: string, directory: Directory, opts?: DirectoryWithDirectoryOpts): Directory { + return new Directory({ + queryTree: [ + ...this._queryTree, + { + operation: "withDirectory", + args: { path, directory, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this directory plus the contents of the given file copied to the given path. + * @param path Location of the copied file (e.g., "/file.txt"). + * @param source Identifier of the file to copy. + * @param opts.permissions Permission given to the copied file (e.g., 0600). + * + * Default: 0644. + */ + withFile(path: string, source: File, opts?: DirectoryWithFileOpts): Directory { + return new Directory({ + queryTree: [ + ...this._queryTree, + { + operation: "withFile", + args: { path, source, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this directory plus a new directory created at the given path. + * @param path Location of the directory created (e.g., "/logs"). + * @param opts.permissions Permission granted to the created directory (e.g., 0777). + * + * Default: 0755. + */ + withNewDirectory(path: string, opts?: DirectoryWithNewDirectoryOpts): Directory { + return new Directory({ + queryTree: [ + ...this._queryTree, + { + operation: "withNewDirectory", + args: { path, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this directory plus a new file written at the given path. + * @param path Location of the written file (e.g., "/file.txt"). + * @param contents Content of the written file (e.g., "Hello world!"). + * @param opts.permissions Permission given to the copied file (e.g., 0600). + * + * Default: 0644. + */ + withNewFile(path: string, contents: string, opts?: DirectoryWithNewFileOpts): Directory { + return new Directory({ + queryTree: [ + ...this._queryTree, + { + operation: "withNewFile", + args: { path, contents, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this directory with all file/dir timestamps set to the given time. + * @param timestamp Timestamp to set dir/files in. + * + * Formatted in seconds following Unix epoch (e.g., 1672531199). + */ + withTimestamps(timestamp: number): Directory { + return new Directory({ + queryTree: [ + ...this._queryTree, + { + operation: "withTimestamps", + args: { timestamp }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this directory with the directory at the given path removed. + * @param path Location of the directory to remove (e.g., ".github/"). + */ + withoutDirectory(path: string): Directory { + return new Directory({ + queryTree: [ + ...this._queryTree, + { + operation: "withoutDirectory", + args: { path }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this directory with the file at the given path removed. + * @param path Location of the file to remove (e.g., "/file.txt"). + */ + withoutFile(path: string): Directory { + return new Directory({ + queryTree: [ + ...this._queryTree, + { + operation: "withoutFile", + args: { path }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Call the provided function with current Directory. + * + * This is useful for reusability and readability by not breaking the calling chain. + */ + with(arg: (param: Directory) => Directory) { + return arg(this) + } +} + + + +/** + * A simple key value object that represents an environment variable. + */ +export class EnvVariable extends BaseClient { + private readonly _name?: string = undefined + private readonly _value?: string = undefined + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + _name?: string, + _value?: string, + ) { + super(parent) + + this._name = _name + this._value = _value + } + + /** + * The environment variable name. + */ + async name(): Promise { + if (this._name) { + return this._name + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "name", + }, + ], + this.client + ) + + + return response + } + + /** + * The environment variable value. + */ + async value(): Promise { + if (this._value) { + return this._value + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "value", + }, + ], + this.client + ) + + + return response + } +} + +/** + * A definition of a field on a custom object defined in a Module. + * A field on an object has a static value, as opposed to a function on an + * object whose value is computed by invoking code (and can accept arguments). + */ +export class FieldTypeDef extends BaseClient { + private readonly _description?: string = undefined + private readonly _name?: string = undefined + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + _description?: string, + _name?: string, + ) { + super(parent) + + this._description = _description + this._name = _name + } + + /** + * A doc string for the field, if any + */ + async description(): Promise { + if (this._description) { + return this._description + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "description", + }, + ], + this.client + ) + + + return response + } + + /** + * The name of the field in the object + */ + async name(): Promise { + if (this._name) { + return this._name + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "name", + }, + ], + this.client + ) + + + return response + } + + /** + * The type of the field + */ + typeDef(): TypeDef { + return new TypeDef({ + queryTree: [ + ...this._queryTree, + { + operation: "typeDef", + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } +} + +/** + * A file. + */ +export class File extends BaseClient { + private readonly _id?: FileID = undefined + private readonly _contents?: string = undefined + private readonly _export?: boolean = undefined + private readonly _size?: number = undefined + private readonly _sync?: FileID = undefined + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + _id?: FileID, + _contents?: string, + _export?: boolean, + _size?: number, + _sync?: FileID, + ) { + super(parent) + + this._id = _id + this._contents = _contents + this._export = _export + this._size = _size + this._sync = _sync + } + + /** + * Retrieves the content-addressed identifier of the file. + */ + async id(): Promise { + if (this._id) { + return this._id + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "id", + }, + ], + this.client + ) + + + return response + } + + /** + * Retrieves the contents of the file. + */ + async contents(): Promise { + if (this._contents) { + return this._contents + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "contents", + }, + ], + this.client + ) + + + return response + } + + /** + * Writes the file to a file path on the host. + * @param path Location of the written directory (e.g., "output.txt"). + * @param opts.allowParentDirPath If allowParentDirPath is true, the path argument can be a directory path, in which case + * the file will be created in that directory. + */ + async export(path: string, opts?: FileExportOpts): Promise { + if (this._export) { + return this._export + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "export", + args: { path, ...opts }, + }, + ], + this.client + ) + + + return response + } + + /** + * Gets the size of the file, in bytes. + */ + async size(): Promise { + if (this._size) { + return this._size + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "size", + }, + ], + this.client + ) + + + return response + } + + /** + * Force evaluation in the engine. + */ + async sync(): Promise { + await computeQuery( + [ + ...this._queryTree, + { + operation: "sync", + }, + ], + this.client + ) + + return this + } + + /** + * Retrieves this file with its created/modified timestamps set to the given time. + * @param timestamp Timestamp to set dir/files in. + * + * Formatted in seconds following Unix epoch (e.g., 1672531199). + */ + withTimestamps(timestamp: number): File { + return new File({ + queryTree: [ + ...this._queryTree, + { + operation: "withTimestamps", + args: { timestamp }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Call the provided function with current File. + * + * This is useful for reusability and readability by not breaking the calling chain. + */ + with(arg: (param: File) => File) { + return arg(this) + } +} + + + + + +/** + * Function represents a resolver provided by a Module. + * + * A function always evaluates against a parent object and is given a set of + * named arguments. + */ +export class Function_ extends BaseClient { + private readonly _id?: FunctionID = undefined + private readonly _description?: string = undefined + private readonly _name?: string = undefined + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + _id?: FunctionID, + _description?: string, + _name?: string, + ) { + super(parent) + + this._id = _id + this._description = _description + this._name = _name + } + + /** + * The ID of the function + */ + async id(): Promise { + if (this._id) { + return this._id + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "id", + }, + ], + this.client + ) + + + return response + } + + /** + * Arguments accepted by this function, if any + */ + async args(): Promise { + type args = { + id: FunctionArgID + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "args", + }, + { + operation: "id" + }, + ], + this.client + ) + + + return response.map( + (r) => new FunctionArg( + { + queryTree: this.queryTree, + host: this.clientHost, + sessionToken: this.sessionToken, + }, + r.id, + ) + ) + } + + /** + * A doc string for the function, if any + */ + async description(): Promise { + if (this._description) { + return this._description + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "description", + }, + ], + this.client + ) + + + return response + } + + /** + * The name of the function + */ + async name(): Promise { + if (this._name) { + return this._name + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "name", + }, + ], + this.client + ) + + + return response + } + + /** + * The type returned by this function + */ + returnType(): TypeDef { + return new TypeDef({ + queryTree: [ + ...this._queryTree, + { + operation: "returnType", + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Returns the function with the provided argument + * @param name The name of the argument + * @param typeDef The type of the argument + * @param opts.description A doc string for the argument, if any + * @param opts.defaultValue A default value to use for this argument if not explicitly set by the caller, if any + */ + withArg(name: string, typeDef: TypeDef, opts?: FunctionWithArgOpts): Function_ { + return new Function_({ + queryTree: [ + ...this._queryTree, + { + operation: "withArg", + args: { name, typeDef, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Returns the function with the doc string + */ + withDescription(description: string): Function_ { + return new Function_({ + queryTree: [ + ...this._queryTree, + { + operation: "withDescription", + args: { description }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Call the provided function with current Function. + * + * This is useful for reusability and readability by not breaking the calling chain. + */ + with(arg: (param: Function_) => Function_) { + return arg(this) + } +} + +/** + * An argument accepted by a function. + * + * This is a specification for an argument at function definition time, not an + * argument passed at function call time. + */ +export class FunctionArg extends BaseClient { + private readonly _id?: FunctionArgID = undefined + private readonly _defaultValue?: JSON = undefined + private readonly _description?: string = undefined + private readonly _name?: string = undefined + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + _id?: FunctionArgID, + _defaultValue?: JSON, + _description?: string, + _name?: string, + ) { + super(parent) + + this._id = _id + this._defaultValue = _defaultValue + this._description = _description + this._name = _name + } + + /** + * The ID of the argument + */ + async id(): Promise { + if (this._id) { + return this._id + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "id", + }, + ], + this.client + ) + + + return response + } + + /** + * A default value to use for this argument when not explicitly set by the caller, if any + */ + async defaultValue(): Promise { + if (this._defaultValue) { + return this._defaultValue + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "defaultValue", + }, + ], + this.client + ) + + + return response + } + + /** + * A doc string for the argument, if any + */ + async description(): Promise { + if (this._description) { + return this._description + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "description", + }, + ], + this.client + ) + + + return response + } + + /** + * The name of the argument + */ + async name(): Promise { + if (this._name) { + return this._name + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "name", + }, + ], + this.client + ) + + + return response + } + + /** + * The type of the argument + */ + typeDef(): TypeDef { + return new TypeDef({ + queryTree: [ + ...this._queryTree, + { + operation: "typeDef", + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } +} + + + + +export class FunctionCall extends BaseClient { + private readonly _name?: string = undefined + private readonly _parent?: JSON = undefined + private readonly _parentName?: string = undefined + private readonly _returnValue?: Void = undefined + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + _name?: string, + _parent?: JSON, + _parentName?: string, + _returnValue?: Void, + ) { + super(parent) + + this._name = _name + this._parent = _parent + this._parentName = _parentName + this._returnValue = _returnValue + } + + /** + * The argument values the function is being invoked with. + */ + async inputArgs(): Promise { + type inputArgs = { + name: string + value: JSON + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "inputArgs", + }, + { + operation: "name value" + }, + ], + this.client + ) + + + return response.map( + (r) => new FunctionCallArgValue( + { + queryTree: this.queryTree, + host: this.clientHost, + sessionToken: this.sessionToken, + }, + r.name, + r.value, + ) + ) + } + + /** + * The name of the function being called. + */ + async name(): Promise { + if (this._name) { + return this._name + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "name", + }, + ], + this.client + ) + + + return response + } + + /** + * The value of the parent object of the function being called. + * If the function is "top-level" to the module, this is always an empty object. + */ + async parent(): Promise { + if (this._parent) { + return this._parent + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "parent", + }, + ], + this.client + ) + + + return response + } + + /** + * The name of the parent object of the function being called. + * If the function is "top-level" to the module, this is the name of the module. + */ + async parentName(): Promise { + if (this._parentName) { + return this._parentName + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "parentName", + }, + ], + this.client + ) + + + return response + } + + /** + * Set the return value of the function call to the provided value. + * The value should be a string of the JSON serialization of the return value. + */ + async returnValue(value: JSON): Promise { + if (this._returnValue) { + return this._returnValue + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "returnValue", + args: { value }, + }, + ], + this.client + ) + + + return response + } +} + + +export class FunctionCallArgValue extends BaseClient { + private readonly _name?: string = undefined + private readonly _value?: JSON = undefined + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + _name?: string, + _value?: JSON, + ) { + super(parent) + + this._name = _name + this._value = _value + } + + /** + * The name of the argument. + */ + async name(): Promise { + if (this._name) { + return this._name + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "name", + }, + ], + this.client + ) + + + return response + } + + /** + * The value of the argument represented as a string of the JSON serialization. + */ + async value(): Promise { + if (this._value) { + return this._value + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "value", + }, + ], + this.client + ) + + + return response + } +} + + + + +export class GeneratedCode extends BaseClient { + private readonly _id?: GeneratedCodeID = undefined + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + _id?: GeneratedCodeID, + ) { + super(parent) + + this._id = _id + } + async id(): Promise { + if (this._id) { + return this._id + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "id", + }, + ], + this.client + ) + + + return response + } + + /** + * The directory containing the generated code + */ + code(): Directory { + return new Directory({ + queryTree: [ + ...this._queryTree, + { + operation: "code", + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * List of paths to mark generated in version control (i.e. .gitattributes) + */ + async vcsGeneratedPaths(): Promise { + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "vcsGeneratedPaths", + }, + ], + this.client + ) + + + return response + } + + /** + * List of paths to ignore in version control (i.e. .gitignore) + */ + async vcsIgnoredPaths(): Promise { + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "vcsIgnoredPaths", + }, + ], + this.client + ) + + + return response + } + + /** + * Set the list of paths to mark generated in version control + */ + withVCSGeneratedPaths(paths: string[]): GeneratedCode { + return new GeneratedCode({ + queryTree: [ + ...this._queryTree, + { + operation: "withVCSGeneratedPaths", + args: { paths }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Set the list of paths to ignore in version control + */ + withVCSIgnoredPaths(paths: string[]): GeneratedCode { + return new GeneratedCode({ + queryTree: [ + ...this._queryTree, + { + operation: "withVCSIgnoredPaths", + args: { paths }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Call the provided function with current GeneratedCode. + * + * This is useful for reusability and readability by not breaking the calling chain. + */ + with(arg: (param: GeneratedCode) => GeneratedCode) { + return arg(this) + } +} + + + +/** + * A git ref (tag, branch or commit). + */ +export class GitRef extends BaseClient { + private readonly _commit?: string = undefined + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + _commit?: string, + ) { + super(parent) + + this._commit = _commit + } + + /** + * The resolved commit id at this ref. + */ + async commit(): Promise { + if (this._commit) { + return this._commit + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "commit", + }, + ], + this.client + ) + + + return response + } + + /** + * The filesystem tree at this ref. + */ + tree(opts?: GitRefTreeOpts): Directory { + return new Directory({ + queryTree: [ + ...this._queryTree, + { + operation: "tree", + args: { ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } +} + +/** + * A git repository. + */ +export class GitRepository extends BaseClient { + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + ) { + super(parent) + + } + + /** + * Returns details on one branch. + * @param name Branch's name (e.g., "main"). + */ + branch(name: string): GitRef { + return new GitRef({ + queryTree: [ + ...this._queryTree, + { + operation: "branch", + args: { name }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Returns details on one commit. + * @param id Identifier of the commit (e.g., "b6315d8f2810962c601af73f86831f6866ea798b"). + */ + commit(id: string): GitRef { + return new GitRef({ + queryTree: [ + ...this._queryTree, + { + operation: "commit", + args: { id }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Returns details on one tag. + * @param name Tag's name (e.g., "v0.3.9"). + */ + tag(name: string): GitRef { + return new GitRef({ + queryTree: [ + ...this._queryTree, + { + operation: "tag", + args: { name }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } +} + +/** + * Information about the host execution environment. + */ +export class Host extends BaseClient { + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + ) { + super(parent) + + } + + /** + * Accesses a directory on the host. + * @param path Location of the directory to access (e.g., "."). + * @param opts.exclude Exclude artifacts that match the given pattern (e.g., ["node_modules/", ".git*"]). + * @param opts.include Include only artifacts that match the given pattern (e.g., ["app/", "package.*"]). + */ + directory(path: string, opts?: HostDirectoryOpts): Directory { + return new Directory({ + queryTree: [ + ...this._queryTree, + { + operation: "directory", + args: { path, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Accesses a file on the host. + * @param path Location of the file to retrieve (e.g., "README.md"). + */ + file(path: string): File { + return new File({ + queryTree: [ + ...this._queryTree, + { + operation: "file", + args: { path }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Creates a service that forwards traffic to a specified address via the host. + * @param ports Ports to expose via the service, forwarding through the host network. + * + * If a port's frontend is unspecified or 0, it defaults to the same as the + * backend port. + * + * An empty set of ports is not valid; an error will be returned. + * @param opts.host Upstream host to forward traffic to. + */ + service(ports: PortForward[], opts?: HostServiceOpts): Service { + return new Service({ + queryTree: [ + ...this._queryTree, + { + operation: "service", + args: { ports, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Sets a secret given a user-defined name and the file path on the host, and returns the secret. + * The file is limited to a size of 512000 bytes. + * @param name The user defined name for this secret. + * @param path Location of the file to set as a secret. + */ + setSecretFile(name: string, path: string): Secret { + return new Secret({ + queryTree: [ + ...this._queryTree, + { + operation: "setSecretFile", + args: { name, path }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Creates a tunnel that forwards traffic from the host to a service. + * @param service Service to send traffic from the tunnel. + * @param opts.native Map each service port to the same port on the host, as if the service were + * running natively. + * + * Note: enabling may result in port conflicts. + * @param opts.ports Configure explicit port forwarding rules for the tunnel. + * + * If a port's frontend is unspecified or 0, a random port will be chosen by + * the host. + * + * If no ports are given, all of the service's ports are forwarded. If native + * is true, each port maps to the same port on the host. If native is false, + * each port maps to a random port chosen by the host. + * + * If ports are given and native is true, the ports are additive. + */ + tunnel(service: Service, opts?: HostTunnelOpts): Service { + return new Service({ + queryTree: [ + ...this._queryTree, + { + operation: "tunnel", + args: { service, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Accesses a Unix socket on the host. + * @param path Location of the Unix socket (e.g., "/var/run/docker.sock"). + */ + unixSocket(path: string): Socket { + return new Socket({ + queryTree: [ + ...this._queryTree, + { + operation: "unixSocket", + args: { path }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } +} + + + + + + + + + + + +/** + * A simple key value object that represents a label. + */ +export class Label extends BaseClient { + private readonly _name?: string = undefined + private readonly _value?: string = undefined + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + _name?: string, + _value?: string, + ) { + super(parent) + + this._name = _name + this._value = _value + } + + /** + * The label name. + */ + async name(): Promise { + if (this._name) { + return this._name + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "name", + }, + ], + this.client + ) + + + return response + } + + /** + * The label value. + */ + async value(): Promise { + if (this._value) { + return this._value + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "value", + }, + ], + this.client + ) + + + return response + } +} + +/** + * A definition of a list type in a Module. + */ +export class ListTypeDef extends BaseClient { + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + ) { + super(parent) + + } + + /** + * The type of the elements in the list + */ + elementTypeDef(): TypeDef { + return new TypeDef({ + queryTree: [ + ...this._queryTree, + { + operation: "elementTypeDef", + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } +} + + +export class Module_ extends BaseClient { + private readonly _id?: ModuleID = undefined + private readonly _description?: string = undefined + private readonly _name?: string = undefined + private readonly _sdk?: string = undefined + private readonly _serve?: Void = undefined + private readonly _sourceDirectorySubPath?: string = undefined + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + _id?: ModuleID, + _description?: string, + _name?: string, + _sdk?: string, + _serve?: Void, + _sourceDirectorySubPath?: string, + ) { + super(parent) + + this._id = _id + this._description = _description + this._name = _name + this._sdk = _sdk + this._serve = _serve + this._sourceDirectorySubPath = _sourceDirectorySubPath + } + + /** + * The ID of the module + */ + async id(): Promise { + if (this._id) { + return this._id + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "id", + }, + ], + this.client + ) + + + return response + } + + /** + * Modules used by this module + */ + async dependencies(): Promise { + type dependencies = { + id: ModuleID + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "dependencies", + }, + { + operation: "id" + }, + ], + this.client + ) + + + return response.map( + (r) => new Module_( + { + queryTree: this.queryTree, + host: this.clientHost, + sessionToken: this.sessionToken, + }, + r.id, + ) + ) + } + + /** + * The dependencies as configured by the module + */ + async dependencyConfig(): Promise { + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "dependencyConfig", + }, + ], + this.client + ) + + + return response + } + + /** + * The doc string of the module, if any + */ + async description(): Promise { + if (this._description) { + return this._description + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "description", + }, + ], + this.client + ) + + + return response + } + + /** + * The code generated by the SDK's runtime + */ + generatedCode(): GeneratedCode { + return new GeneratedCode({ + queryTree: [ + ...this._queryTree, + { + operation: "generatedCode", + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * The name of the module + */ + async name(): Promise { + if (this._name) { + return this._name + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "name", + }, + ], + this.client + ) + + + return response + } + + /** + * Objects served by this module + */ + async objects(): Promise { + type objects = { + id: TypeDefID + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "objects", + }, + { + operation: "id" + }, + ], + this.client + ) + + + return response.map( + (r) => new TypeDef( + { + queryTree: this.queryTree, + host: this.clientHost, + sessionToken: this.sessionToken, + }, + r.id, + ) + ) + } + + /** + * The SDK used by this module. Either a name of a builtin SDK or a module ref pointing to the SDK's implementation. + */ + async sdk(): Promise { + if (this._sdk) { + return this._sdk + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "sdk", + }, + ], + this.client + ) + + + return response + } + + /** + * Serve a module's API in the current session. + * Note: this can only be called once per session. + * In the future, it could return a stream or service to remove the side effect. + */ + async serve(): Promise { + if (this._serve) { + return this._serve + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "serve", + }, + ], + this.client + ) + + + return response + } + + /** + * The directory containing the module's source code + */ + sourceDirectory(): Directory { + return new Directory({ + queryTree: [ + ...this._queryTree, + { + operation: "sourceDirectory", + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * The module's subpath within the source directory + */ + async sourceDirectorySubPath(): Promise { + if (this._sourceDirectorySubPath) { + return this._sourceDirectorySubPath + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "sourceDirectorySubPath", + }, + ], + this.client + ) + + + return response + } + + /** + * This module plus the given Object type and associated functions + */ + withObject(object: TypeDef): Module_ { + return new Module_({ + queryTree: [ + ...this._queryTree, + { + operation: "withObject", + args: { object }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Call the provided function with current Module. + * + * This is useful for reusability and readability by not breaking the calling chain. + */ + with(arg: (param: Module_) => Module_) { + return arg(this) + } +} + +/** + * Static configuration for a module (e.g. parsed contents of dagger.json) + */ +export class ModuleConfig extends BaseClient { + private readonly _name?: string = undefined + private readonly _root?: string = undefined + private readonly _sdk?: string = undefined + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + _name?: string, + _root?: string, + _sdk?: string, + ) { + super(parent) + + this._name = _name + this._root = _root + this._sdk = _sdk + } + + /** + * Modules that this module depends on. + */ + async dependencies(): Promise { + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "dependencies", + }, + ], + this.client + ) + + + return response + } + + /** + * Exclude these file globs when loading the module root. + */ + async exclude(): Promise { + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "exclude", + }, + ], + this.client + ) + + + return response + } + + /** + * Include only these file globs when loading the module root. + */ + async include(): Promise { + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "include", + }, + ], + this.client + ) + + + return response + } + + /** + * The name of the module. + */ + async name(): Promise { + if (this._name) { + return this._name + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "name", + }, + ], + this.client + ) + + + return response + } + + /** + * The root directory of the module's project, which may be above the module source code. + */ + async root(): Promise { + if (this._root) { + return this._root + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "root", + }, + ], + this.client + ) + + + return response + } + + /** + * Either the name of a built-in SDK ('go', 'python', etc.) OR a module reference pointing to the SDK's module implementation. + */ + async sdk(): Promise { + if (this._sdk) { + return this._sdk + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "sdk", + }, + ], + this.client + ) + + + return response + } +} + + + + + +/** + * A definition of a custom object defined in a Module. + */ +export class ObjectTypeDef extends BaseClient { + private readonly _description?: string = undefined + private readonly _name?: string = undefined + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + _description?: string, + _name?: string, + ) { + super(parent) + + this._description = _description + this._name = _name + } + + /** + * The doc string for the object, if any + */ + async description(): Promise { + if (this._description) { + return this._description + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "description", + }, + ], + this.client + ) + + + return response + } + + /** + * Static fields defined on this object, if any + */ + async fields(): Promise { + type fields = { + description: string + name: string + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "fields", + }, + { + operation: "description name" + }, + ], + this.client + ) + + + return response.map( + (r) => new FieldTypeDef( + { + queryTree: this.queryTree, + host: this.clientHost, + sessionToken: this.sessionToken, + }, + r.description, + r.name, + ) + ) + } + + /** + * Functions defined on this object, if any + */ + async functions(): Promise { + type functions = { + id: FunctionID + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "functions", + }, + { + operation: "id" + }, + ], + this.client + ) + + + return response.map( + (r) => new Function_( + { + queryTree: this.queryTree, + host: this.clientHost, + sessionToken: this.sessionToken, + }, + r.id, + ) + ) + } + + /** + * The name of the object + */ + async name(): Promise { + if (this._name) { + return this._name + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "name", + }, + ], + this.client + ) + + + return response + } +} + + + + + +/** + * A port exposed by a container. + */ +export class Port extends BaseClient { + private readonly _description?: string = undefined + private readonly _port?: number = undefined + private readonly _protocol?: NetworkProtocol = undefined + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + _description?: string, + _port?: number, + _protocol?: NetworkProtocol, + ) { + super(parent) + + this._description = _description + this._port = _port + this._protocol = _protocol + } + + /** + * The port description. + */ + async description(): Promise { + if (this._description) { + return this._description + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "description", + }, + ], + this.client + ) + + + return response + } + + /** + * The port number. + */ + async port(): Promise { + if (this._port) { + return this._port + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "port", + }, + ], + this.client + ) + + + return response + } + + /** + * The transport layer network protocol. + */ + async protocol(): Promise { + if (this._protocol) { + return this._protocol + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "protocol", + }, + ], + this.client + ) + + + return response + } +} + + + + +export class Client extends BaseClient { + private readonly _checkVersionCompatibility?: boolean = undefined + private readonly _defaultPlatform?: Platform = undefined + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + _checkVersionCompatibility?: boolean, + _defaultPlatform?: Platform, + ) { + super(parent) + + this._checkVersionCompatibility = _checkVersionCompatibility + this._defaultPlatform = _defaultPlatform + } + + /** + * Constructs a cache volume for a given cache key. + * @param key A string identifier to target this cache volume (e.g., "modules-cache"). + */ + cacheVolume(key: string): CacheVolume { + return new CacheVolume({ + queryTree: [ + ...this._queryTree, + { + operation: "cacheVolume", + args: { key }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Checks if the current Dagger Engine is compatible with an SDK's required version. + * @param version The SDK's required version. + */ + async checkVersionCompatibility(version: string): Promise { + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "checkVersionCompatibility", + args: { version }, + }, + ], + this.client + ) + + + return response + } + + /** + * Creates a scratch container or loads one by ID. + * + * Optional platform argument initializes new containers to execute and publish + * as that platform. Platform defaults to that of the builder's host. + */ + container(opts?: ClientContainerOpts): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "container", + args: { ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * The FunctionCall context that the SDK caller is currently executing in. + * If the caller is not currently executing in a function, this will return + * an error. + */ + currentFunctionCall(): FunctionCall { + return new FunctionCall({ + queryTree: [ + ...this._queryTree, + { + operation: "currentFunctionCall", + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * The module currently being served in the session, if any. + */ + currentModule(): Module_ { + return new Module_({ + queryTree: [ + ...this._queryTree, + { + operation: "currentModule", + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * The default platform of the builder. + */ + async defaultPlatform(): Promise { + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "defaultPlatform", + }, + ], + this.client + ) + + + return response + } + + /** + * Creates an empty directory or loads one by ID. + */ + directory(opts?: ClientDirectoryOpts): Directory { + return new Directory({ + queryTree: [ + ...this._queryTree, + { + operation: "directory", + args: { ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Loads a file by ID. + * @deprecated Use loadFileFromID instead. + */ + file(id: FileID): File { + return new File({ + queryTree: [ + ...this._queryTree, + { + operation: "file", + args: { id }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Create a function. + */ + function_(name: string, returnType: TypeDef): Function_ { + return new Function_({ + queryTree: [ + ...this._queryTree, + { + operation: "function", + args: { name, returnType }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Create a code generation result, given a directory containing the generated + * code. + */ + generatedCode(code: Directory): GeneratedCode { + return new GeneratedCode({ + queryTree: [ + ...this._queryTree, + { + operation: "generatedCode", + args: { code }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Queries a git repository. + * @param url Url of the git repository. + * Can be formatted as https://{host}/{owner}/{repo}, git@{host}:{owner}/{repo} + * Suffix ".git" is optional. + * @param opts.keepGitDir Set to true to keep .git directory. + * @param opts.sshKnownHosts Set SSH known hosts + * @param opts.sshAuthSocket Set SSH auth socket + * @param opts.experimentalServiceHost A service which must be started before the repo is fetched. + */ + git(url: string, opts?: ClientGitOpts): GitRepository { + return new GitRepository({ + queryTree: [ + ...this._queryTree, + { + operation: "git", + args: { url, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Queries the host environment. + */ + host(): Host { + return new Host({ + queryTree: [ + ...this._queryTree, + { + operation: "host", + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Returns a file containing an http remote url content. + * @param url HTTP url to get the content from (e.g., "https://docs.dagger.io"). + * @param opts.experimentalServiceHost A service which must be started before the URL is fetched. + */ + http(url: string, opts?: ClientHttpOpts): File { + return new File({ + queryTree: [ + ...this._queryTree, + { + operation: "http", + args: { url, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Load a CacheVolume from its ID. + */ + loadCacheVolumeFromID(id: CacheVolumeID): CacheVolume { + return new CacheVolume({ + queryTree: [ + ...this._queryTree, + { + operation: "loadCacheVolumeFromID", + args: { id }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Loads a container from an ID. + */ + loadContainerFromID(id: ContainerID): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "loadContainerFromID", + args: { id }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Load a Directory from its ID. + */ + loadDirectoryFromID(id: DirectoryID): Directory { + return new Directory({ + queryTree: [ + ...this._queryTree, + { + operation: "loadDirectoryFromID", + args: { id }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Load a File from its ID. + */ + loadFileFromID(id: FileID): File { + return new File({ + queryTree: [ + ...this._queryTree, + { + operation: "loadFileFromID", + args: { id }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Load a function argument by ID. + */ + loadFunctionArgFromID(id: FunctionArgID): FunctionArg { + return new FunctionArg({ + queryTree: [ + ...this._queryTree, + { + operation: "loadFunctionArgFromID", + args: { id }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Load a function by ID. + */ + loadFunctionFromID(id: FunctionID): Function_ { + return new Function_({ + queryTree: [ + ...this._queryTree, + { + operation: "loadFunctionFromID", + args: { id }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Load a GeneratedCode by ID. + */ + loadGeneratedCodeFromID(id: GeneratedCodeID): GeneratedCode { + return new GeneratedCode({ + queryTree: [ + ...this._queryTree, + { + operation: "loadGeneratedCodeFromID", + args: { id }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Load a module by ID. + */ + loadModuleFromID(id: ModuleID): Module_ { + return new Module_({ + queryTree: [ + ...this._queryTree, + { + operation: "loadModuleFromID", + args: { id }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Load a Secret from its ID. + */ + loadSecretFromID(id: SecretID): Secret { + return new Secret({ + queryTree: [ + ...this._queryTree, + { + operation: "loadSecretFromID", + args: { id }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Loads a service from ID. + */ + loadServiceFromID(id: ServiceID): Service { + return new Service({ + queryTree: [ + ...this._queryTree, + { + operation: "loadServiceFromID", + args: { id }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Load a Socket from its ID. + */ + loadSocketFromID(id: SocketID): Socket { + return new Socket({ + queryTree: [ + ...this._queryTree, + { + operation: "loadSocketFromID", + args: { id }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Load a TypeDef by ID. + */ + loadTypeDefFromID(id: TypeDefID): TypeDef { + return new TypeDef({ + queryTree: [ + ...this._queryTree, + { + operation: "loadTypeDefFromID", + args: { id }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Create a new module. + */ + module_(): Module_ { + return new Module_({ + queryTree: [ + ...this._queryTree, + { + operation: "module", + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Load the static configuration for a module from the given source directory and optional subpath. + */ + moduleConfig(sourceDirectory: Directory, opts?: ClientModuleConfigOpts): ModuleConfig { + return new ModuleConfig({ + queryTree: [ + ...this._queryTree, + { + operation: "moduleConfig", + args: { sourceDirectory, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Creates a named sub-pipeline. + * @param name Pipeline name. + * @param opts.description Pipeline description. + * @param opts.labels Pipeline labels. + */ + pipeline(name: string, opts?: ClientPipelineOpts): Client { + return new Client({ + queryTree: [ + ...this._queryTree, + { + operation: "pipeline", + args: { name, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Loads a secret from its ID. + * @deprecated Use loadSecretFromID instead + */ + secret(id: SecretID): Secret { + return new Secret({ + queryTree: [ + ...this._queryTree, + { + operation: "secret", + args: { id }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Sets a secret given a user defined name to its plaintext and returns the secret. + * The plaintext value is limited to a size of 128000 bytes. + * @param name The user defined name for this secret + * @param plaintext The plaintext of the secret + */ + setSecret(name: string, plaintext: string): Secret { + return new Secret({ + queryTree: [ + ...this._queryTree, + { + operation: "setSecret", + args: { name, plaintext }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Loads a socket by its ID. + * @deprecated Use loadSocketFromID instead. + */ + socket(opts?: ClientSocketOpts): Socket { + return new Socket({ + queryTree: [ + ...this._queryTree, + { + operation: "socket", + args: { ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Create a new TypeDef. + */ + typeDef(): TypeDef { + return new TypeDef({ + queryTree: [ + ...this._queryTree, + { + operation: "typeDef", + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Call the provided function with current Client. + * + * This is useful for reusability and readability by not breaking the calling chain. + */ + with(arg: (param: Client) => Client) { + return arg(this) + } +} + +/** + * A reference to a secret value, which can be handled more safely than the value itself. + */ +export class Secret extends BaseClient { + private readonly _id?: SecretID = undefined + private readonly _plaintext?: string = undefined + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + _id?: SecretID, + _plaintext?: string, + ) { + super(parent) + + this._id = _id + this._plaintext = _plaintext + } + + /** + * The identifier for this secret. + */ + async id(): Promise { + if (this._id) { + return this._id + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "id", + }, + ], + this.client + ) + + + return response + } + + /** + * The value of this secret. + */ + async plaintext(): Promise { + if (this._plaintext) { + return this._plaintext + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "plaintext", + }, + ], + this.client + ) + + + return response + } +} + + + + +export class Service extends BaseClient { + private readonly _id?: ServiceID = undefined + private readonly _endpoint?: string = undefined + private readonly _hostname?: string = undefined + private readonly _start?: ServiceID = undefined + private readonly _stop?: ServiceID = undefined + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + _id?: ServiceID, + _endpoint?: string, + _hostname?: string, + _start?: ServiceID, + _stop?: ServiceID, + ) { + super(parent) + + this._id = _id + this._endpoint = _endpoint + this._hostname = _hostname + this._start = _start + this._stop = _stop + } + + /** + * A unique identifier for this service. + */ + async id(): Promise { + if (this._id) { + return this._id + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "id", + }, + ], + this.client + ) + + + return response + } + + /** + * Retrieves an endpoint that clients can use to reach this container. + * + * If no port is specified, the first exposed port is used. If none exist an error is returned. + * + * If a scheme is specified, a URL is returned. Otherwise, a host:port pair is returned. + * @param opts.port The exposed port number for the endpoint + * @param opts.scheme Return a URL with the given scheme, eg. http for http:// + */ + async endpoint(opts?: ServiceEndpointOpts): Promise { + if (this._endpoint) { + return this._endpoint + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "endpoint", + args: { ...opts }, + }, + ], + this.client + ) + + + return response + } + + /** + * Retrieves a hostname which can be used by clients to reach this container. + */ + async hostname(): Promise { + if (this._hostname) { + return this._hostname + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "hostname", + }, + ], + this.client + ) + + + return response + } + + /** + * Retrieves the list of ports provided by the service. + */ + async ports(): Promise { + type ports = { + description: string + port: number + protocol: NetworkProtocol + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "ports", + }, + { + operation: "description port protocol" + }, + ], + this.client + ) + + + return response.map( + (r) => new Port( + { + queryTree: this.queryTree, + host: this.clientHost, + sessionToken: this.sessionToken, + }, + r.description, + r.port, + r.protocol, + ) + ) + } + + /** + * Start the service and wait for its health checks to succeed. + * + * Services bound to a Container do not need to be manually started. + */ + async start(): Promise { + await computeQuery( + [ + ...this._queryTree, + { + operation: "start", + }, + ], + this.client + ) + + return this + } + + /** + * Stop the service. + */ + async stop(): Promise { + await computeQuery( + [ + ...this._queryTree, + { + operation: "stop", + }, + ], + this.client + ) + + return this + } +} + + + + +export class Socket extends BaseClient { + private readonly _id?: SocketID = undefined + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + _id?: SocketID, + ) { + super(parent) + + this._id = _id + } + + /** + * The content-addressed identifier of the socket. + */ + async id(): Promise { + if (this._id) { + return this._id + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "id", + }, + ], + this.client + ) + + + return response + } +} + + + + + +/** + * A definition of a parameter or return type in a Module. + */ +export class TypeDef extends BaseClient { + private readonly _id?: TypeDefID = undefined + private readonly _kind?: TypeDefKind = undefined + private readonly _optional?: boolean = undefined + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + _id?: TypeDefID, + _kind?: TypeDefKind, + _optional?: boolean, + ) { + super(parent) + + this._id = _id + this._kind = _kind + this._optional = _optional + } + async id(): Promise { + if (this._id) { + return this._id + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "id", + }, + ], + this.client + ) + + + return response + } + + /** + * If kind is LIST, the list-specific type definition. + * If kind is not LIST, this will be null. + */ + asList(): ListTypeDef { + return new ListTypeDef({ + queryTree: [ + ...this._queryTree, + { + operation: "asList", + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * If kind is OBJECT, the object-specific type definition. + * If kind is not OBJECT, this will be null. + */ + asObject(): ObjectTypeDef { + return new ObjectTypeDef({ + queryTree: [ + ...this._queryTree, + { + operation: "asObject", + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * The kind of type this is (e.g. primitive, list, object) + */ + async kind(): Promise { + if (this._kind) { + return this._kind + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "kind", + }, + ], + this.client + ) + + + return response + } + + /** + * Whether this type can be set to null. Defaults to false. + */ + async optional(): Promise { + if (this._optional) { + return this._optional + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "optional", + }, + ], + this.client + ) + + + return response + } + + /** + * Adds a static field for an Object TypeDef, failing if the type is not an object. + * @param name The name of the field in the object + * @param typeDef The type of the field + * @param opts.description A doc string for the field, if any + */ + withField(name: string, typeDef: TypeDef, opts?: TypeDefWithFieldOpts): TypeDef { + return new TypeDef({ + queryTree: [ + ...this._queryTree, + { + operation: "withField", + args: { name, typeDef, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Adds a function for an Object TypeDef, failing if the type is not an object. + */ + withFunction(function_: Function_): TypeDef { + return new TypeDef({ + queryTree: [ + ...this._queryTree, + { + operation: "withFunction", + args: { function_ }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Sets the kind of the type. + */ + withKind(kind: TypeDefKind): TypeDef { + return new TypeDef({ + queryTree: [ + ...this._queryTree, + { + operation: "withKind", + args: { kind }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Returns a TypeDef of kind List with the provided type for its elements. + */ + withListOf(elementType: TypeDef): TypeDef { + return new TypeDef({ + queryTree: [ + ...this._queryTree, + { + operation: "withListOf", + args: { elementType }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Returns a TypeDef of kind Object with the provided name. + * + * Note that an object's fields and functions may be omitted if the intent is + * only to refer to an object. This is how functions are able to return their + * own object, or any other circular reference. + */ + withObject(name: string, opts?: TypeDefWithObjectOpts): TypeDef { + return new TypeDef({ + queryTree: [ + ...this._queryTree, + { + operation: "withObject", + args: { name, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Sets whether this type can be set to null. + */ + withOptional(optional: boolean): TypeDef { + return new TypeDef({ + queryTree: [ + ...this._queryTree, + { + operation: "withOptional", + args: { optional }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Call the provided function with current TypeDef. + * + * This is useful for reusability and readability by not breaking the calling chain. + */ + with(arg: (param: TypeDef) => TypeDef) { + return arg(this) + } +} + + + + + + + diff --git a/example/.fluentci/sdk/connect.ts b/example/.fluentci/sdk/connect.ts new file mode 100644 index 0000000..bd26cf7 --- /dev/null +++ b/example/.fluentci/sdk/connect.ts @@ -0,0 +1,89 @@ +import { Writable } from "node:stream"; + +import { Client } from "./client.gen.ts"; + +/** + * ConnectOpts defines option used to connect to an engine. + */ +export interface ConnectOpts { + /** + * Use to overwrite Dagger workdir + * @defaultValue process.cwd() + */ + Workdir?: string; + /** + * Enable logs output + * @example + * LogOutput + * ```ts + * connect(async (client: Client) => { + const source = await client.host().workdir().id() + ... + }, {LogOutput: process.stdout}) + ``` + */ + LogOutput?: Writable; +} + +export type CallbackFct = (client: Client) => Promise; + +export interface ConnectParams { + port: number; + session_token: string; +} + +/** + * connect runs GraphQL server and initializes a + * GraphQL client to execute query on it through its callback. + * This implementation is based on the existing Go SDK. + */ +export async function connect( + cb: CallbackFct, + config: ConnectOpts = {} +): Promise { + let client: Client; + // let close: null | (() => void) = null; + + if (Deno.env.has("FLUENTCI_TOKEN") && Deno.env.has("FLUENTCI_SESSION_ID")) { + const client = new Client({ + host: Deno.env.get("FLUENTCI_HOST") || "vm.fluentci.io", + sessionToken: Deno.env.get("FLUENTCI_TOKEN"), + }); + await cb(client).finally(() => { + if (close) { + close(); + } + }); + return; + } + + // Prefer DAGGER_SESSION_PORT if set + const daggerSessionPort = Deno.env.get("DAGGER_SESSION_PORT"); + if (daggerSessionPort) { + const sessionToken = Deno.env.get("DAGGER_SESSION_TOKEN"); + if (!sessionToken) { + throw new Error( + "DAGGER_SESSION_TOKEN must be set when using DAGGER_SESSION_PORT" + ); + } + + if (config.Workdir && config.Workdir !== "") { + throw new Error( + "cannot configure workdir for existing session (please use --workdir or host.directory with absolute paths instead)" + ); + } + + client = new Client({ + host: `127.0.0.1:${daggerSessionPort}`, + sessionToken: sessionToken, + }); + } else { + throw new Error("DAGGER_SESSION_PORT must be set"); + } + + await cb(client).finally(() => { + if (close) { + close(); + } + }); +} diff --git a/example/.fluentci/sdk/utils.ts b/example/.fluentci/sdk/utils.ts new file mode 100644 index 0000000..075e278 --- /dev/null +++ b/example/.fluentci/sdk/utils.ts @@ -0,0 +1,251 @@ +// deno-lint-ignore-file no-explicit-any +import { + ClientError, + gql, + GraphQLClient, + GraphQLRequestError, + TooManyNestedObjectsError, + UnknownDaggerError, + NotAwaitedRequestError, + ExecError, +} from "../deps.ts"; + +import { Metadata, QueryTree } from "./client.gen.ts"; + +/** + * Format argument into GraphQL query format. + */ +function buildArgs(args: any): string { + const metadata: Metadata = args.__metadata || {}; + + // Remove unwanted quotes + const formatValue = (key: string, value: string) => { + // Special treatment for enumeration, they must be inserted without quotes + if (metadata[key]?.is_enum) { + return JSON.stringify(value).replace(/['"]+/g, ""); + } + + return JSON.stringify(value).replace( + /\{"[a-zA-Z]+":|,"[a-zA-Z]+":/gi, + (str) => { + return str.replace(/"/g, ""); + } + ); + }; + + if (args === undefined || args === null) { + return ""; + } + + const formattedArgs = Object.entries(args).reduce( + (acc: any, [key, value]) => { + // Ignore internal metadata key + if (key === "__metadata") { + return acc; + } + + if (value !== undefined && value !== null) { + acc.push(`${key}: ${formatValue(key, value as string)}`); + } + + return acc; + }, + [] + ); + + if (formattedArgs.length === 0) { + return ""; + } + + return `(${formattedArgs})`; +} + +/** + * Find QueryTree, convert them into GraphQl query + * then compute and return the result to the appropriate field + */ +async function computeNestedQuery( + query: QueryTree[], + client: GraphQLClient +): Promise { + // Check if there is a nested queryTree to be executed + const isQueryTree = (value: any) => value["_queryTree"] !== undefined; + + // Check if there is a nested array of queryTree to be executed + const isArrayQueryTree = (value: any[]) => + value.every((v) => v instanceof Object && isQueryTree(v)); + + // Prepare query tree for final query by computing nested queries + // and building it with their results. + const computeQueryTree = async (value: any): Promise => { + // Resolve sub queries if operation's args is a subquery + for (const op of value["_queryTree"]) { + await computeNestedQuery([op], client); + } + + // push an id that will be used by the container + return buildQuery([ + ...value["_queryTree"], + { + operation: "id", + }, + ]); + }; + + // Remove all undefined args and assert args type + const queryToExec = query.filter((q): q is Required => !!q.args); + + for (const q of queryToExec) { + await Promise.all( + // Compute nested query for single object + Object.entries(q.args).map(async ([key, value]: any) => { + if (value instanceof Object && isQueryTree(value)) { + // push an id that will be used by the container + const getQueryTree = await computeQueryTree(value); + + q.args[key] = await compute(getQueryTree, client); + } + + // Compute nested query for array of object + if (Array.isArray(value) && isArrayQueryTree(value)) { + const tmp: any = q.args[key]; + + for (let i = 0; i < value.length; i++) { + // push an id that will be used by the container + const getQueryTree = await computeQueryTree(value[i]); + + tmp[i] = await compute(getQueryTree, client); + } + + q.args[key] = tmp; + } + }) + ); + } +} + +/** + * Convert the queryTree into a GraphQL query + * @param q + * @returns + */ +export function buildQuery(q: QueryTree[]): string { + const query = q.reduce((acc, { operation, args }, i) => { + const qLen = q.length; + + acc += ` ${operation} ${args ? `${buildArgs(args)}` : ""} ${ + qLen - 1 !== i ? "{" : "}".repeat(qLen - 1) + }`; + + return acc; + }, ""); + + return `{${query + .replaceAll('"StringKind"', "StringKind") + .replaceAll('"VoidKind"', "VoidKind") + .replaceAll('"IntegerKind"', "IntegerKind") + .replaceAll('"BooleanKind"', "BooleanKind") + .replaceAll('"ObjectKind"', "ObjectKind") + .replaceAll('"ListKind"', "ListKind") + .replaceAll("function_", "function")} }`; +} + +/** + * Convert querytree into a Graphql query then compute it + * @param q | QueryTree[] + * @param client | GraphQLClient + * @returns + */ +export async function computeQuery( + q: QueryTree[], + client: GraphQLClient +): Promise { + await computeNestedQuery(q, client); + + const query = buildQuery(q); + + return await compute(query, client); +} + +/** + * Return a Graphql query result flattened + * @param response any + * @returns + */ +export function queryFlatten(response: any): T { + // Recursion break condition + // If our response is not an object or an array we assume we reached the value + if (!(response instanceof Object) || Array.isArray(response)) { + return response; + } + + const keys = Object.keys(response); + + if (keys.length != 1) { + // Dagger is currently expecting to only return one value + // If the response is nested in a way were more than one object is nested inside throw an error + throw new TooManyNestedObjectsError( + "Too many nested objects inside graphql response", + { response: response } + ); + } + + const nestedKey = keys[0]; + + return queryFlatten(response[nestedKey]); +} + +/** + * Send a GraphQL document to the server + * return a flatten result + * @hidden + */ +export async function compute( + query: string, + client: GraphQLClient +): Promise { + let computeQuery: Awaited; + try { + computeQuery = await client.request( + gql` + ${query} + ` + ); + } catch (e: any) { + if (e instanceof ClientError) { + const msg = e.response.errors?.[0]?.message ?? `API Error`; + const ext = e.response.errors?.[0]?.extensions; + + if (ext?._type === "EXEC_ERROR") { + throw new ExecError(msg, { + cmd: (ext.cmd as string[]) ?? [], + exitCode: (ext.exitCode as number) ?? -1, + stdout: (ext.stdout as string) ?? "", + stderr: (ext.stderr as string) ?? "", + }); + } + + throw new GraphQLRequestError(msg, { + request: e.request, + response: e.response, + cause: e, + }); + } + + // Looking for connection error in case the function has not been awaited. + if (e.errno === "ECONNREFUSED") { + throw new NotAwaitedRequestError( + "Encountered an error while requesting data via graphql through a synchronous call. Make sure the function called is awaited.", + { cause: e } + ); + } + + // Just throw the unknown error + throw new UnknownDaggerError( + "Encountered an unknown error while requesting data via graphql", + { cause: e } + ); + } + + return queryFlatten(computeQuery); +} diff --git a/example/.fluentci/src/aws/config.ts b/example/.fluentci/src/aws/config.ts index a534a2e..9cb4d70 100644 --- a/example/.fluentci/src/aws/config.ts +++ b/example/.fluentci/src/aws/config.ts @@ -13,7 +13,7 @@ export function generateYaml(): BuildSpec { 'export DENO_INSTALL="$HOME/.deno"', 'export PATH="$DENO_INSTALL/bin:$PATH"', "deno install -A -r https://cli.fluentci.io -n fluentci", - "curl -L https://dl.dagger.io/dagger/install.sh | DAGGER_VERSION=0.8.1 sh", + "curl -L https://dl.dagger.io/dagger/install.sh | DAGGER_VERSION=0.9.3 sh", "mv bin/dagger /usr/local/bin", "dagger version", ], diff --git a/example/.fluentci/src/azure/config.ts b/example/.fluentci/src/azure/config.ts index f9f9b53..4c4c4a7 100644 --- a/example/.fluentci/src/azure/config.ts +++ b/example/.fluentci/src/azure/config.ts @@ -14,7 +14,7 @@ export function generateYaml(): AzurePipeline { `; const setupDagger = `\ - curl -L https://dl.dagger.io/dagger/install.sh | DAGGER_VERSION=0.8.1 sh + curl -L https://dl.dagger.io/dagger/install.sh | DAGGER_VERSION=0.9.3 sh sudo mv bin/dagger /usr/local/bin dagger version `; diff --git a/example/.fluentci/src/circleci/config.ts b/example/.fluentci/src/circleci/config.ts index 9664dfb..754f484 100644 --- a/example/.fluentci/src/circleci/config.ts +++ b/example/.fluentci/src/circleci/config.ts @@ -23,7 +23,7 @@ export PATH="$DENO_INSTALL/bin:$PATH"`, }, { run: `\ -curl -L https://dl.dagger.io/dagger/install.sh | DAGGER_VERSION=0.8.1 sh +curl -L https://dl.dagger.io/dagger/install.sh | DAGGER_VERSION=0.9.3 sh sudo mv bin/dagger /usr/local/bin dagger version`, }, diff --git a/example/.fluentci/src/dagger/jobs.ts b/example/.fluentci/src/dagger/jobs.ts index 12ae292..b34cf94 100644 --- a/example/.fluentci/src/dagger/jobs.ts +++ b/example/.fluentci/src/dagger/jobs.ts @@ -1,4 +1,5 @@ -import Client, { connect } from "../../deps.ts"; +import Client from "../../deps.ts"; +import { connect } from "../../sdk/connect.ts"; export enum Job { test = "test", diff --git a/example/.fluentci/src/dagger/queries.ts b/example/.fluentci/src/dagger/queries.ts index 33cdb13..7b12dca 100644 --- a/example/.fluentci/src/dagger/queries.ts +++ b/example/.fluentci/src/dagger/queries.ts @@ -1,13 +1,13 @@ import { gql } from "../../deps.ts"; export const run = gql` - query Run($command: String!, $src: String!, $bunVersion: String!) { + query Run($command: String!, $src: String, $bunVersion: String) { run(command: $command, src: $src, bunVersion: $bunVersion) } `; export const test = gql` - query Test($src: String!, $bunVersion: String!) { + query Test($src: String, $bunVersion: String) { test(src: $src, bunVersion: $bunVersion) } `; diff --git a/example/.fluentci/src/dagger/runner.ts b/example/.fluentci/src/dagger/runner.ts index e23879e..59f82f5 100644 --- a/example/.fluentci/src/dagger/runner.ts +++ b/example/.fluentci/src/dagger/runner.ts @@ -1,3 +1,17 @@ import pipeline from "./pipeline.ts"; +import { parse, camelCase, snakeCase } from "../../deps.ts"; -pipeline(".", Deno.args); +const args = parse(Deno.args.map((x) => x.split(" ")).flat()); + +if (!Array.isArray(Deno.args)) { + for (const param of Object.keys(args) + .filter((x) => x !== "_") + .map((x) => snakeCase(x).toUpperCase())) { + Deno.env.set(param, args[camelCase(param)]); + } +} + +await pipeline( + ".", + Array.isArray(Deno.args) ? Deno.args : (args._ as string[]) +); diff --git a/example/.fluentci/src/dagger/schema.ts b/example/.fluentci/src/dagger/schema.ts index 1f853db..50e7bc3 100644 --- a/example/.fluentci/src/dagger/schema.ts +++ b/example/.fluentci/src/dagger/schema.ts @@ -14,17 +14,17 @@ const Query = queryType({ definition(t) { t.string("test", { args: { - src: nonNull(stringArg()), - bunVersion: nonNull(stringArg()), + src: stringArg(), + bunVersion: stringArg(), }, resolve: async (_root, args, _ctx) => - await test(args.src, args.bunVersion), + await test(args.src || undefined, args.bunVersion), }); t.string("run", { args: { command: nonNull(stringArg()), - src: nonNull(stringArg()), - bunVersion: nonNull(stringArg()), + src: stringArg(), + bunVersion: stringArg(), }, resolve: async (_root, args, _ctx) => await run(args.command, args.src, args.bunVersion), diff --git a/example/.fluentci/src/github/config.ts b/example/.fluentci/src/github/config.ts index 06e4ba8..4d11c5e 100644 --- a/example/.fluentci/src/github/config.ts +++ b/example/.fluentci/src/github/config.ts @@ -11,11 +11,6 @@ export function generateYaml(): Workflow { branches: ["main"], }; - const setupDagger = `\ - curl -L https://dl.dagger.io/dagger/install.sh | DAGGER_VERSION=0.8.1 sh - sudo mv bin/dagger /usr/local/bin - dagger version`; - const tests: JobSpec = { "runs-on": "ubuntu-latest", steps: [ @@ -23,18 +18,8 @@ export function generateYaml(): Workflow { uses: "actions/checkout@v2", }, { - uses: "denoland/setup-deno@v1", - with: { - "deno-version": "v1.37", - }, - }, - { - name: "Setup Fluent CI CLI", - run: "deno install -A -r https://cli.fluentci.io -n fluentci", - }, - { - name: "Setup Dagger", - run: setupDagger, + name: "Setup Fluent CI", + uses: "fluentci-io/setup-fluentci@v2", }, { name: "Run Dagger Pipelines", diff --git a/example/.fluentci/src/gitlab/config.ts b/example/.fluentci/src/gitlab/config.ts index 92f9437..384ebc6 100644 --- a/example/.fluentci/src/gitlab/config.ts +++ b/example/.fluentci/src/gitlab/config.ts @@ -21,7 +21,7 @@ export function generateYaml(): GitlabCI { ` apk add docker-cli curl unzip deno install -A -r https://cli.fluentci.io -n fluentci - curl -L https://dl.dagger.io/dagger/install.sh | DAGGER_VERSION=0.8.1 sh + curl -L https://dl.dagger.io/dagger/install.sh | DAGGER_VERSION=0.9.3 sh mv bin/dagger /usr/local/bin dagger version ` diff --git a/example/test.gql b/example/test.gql index c2742df..9eb9ab0 100644 --- a/example/test.gql +++ b/example/test.gql @@ -1,5 +1,5 @@ { bun { - test(src: ".", bunVersion: "1.0.3") + test() } } diff --git a/gen/nexus.ts b/gen/nexus.ts index 9fac157..e3ec88c 100644 --- a/gen/nexus.ts +++ b/gen/nexus.ts @@ -58,13 +58,13 @@ export interface NexusGenFieldTypeNames { export interface NexusGenArgTypes { Query: { run: { // args - bunVersion: string; // String! + bunVersion?: string | null; // String command: string; // String! - src: string; // String! + src?: string | null; // String } test: { // args - bunVersion: string; // String! - src: string; // String! + bunVersion?: string | null; // String + src?: string | null; // String } } } diff --git a/schema.graphql b/schema.graphql index 49b6fc3..882993d 100644 --- a/schema.graphql +++ b/schema.graphql @@ -3,6 +3,6 @@ type Query { - run(bunVersion: String!, command: String!, src: String!): String - test(bunVersion: String!, src: String!): String + run(bunVersion: String, command: String!, src: String): String + test(bunVersion: String, src: String): String } \ No newline at end of file diff --git a/sdk/client.gen.ts b/sdk/client.gen.ts new file mode 100644 index 0000000..d18ad57 --- /dev/null +++ b/sdk/client.gen.ts @@ -0,0 +1,5993 @@ +/** + * This file was auto-generated by `client-gen`. + * Do not make direct changes to the file. + */ +import { GraphQLClient } from "../deps.ts" + +import { computeQuery } from "./utils.ts" + +/** + * @hidden + */ +export type QueryTree = { + operation: string + args?: Record +} + +/** + * @hidden + */ +export type Metadata = { + [key: string]: { + is_enum?: boolean + } +} + +interface ClientConfig { + queryTree?: QueryTree[] + host?: string + sessionToken?: string +} + +class BaseClient { + protected _queryTree: QueryTree[] + protected client: GraphQLClient + /** + * @defaultValue `127.0.0.1:8080` + */ + public clientHost: string + public sessionToken: string + + /** + * @hidden + */ + constructor({ queryTree, host, sessionToken }: ClientConfig = {}) { + this._queryTree = queryTree || [] + this.clientHost = host || "127.0.0.1:8080" + this.sessionToken = sessionToken || "" + this.client = new GraphQLClient(`http://${host}/query`, { + headers: { + Authorization: + "Basic " + btoa(sessionToken + ":"), + }, + }) + } + + /** + * @hidden + */ + get queryTree() { + return this._queryTree + } +} + +export type BuildArg = { + /** + * The build argument name. + */ + name: string + + /** + * The build argument value. + */ + value: string +} + +/** + * Sharing mode of the cache volume. + */ +export enum CacheSharingMode { + + /** + * Shares the cache volume amongst many build pipelines, + * but will serialize the writes + */ + Locked = "LOCKED", + + /** + * Keeps a cache volume for a single build pipeline + */ + Private = "PRIVATE", + + /** + * Shares the cache volume amongst many build pipelines + */ + Shared = "SHARED", +} +/** + * A global cache volume identifier. + */ +export type CacheVolumeID = string & {__CacheVolumeID: never} + +export type ContainerAsTarballOpts = { + /** + * Identifiers for other platform specific containers. + * Used for multi-platform image. + */ + platformVariants?: Container[] + + /** + * Force each layer of the image to use the specified compression algorithm. + * If this is unset, then if a layer already has a compressed blob in the engine's + * cache, that will be used (this can result in a mix of compression algorithms for + * different layers). If this is unset and a layer has no compressed blob in the + * engine's cache, then it will be compressed using Gzip. + */ + forcedCompression?: ImageLayerCompression + + /** + * Use the specified media types for the image's layers. Defaults to OCI, which + * is largely compatible with most recent container runtimes, but Docker may be needed + * for older runtimes without OCI support. + */ + mediaTypes?: ImageMediaTypes +} + +export type ContainerBuildOpts = { + /** + * Path to the Dockerfile to use. + * + * Default: './Dockerfile'. + */ + dockerfile?: string + + /** + * Additional build arguments. + */ + buildArgs?: BuildArg[] + + /** + * Target build stage to build. + */ + target?: string + + /** + * Secrets to pass to the build. + * + * They will be mounted at /run/secrets/[secret-name] in the build container + * + * They can be accessed in the Dockerfile using the "secret" mount type + * and mount path /run/secrets/[secret-name] + * e.g. RUN --mount=type=secret,id=my-secret curl url?token=$(cat /run/secrets/my-secret)" + */ + secrets?: Secret[] +} + +export type ContainerExportOpts = { + /** + * Identifiers for other platform specific containers. + * Used for multi-platform image. + */ + platformVariants?: Container[] + + /** + * Force each layer of the exported image to use the specified compression algorithm. + * If this is unset, then if a layer already has a compressed blob in the engine's + * cache, that will be used (this can result in a mix of compression algorithms for + * different layers). If this is unset and a layer has no compressed blob in the + * engine's cache, then it will be compressed using Gzip. + */ + forcedCompression?: ImageLayerCompression + + /** + * Use the specified media types for the exported image's layers. Defaults to OCI, which + * is largely compatible with most recent container runtimes, but Docker may be needed + * for older runtimes without OCI support. + */ + mediaTypes?: ImageMediaTypes +} + +export type ContainerImportOpts = { + /** + * Identifies the tag to import from the archive, if the archive bundles + * multiple tags. + */ + tag?: string +} + +export type ContainerPipelineOpts = { + /** + * Pipeline description. + */ + description?: string + + /** + * Pipeline labels. + */ + labels?: PipelineLabel[] +} + +export type ContainerPublishOpts = { + /** + * Identifiers for other platform specific containers. + * Used for multi-platform image. + */ + platformVariants?: Container[] + + /** + * Force each layer of the published image to use the specified compression algorithm. + * If this is unset, then if a layer already has a compressed blob in the engine's + * cache, that will be used (this can result in a mix of compression algorithms for + * different layers). If this is unset and a layer has no compressed blob in the + * engine's cache, then it will be compressed using Gzip. + */ + forcedCompression?: ImageLayerCompression + + /** + * Use the specified media types for the published image's layers. Defaults to OCI, which + * is largely compatible with most recent registries, but Docker may be needed for older + * registries without OCI support. + */ + mediaTypes?: ImageMediaTypes +} + +export type ContainerWithDefaultArgsOpts = { + /** + * Arguments to prepend to future executions (e.g., ["-v", "--no-cache"]). + */ + args?: string[] +} + +export type ContainerWithDirectoryOpts = { + /** + * Patterns to exclude in the written directory (e.g., ["node_modules/**", ".gitignore", ".git/"]). + */ + exclude?: string[] + + /** + * Patterns to include in the written directory (e.g., ["*.go", "go.mod", "go.sum"]). + */ + include?: string[] + + /** + * A user:group to set for the directory and its contents. + * + * The user and group can either be an ID (1000:1000) or a name (foo:bar). + * + * If the group is omitted, it defaults to the same as the user. + */ + owner?: string +} + +export type ContainerWithEnvVariableOpts = { + /** + * Replace ${VAR} or $VAR in the value according to the current environment + * variables defined in the container (e.g., "/opt/bin:$PATH"). + */ + expand?: boolean +} + +export type ContainerWithExecOpts = { + /** + * If the container has an entrypoint, ignore it for args rather than using it to wrap them. + */ + skipEntrypoint?: boolean + + /** + * Content to write to the command's standard input before closing (e.g., "Hello world"). + */ + stdin?: string + + /** + * Redirect the command's standard output to a file in the container (e.g., "/tmp/stdout"). + */ + redirectStdout?: string + + /** + * Redirect the command's standard error to a file in the container (e.g., "/tmp/stderr"). + */ + redirectStderr?: string + + /** + * Provides dagger access to the executed command. + * + * Do not use this option unless you trust the command being executed. + * The command being executed WILL BE GRANTED FULL ACCESS TO YOUR HOST FILESYSTEM. + */ + experimentalPrivilegedNesting?: boolean + + /** + * Execute the command with all root capabilities. This is similar to running a command + * with "sudo" or executing `docker run` with the `--privileged` flag. Containerization + * does not provide any security guarantees when using this option. It should only be used + * when absolutely necessary and only with trusted commands. + */ + insecureRootCapabilities?: boolean +} + +export type ContainerWithExposedPortOpts = { + /** + * Transport layer network protocol + */ + protocol?: NetworkProtocol + + /** + * Optional port description + */ + description?: string +} + +export type ContainerWithFileOpts = { + /** + * Permission given to the copied file (e.g., 0600). + * + * Default: 0644. + */ + permissions?: number + + /** + * A user:group to set for the file. + * + * The user and group can either be an ID (1000:1000) or a name (foo:bar). + * + * If the group is omitted, it defaults to the same as the user. + */ + owner?: string +} + +export type ContainerWithMountedCacheOpts = { + /** + * Identifier of the directory to use as the cache volume's root. + */ + source?: Directory + + /** + * Sharing mode of the cache volume. + */ + sharing?: CacheSharingMode + + /** + * A user:group to set for the mounted cache directory. + * + * Note that this changes the ownership of the specified mount along with the + * initial filesystem provided by source (if any). It does not have any effect + * if/when the cache has already been created. + * + * The user and group can either be an ID (1000:1000) or a name (foo:bar). + * + * If the group is omitted, it defaults to the same as the user. + */ + owner?: string +} + +export type ContainerWithMountedDirectoryOpts = { + /** + * A user:group to set for the mounted directory and its contents. + * + * The user and group can either be an ID (1000:1000) or a name (foo:bar). + * + * If the group is omitted, it defaults to the same as the user. + */ + owner?: string +} + +export type ContainerWithMountedFileOpts = { + /** + * A user or user:group to set for the mounted file. + * + * The user and group can either be an ID (1000:1000) or a name (foo:bar). + * + * If the group is omitted, it defaults to the same as the user. + */ + owner?: string +} + +export type ContainerWithMountedSecretOpts = { + /** + * A user:group to set for the mounted secret. + * + * The user and group can either be an ID (1000:1000) or a name (foo:bar). + * + * If the group is omitted, it defaults to the same as the user. + */ + owner?: string + + /** + * Permission given to the mounted secret (e.g., 0600). + * This option requires an owner to be set to be active. + * + * Default: 0400. + */ + mode?: number +} + +export type ContainerWithNewFileOpts = { + /** + * Content of the file to write (e.g., "Hello world!"). + */ + contents?: string + + /** + * Permission given to the written file (e.g., 0600). + * + * Default: 0644. + */ + permissions?: number + + /** + * A user:group to set for the file. + * + * The user and group can either be an ID (1000:1000) or a name (foo:bar). + * + * If the group is omitted, it defaults to the same as the user. + */ + owner?: string +} + +export type ContainerWithUnixSocketOpts = { + /** + * A user:group to set for the mounted socket. + * + * The user and group can either be an ID (1000:1000) or a name (foo:bar). + * + * If the group is omitted, it defaults to the same as the user. + */ + owner?: string +} + +export type ContainerWithoutExposedPortOpts = { + /** + * Port protocol to unexpose + */ + protocol?: NetworkProtocol +} + +/** + * A unique container identifier. Null designates an empty container (scratch). + */ +export type ContainerID = string & {__ContainerID: never} + +/** + * The `DateTime` scalar type represents a DateTime. The DateTime is serialized as an RFC 3339 quoted string + */ +export type DateTime = string & {__DateTime: never} + +export type DirectoryAsModuleOpts = { + /** + * An optional subpath of the directory which contains the module's source + * code. + * + * This is needed when the module code is in a subdirectory but requires + * parent directories to be loaded in order to execute. For example, the + * module source code may need a go.mod, project.toml, package.json, etc. file + * from a parent directory. + * + * If not set, the module source code is loaded from the root of the + * directory. + */ + sourceSubpath?: string +} + +export type DirectoryDockerBuildOpts = { + /** + * Path to the Dockerfile to use (e.g., "frontend.Dockerfile"). + * + * Defaults: './Dockerfile'. + */ + dockerfile?: string + + /** + * The platform to build. + */ + platform?: Platform + + /** + * Build arguments to use in the build. + */ + buildArgs?: BuildArg[] + + /** + * Target build stage to build. + */ + target?: string + + /** + * Secrets to pass to the build. + * + * They will be mounted at /run/secrets/[secret-name]. + */ + secrets?: Secret[] +} + +export type DirectoryEntriesOpts = { + /** + * Location of the directory to look at (e.g., "/src"). + */ + path?: string +} + +export type DirectoryPipelineOpts = { + /** + * Pipeline description. + */ + description?: string + + /** + * Pipeline labels. + */ + labels?: PipelineLabel[] +} + +export type DirectoryWithDirectoryOpts = { + /** + * Exclude artifacts that match the given pattern (e.g., ["node_modules/", ".git*"]). + */ + exclude?: string[] + + /** + * Include only artifacts that match the given pattern (e.g., ["app/", "package.*"]). + */ + include?: string[] +} + +export type DirectoryWithFileOpts = { + /** + * Permission given to the copied file (e.g., 0600). + * + * Default: 0644. + */ + permissions?: number +} + +export type DirectoryWithNewDirectoryOpts = { + /** + * Permission granted to the created directory (e.g., 0777). + * + * Default: 0755. + */ + permissions?: number +} + +export type DirectoryWithNewFileOpts = { + /** + * Permission given to the copied file (e.g., 0600). + * + * Default: 0644. + */ + permissions?: number +} + +/** + * A content-addressed directory identifier. + */ +export type DirectoryID = string & {__DirectoryID: never} + +export type FileExportOpts = { + /** + * If allowParentDirPath is true, the path argument can be a directory path, in which case + * the file will be created in that directory. + */ + allowParentDirPath?: boolean +} + +/** + * A file identifier. + */ +export type FileID = string & {__FileID: never} + +export type FunctionWithArgOpts = { + /** + * A doc string for the argument, if any + */ + description?: string + + /** + * A default value to use for this argument if not explicitly set by the caller, if any + */ + defaultValue?: JSON +} + +/** + * A reference to a FunctionArg. + */ +export type FunctionArgID = string & {__FunctionArgID: never} + +/** + * A reference to a Function. + */ +export type FunctionID = string & {__FunctionID: never} + +/** + * A reference to GeneratedCode. + */ +export type GeneratedCodeID = string & {__GeneratedCodeID: never} + +export type GitRefTreeOpts = { + sshKnownHosts?: string + sshAuthSocket?: Socket +} + +export type HostDirectoryOpts = { + /** + * Exclude artifacts that match the given pattern (e.g., ["node_modules/", ".git*"]). + */ + exclude?: string[] + + /** + * Include only artifacts that match the given pattern (e.g., ["app/", "package.*"]). + */ + include?: string[] +} + +export type HostServiceOpts = { + /** + * Upstream host to forward traffic to. + */ + host?: string +} + +export type HostTunnelOpts = { + /** + * Map each service port to the same port on the host, as if the service were + * running natively. + * + * Note: enabling may result in port conflicts. + */ + native?: boolean + + /** + * Configure explicit port forwarding rules for the tunnel. + * + * If a port's frontend is unspecified or 0, a random port will be chosen by + * the host. + * + * If no ports are given, all of the service's ports are forwarded. If native + * is true, each port maps to the same port on the host. If native is false, + * each port maps to a random port chosen by the host. + * + * If ports are given and native is true, the ports are additive. + */ + ports?: PortForward[] +} + +/** + * The `ID` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `"4"`) or integer (such as `4`) input value will be accepted as an ID. + */ +export type ID = string & {__ID: never} + +/** + * Compression algorithm to use for image layers. + */ +export enum ImageLayerCompression { + Estargz = "EStarGZ", + Gzip = "Gzip", + Uncompressed = "Uncompressed", + Zstd = "Zstd", +} +/** + * Mediatypes to use in published or exported image metadata. + */ +export enum ImageMediaTypes { + Dockermediatypes = "DockerMediaTypes", + Ocimediatypes = "OCIMediaTypes", +} +/** + * An arbitrary JSON-encoded value. + */ +export type JSON = string & {__JSON: never} + +/** + * A reference to a Module. + */ +export type ModuleID = string & {__ModuleID: never} + +/** + * Transport layer network protocol associated to a port. + */ +export enum NetworkProtocol { + + /** + * TCP (Transmission Control Protocol) + */ + Tcp = "TCP", + + /** + * UDP (User Datagram Protocol) + */ + Udp = "UDP", +} +export type PipelineLabel = { + /** + * Label name. + */ + name: string + + /** + * Label value. + */ + value: string +} + +/** + * The platform config OS and architecture in a Container. + * + * The format is [os]/[platform]/[version] (e.g., "darwin/arm64/v7", "windows/amd64", "linux/arm64"). + */ +export type Platform = string & {__Platform: never} + +export type PortForward = { + /** + * Destination port for traffic. + */ + backend: number + + /** + * Port to expose to clients. If unspecified, a default will be chosen. + */ + frontend?: number + + /** + * Protocol to use for traffic. + */ + protocol?: NetworkProtocol +} + +export type ClientContainerOpts = { + id?: ContainerID + platform?: Platform +} + +export type ClientDirectoryOpts = { + id?: DirectoryID +} + +export type ClientGitOpts = { + /** + * Set to true to keep .git directory. + */ + keepGitDir?: boolean + + /** + * Set SSH known hosts + */ + sshKnownHosts?: string + + /** + * Set SSH auth socket + */ + sshAuthSocket?: Socket + + /** + * A service which must be started before the repo is fetched. + */ + experimentalServiceHost?: Service +} + +export type ClientHttpOpts = { + /** + * A service which must be started before the URL is fetched. + */ + experimentalServiceHost?: Service +} + +export type ClientModuleConfigOpts = { + subpath?: string +} + +export type ClientPipelineOpts = { + /** + * Pipeline description. + */ + description?: string + + /** + * Pipeline labels. + */ + labels?: PipelineLabel[] +} + +export type ClientSocketOpts = { + id?: SocketID +} + +/** + * A unique identifier for a secret. + */ +export type SecretID = string & {__SecretID: never} + +export type ServiceEndpointOpts = { + /** + * The exposed port number for the endpoint + */ + port?: number + + /** + * Return a URL with the given scheme, eg. http for http:// + */ + scheme?: string +} + +/** + * A unique service identifier. + */ +export type ServiceID = string & {__ServiceID: never} + +/** + * A content-addressed socket identifier. + */ +export type SocketID = string & {__SocketID: never} + +export type TypeDefWithFieldOpts = { + /** + * A doc string for the field, if any + */ + description?: string +} + +export type TypeDefWithObjectOpts = { + description?: string +} + +/** + * A reference to a TypeDef. + */ +export type TypeDefID = string & {__TypeDefID: never} + +/** + * Distinguishes the different kinds of TypeDefs. + */ +export enum TypeDefKind { + + /** + * A boolean value + */ + Booleankind = "BooleanKind", + + /** + * An integer value + */ + Integerkind = "IntegerKind", + + /** + * A list of values all having the same type. + * + * Always paired with a ListTypeDef. + */ + Listkind = "ListKind", + + /** + * A named type defined in the GraphQL schema, with fields and functions. + * + * Always paired with an ObjectTypeDef. + */ + Objectkind = "ObjectKind", + + /** + * A string value + */ + Stringkind = "StringKind", + + /** + * A special kind used to signify that no value is returned. + * + * This is used for functions that have no return value. The outer TypeDef + * specifying this Kind is always Optional, as the Void is never actually + * represented. + */ + Voidkind = "VoidKind", +} +/** + * The absense of a value. + * + * A Null Void is used as a placeholder for resolvers that do not return anything. + */ +export type Void = string & {__Void: never} + +export type __TypeEnumValuesOpts = { + includeDeprecated?: boolean +} + +export type __TypeFieldsOpts = { + includeDeprecated?: boolean +} + + + + + + + + +/** + * A directory whose contents persist across runs. + */ +export class CacheVolume extends BaseClient { + private readonly _id?: CacheVolumeID = undefined + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + _id?: CacheVolumeID, + ) { + super(parent) + + this._id = _id + } + async id(): Promise { + if (this._id) { + return this._id + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "id", + }, + ], + this.client + ) + + + return response + } +} + + + +/** + * An OCI-compatible container, also known as a docker container. + */ +export class Container extends BaseClient { + private readonly _id?: ContainerID = undefined + private readonly _envVariable?: string = undefined + private readonly _export?: boolean = undefined + private readonly _imageRef?: string = undefined + private readonly _label?: string = undefined + private readonly _platform?: Platform = undefined + private readonly _publish?: string = undefined + private readonly _shellEndpoint?: string = undefined + private readonly _stderr?: string = undefined + private readonly _stdout?: string = undefined + private readonly _sync?: ContainerID = undefined + private readonly _user?: string = undefined + private readonly _workdir?: string = undefined + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + _id?: ContainerID, + _envVariable?: string, + _export?: boolean, + _imageRef?: string, + _label?: string, + _platform?: Platform, + _publish?: string, + _shellEndpoint?: string, + _stderr?: string, + _stdout?: string, + _sync?: ContainerID, + _user?: string, + _workdir?: string, + ) { + super(parent) + + this._id = _id + this._envVariable = _envVariable + this._export = _export + this._imageRef = _imageRef + this._label = _label + this._platform = _platform + this._publish = _publish + this._shellEndpoint = _shellEndpoint + this._stderr = _stderr + this._stdout = _stdout + this._sync = _sync + this._user = _user + this._workdir = _workdir + } + + /** + * A unique identifier for this container. + */ + async id(): Promise { + if (this._id) { + return this._id + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "id", + }, + ], + this.client + ) + + + return response + } + + /** + * Turn the container into a Service. + * + * Be sure to set any exposed ports before this conversion. + */ + asService(): Service { + return new Service({ + queryTree: [ + ...this._queryTree, + { + operation: "asService", + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Returns a File representing the container serialized to a tarball. + * @param opts.platformVariants Identifiers for other platform specific containers. + * Used for multi-platform image. + * @param opts.forcedCompression Force each layer of the image to use the specified compression algorithm. + * If this is unset, then if a layer already has a compressed blob in the engine's + * cache, that will be used (this can result in a mix of compression algorithms for + * different layers). If this is unset and a layer has no compressed blob in the + * engine's cache, then it will be compressed using Gzip. + * @param opts.mediaTypes Use the specified media types for the image's layers. Defaults to OCI, which + * is largely compatible with most recent container runtimes, but Docker may be needed + * for older runtimes without OCI support. + */ + asTarball(opts?: ContainerAsTarballOpts): File { + const metadata: Metadata = { + forcedCompression: { is_enum: true }, + mediaTypes: { is_enum: true }, + } + + return new File({ + queryTree: [ + ...this._queryTree, + { + operation: "asTarball", + args: { ...opts, __metadata: metadata }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Initializes this container from a Dockerfile build. + * @param context Directory context used by the Dockerfile. + * @param opts.dockerfile Path to the Dockerfile to use. + * + * Default: './Dockerfile'. + * @param opts.buildArgs Additional build arguments. + * @param opts.target Target build stage to build. + * @param opts.secrets Secrets to pass to the build. + * + * They will be mounted at /run/secrets/[secret-name] in the build container + * + * They can be accessed in the Dockerfile using the "secret" mount type + * and mount path /run/secrets/[secret-name] + * e.g. RUN --mount=type=secret,id=my-secret curl url?token=$(cat /run/secrets/my-secret)" + */ + build(context: Directory, opts?: ContainerBuildOpts): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "build", + args: { context, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves default arguments for future commands. + */ + async defaultArgs(): Promise { + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "defaultArgs", + }, + ], + this.client + ) + + + return response + } + + /** + * Retrieves a directory at the given path. + * + * Mounts are included. + * @param path The path of the directory to retrieve (e.g., "./src"). + */ + directory(path: string): Directory { + return new Directory({ + queryTree: [ + ...this._queryTree, + { + operation: "directory", + args: { path }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves entrypoint to be prepended to the arguments of all commands. + */ + async entrypoint(): Promise { + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "entrypoint", + }, + ], + this.client + ) + + + return response + } + + /** + * Retrieves the value of the specified environment variable. + * @param name The name of the environment variable to retrieve (e.g., "PATH"). + */ + async envVariable(name: string): Promise { + if (this._envVariable) { + return this._envVariable + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "envVariable", + args: { name }, + }, + ], + this.client + ) + + + return response + } + + /** + * Retrieves the list of environment variables passed to commands. + */ + async envVariables(): Promise { + type envVariables = { + name: string + value: string + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "envVariables", + }, + { + operation: "name value" + }, + ], + this.client + ) + + + return response.map( + (r) => new EnvVariable( + { + queryTree: this.queryTree, + host: this.clientHost, + sessionToken: this.sessionToken, + }, + r.name, + r.value, + ) + ) + } + + /** + * EXPERIMENTAL API! Subject to change/removal at any time. + * + * experimentalWithAllGPUs configures all available GPUs on the host to be accessible to this container. + * This currently works for Nvidia devices only. + */ + experimentalWithAllGPUs(): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "experimentalWithAllGPUs", + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * EXPERIMENTAL API! Subject to change/removal at any time. + * + * experimentalWithGPU configures the provided list of devices to be accesible to this container. + * This currently works for Nvidia devices only. + */ + experimentalWithGPU(devices: string[]): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "experimentalWithGPU", + args: { devices }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Writes the container as an OCI tarball to the destination file path on the host for the specified platform variants. + * + * Return true on success. + * It can also publishes platform variants. + * @param path Host's destination path (e.g., "./tarball"). + * Path can be relative to the engine's workdir or absolute. + * @param opts.platformVariants Identifiers for other platform specific containers. + * Used for multi-platform image. + * @param opts.forcedCompression Force each layer of the exported image to use the specified compression algorithm. + * If this is unset, then if a layer already has a compressed blob in the engine's + * cache, that will be used (this can result in a mix of compression algorithms for + * different layers). If this is unset and a layer has no compressed blob in the + * engine's cache, then it will be compressed using Gzip. + * @param opts.mediaTypes Use the specified media types for the exported image's layers. Defaults to OCI, which + * is largely compatible with most recent container runtimes, but Docker may be needed + * for older runtimes without OCI support. + */ + async export(path: string, opts?: ContainerExportOpts): Promise { + if (this._export) { + return this._export + } + + const metadata: Metadata = { + forcedCompression: { is_enum: true }, + mediaTypes: { is_enum: true }, + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "export", + args: { path, ...opts, __metadata: metadata }, + }, + ], + this.client + ) + + + return response + } + + /** + * Retrieves the list of exposed ports. + * + * This includes ports already exposed by the image, even if not + * explicitly added with dagger. + */ + async exposedPorts(): Promise { + type exposedPorts = { + description: string + port: number + protocol: NetworkProtocol + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "exposedPorts", + }, + { + operation: "description port protocol" + }, + ], + this.client + ) + + + return response.map( + (r) => new Port( + { + queryTree: this.queryTree, + host: this.clientHost, + sessionToken: this.sessionToken, + }, + r.description, + r.port, + r.protocol, + ) + ) + } + + /** + * Retrieves a file at the given path. + * + * Mounts are included. + * @param path The path of the file to retrieve (e.g., "./README.md"). + */ + file(path: string): File { + return new File({ + queryTree: [ + ...this._queryTree, + { + operation: "file", + args: { path }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Initializes this container from a pulled base image. + * @param address Image's address from its registry. + * + * Formatted as [host]/[user]/[repo]:[tag] (e.g., "docker.io/dagger/dagger:main"). + */ + from(address: string): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "from", + args: { address }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * The unique image reference which can only be retrieved immediately after the 'Container.From' call. + */ + async imageRef(): Promise { + if (this._imageRef) { + return this._imageRef + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "imageRef", + }, + ], + this.client + ) + + + return response + } + + /** + * Reads the container from an OCI tarball. + * + * NOTE: this involves unpacking the tarball to an OCI store on the host at + * $XDG_CACHE_DIR/dagger/oci. This directory can be removed whenever you like. + * @param source File to read the container from. + * @param opts.tag Identifies the tag to import from the archive, if the archive bundles + * multiple tags. + */ + import_(source: File, opts?: ContainerImportOpts): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "import", + args: { source, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves the value of the specified label. + */ + async label(name: string): Promise { + if (this._label) { + return this._label + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "label", + args: { name }, + }, + ], + this.client + ) + + + return response + } + + /** + * Retrieves the list of labels passed to container. + */ + async labels(): Promise { + type labels = { + name: string + value: string + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "labels", + }, + { + operation: "name value" + }, + ], + this.client + ) + + + return response.map( + (r) => new Label( + { + queryTree: this.queryTree, + host: this.clientHost, + sessionToken: this.sessionToken, + }, + r.name, + r.value, + ) + ) + } + + /** + * Retrieves the list of paths where a directory is mounted. + */ + async mounts(): Promise { + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "mounts", + }, + ], + this.client + ) + + + return response + } + + /** + * Creates a named sub-pipeline + * @param name Pipeline name. + * @param opts.description Pipeline description. + * @param opts.labels Pipeline labels. + */ + pipeline(name: string, opts?: ContainerPipelineOpts): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "pipeline", + args: { name, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * The platform this container executes and publishes as. + */ + async platform(): Promise { + if (this._platform) { + return this._platform + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "platform", + }, + ], + this.client + ) + + + return response + } + + /** + * Publishes this container as a new image to the specified address. + * + * Publish returns a fully qualified ref. + * It can also publish platform variants. + * @param address Registry's address to publish the image to. + * + * Formatted as [host]/[user]/[repo]:[tag] (e.g. "docker.io/dagger/dagger:main"). + * @param opts.platformVariants Identifiers for other platform specific containers. + * Used for multi-platform image. + * @param opts.forcedCompression Force each layer of the published image to use the specified compression algorithm. + * If this is unset, then if a layer already has a compressed blob in the engine's + * cache, that will be used (this can result in a mix of compression algorithms for + * different layers). If this is unset and a layer has no compressed blob in the + * engine's cache, then it will be compressed using Gzip. + * @param opts.mediaTypes Use the specified media types for the published image's layers. Defaults to OCI, which + * is largely compatible with most recent registries, but Docker may be needed for older + * registries without OCI support. + */ + async publish(address: string, opts?: ContainerPublishOpts): Promise { + if (this._publish) { + return this._publish + } + + const metadata: Metadata = { + forcedCompression: { is_enum: true }, + mediaTypes: { is_enum: true }, + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "publish", + args: { address, ...opts, __metadata: metadata }, + }, + ], + this.client + ) + + + return response + } + + /** + * Retrieves this container's root filesystem. Mounts are not included. + */ + rootfs(): Directory { + return new Directory({ + queryTree: [ + ...this._queryTree, + { + operation: "rootfs", + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Return a websocket endpoint that, if connected to, will start the container with a TTY streamed + * over the websocket. + * + * Primarily intended for internal use with the dagger CLI. + */ + async shellEndpoint(): Promise { + if (this._shellEndpoint) { + return this._shellEndpoint + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "shellEndpoint", + }, + ], + this.client + ) + + + return response + } + + /** + * The error stream of the last executed command. + * + * Will execute default command if none is set, or error if there's no default. + */ + async stderr(): Promise { + if (this._stderr) { + return this._stderr + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "stderr", + }, + ], + this.client + ) + + + return response + } + + /** + * The output stream of the last executed command. + * + * Will execute default command if none is set, or error if there's no default. + */ + async stdout(): Promise { + if (this._stdout) { + return this._stdout + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "stdout", + }, + ], + this.client + ) + + + return response + } + + /** + * Forces evaluation of the pipeline in the engine. + * + * It doesn't run the default command if no exec has been set. + */ + async sync(): Promise { + await computeQuery( + [ + ...this._queryTree, + { + operation: "sync", + }, + ], + this.client + ) + + return this + } + + /** + * Retrieves the user to be set for all commands. + */ + async user(): Promise { + if (this._user) { + return this._user + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "user", + }, + ], + this.client + ) + + + return response + } + + /** + * Configures default arguments for future commands. + * @param opts.args Arguments to prepend to future executions (e.g., ["-v", "--no-cache"]). + */ + withDefaultArgs(opts?: ContainerWithDefaultArgsOpts): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withDefaultArgs", + args: { ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this container plus a directory written at the given path. + * @param path Location of the written directory (e.g., "/tmp/directory"). + * @param directory Identifier of the directory to write + * @param opts.exclude Patterns to exclude in the written directory (e.g., ["node_modules/**", ".gitignore", ".git/"]). + * @param opts.include Patterns to include in the written directory (e.g., ["*.go", "go.mod", "go.sum"]). + * @param opts.owner A user:group to set for the directory and its contents. + * + * The user and group can either be an ID (1000:1000) or a name (foo:bar). + * + * If the group is omitted, it defaults to the same as the user. + */ + withDirectory(path: string, directory: Directory, opts?: ContainerWithDirectoryOpts): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withDirectory", + args: { path, directory, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this container but with a different command entrypoint. + * @param args Entrypoint to use for future executions (e.g., ["go", "run"]). + */ + withEntrypoint(args: string[]): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withEntrypoint", + args: { args }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this container plus the given environment variable. + * @param name The name of the environment variable (e.g., "HOST"). + * @param value The value of the environment variable. (e.g., "localhost"). + * @param opts.expand Replace ${VAR} or $VAR in the value according to the current environment + * variables defined in the container (e.g., "/opt/bin:$PATH"). + */ + withEnvVariable(name: string, value: string, opts?: ContainerWithEnvVariableOpts): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withEnvVariable", + args: { name, value, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this container after executing the specified command inside it. + * @param args Command to run instead of the container's default command (e.g., ["run", "main.go"]). + * + * If empty, the container's default command is used. + * @param opts.skipEntrypoint If the container has an entrypoint, ignore it for args rather than using it to wrap them. + * @param opts.stdin Content to write to the command's standard input before closing (e.g., "Hello world"). + * @param opts.redirectStdout Redirect the command's standard output to a file in the container (e.g., "/tmp/stdout"). + * @param opts.redirectStderr Redirect the command's standard error to a file in the container (e.g., "/tmp/stderr"). + * @param opts.experimentalPrivilegedNesting Provides dagger access to the executed command. + * + * Do not use this option unless you trust the command being executed. + * The command being executed WILL BE GRANTED FULL ACCESS TO YOUR HOST FILESYSTEM. + * @param opts.insecureRootCapabilities Execute the command with all root capabilities. This is similar to running a command + * with "sudo" or executing `docker run` with the `--privileged` flag. Containerization + * does not provide any security guarantees when using this option. It should only be used + * when absolutely necessary and only with trusted commands. + */ + withExec(args: string[], opts?: ContainerWithExecOpts): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withExec", + args: { args, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Expose a network port. + * + * Exposed ports serve two purposes: + * - For health checks and introspection, when running services + * - For setting the EXPOSE OCI field when publishing the container + * @param port Port number to expose + * @param opts.protocol Transport layer network protocol + * @param opts.description Optional port description + */ + withExposedPort(port: number, opts?: ContainerWithExposedPortOpts): Container { + const metadata: Metadata = { + protocol: { is_enum: true }, + } + + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withExposedPort", + args: { port, ...opts, __metadata: metadata }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this container plus the contents of the given file copied to the given path. + * @param path Location of the copied file (e.g., "/tmp/file.txt"). + * @param source Identifier of the file to copy. + * @param opts.permissions Permission given to the copied file (e.g., 0600). + * + * Default: 0644. + * @param opts.owner A user:group to set for the file. + * + * The user and group can either be an ID (1000:1000) or a name (foo:bar). + * + * If the group is omitted, it defaults to the same as the user. + */ + withFile(path: string, source: File, opts?: ContainerWithFileOpts): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withFile", + args: { path, source, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Indicate that subsequent operations should be featured more prominently in + * the UI. + */ + withFocus(): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withFocus", + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this container plus the given label. + * @param name The name of the label (e.g., "org.opencontainers.artifact.created"). + * @param value The value of the label (e.g., "2023-01-01T00:00:00Z"). + */ + withLabel(name: string, value: string): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withLabel", + args: { name, value }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this container plus a cache volume mounted at the given path. + * @param path Location of the cache directory (e.g., "/cache/node_modules"). + * @param cache Identifier of the cache volume to mount. + * @param opts.source Identifier of the directory to use as the cache volume's root. + * @param opts.sharing Sharing mode of the cache volume. + * @param opts.owner A user:group to set for the mounted cache directory. + * + * Note that this changes the ownership of the specified mount along with the + * initial filesystem provided by source (if any). It does not have any effect + * if/when the cache has already been created. + * + * The user and group can either be an ID (1000:1000) or a name (foo:bar). + * + * If the group is omitted, it defaults to the same as the user. + */ + withMountedCache(path: string, cache: CacheVolume, opts?: ContainerWithMountedCacheOpts): Container { + const metadata: Metadata = { + sharing: { is_enum: true }, + } + + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withMountedCache", + args: { path, cache, ...opts, __metadata: metadata }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this container plus a directory mounted at the given path. + * @param path Location of the mounted directory (e.g., "/mnt/directory"). + * @param source Identifier of the mounted directory. + * @param opts.owner A user:group to set for the mounted directory and its contents. + * + * The user and group can either be an ID (1000:1000) or a name (foo:bar). + * + * If the group is omitted, it defaults to the same as the user. + */ + withMountedDirectory(path: string, source: Directory, opts?: ContainerWithMountedDirectoryOpts): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withMountedDirectory", + args: { path, source, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this container plus a file mounted at the given path. + * @param path Location of the mounted file (e.g., "/tmp/file.txt"). + * @param source Identifier of the mounted file. + * @param opts.owner A user or user:group to set for the mounted file. + * + * The user and group can either be an ID (1000:1000) or a name (foo:bar). + * + * If the group is omitted, it defaults to the same as the user. + */ + withMountedFile(path: string, source: File, opts?: ContainerWithMountedFileOpts): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withMountedFile", + args: { path, source, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this container plus a secret mounted into a file at the given path. + * @param path Location of the secret file (e.g., "/tmp/secret.txt"). + * @param source Identifier of the secret to mount. + * @param opts.owner A user:group to set for the mounted secret. + * + * The user and group can either be an ID (1000:1000) or a name (foo:bar). + * + * If the group is omitted, it defaults to the same as the user. + * @param opts.mode Permission given to the mounted secret (e.g., 0600). + * This option requires an owner to be set to be active. + * + * Default: 0400. + */ + withMountedSecret(path: string, source: Secret, opts?: ContainerWithMountedSecretOpts): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withMountedSecret", + args: { path, source, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this container plus a temporary directory mounted at the given path. + * @param path Location of the temporary directory (e.g., "/tmp/temp_dir"). + */ + withMountedTemp(path: string): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withMountedTemp", + args: { path }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this container plus a new file written at the given path. + * @param path Location of the written file (e.g., "/tmp/file.txt"). + * @param opts.contents Content of the file to write (e.g., "Hello world!"). + * @param opts.permissions Permission given to the written file (e.g., 0600). + * + * Default: 0644. + * @param opts.owner A user:group to set for the file. + * + * The user and group can either be an ID (1000:1000) or a name (foo:bar). + * + * If the group is omitted, it defaults to the same as the user. + */ + withNewFile(path: string, opts?: ContainerWithNewFileOpts): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withNewFile", + args: { path, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this container with a registry authentication for a given address. + * @param address Registry's address to bind the authentication to. + * Formatted as [host]/[user]/[repo]:[tag] (e.g. docker.io/dagger/dagger:main). + * @param username The username of the registry's account (e.g., "Dagger"). + * @param secret The API key, password or token to authenticate to this registry. + */ + withRegistryAuth(address: string, username: string, secret: Secret): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withRegistryAuth", + args: { address, username, secret }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Initializes this container from this DirectoryID. + */ + withRootfs(directory: Directory): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withRootfs", + args: { directory }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this container plus an env variable containing the given secret. + * @param name The name of the secret variable (e.g., "API_SECRET"). + * @param secret The identifier of the secret value. + */ + withSecretVariable(name: string, secret: Secret): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withSecretVariable", + args: { name, secret }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Establish a runtime dependency on a service. + * + * The service will be started automatically when needed and detached when it is + * no longer needed, executing the default command if none is set. + * + * The service will be reachable from the container via the provided hostname alias. + * + * The service dependency will also convey to any files or directories produced by the container. + * @param alias A name that can be used to reach the service from the container + * @param service Identifier of the service container + */ + withServiceBinding(alias: string, service: Service): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withServiceBinding", + args: { alias, service }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this container plus a socket forwarded to the given Unix socket path. + * @param path Location of the forwarded Unix socket (e.g., "/tmp/socket"). + * @param source Identifier of the socket to forward. + * @param opts.owner A user:group to set for the mounted socket. + * + * The user and group can either be an ID (1000:1000) or a name (foo:bar). + * + * If the group is omitted, it defaults to the same as the user. + */ + withUnixSocket(path: string, source: Socket, opts?: ContainerWithUnixSocketOpts): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withUnixSocket", + args: { path, source, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this container with a different command user. + * @param name The user to set (e.g., "root"). + */ + withUser(name: string): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withUser", + args: { name }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this container with a different working directory. + * @param path The path to set as the working directory (e.g., "/app"). + */ + withWorkdir(path: string): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withWorkdir", + args: { path }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this container minus the given environment variable. + * @param name The name of the environment variable (e.g., "HOST"). + */ + withoutEnvVariable(name: string): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withoutEnvVariable", + args: { name }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Unexpose a previously exposed port. + * @param port Port number to unexpose + * @param opts.protocol Port protocol to unexpose + */ + withoutExposedPort(port: number, opts?: ContainerWithoutExposedPortOpts): Container { + const metadata: Metadata = { + protocol: { is_enum: true }, + } + + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withoutExposedPort", + args: { port, ...opts, __metadata: metadata }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Indicate that subsequent operations should not be featured more prominently + * in the UI. + * + * This is the initial state of all containers. + */ + withoutFocus(): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withoutFocus", + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this container minus the given environment label. + * @param name The name of the label to remove (e.g., "org.opencontainers.artifact.created"). + */ + withoutLabel(name: string): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withoutLabel", + args: { name }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this container after unmounting everything at the given path. + * @param path Location of the cache directory (e.g., "/cache/node_modules"). + */ + withoutMount(path: string): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withoutMount", + args: { path }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this container without the registry authentication of a given address. + * @param address Registry's address to remove the authentication from. + * Formatted as [host]/[user]/[repo]:[tag] (e.g. docker.io/dagger/dagger:main). + */ + withoutRegistryAuth(address: string): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withoutRegistryAuth", + args: { address }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this container with a previously added Unix socket removed. + * @param path Location of the socket to remove (e.g., "/tmp/socket"). + */ + withoutUnixSocket(path: string): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "withoutUnixSocket", + args: { path }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves the working directory for all commands. + */ + async workdir(): Promise { + if (this._workdir) { + return this._workdir + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "workdir", + }, + ], + this.client + ) + + + return response + } + + /** + * Call the provided function with current Container. + * + * This is useful for reusability and readability by not breaking the calling chain. + */ + with(arg: (param: Container) => Container) { + return arg(this) + } +} + + + + + +/** + * A directory. + */ +export class Directory extends BaseClient { + private readonly _id?: DirectoryID = undefined + private readonly _export?: boolean = undefined + private readonly _sync?: DirectoryID = undefined + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + _id?: DirectoryID, + _export?: boolean, + _sync?: DirectoryID, + ) { + super(parent) + + this._id = _id + this._export = _export + this._sync = _sync + } + + /** + * The content-addressed identifier of the directory. + */ + async id(): Promise { + if (this._id) { + return this._id + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "id", + }, + ], + this.client + ) + + + return response + } + + /** + * Load the directory as a Dagger module + * @param opts.sourceSubpath An optional subpath of the directory which contains the module's source + * code. + * + * This is needed when the module code is in a subdirectory but requires + * parent directories to be loaded in order to execute. For example, the + * module source code may need a go.mod, project.toml, package.json, etc. file + * from a parent directory. + * + * If not set, the module source code is loaded from the root of the + * directory. + */ + asModule(opts?: DirectoryAsModuleOpts): Module_ { + return new Module_({ + queryTree: [ + ...this._queryTree, + { + operation: "asModule", + args: { ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Gets the difference between this directory and an another directory. + * @param other Identifier of the directory to compare. + */ + diff(other: Directory): Directory { + return new Directory({ + queryTree: [ + ...this._queryTree, + { + operation: "diff", + args: { other }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves a directory at the given path. + * @param path Location of the directory to retrieve (e.g., "/src"). + */ + directory(path: string): Directory { + return new Directory({ + queryTree: [ + ...this._queryTree, + { + operation: "directory", + args: { path }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Builds a new Docker container from this directory. + * @param opts.dockerfile Path to the Dockerfile to use (e.g., "frontend.Dockerfile"). + * + * Defaults: './Dockerfile'. + * @param opts.platform The platform to build. + * @param opts.buildArgs Build arguments to use in the build. + * @param opts.target Target build stage to build. + * @param opts.secrets Secrets to pass to the build. + * + * They will be mounted at /run/secrets/[secret-name]. + */ + dockerBuild(opts?: DirectoryDockerBuildOpts): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "dockerBuild", + args: { ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Returns a list of files and directories at the given path. + * @param opts.path Location of the directory to look at (e.g., "/src"). + */ + async entries(opts?: DirectoryEntriesOpts): Promise { + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "entries", + args: { ...opts }, + }, + ], + this.client + ) + + + return response + } + + /** + * Writes the contents of the directory to a path on the host. + * @param path Location of the copied directory (e.g., "logs/"). + */ + async export(path: string): Promise { + if (this._export) { + return this._export + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "export", + args: { path }, + }, + ], + this.client + ) + + + return response + } + + /** + * Retrieves a file at the given path. + * @param path Location of the file to retrieve (e.g., "README.md"). + */ + file(path: string): File { + return new File({ + queryTree: [ + ...this._queryTree, + { + operation: "file", + args: { path }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Returns a list of files and directories that matche the given pattern. + * @param pattern Pattern to match (e.g., "*.md"). + */ + async glob(pattern: string): Promise { + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "glob", + args: { pattern }, + }, + ], + this.client + ) + + + return response + } + + /** + * Creates a named sub-pipeline + * @param name Pipeline name. + * @param opts.description Pipeline description. + * @param opts.labels Pipeline labels. + */ + pipeline(name: string, opts?: DirectoryPipelineOpts): Directory { + return new Directory({ + queryTree: [ + ...this._queryTree, + { + operation: "pipeline", + args: { name, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Force evaluation in the engine. + */ + async sync(): Promise { + await computeQuery( + [ + ...this._queryTree, + { + operation: "sync", + }, + ], + this.client + ) + + return this + } + + /** + * Retrieves this directory plus a directory written at the given path. + * @param path Location of the written directory (e.g., "/src/"). + * @param directory Identifier of the directory to copy. + * @param opts.exclude Exclude artifacts that match the given pattern (e.g., ["node_modules/", ".git*"]). + * @param opts.include Include only artifacts that match the given pattern (e.g., ["app/", "package.*"]). + */ + withDirectory(path: string, directory: Directory, opts?: DirectoryWithDirectoryOpts): Directory { + return new Directory({ + queryTree: [ + ...this._queryTree, + { + operation: "withDirectory", + args: { path, directory, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this directory plus the contents of the given file copied to the given path. + * @param path Location of the copied file (e.g., "/file.txt"). + * @param source Identifier of the file to copy. + * @param opts.permissions Permission given to the copied file (e.g., 0600). + * + * Default: 0644. + */ + withFile(path: string, source: File, opts?: DirectoryWithFileOpts): Directory { + return new Directory({ + queryTree: [ + ...this._queryTree, + { + operation: "withFile", + args: { path, source, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this directory plus a new directory created at the given path. + * @param path Location of the directory created (e.g., "/logs"). + * @param opts.permissions Permission granted to the created directory (e.g., 0777). + * + * Default: 0755. + */ + withNewDirectory(path: string, opts?: DirectoryWithNewDirectoryOpts): Directory { + return new Directory({ + queryTree: [ + ...this._queryTree, + { + operation: "withNewDirectory", + args: { path, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this directory plus a new file written at the given path. + * @param path Location of the written file (e.g., "/file.txt"). + * @param contents Content of the written file (e.g., "Hello world!"). + * @param opts.permissions Permission given to the copied file (e.g., 0600). + * + * Default: 0644. + */ + withNewFile(path: string, contents: string, opts?: DirectoryWithNewFileOpts): Directory { + return new Directory({ + queryTree: [ + ...this._queryTree, + { + operation: "withNewFile", + args: { path, contents, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this directory with all file/dir timestamps set to the given time. + * @param timestamp Timestamp to set dir/files in. + * + * Formatted in seconds following Unix epoch (e.g., 1672531199). + */ + withTimestamps(timestamp: number): Directory { + return new Directory({ + queryTree: [ + ...this._queryTree, + { + operation: "withTimestamps", + args: { timestamp }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this directory with the directory at the given path removed. + * @param path Location of the directory to remove (e.g., ".github/"). + */ + withoutDirectory(path: string): Directory { + return new Directory({ + queryTree: [ + ...this._queryTree, + { + operation: "withoutDirectory", + args: { path }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Retrieves this directory with the file at the given path removed. + * @param path Location of the file to remove (e.g., "/file.txt"). + */ + withoutFile(path: string): Directory { + return new Directory({ + queryTree: [ + ...this._queryTree, + { + operation: "withoutFile", + args: { path }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Call the provided function with current Directory. + * + * This is useful for reusability and readability by not breaking the calling chain. + */ + with(arg: (param: Directory) => Directory) { + return arg(this) + } +} + + + +/** + * A simple key value object that represents an environment variable. + */ +export class EnvVariable extends BaseClient { + private readonly _name?: string = undefined + private readonly _value?: string = undefined + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + _name?: string, + _value?: string, + ) { + super(parent) + + this._name = _name + this._value = _value + } + + /** + * The environment variable name. + */ + async name(): Promise { + if (this._name) { + return this._name + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "name", + }, + ], + this.client + ) + + + return response + } + + /** + * The environment variable value. + */ + async value(): Promise { + if (this._value) { + return this._value + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "value", + }, + ], + this.client + ) + + + return response + } +} + +/** + * A definition of a field on a custom object defined in a Module. + * A field on an object has a static value, as opposed to a function on an + * object whose value is computed by invoking code (and can accept arguments). + */ +export class FieldTypeDef extends BaseClient { + private readonly _description?: string = undefined + private readonly _name?: string = undefined + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + _description?: string, + _name?: string, + ) { + super(parent) + + this._description = _description + this._name = _name + } + + /** + * A doc string for the field, if any + */ + async description(): Promise { + if (this._description) { + return this._description + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "description", + }, + ], + this.client + ) + + + return response + } + + /** + * The name of the field in the object + */ + async name(): Promise { + if (this._name) { + return this._name + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "name", + }, + ], + this.client + ) + + + return response + } + + /** + * The type of the field + */ + typeDef(): TypeDef { + return new TypeDef({ + queryTree: [ + ...this._queryTree, + { + operation: "typeDef", + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } +} + +/** + * A file. + */ +export class File extends BaseClient { + private readonly _id?: FileID = undefined + private readonly _contents?: string = undefined + private readonly _export?: boolean = undefined + private readonly _size?: number = undefined + private readonly _sync?: FileID = undefined + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + _id?: FileID, + _contents?: string, + _export?: boolean, + _size?: number, + _sync?: FileID, + ) { + super(parent) + + this._id = _id + this._contents = _contents + this._export = _export + this._size = _size + this._sync = _sync + } + + /** + * Retrieves the content-addressed identifier of the file. + */ + async id(): Promise { + if (this._id) { + return this._id + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "id", + }, + ], + this.client + ) + + + return response + } + + /** + * Retrieves the contents of the file. + */ + async contents(): Promise { + if (this._contents) { + return this._contents + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "contents", + }, + ], + this.client + ) + + + return response + } + + /** + * Writes the file to a file path on the host. + * @param path Location of the written directory (e.g., "output.txt"). + * @param opts.allowParentDirPath If allowParentDirPath is true, the path argument can be a directory path, in which case + * the file will be created in that directory. + */ + async export(path: string, opts?: FileExportOpts): Promise { + if (this._export) { + return this._export + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "export", + args: { path, ...opts }, + }, + ], + this.client + ) + + + return response + } + + /** + * Gets the size of the file, in bytes. + */ + async size(): Promise { + if (this._size) { + return this._size + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "size", + }, + ], + this.client + ) + + + return response + } + + /** + * Force evaluation in the engine. + */ + async sync(): Promise { + await computeQuery( + [ + ...this._queryTree, + { + operation: "sync", + }, + ], + this.client + ) + + return this + } + + /** + * Retrieves this file with its created/modified timestamps set to the given time. + * @param timestamp Timestamp to set dir/files in. + * + * Formatted in seconds following Unix epoch (e.g., 1672531199). + */ + withTimestamps(timestamp: number): File { + return new File({ + queryTree: [ + ...this._queryTree, + { + operation: "withTimestamps", + args: { timestamp }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Call the provided function with current File. + * + * This is useful for reusability and readability by not breaking the calling chain. + */ + with(arg: (param: File) => File) { + return arg(this) + } +} + + + + + +/** + * Function represents a resolver provided by a Module. + * + * A function always evaluates against a parent object and is given a set of + * named arguments. + */ +export class Function_ extends BaseClient { + private readonly _id?: FunctionID = undefined + private readonly _description?: string = undefined + private readonly _name?: string = undefined + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + _id?: FunctionID, + _description?: string, + _name?: string, + ) { + super(parent) + + this._id = _id + this._description = _description + this._name = _name + } + + /** + * The ID of the function + */ + async id(): Promise { + if (this._id) { + return this._id + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "id", + }, + ], + this.client + ) + + + return response + } + + /** + * Arguments accepted by this function, if any + */ + async args(): Promise { + type args = { + id: FunctionArgID + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "args", + }, + { + operation: "id" + }, + ], + this.client + ) + + + return response.map( + (r) => new FunctionArg( + { + queryTree: this.queryTree, + host: this.clientHost, + sessionToken: this.sessionToken, + }, + r.id, + ) + ) + } + + /** + * A doc string for the function, if any + */ + async description(): Promise { + if (this._description) { + return this._description + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "description", + }, + ], + this.client + ) + + + return response + } + + /** + * The name of the function + */ + async name(): Promise { + if (this._name) { + return this._name + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "name", + }, + ], + this.client + ) + + + return response + } + + /** + * The type returned by this function + */ + returnType(): TypeDef { + return new TypeDef({ + queryTree: [ + ...this._queryTree, + { + operation: "returnType", + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Returns the function with the provided argument + * @param name The name of the argument + * @param typeDef The type of the argument + * @param opts.description A doc string for the argument, if any + * @param opts.defaultValue A default value to use for this argument if not explicitly set by the caller, if any + */ + withArg(name: string, typeDef: TypeDef, opts?: FunctionWithArgOpts): Function_ { + return new Function_({ + queryTree: [ + ...this._queryTree, + { + operation: "withArg", + args: { name, typeDef, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Returns the function with the doc string + */ + withDescription(description: string): Function_ { + return new Function_({ + queryTree: [ + ...this._queryTree, + { + operation: "withDescription", + args: { description }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Call the provided function with current Function. + * + * This is useful for reusability and readability by not breaking the calling chain. + */ + with(arg: (param: Function_) => Function_) { + return arg(this) + } +} + +/** + * An argument accepted by a function. + * + * This is a specification for an argument at function definition time, not an + * argument passed at function call time. + */ +export class FunctionArg extends BaseClient { + private readonly _id?: FunctionArgID = undefined + private readonly _defaultValue?: JSON = undefined + private readonly _description?: string = undefined + private readonly _name?: string = undefined + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + _id?: FunctionArgID, + _defaultValue?: JSON, + _description?: string, + _name?: string, + ) { + super(parent) + + this._id = _id + this._defaultValue = _defaultValue + this._description = _description + this._name = _name + } + + /** + * The ID of the argument + */ + async id(): Promise { + if (this._id) { + return this._id + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "id", + }, + ], + this.client + ) + + + return response + } + + /** + * A default value to use for this argument when not explicitly set by the caller, if any + */ + async defaultValue(): Promise { + if (this._defaultValue) { + return this._defaultValue + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "defaultValue", + }, + ], + this.client + ) + + + return response + } + + /** + * A doc string for the argument, if any + */ + async description(): Promise { + if (this._description) { + return this._description + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "description", + }, + ], + this.client + ) + + + return response + } + + /** + * The name of the argument + */ + async name(): Promise { + if (this._name) { + return this._name + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "name", + }, + ], + this.client + ) + + + return response + } + + /** + * The type of the argument + */ + typeDef(): TypeDef { + return new TypeDef({ + queryTree: [ + ...this._queryTree, + { + operation: "typeDef", + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } +} + + + + +export class FunctionCall extends BaseClient { + private readonly _name?: string = undefined + private readonly _parent?: JSON = undefined + private readonly _parentName?: string = undefined + private readonly _returnValue?: Void = undefined + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + _name?: string, + _parent?: JSON, + _parentName?: string, + _returnValue?: Void, + ) { + super(parent) + + this._name = _name + this._parent = _parent + this._parentName = _parentName + this._returnValue = _returnValue + } + + /** + * The argument values the function is being invoked with. + */ + async inputArgs(): Promise { + type inputArgs = { + name: string + value: JSON + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "inputArgs", + }, + { + operation: "name value" + }, + ], + this.client + ) + + + return response.map( + (r) => new FunctionCallArgValue( + { + queryTree: this.queryTree, + host: this.clientHost, + sessionToken: this.sessionToken, + }, + r.name, + r.value, + ) + ) + } + + /** + * The name of the function being called. + */ + async name(): Promise { + if (this._name) { + return this._name + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "name", + }, + ], + this.client + ) + + + return response + } + + /** + * The value of the parent object of the function being called. + * If the function is "top-level" to the module, this is always an empty object. + */ + async parent(): Promise { + if (this._parent) { + return this._parent + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "parent", + }, + ], + this.client + ) + + + return response + } + + /** + * The name of the parent object of the function being called. + * If the function is "top-level" to the module, this is the name of the module. + */ + async parentName(): Promise { + if (this._parentName) { + return this._parentName + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "parentName", + }, + ], + this.client + ) + + + return response + } + + /** + * Set the return value of the function call to the provided value. + * The value should be a string of the JSON serialization of the return value. + */ + async returnValue(value: JSON): Promise { + if (this._returnValue) { + return this._returnValue + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "returnValue", + args: { value }, + }, + ], + this.client + ) + + + return response + } +} + + +export class FunctionCallArgValue extends BaseClient { + private readonly _name?: string = undefined + private readonly _value?: JSON = undefined + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + _name?: string, + _value?: JSON, + ) { + super(parent) + + this._name = _name + this._value = _value + } + + /** + * The name of the argument. + */ + async name(): Promise { + if (this._name) { + return this._name + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "name", + }, + ], + this.client + ) + + + return response + } + + /** + * The value of the argument represented as a string of the JSON serialization. + */ + async value(): Promise { + if (this._value) { + return this._value + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "value", + }, + ], + this.client + ) + + + return response + } +} + + + + +export class GeneratedCode extends BaseClient { + private readonly _id?: GeneratedCodeID = undefined + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + _id?: GeneratedCodeID, + ) { + super(parent) + + this._id = _id + } + async id(): Promise { + if (this._id) { + return this._id + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "id", + }, + ], + this.client + ) + + + return response + } + + /** + * The directory containing the generated code + */ + code(): Directory { + return new Directory({ + queryTree: [ + ...this._queryTree, + { + operation: "code", + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * List of paths to mark generated in version control (i.e. .gitattributes) + */ + async vcsGeneratedPaths(): Promise { + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "vcsGeneratedPaths", + }, + ], + this.client + ) + + + return response + } + + /** + * List of paths to ignore in version control (i.e. .gitignore) + */ + async vcsIgnoredPaths(): Promise { + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "vcsIgnoredPaths", + }, + ], + this.client + ) + + + return response + } + + /** + * Set the list of paths to mark generated in version control + */ + withVCSGeneratedPaths(paths: string[]): GeneratedCode { + return new GeneratedCode({ + queryTree: [ + ...this._queryTree, + { + operation: "withVCSGeneratedPaths", + args: { paths }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Set the list of paths to ignore in version control + */ + withVCSIgnoredPaths(paths: string[]): GeneratedCode { + return new GeneratedCode({ + queryTree: [ + ...this._queryTree, + { + operation: "withVCSIgnoredPaths", + args: { paths }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Call the provided function with current GeneratedCode. + * + * This is useful for reusability and readability by not breaking the calling chain. + */ + with(arg: (param: GeneratedCode) => GeneratedCode) { + return arg(this) + } +} + + + +/** + * A git ref (tag, branch or commit). + */ +export class GitRef extends BaseClient { + private readonly _commit?: string = undefined + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + _commit?: string, + ) { + super(parent) + + this._commit = _commit + } + + /** + * The resolved commit id at this ref. + */ + async commit(): Promise { + if (this._commit) { + return this._commit + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "commit", + }, + ], + this.client + ) + + + return response + } + + /** + * The filesystem tree at this ref. + */ + tree(opts?: GitRefTreeOpts): Directory { + return new Directory({ + queryTree: [ + ...this._queryTree, + { + operation: "tree", + args: { ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } +} + +/** + * A git repository. + */ +export class GitRepository extends BaseClient { + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + ) { + super(parent) + + } + + /** + * Returns details on one branch. + * @param name Branch's name (e.g., "main"). + */ + branch(name: string): GitRef { + return new GitRef({ + queryTree: [ + ...this._queryTree, + { + operation: "branch", + args: { name }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Returns details on one commit. + * @param id Identifier of the commit (e.g., "b6315d8f2810962c601af73f86831f6866ea798b"). + */ + commit(id: string): GitRef { + return new GitRef({ + queryTree: [ + ...this._queryTree, + { + operation: "commit", + args: { id }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Returns details on one tag. + * @param name Tag's name (e.g., "v0.3.9"). + */ + tag(name: string): GitRef { + return new GitRef({ + queryTree: [ + ...this._queryTree, + { + operation: "tag", + args: { name }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } +} + +/** + * Information about the host execution environment. + */ +export class Host extends BaseClient { + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + ) { + super(parent) + + } + + /** + * Accesses a directory on the host. + * @param path Location of the directory to access (e.g., "."). + * @param opts.exclude Exclude artifacts that match the given pattern (e.g., ["node_modules/", ".git*"]). + * @param opts.include Include only artifacts that match the given pattern (e.g., ["app/", "package.*"]). + */ + directory(path: string, opts?: HostDirectoryOpts): Directory { + return new Directory({ + queryTree: [ + ...this._queryTree, + { + operation: "directory", + args: { path, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Accesses a file on the host. + * @param path Location of the file to retrieve (e.g., "README.md"). + */ + file(path: string): File { + return new File({ + queryTree: [ + ...this._queryTree, + { + operation: "file", + args: { path }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Creates a service that forwards traffic to a specified address via the host. + * @param ports Ports to expose via the service, forwarding through the host network. + * + * If a port's frontend is unspecified or 0, it defaults to the same as the + * backend port. + * + * An empty set of ports is not valid; an error will be returned. + * @param opts.host Upstream host to forward traffic to. + */ + service(ports: PortForward[], opts?: HostServiceOpts): Service { + return new Service({ + queryTree: [ + ...this._queryTree, + { + operation: "service", + args: { ports, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Sets a secret given a user-defined name and the file path on the host, and returns the secret. + * The file is limited to a size of 512000 bytes. + * @param name The user defined name for this secret. + * @param path Location of the file to set as a secret. + */ + setSecretFile(name: string, path: string): Secret { + return new Secret({ + queryTree: [ + ...this._queryTree, + { + operation: "setSecretFile", + args: { name, path }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Creates a tunnel that forwards traffic from the host to a service. + * @param service Service to send traffic from the tunnel. + * @param opts.native Map each service port to the same port on the host, as if the service were + * running natively. + * + * Note: enabling may result in port conflicts. + * @param opts.ports Configure explicit port forwarding rules for the tunnel. + * + * If a port's frontend is unspecified or 0, a random port will be chosen by + * the host. + * + * If no ports are given, all of the service's ports are forwarded. If native + * is true, each port maps to the same port on the host. If native is false, + * each port maps to a random port chosen by the host. + * + * If ports are given and native is true, the ports are additive. + */ + tunnel(service: Service, opts?: HostTunnelOpts): Service { + return new Service({ + queryTree: [ + ...this._queryTree, + { + operation: "tunnel", + args: { service, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Accesses a Unix socket on the host. + * @param path Location of the Unix socket (e.g., "/var/run/docker.sock"). + */ + unixSocket(path: string): Socket { + return new Socket({ + queryTree: [ + ...this._queryTree, + { + operation: "unixSocket", + args: { path }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } +} + + + + + + + + + + + +/** + * A simple key value object that represents a label. + */ +export class Label extends BaseClient { + private readonly _name?: string = undefined + private readonly _value?: string = undefined + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + _name?: string, + _value?: string, + ) { + super(parent) + + this._name = _name + this._value = _value + } + + /** + * The label name. + */ + async name(): Promise { + if (this._name) { + return this._name + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "name", + }, + ], + this.client + ) + + + return response + } + + /** + * The label value. + */ + async value(): Promise { + if (this._value) { + return this._value + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "value", + }, + ], + this.client + ) + + + return response + } +} + +/** + * A definition of a list type in a Module. + */ +export class ListTypeDef extends BaseClient { + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + ) { + super(parent) + + } + + /** + * The type of the elements in the list + */ + elementTypeDef(): TypeDef { + return new TypeDef({ + queryTree: [ + ...this._queryTree, + { + operation: "elementTypeDef", + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } +} + + +export class Module_ extends BaseClient { + private readonly _id?: ModuleID = undefined + private readonly _description?: string = undefined + private readonly _name?: string = undefined + private readonly _sdk?: string = undefined + private readonly _serve?: Void = undefined + private readonly _sourceDirectorySubPath?: string = undefined + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + _id?: ModuleID, + _description?: string, + _name?: string, + _sdk?: string, + _serve?: Void, + _sourceDirectorySubPath?: string, + ) { + super(parent) + + this._id = _id + this._description = _description + this._name = _name + this._sdk = _sdk + this._serve = _serve + this._sourceDirectorySubPath = _sourceDirectorySubPath + } + + /** + * The ID of the module + */ + async id(): Promise { + if (this._id) { + return this._id + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "id", + }, + ], + this.client + ) + + + return response + } + + /** + * Modules used by this module + */ + async dependencies(): Promise { + type dependencies = { + id: ModuleID + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "dependencies", + }, + { + operation: "id" + }, + ], + this.client + ) + + + return response.map( + (r) => new Module_( + { + queryTree: this.queryTree, + host: this.clientHost, + sessionToken: this.sessionToken, + }, + r.id, + ) + ) + } + + /** + * The dependencies as configured by the module + */ + async dependencyConfig(): Promise { + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "dependencyConfig", + }, + ], + this.client + ) + + + return response + } + + /** + * The doc string of the module, if any + */ + async description(): Promise { + if (this._description) { + return this._description + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "description", + }, + ], + this.client + ) + + + return response + } + + /** + * The code generated by the SDK's runtime + */ + generatedCode(): GeneratedCode { + return new GeneratedCode({ + queryTree: [ + ...this._queryTree, + { + operation: "generatedCode", + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * The name of the module + */ + async name(): Promise { + if (this._name) { + return this._name + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "name", + }, + ], + this.client + ) + + + return response + } + + /** + * Objects served by this module + */ + async objects(): Promise { + type objects = { + id: TypeDefID + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "objects", + }, + { + operation: "id" + }, + ], + this.client + ) + + + return response.map( + (r) => new TypeDef( + { + queryTree: this.queryTree, + host: this.clientHost, + sessionToken: this.sessionToken, + }, + r.id, + ) + ) + } + + /** + * The SDK used by this module. Either a name of a builtin SDK or a module ref pointing to the SDK's implementation. + */ + async sdk(): Promise { + if (this._sdk) { + return this._sdk + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "sdk", + }, + ], + this.client + ) + + + return response + } + + /** + * Serve a module's API in the current session. + * Note: this can only be called once per session. + * In the future, it could return a stream or service to remove the side effect. + */ + async serve(): Promise { + if (this._serve) { + return this._serve + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "serve", + }, + ], + this.client + ) + + + return response + } + + /** + * The directory containing the module's source code + */ + sourceDirectory(): Directory { + return new Directory({ + queryTree: [ + ...this._queryTree, + { + operation: "sourceDirectory", + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * The module's subpath within the source directory + */ + async sourceDirectorySubPath(): Promise { + if (this._sourceDirectorySubPath) { + return this._sourceDirectorySubPath + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "sourceDirectorySubPath", + }, + ], + this.client + ) + + + return response + } + + /** + * This module plus the given Object type and associated functions + */ + withObject(object: TypeDef): Module_ { + return new Module_({ + queryTree: [ + ...this._queryTree, + { + operation: "withObject", + args: { object }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Call the provided function with current Module. + * + * This is useful for reusability and readability by not breaking the calling chain. + */ + with(arg: (param: Module_) => Module_) { + return arg(this) + } +} + +/** + * Static configuration for a module (e.g. parsed contents of dagger.json) + */ +export class ModuleConfig extends BaseClient { + private readonly _name?: string = undefined + private readonly _root?: string = undefined + private readonly _sdk?: string = undefined + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + _name?: string, + _root?: string, + _sdk?: string, + ) { + super(parent) + + this._name = _name + this._root = _root + this._sdk = _sdk + } + + /** + * Modules that this module depends on. + */ + async dependencies(): Promise { + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "dependencies", + }, + ], + this.client + ) + + + return response + } + + /** + * Exclude these file globs when loading the module root. + */ + async exclude(): Promise { + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "exclude", + }, + ], + this.client + ) + + + return response + } + + /** + * Include only these file globs when loading the module root. + */ + async include(): Promise { + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "include", + }, + ], + this.client + ) + + + return response + } + + /** + * The name of the module. + */ + async name(): Promise { + if (this._name) { + return this._name + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "name", + }, + ], + this.client + ) + + + return response + } + + /** + * The root directory of the module's project, which may be above the module source code. + */ + async root(): Promise { + if (this._root) { + return this._root + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "root", + }, + ], + this.client + ) + + + return response + } + + /** + * Either the name of a built-in SDK ('go', 'python', etc.) OR a module reference pointing to the SDK's module implementation. + */ + async sdk(): Promise { + if (this._sdk) { + return this._sdk + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "sdk", + }, + ], + this.client + ) + + + return response + } +} + + + + + +/** + * A definition of a custom object defined in a Module. + */ +export class ObjectTypeDef extends BaseClient { + private readonly _description?: string = undefined + private readonly _name?: string = undefined + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + _description?: string, + _name?: string, + ) { + super(parent) + + this._description = _description + this._name = _name + } + + /** + * The doc string for the object, if any + */ + async description(): Promise { + if (this._description) { + return this._description + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "description", + }, + ], + this.client + ) + + + return response + } + + /** + * Static fields defined on this object, if any + */ + async fields(): Promise { + type fields = { + description: string + name: string + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "fields", + }, + { + operation: "description name" + }, + ], + this.client + ) + + + return response.map( + (r) => new FieldTypeDef( + { + queryTree: this.queryTree, + host: this.clientHost, + sessionToken: this.sessionToken, + }, + r.description, + r.name, + ) + ) + } + + /** + * Functions defined on this object, if any + */ + async functions(): Promise { + type functions = { + id: FunctionID + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "functions", + }, + { + operation: "id" + }, + ], + this.client + ) + + + return response.map( + (r) => new Function_( + { + queryTree: this.queryTree, + host: this.clientHost, + sessionToken: this.sessionToken, + }, + r.id, + ) + ) + } + + /** + * The name of the object + */ + async name(): Promise { + if (this._name) { + return this._name + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "name", + }, + ], + this.client + ) + + + return response + } +} + + + + + +/** + * A port exposed by a container. + */ +export class Port extends BaseClient { + private readonly _description?: string = undefined + private readonly _port?: number = undefined + private readonly _protocol?: NetworkProtocol = undefined + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + _description?: string, + _port?: number, + _protocol?: NetworkProtocol, + ) { + super(parent) + + this._description = _description + this._port = _port + this._protocol = _protocol + } + + /** + * The port description. + */ + async description(): Promise { + if (this._description) { + return this._description + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "description", + }, + ], + this.client + ) + + + return response + } + + /** + * The port number. + */ + async port(): Promise { + if (this._port) { + return this._port + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "port", + }, + ], + this.client + ) + + + return response + } + + /** + * The transport layer network protocol. + */ + async protocol(): Promise { + if (this._protocol) { + return this._protocol + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "protocol", + }, + ], + this.client + ) + + + return response + } +} + + + + +export class Client extends BaseClient { + private readonly _checkVersionCompatibility?: boolean = undefined + private readonly _defaultPlatform?: Platform = undefined + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + _checkVersionCompatibility?: boolean, + _defaultPlatform?: Platform, + ) { + super(parent) + + this._checkVersionCompatibility = _checkVersionCompatibility + this._defaultPlatform = _defaultPlatform + } + + /** + * Constructs a cache volume for a given cache key. + * @param key A string identifier to target this cache volume (e.g., "modules-cache"). + */ + cacheVolume(key: string): CacheVolume { + return new CacheVolume({ + queryTree: [ + ...this._queryTree, + { + operation: "cacheVolume", + args: { key }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Checks if the current Dagger Engine is compatible with an SDK's required version. + * @param version The SDK's required version. + */ + async checkVersionCompatibility(version: string): Promise { + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "checkVersionCompatibility", + args: { version }, + }, + ], + this.client + ) + + + return response + } + + /** + * Creates a scratch container or loads one by ID. + * + * Optional platform argument initializes new containers to execute and publish + * as that platform. Platform defaults to that of the builder's host. + */ + container(opts?: ClientContainerOpts): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "container", + args: { ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * The FunctionCall context that the SDK caller is currently executing in. + * If the caller is not currently executing in a function, this will return + * an error. + */ + currentFunctionCall(): FunctionCall { + return new FunctionCall({ + queryTree: [ + ...this._queryTree, + { + operation: "currentFunctionCall", + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * The module currently being served in the session, if any. + */ + currentModule(): Module_ { + return new Module_({ + queryTree: [ + ...this._queryTree, + { + operation: "currentModule", + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * The default platform of the builder. + */ + async defaultPlatform(): Promise { + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "defaultPlatform", + }, + ], + this.client + ) + + + return response + } + + /** + * Creates an empty directory or loads one by ID. + */ + directory(opts?: ClientDirectoryOpts): Directory { + return new Directory({ + queryTree: [ + ...this._queryTree, + { + operation: "directory", + args: { ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Loads a file by ID. + * @deprecated Use loadFileFromID instead. + */ + file(id: FileID): File { + return new File({ + queryTree: [ + ...this._queryTree, + { + operation: "file", + args: { id }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Create a function. + */ + function_(name: string, returnType: TypeDef): Function_ { + return new Function_({ + queryTree: [ + ...this._queryTree, + { + operation: "function", + args: { name, returnType }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Create a code generation result, given a directory containing the generated + * code. + */ + generatedCode(code: Directory): GeneratedCode { + return new GeneratedCode({ + queryTree: [ + ...this._queryTree, + { + operation: "generatedCode", + args: { code }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Queries a git repository. + * @param url Url of the git repository. + * Can be formatted as https://{host}/{owner}/{repo}, git@{host}:{owner}/{repo} + * Suffix ".git" is optional. + * @param opts.keepGitDir Set to true to keep .git directory. + * @param opts.sshKnownHosts Set SSH known hosts + * @param opts.sshAuthSocket Set SSH auth socket + * @param opts.experimentalServiceHost A service which must be started before the repo is fetched. + */ + git(url: string, opts?: ClientGitOpts): GitRepository { + return new GitRepository({ + queryTree: [ + ...this._queryTree, + { + operation: "git", + args: { url, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Queries the host environment. + */ + host(): Host { + return new Host({ + queryTree: [ + ...this._queryTree, + { + operation: "host", + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Returns a file containing an http remote url content. + * @param url HTTP url to get the content from (e.g., "https://docs.dagger.io"). + * @param opts.experimentalServiceHost A service which must be started before the URL is fetched. + */ + http(url: string, opts?: ClientHttpOpts): File { + return new File({ + queryTree: [ + ...this._queryTree, + { + operation: "http", + args: { url, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Load a CacheVolume from its ID. + */ + loadCacheVolumeFromID(id: CacheVolumeID): CacheVolume { + return new CacheVolume({ + queryTree: [ + ...this._queryTree, + { + operation: "loadCacheVolumeFromID", + args: { id }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Loads a container from an ID. + */ + loadContainerFromID(id: ContainerID): Container { + return new Container({ + queryTree: [ + ...this._queryTree, + { + operation: "loadContainerFromID", + args: { id }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Load a Directory from its ID. + */ + loadDirectoryFromID(id: DirectoryID): Directory { + return new Directory({ + queryTree: [ + ...this._queryTree, + { + operation: "loadDirectoryFromID", + args: { id }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Load a File from its ID. + */ + loadFileFromID(id: FileID): File { + return new File({ + queryTree: [ + ...this._queryTree, + { + operation: "loadFileFromID", + args: { id }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Load a function argument by ID. + */ + loadFunctionArgFromID(id: FunctionArgID): FunctionArg { + return new FunctionArg({ + queryTree: [ + ...this._queryTree, + { + operation: "loadFunctionArgFromID", + args: { id }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Load a function by ID. + */ + loadFunctionFromID(id: FunctionID): Function_ { + return new Function_({ + queryTree: [ + ...this._queryTree, + { + operation: "loadFunctionFromID", + args: { id }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Load a GeneratedCode by ID. + */ + loadGeneratedCodeFromID(id: GeneratedCodeID): GeneratedCode { + return new GeneratedCode({ + queryTree: [ + ...this._queryTree, + { + operation: "loadGeneratedCodeFromID", + args: { id }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Load a module by ID. + */ + loadModuleFromID(id: ModuleID): Module_ { + return new Module_({ + queryTree: [ + ...this._queryTree, + { + operation: "loadModuleFromID", + args: { id }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Load a Secret from its ID. + */ + loadSecretFromID(id: SecretID): Secret { + return new Secret({ + queryTree: [ + ...this._queryTree, + { + operation: "loadSecretFromID", + args: { id }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Loads a service from ID. + */ + loadServiceFromID(id: ServiceID): Service { + return new Service({ + queryTree: [ + ...this._queryTree, + { + operation: "loadServiceFromID", + args: { id }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Load a Socket from its ID. + */ + loadSocketFromID(id: SocketID): Socket { + return new Socket({ + queryTree: [ + ...this._queryTree, + { + operation: "loadSocketFromID", + args: { id }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Load a TypeDef by ID. + */ + loadTypeDefFromID(id: TypeDefID): TypeDef { + return new TypeDef({ + queryTree: [ + ...this._queryTree, + { + operation: "loadTypeDefFromID", + args: { id }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Create a new module. + */ + module_(): Module_ { + return new Module_({ + queryTree: [ + ...this._queryTree, + { + operation: "module", + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Load the static configuration for a module from the given source directory and optional subpath. + */ + moduleConfig(sourceDirectory: Directory, opts?: ClientModuleConfigOpts): ModuleConfig { + return new ModuleConfig({ + queryTree: [ + ...this._queryTree, + { + operation: "moduleConfig", + args: { sourceDirectory, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Creates a named sub-pipeline. + * @param name Pipeline name. + * @param opts.description Pipeline description. + * @param opts.labels Pipeline labels. + */ + pipeline(name: string, opts?: ClientPipelineOpts): Client { + return new Client({ + queryTree: [ + ...this._queryTree, + { + operation: "pipeline", + args: { name, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Loads a secret from its ID. + * @deprecated Use loadSecretFromID instead + */ + secret(id: SecretID): Secret { + return new Secret({ + queryTree: [ + ...this._queryTree, + { + operation: "secret", + args: { id }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Sets a secret given a user defined name to its plaintext and returns the secret. + * The plaintext value is limited to a size of 128000 bytes. + * @param name The user defined name for this secret + * @param plaintext The plaintext of the secret + */ + setSecret(name: string, plaintext: string): Secret { + return new Secret({ + queryTree: [ + ...this._queryTree, + { + operation: "setSecret", + args: { name, plaintext }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Loads a socket by its ID. + * @deprecated Use loadSocketFromID instead. + */ + socket(opts?: ClientSocketOpts): Socket { + return new Socket({ + queryTree: [ + ...this._queryTree, + { + operation: "socket", + args: { ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Create a new TypeDef. + */ + typeDef(): TypeDef { + return new TypeDef({ + queryTree: [ + ...this._queryTree, + { + operation: "typeDef", + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Call the provided function with current Client. + * + * This is useful for reusability and readability by not breaking the calling chain. + */ + with(arg: (param: Client) => Client) { + return arg(this) + } +} + +/** + * A reference to a secret value, which can be handled more safely than the value itself. + */ +export class Secret extends BaseClient { + private readonly _id?: SecretID = undefined + private readonly _plaintext?: string = undefined + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + _id?: SecretID, + _plaintext?: string, + ) { + super(parent) + + this._id = _id + this._plaintext = _plaintext + } + + /** + * The identifier for this secret. + */ + async id(): Promise { + if (this._id) { + return this._id + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "id", + }, + ], + this.client + ) + + + return response + } + + /** + * The value of this secret. + */ + async plaintext(): Promise { + if (this._plaintext) { + return this._plaintext + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "plaintext", + }, + ], + this.client + ) + + + return response + } +} + + + + +export class Service extends BaseClient { + private readonly _id?: ServiceID = undefined + private readonly _endpoint?: string = undefined + private readonly _hostname?: string = undefined + private readonly _start?: ServiceID = undefined + private readonly _stop?: ServiceID = undefined + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + _id?: ServiceID, + _endpoint?: string, + _hostname?: string, + _start?: ServiceID, + _stop?: ServiceID, + ) { + super(parent) + + this._id = _id + this._endpoint = _endpoint + this._hostname = _hostname + this._start = _start + this._stop = _stop + } + + /** + * A unique identifier for this service. + */ + async id(): Promise { + if (this._id) { + return this._id + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "id", + }, + ], + this.client + ) + + + return response + } + + /** + * Retrieves an endpoint that clients can use to reach this container. + * + * If no port is specified, the first exposed port is used. If none exist an error is returned. + * + * If a scheme is specified, a URL is returned. Otherwise, a host:port pair is returned. + * @param opts.port The exposed port number for the endpoint + * @param opts.scheme Return a URL with the given scheme, eg. http for http:// + */ + async endpoint(opts?: ServiceEndpointOpts): Promise { + if (this._endpoint) { + return this._endpoint + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "endpoint", + args: { ...opts }, + }, + ], + this.client + ) + + + return response + } + + /** + * Retrieves a hostname which can be used by clients to reach this container. + */ + async hostname(): Promise { + if (this._hostname) { + return this._hostname + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "hostname", + }, + ], + this.client + ) + + + return response + } + + /** + * Retrieves the list of ports provided by the service. + */ + async ports(): Promise { + type ports = { + description: string + port: number + protocol: NetworkProtocol + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "ports", + }, + { + operation: "description port protocol" + }, + ], + this.client + ) + + + return response.map( + (r) => new Port( + { + queryTree: this.queryTree, + host: this.clientHost, + sessionToken: this.sessionToken, + }, + r.description, + r.port, + r.protocol, + ) + ) + } + + /** + * Start the service and wait for its health checks to succeed. + * + * Services bound to a Container do not need to be manually started. + */ + async start(): Promise { + await computeQuery( + [ + ...this._queryTree, + { + operation: "start", + }, + ], + this.client + ) + + return this + } + + /** + * Stop the service. + */ + async stop(): Promise { + await computeQuery( + [ + ...this._queryTree, + { + operation: "stop", + }, + ], + this.client + ) + + return this + } +} + + + + +export class Socket extends BaseClient { + private readonly _id?: SocketID = undefined + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + _id?: SocketID, + ) { + super(parent) + + this._id = _id + } + + /** + * The content-addressed identifier of the socket. + */ + async id(): Promise { + if (this._id) { + return this._id + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "id", + }, + ], + this.client + ) + + + return response + } +} + + + + + +/** + * A definition of a parameter or return type in a Module. + */ +export class TypeDef extends BaseClient { + private readonly _id?: TypeDefID = undefined + private readonly _kind?: TypeDefKind = undefined + private readonly _optional?: boolean = undefined + + /** + * Constructor is used for internal usage only, do not create object from it. + */ + constructor( + parent?: { queryTree?: QueryTree[], host?: string, sessionToken?: string }, + _id?: TypeDefID, + _kind?: TypeDefKind, + _optional?: boolean, + ) { + super(parent) + + this._id = _id + this._kind = _kind + this._optional = _optional + } + async id(): Promise { + if (this._id) { + return this._id + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "id", + }, + ], + this.client + ) + + + return response + } + + /** + * If kind is LIST, the list-specific type definition. + * If kind is not LIST, this will be null. + */ + asList(): ListTypeDef { + return new ListTypeDef({ + queryTree: [ + ...this._queryTree, + { + operation: "asList", + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * If kind is OBJECT, the object-specific type definition. + * If kind is not OBJECT, this will be null. + */ + asObject(): ObjectTypeDef { + return new ObjectTypeDef({ + queryTree: [ + ...this._queryTree, + { + operation: "asObject", + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * The kind of type this is (e.g. primitive, list, object) + */ + async kind(): Promise { + if (this._kind) { + return this._kind + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "kind", + }, + ], + this.client + ) + + + return response + } + + /** + * Whether this type can be set to null. Defaults to false. + */ + async optional(): Promise { + if (this._optional) { + return this._optional + } + + const response: Awaited = await computeQuery( + [ + ...this._queryTree, + { + operation: "optional", + }, + ], + this.client + ) + + + return response + } + + /** + * Adds a static field for an Object TypeDef, failing if the type is not an object. + * @param name The name of the field in the object + * @param typeDef The type of the field + * @param opts.description A doc string for the field, if any + */ + withField(name: string, typeDef: TypeDef, opts?: TypeDefWithFieldOpts): TypeDef { + return new TypeDef({ + queryTree: [ + ...this._queryTree, + { + operation: "withField", + args: { name, typeDef, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Adds a function for an Object TypeDef, failing if the type is not an object. + */ + withFunction(function_: Function_): TypeDef { + return new TypeDef({ + queryTree: [ + ...this._queryTree, + { + operation: "withFunction", + args: { function_ }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Sets the kind of the type. + */ + withKind(kind: TypeDefKind): TypeDef { + return new TypeDef({ + queryTree: [ + ...this._queryTree, + { + operation: "withKind", + args: { kind }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Returns a TypeDef of kind List with the provided type for its elements. + */ + withListOf(elementType: TypeDef): TypeDef { + return new TypeDef({ + queryTree: [ + ...this._queryTree, + { + operation: "withListOf", + args: { elementType }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Returns a TypeDef of kind Object with the provided name. + * + * Note that an object's fields and functions may be omitted if the intent is + * only to refer to an object. This is how functions are able to return their + * own object, or any other circular reference. + */ + withObject(name: string, opts?: TypeDefWithObjectOpts): TypeDef { + return new TypeDef({ + queryTree: [ + ...this._queryTree, + { + operation: "withObject", + args: { name, ...opts }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Sets whether this type can be set to null. + */ + withOptional(optional: boolean): TypeDef { + return new TypeDef({ + queryTree: [ + ...this._queryTree, + { + operation: "withOptional", + args: { optional }, + }, + ], + host: this.clientHost, + sessionToken: this.sessionToken, + }) + } + + /** + * Call the provided function with current TypeDef. + * + * This is useful for reusability and readability by not breaking the calling chain. + */ + with(arg: (param: TypeDef) => TypeDef) { + return arg(this) + } +} + + + + + + + diff --git a/sdk/connect.ts b/sdk/connect.ts new file mode 100644 index 0000000..bd26cf7 --- /dev/null +++ b/sdk/connect.ts @@ -0,0 +1,89 @@ +import { Writable } from "node:stream"; + +import { Client } from "./client.gen.ts"; + +/** + * ConnectOpts defines option used to connect to an engine. + */ +export interface ConnectOpts { + /** + * Use to overwrite Dagger workdir + * @defaultValue process.cwd() + */ + Workdir?: string; + /** + * Enable logs output + * @example + * LogOutput + * ```ts + * connect(async (client: Client) => { + const source = await client.host().workdir().id() + ... + }, {LogOutput: process.stdout}) + ``` + */ + LogOutput?: Writable; +} + +export type CallbackFct = (client: Client) => Promise; + +export interface ConnectParams { + port: number; + session_token: string; +} + +/** + * connect runs GraphQL server and initializes a + * GraphQL client to execute query on it through its callback. + * This implementation is based on the existing Go SDK. + */ +export async function connect( + cb: CallbackFct, + config: ConnectOpts = {} +): Promise { + let client: Client; + // let close: null | (() => void) = null; + + if (Deno.env.has("FLUENTCI_TOKEN") && Deno.env.has("FLUENTCI_SESSION_ID")) { + const client = new Client({ + host: Deno.env.get("FLUENTCI_HOST") || "vm.fluentci.io", + sessionToken: Deno.env.get("FLUENTCI_TOKEN"), + }); + await cb(client).finally(() => { + if (close) { + close(); + } + }); + return; + } + + // Prefer DAGGER_SESSION_PORT if set + const daggerSessionPort = Deno.env.get("DAGGER_SESSION_PORT"); + if (daggerSessionPort) { + const sessionToken = Deno.env.get("DAGGER_SESSION_TOKEN"); + if (!sessionToken) { + throw new Error( + "DAGGER_SESSION_TOKEN must be set when using DAGGER_SESSION_PORT" + ); + } + + if (config.Workdir && config.Workdir !== "") { + throw new Error( + "cannot configure workdir for existing session (please use --workdir or host.directory with absolute paths instead)" + ); + } + + client = new Client({ + host: `127.0.0.1:${daggerSessionPort}`, + sessionToken: sessionToken, + }); + } else { + throw new Error("DAGGER_SESSION_PORT must be set"); + } + + await cb(client).finally(() => { + if (close) { + close(); + } + }); +} diff --git a/sdk/utils.ts b/sdk/utils.ts new file mode 100644 index 0000000..075e278 --- /dev/null +++ b/sdk/utils.ts @@ -0,0 +1,251 @@ +// deno-lint-ignore-file no-explicit-any +import { + ClientError, + gql, + GraphQLClient, + GraphQLRequestError, + TooManyNestedObjectsError, + UnknownDaggerError, + NotAwaitedRequestError, + ExecError, +} from "../deps.ts"; + +import { Metadata, QueryTree } from "./client.gen.ts"; + +/** + * Format argument into GraphQL query format. + */ +function buildArgs(args: any): string { + const metadata: Metadata = args.__metadata || {}; + + // Remove unwanted quotes + const formatValue = (key: string, value: string) => { + // Special treatment for enumeration, they must be inserted without quotes + if (metadata[key]?.is_enum) { + return JSON.stringify(value).replace(/['"]+/g, ""); + } + + return JSON.stringify(value).replace( + /\{"[a-zA-Z]+":|,"[a-zA-Z]+":/gi, + (str) => { + return str.replace(/"/g, ""); + } + ); + }; + + if (args === undefined || args === null) { + return ""; + } + + const formattedArgs = Object.entries(args).reduce( + (acc: any, [key, value]) => { + // Ignore internal metadata key + if (key === "__metadata") { + return acc; + } + + if (value !== undefined && value !== null) { + acc.push(`${key}: ${formatValue(key, value as string)}`); + } + + return acc; + }, + [] + ); + + if (formattedArgs.length === 0) { + return ""; + } + + return `(${formattedArgs})`; +} + +/** + * Find QueryTree, convert them into GraphQl query + * then compute and return the result to the appropriate field + */ +async function computeNestedQuery( + query: QueryTree[], + client: GraphQLClient +): Promise { + // Check if there is a nested queryTree to be executed + const isQueryTree = (value: any) => value["_queryTree"] !== undefined; + + // Check if there is a nested array of queryTree to be executed + const isArrayQueryTree = (value: any[]) => + value.every((v) => v instanceof Object && isQueryTree(v)); + + // Prepare query tree for final query by computing nested queries + // and building it with their results. + const computeQueryTree = async (value: any): Promise => { + // Resolve sub queries if operation's args is a subquery + for (const op of value["_queryTree"]) { + await computeNestedQuery([op], client); + } + + // push an id that will be used by the container + return buildQuery([ + ...value["_queryTree"], + { + operation: "id", + }, + ]); + }; + + // Remove all undefined args and assert args type + const queryToExec = query.filter((q): q is Required => !!q.args); + + for (const q of queryToExec) { + await Promise.all( + // Compute nested query for single object + Object.entries(q.args).map(async ([key, value]: any) => { + if (value instanceof Object && isQueryTree(value)) { + // push an id that will be used by the container + const getQueryTree = await computeQueryTree(value); + + q.args[key] = await compute(getQueryTree, client); + } + + // Compute nested query for array of object + if (Array.isArray(value) && isArrayQueryTree(value)) { + const tmp: any = q.args[key]; + + for (let i = 0; i < value.length; i++) { + // push an id that will be used by the container + const getQueryTree = await computeQueryTree(value[i]); + + tmp[i] = await compute(getQueryTree, client); + } + + q.args[key] = tmp; + } + }) + ); + } +} + +/** + * Convert the queryTree into a GraphQL query + * @param q + * @returns + */ +export function buildQuery(q: QueryTree[]): string { + const query = q.reduce((acc, { operation, args }, i) => { + const qLen = q.length; + + acc += ` ${operation} ${args ? `${buildArgs(args)}` : ""} ${ + qLen - 1 !== i ? "{" : "}".repeat(qLen - 1) + }`; + + return acc; + }, ""); + + return `{${query + .replaceAll('"StringKind"', "StringKind") + .replaceAll('"VoidKind"', "VoidKind") + .replaceAll('"IntegerKind"', "IntegerKind") + .replaceAll('"BooleanKind"', "BooleanKind") + .replaceAll('"ObjectKind"', "ObjectKind") + .replaceAll('"ListKind"', "ListKind") + .replaceAll("function_", "function")} }`; +} + +/** + * Convert querytree into a Graphql query then compute it + * @param q | QueryTree[] + * @param client | GraphQLClient + * @returns + */ +export async function computeQuery( + q: QueryTree[], + client: GraphQLClient +): Promise { + await computeNestedQuery(q, client); + + const query = buildQuery(q); + + return await compute(query, client); +} + +/** + * Return a Graphql query result flattened + * @param response any + * @returns + */ +export function queryFlatten(response: any): T { + // Recursion break condition + // If our response is not an object or an array we assume we reached the value + if (!(response instanceof Object) || Array.isArray(response)) { + return response; + } + + const keys = Object.keys(response); + + if (keys.length != 1) { + // Dagger is currently expecting to only return one value + // If the response is nested in a way were more than one object is nested inside throw an error + throw new TooManyNestedObjectsError( + "Too many nested objects inside graphql response", + { response: response } + ); + } + + const nestedKey = keys[0]; + + return queryFlatten(response[nestedKey]); +} + +/** + * Send a GraphQL document to the server + * return a flatten result + * @hidden + */ +export async function compute( + query: string, + client: GraphQLClient +): Promise { + let computeQuery: Awaited; + try { + computeQuery = await client.request( + gql` + ${query} + ` + ); + } catch (e: any) { + if (e instanceof ClientError) { + const msg = e.response.errors?.[0]?.message ?? `API Error`; + const ext = e.response.errors?.[0]?.extensions; + + if (ext?._type === "EXEC_ERROR") { + throw new ExecError(msg, { + cmd: (ext.cmd as string[]) ?? [], + exitCode: (ext.exitCode as number) ?? -1, + stdout: (ext.stdout as string) ?? "", + stderr: (ext.stderr as string) ?? "", + }); + } + + throw new GraphQLRequestError(msg, { + request: e.request, + response: e.response, + cause: e, + }); + } + + // Looking for connection error in case the function has not been awaited. + if (e.errno === "ECONNREFUSED") { + throw new NotAwaitedRequestError( + "Encountered an error while requesting data via graphql through a synchronous call. Make sure the function called is awaited.", + { cause: e } + ); + } + + // Just throw the unknown error + throw new UnknownDaggerError( + "Encountered an unknown error while requesting data via graphql", + { cause: e } + ); + } + + return queryFlatten(computeQuery); +} diff --git a/src/dagger/jobs.ts b/src/dagger/jobs.ts index 12ae292..b34cf94 100644 --- a/src/dagger/jobs.ts +++ b/src/dagger/jobs.ts @@ -1,4 +1,5 @@ -import Client, { connect } from "../../deps.ts"; +import Client from "../../deps.ts"; +import { connect } from "../../sdk/connect.ts"; export enum Job { test = "test", diff --git a/src/dagger/queries.ts b/src/dagger/queries.ts index 33cdb13..7b12dca 100644 --- a/src/dagger/queries.ts +++ b/src/dagger/queries.ts @@ -1,13 +1,13 @@ import { gql } from "../../deps.ts"; export const run = gql` - query Run($command: String!, $src: String!, $bunVersion: String!) { + query Run($command: String!, $src: String, $bunVersion: String) { run(command: $command, src: $src, bunVersion: $bunVersion) } `; export const test = gql` - query Test($src: String!, $bunVersion: String!) { + query Test($src: String, $bunVersion: String) { test(src: $src, bunVersion: $bunVersion) } `; diff --git a/src/dagger/schema.ts b/src/dagger/schema.ts index 1f853db..50e7bc3 100644 --- a/src/dagger/schema.ts +++ b/src/dagger/schema.ts @@ -14,17 +14,17 @@ const Query = queryType({ definition(t) { t.string("test", { args: { - src: nonNull(stringArg()), - bunVersion: nonNull(stringArg()), + src: stringArg(), + bunVersion: stringArg(), }, resolve: async (_root, args, _ctx) => - await test(args.src, args.bunVersion), + await test(args.src || undefined, args.bunVersion), }); t.string("run", { args: { command: nonNull(stringArg()), - src: nonNull(stringArg()), - bunVersion: nonNull(stringArg()), + src: stringArg(), + bunVersion: stringArg(), }, resolve: async (_root, args, _ctx) => await run(args.command, args.src, args.bunVersion),