dagger-env 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # dagger-env
2
2
 
3
- A type-safe, reusable environment configuration abstraction for Dagger modules with full Zod v4 validation and Infisical integration.
3
+ A type-safe, reusable environment configuration abstraction for Dagger modules with full Zod v4 validation and Infisical or 1Password integration.
4
4
 
5
5
  ## Features
6
6
 
@@ -9,7 +9,7 @@ A type-safe, reusable environment configuration abstraction for Dagger modules w
9
9
  - 🎯 **Consistent**: Standardized API across all Dagger modules
10
10
  - 🛡️ **Validated**: Runtime validation of arguments, environment variables, and secrets
11
11
  - 📦 **Modular**: Secret presets and derived environment variables
12
- - 🔐 **Infisical Integration**: Built-in command runner backed by `infisical export`
12
+ - 🔐 **Infisical & 1Password Integration**: Built-in command runner backed by `infisical export` or `op run`
13
13
  - 🚀 **Easy to use**: Simple configuration-based setup
14
14
 
15
15
  ## Installation
@@ -18,7 +18,7 @@ A type-safe, reusable environment configuration abstraction for Dagger modules w
18
18
  npm install dagger-env zod
19
19
  ```
20
20
 
21
- **Note:** The command runner functionality (`dagger-env/run`) requires the Infisical CLI (`infisical`) to be installed and authenticated.
21
+ **Note:** The command runner functionality (`dagger-env/run`) requires either the Infisical CLI (`infisical`) or the 1Password CLI (`op`) to be installed and authenticated, depending on which provider you configure.
22
22
 
23
23
  ## Quick Start
24
24
 
@@ -73,9 +73,9 @@ export class MyModule {
73
73
  }
74
74
  ```
75
75
 
76
- ## Command Runner (Infisical Integration)
76
+ ## Command Runner (Infisical or 1Password Integration)
77
77
 
78
- For projects using Infisical for secret management, `dagger-env` provides a convenient command runner that fetches secrets with `infisical export` and passes them to `dagger call` via the `DAGGER_OPTIONS` environment variable:
78
+ `dagger-env` provides a convenient command runner that fetches secrets from Infisical (via `infisical export`) or 1Password (via `op item get`) and passes them to `dagger call` via the `DAGGER_OPTIONS` environment variable. The provider is selected by the shape of the config you pass to `createDaggerCommandRunner()`:
79
79
 
80
80
  ```typescript
81
81
  import { createDaggerEnv } from 'dagger-env'
@@ -100,7 +100,7 @@ const myDaggerEnv = createDaggerEnv({
100
100
  derivedEnvVars: {}
101
101
  })
102
102
 
103
- // Create a command runner - simply pass your DaggerEnv instance
103
+ // Create a command runner with Infisical - simply pass your DaggerEnv instance
104
104
  const runDaggerCommand = createDaggerCommandRunner({
105
105
  projectId: 'your-project-id',
106
106
  env: 'prod',
@@ -116,6 +116,25 @@ await runDaggerCommand('test', {
116
116
  })
117
117
  ```
118
118
 
119
+ ### 1Password Integration
120
+
121
+ To use 1Password instead of Infisical, pass `opVault`/`opItem`/`opSections` instead of `projectId`/`env`/`path`. Secrets are read from the specified sections of a 1Password item, and `dagger call` is wrapped in `op run --no-masking` so that `op://` references in the environment are resolved:
122
+
123
+ ```typescript
124
+ const runDaggerCommand = createDaggerCommandRunner({
125
+ opVault: 'your-vault-id',
126
+ opItem: 'your-item-id',
127
+ opSections: [
128
+ {
129
+ id: 'your-section-id',
130
+ label: 'Shared'
131
+ }
132
+ ],
133
+ dockerCommands: ['build', 'deploy', 'test'],
134
+ daggerEnv: myDaggerEnv
135
+ })
136
+ ```
137
+
119
138
  ### Advanced Configuration
120
139
 
121
140
  ```typescript
@@ -200,13 +219,22 @@ Returns array of secret names for a specific preset.
200
219
 
201
220
  #### `createDaggerCommandRunner(config)`
202
221
 
203
- Creates a function to run Dagger commands with Infisical integration.
222
+ Creates a function to run Dagger commands with Infisical or 1Password integration. Pass either the Infisical or 1Password parameters to select the provider.
204
223
 
205
- **Parameters:**
224
+ **Infisical parameters:**
206
225
 
207
226
  - `config.projectId`: Infisical project ID
208
227
  - `config.env`: Infisical environment slug (e.g. `prod`)
209
228
  - `config.path`: Infisical folder path to fetch secrets from (e.g. `/ci/my-repo`)
229
+
230
+ **1Password parameters:**
231
+
232
+ - `config.opVault`: 1Password vault ID
233
+ - `config.opItem`: 1Password item ID
234
+ - `config.opSections`: Array of 1Password sections to include for secrets
235
+
236
+ **Shared parameters:**
237
+
210
238
  - `config.dockerCommands`: Optional array of command names that should include Docker socket
211
239
  - `config.beforeCommand`: Optional async function to run before executing the command
212
240
  - `config.daggerEnv`: DaggerEnv instance for schema validation and type safety
package/dist/op.d.ts ADDED
@@ -0,0 +1,90 @@
1
+ import { z } from 'zod/v4';
2
+ /**
3
+ * 1Password vault information
4
+ */
5
+ export type OPVault = z.infer<typeof OPVault>;
6
+ export declare const OPVault: z.ZodObject<{
7
+ id: z.ZodString;
8
+ name: z.ZodString;
9
+ }, z.core.$strip>;
10
+ /**
11
+ * 1Password item section information
12
+ */
13
+ export type OPSection = z.infer<typeof OPSection>;
14
+ export declare const OPSection: z.ZodObject<{
15
+ id: z.ZodString;
16
+ label: z.ZodOptional<z.ZodString>;
17
+ }, z.core.$strip>;
18
+ /**
19
+ * 1Password item field
20
+ */
21
+ export type OPField = z.infer<typeof OPField>;
22
+ export declare const OPField: z.ZodObject<{
23
+ id: z.ZodString;
24
+ section: z.ZodOptional<z.ZodObject<{
25
+ id: z.ZodString;
26
+ label: z.ZodOptional<z.ZodString>;
27
+ }, z.core.$strip>>;
28
+ type: z.ZodEnum<{
29
+ STRING: "STRING";
30
+ CONCEALED: "CONCEALED";
31
+ MONTH_YEAR: "MONTH_YEAR";
32
+ }>;
33
+ purpose: z.ZodOptional<z.ZodEnum<{
34
+ NOTES: "NOTES";
35
+ }>>;
36
+ label: z.ZodString;
37
+ value: z.ZodString;
38
+ reference: z.ZodString;
39
+ }, z.core.$strip>;
40
+ /**
41
+ * 1Password item category
42
+ */
43
+ export type OPCategory = z.infer<typeof OPCategory>;
44
+ export declare const OPCategory: z.ZodEnum<{
45
+ SECURE_NOTE: "SECURE_NOTE";
46
+ }>;
47
+ /**
48
+ * Complete 1Password item structure
49
+ */
50
+ export type OPItem = z.infer<typeof OPItem>;
51
+ export declare const OPItem: z.ZodObject<{
52
+ id: z.ZodString;
53
+ title: z.ZodString;
54
+ favorite: z.ZodBoolean;
55
+ version: z.ZodInt;
56
+ vault: z.ZodObject<{
57
+ id: z.ZodString;
58
+ name: z.ZodString;
59
+ }, z.core.$strip>;
60
+ category: z.ZodEnum<{
61
+ SECURE_NOTE: "SECURE_NOTE";
62
+ }>;
63
+ last_edited_by: z.ZodString;
64
+ created_at: z.ZodISODateTime;
65
+ updated_at: z.ZodISODateTime;
66
+ additional_information: z.ZodString;
67
+ sections: z.ZodArray<z.ZodObject<{
68
+ id: z.ZodString;
69
+ label: z.ZodOptional<z.ZodString>;
70
+ }, z.core.$strip>>;
71
+ fields: z.ZodArray<z.ZodObject<{
72
+ id: z.ZodString;
73
+ section: z.ZodOptional<z.ZodObject<{
74
+ id: z.ZodString;
75
+ label: z.ZodOptional<z.ZodString>;
76
+ }, z.core.$strip>>;
77
+ type: z.ZodEnum<{
78
+ STRING: "STRING";
79
+ CONCEALED: "CONCEALED";
80
+ MONTH_YEAR: "MONTH_YEAR";
81
+ }>;
82
+ purpose: z.ZodOptional<z.ZodEnum<{
83
+ NOTES: "NOTES";
84
+ }>>;
85
+ label: z.ZodString;
86
+ value: z.ZodString;
87
+ reference: z.ZodString;
88
+ }, z.core.$strip>>;
89
+ }, z.core.$strip>;
90
+ //# sourceMappingURL=op.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"op.d.ts","sourceRoot":"","sources":["../src/op.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAA;AAE1B;;GAEG;AACH,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,OAAO,CAAC,CAAA;AAC7C,eAAO,MAAM,OAAO;;;iBAGlB,CAAA;AAEF;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAA;AACjD,eAAO,MAAM,SAAS;;;iBAGpB,CAAA;AAEF;;GAEG;AACH,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,OAAO,CAAC,CAAA;AAC7C,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;iBAQlB,CAAA;AAEF;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAA;AACnD,eAAO,MAAM,UAAU;;EAA0B,CAAA;AAEjD;;GAEG;AACH,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,MAAM,CAAC,CAAA;AAC3C,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAajB,CAAA"}
package/dist/op.js ADDED
@@ -0,0 +1,34 @@
1
+ import { z } from 'zod/v4';
2
+ export const OPVault = z.object({
3
+ id: z.string(),
4
+ name: z.string(),
5
+ });
6
+ export const OPSection = z.object({
7
+ id: z.string(),
8
+ label: z.string().optional(),
9
+ });
10
+ export const OPField = z.object({
11
+ id: z.string(),
12
+ section: OPSection.optional(),
13
+ type: z.enum(['STRING', 'CONCEALED', 'MONTH_YEAR']),
14
+ purpose: z.enum(['NOTES']).optional(),
15
+ label: z.string(),
16
+ value: z.string(),
17
+ reference: z.string(),
18
+ });
19
+ export const OPCategory = z.enum(['SECURE_NOTE']);
20
+ export const OPItem = z.object({
21
+ id: z.string(),
22
+ title: z.string(),
23
+ favorite: z.boolean(),
24
+ version: z.int(),
25
+ vault: OPVault,
26
+ category: OPCategory,
27
+ last_edited_by: z.string(),
28
+ created_at: z.iso.datetime(),
29
+ updated_at: z.iso.datetime(),
30
+ additional_information: z.string(),
31
+ sections: z.array(OPSection),
32
+ fields: z.array(OPField),
33
+ });
34
+ //# sourceMappingURL=op.js.map
package/dist/op.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"op.js","sourceRoot":"","sources":["../src/op.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAA;AAM1B,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/B,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE;IACd,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;CAChB,CAAC,CAAA;AAMF,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,CAAC,MAAM,CAAC;IACjC,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE;IACd,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC5B,CAAC,CAAA;AAMF,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/B,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE;IACd,OAAO,EAAE,SAAS,CAAC,QAAQ,EAAE;IAC7B,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,WAAW,EAAE,YAAY,CAAC,CAAC;IACnD,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,EAAE;IACrC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;IACjB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;IACjB,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE;CACrB,CAAC,CAAA;AAMF,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,aAAa,CAAC,CAAC,CAAA;AAMjD,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9B,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE;IACd,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;IACjB,QAAQ,EAAE,CAAC,CAAC,OAAO,EAAE;IACrB,OAAO,EAAE,CAAC,CAAC,GAAG,EAAE;IAChB,KAAK,EAAE,OAAO;IACd,QAAQ,EAAE,UAAU;IACpB,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE;IAC1B,UAAU,EAAE,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE;IAC5B,UAAU,EAAE,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE;IAC5B,sBAAsB,EAAE,CAAC,CAAC,MAAM,EAAE;IAClC,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC;IAC5B,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC;CACxB,CAAC,CAAA","sourcesContent":["import { z } from 'zod/v4'\n\n/**\n * 1Password vault information\n */\nexport type OPVault = z.infer<typeof OPVault>\nexport const OPVault = z.object({\n\tid: z.string(),\n\tname: z.string(),\n})\n\n/**\n * 1Password item section information\n */\nexport type OPSection = z.infer<typeof OPSection>\nexport const OPSection = z.object({\n\tid: z.string(),\n\tlabel: z.string().optional(),\n})\n\n/**\n * 1Password item field\n */\nexport type OPField = z.infer<typeof OPField>\nexport const OPField = z.object({\n\tid: z.string(),\n\tsection: OPSection.optional(),\n\ttype: z.enum(['STRING', 'CONCEALED', 'MONTH_YEAR']),\n\tpurpose: z.enum(['NOTES']).optional(),\n\tlabel: z.string(),\n\tvalue: z.string(),\n\treference: z.string(),\n})\n\n/**\n * 1Password item category\n */\nexport type OPCategory = z.infer<typeof OPCategory>\nexport const OPCategory = z.enum(['SECURE_NOTE'])\n\n/**\n * Complete 1Password item structure\n */\nexport type OPItem = z.infer<typeof OPItem>\nexport const OPItem = z.object({\n\tid: z.string(),\n\ttitle: z.string(),\n\tfavorite: z.boolean(),\n\tversion: z.int(),\n\tvault: OPVault,\n\tcategory: OPCategory,\n\tlast_edited_by: z.string(),\n\tcreated_at: z.iso.datetime(),\n\tupdated_at: z.iso.datetime(),\n\tadditional_information: z.string(),\n\tsections: z.array(OPSection),\n\tfields: z.array(OPField),\n})\n"]}
@@ -9,15 +9,34 @@ export declare const InfisicalSecret: z.ZodObject<{
9
9
  value: z.ZodString;
10
10
  }, z.core.$strip>;
11
11
  /**
12
- * Configuration for running Dagger commands with Infisical integration
12
+ * Infisical secrets provider configuration
13
13
  */
14
- export interface RunDaggerCommandConfig<T extends DaggerEnvConfig = DaggerEnvConfig> {
14
+ export interface InfisicalProviderConfig {
15
15
  /** Infisical project ID */
16
16
  projectId: string;
17
17
  /** Infisical environment slug (e.g. `prod`) */
18
18
  env: string;
19
19
  /** Infisical folder path to fetch secrets from (e.g. `/ci/my-repo`) */
20
20
  path: string;
21
+ }
22
+ /**
23
+ * 1Password secrets provider configuration
24
+ */
25
+ export interface OnePasswordProviderConfig {
26
+ /** 1Password vault ID */
27
+ opVault: string;
28
+ /** 1Password item ID */
29
+ opItem: string;
30
+ /** 1Password sections to include for secrets */
31
+ opSections: Array<{
32
+ label: string;
33
+ id: string;
34
+ }>;
35
+ }
36
+ /**
37
+ * Configuration shared by all secrets providers
38
+ */
39
+ export interface RunDaggerCommandBaseConfig<T extends DaggerEnvConfig = DaggerEnvConfig> {
21
40
  /** Commands that should include Docker socket if available */
22
41
  dockerCommands?: string[];
23
42
  /** Hook to run before executing the command (e.g., vendor file setup) */
@@ -25,6 +44,10 @@ export interface RunDaggerCommandConfig<T extends DaggerEnvConfig = DaggerEnvCon
25
44
  /** DaggerEnv instance for schema validation and type safety */
26
45
  daggerEnv: DaggerEnv<T>;
27
46
  }
47
+ /**
48
+ * Configuration for running Dagger commands with Infisical or 1Password integration
49
+ */
50
+ export type RunDaggerCommandConfig<T extends DaggerEnvConfig = DaggerEnvConfig> = RunDaggerCommandBaseConfig<T> & (InfisicalProviderConfig | OnePasswordProviderConfig);
28
51
  /**
29
52
  * Options for individual command execution
30
53
  */
@@ -37,9 +60,10 @@ export interface RunDaggerCommandOptions {
37
60
  extraArgs?: string[];
38
61
  }
39
62
  /**
40
- * Creates a function to run Dagger commands with Infisical integration
63
+ * Creates a function to run Dagger commands with Infisical or 1Password integration
41
64
  * @param config Configuration for the command runner
42
65
  * @returns Function to execute Dagger commands
43
66
  */
44
- export declare function createDaggerCommandRunner<T extends DaggerEnvConfig>(config: RunDaggerCommandConfig<T>): (commandName: string, options?: RunDaggerCommandOptions) => Promise<void>;
67
+ export declare function createDaggerCommandRunner<T extends DaggerEnvConfig>(config: RunDaggerCommandBaseConfig<T> & InfisicalProviderConfig): (commandName: string, options?: RunDaggerCommandOptions) => Promise<void>;
68
+ export declare function createDaggerCommandRunner<T extends DaggerEnvConfig>(config: RunDaggerCommandBaseConfig<T> & OnePasswordProviderConfig): (commandName: string, options?: RunDaggerCommandOptions) => Promise<void>;
45
69
  //# sourceMappingURL=run-dagger-cmd.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"run-dagger-cmd.d.ts","sourceRoot":"","sources":["../src/run-dagger-cmd.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAA;AAG1B,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEjE;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAA;AAC7D,eAAO,MAAM,eAAe;;;iBAG1B,CAAA;AAEF;;GAEG;AACH,MAAM,WAAW,sBAAsB,CAAC,CAAC,SAAS,eAAe,GAAG,eAAe;IAClF,2BAA2B;IAC3B,SAAS,EAAE,MAAM,CAAA;IACjB,+CAA+C;IAC/C,GAAG,EAAE,MAAM,CAAA;IACX,uEAAuE;IACvE,IAAI,EAAE,MAAM,CAAA;IACZ,8DAA8D;IAC9D,cAAc,CAAC,EAAE,MAAM,EAAE,CAAA;IACzB,yEAAyE;IACzE,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IACnC,+DAA+D;IAC/D,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,CAAA;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACvC,8CAA8C;IAC9C,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAC1B,uCAAuC;IACvC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC5B,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAA;CACpB;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,CAAC,SAAS,eAAe,EAClE,MAAM,EAAE,sBAAsB,CAAC,CAAC,CAAC,IAGhC,aAAa,MAAM,EACnB,UAAU,uBAAuB,KAC/B,OAAO,CAAC,IAAI,CAAC,CA8EhB"}
1
+ {"version":3,"file":"run-dagger-cmd.d.ts","sourceRoot":"","sources":["../src/run-dagger-cmd.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAA;AAK1B,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEjE;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAA;AAC7D,eAAO,MAAM,eAAe;;;iBAG1B,CAAA;AAEF;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACvC,2BAA2B;IAC3B,SAAS,EAAE,MAAM,CAAA;IACjB,+CAA+C;IAC/C,GAAG,EAAE,MAAM,CAAA;IACX,uEAAuE;IACvE,IAAI,EAAE,MAAM,CAAA;CACZ;AAED;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACzC,yBAAyB;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,wBAAwB;IACxB,MAAM,EAAE,MAAM,CAAA;IACd,gDAAgD;IAChD,UAAU,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAChD;AAED;;GAEG;AACH,MAAM,WAAW,0BAA0B,CAAC,CAAC,SAAS,eAAe,GAAG,eAAe;IACtF,8DAA8D;IAC9D,cAAc,CAAC,EAAE,MAAM,EAAE,CAAA;IACzB,yEAAyE;IACzE,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IACnC,+DAA+D;IAC/D,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,CAAA;CACvB;AAED;;GAEG;AACH,MAAM,MAAM,sBAAsB,CAAC,CAAC,SAAS,eAAe,GAAG,eAAe,IAC7E,0BAA0B,CAAC,CAAC,CAAC,GAAG,CAAC,uBAAuB,GAAG,yBAAyB,CAAC,CAAA;AAEtF;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACvC,8CAA8C;IAC9C,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAC1B,uCAAuC;IACvC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC5B,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAA;CACpB;AA4CD;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,CAAC,SAAS,eAAe,EAClE,MAAM,EAAE,0BAA0B,CAAC,CAAC,CAAC,GAAG,uBAAuB,GAC7D,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,uBAAuB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;AAC5E,wBAAgB,yBAAyB,CAAC,CAAC,SAAS,eAAe,EAClE,MAAM,EAAE,0BAA0B,CAAC,CAAC,CAAC,GAAG,yBAAyB,GAC/D,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,uBAAuB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA"}
@@ -1,14 +1,33 @@
1
1
  import { z } from 'zod/v4';
2
2
  import { $, fs } from 'zx';
3
+ import { OPItem, OPSection } from './op.js';
3
4
  export const InfisicalSecret = z.object({
4
5
  key: z.string(),
5
6
  value: z.string(),
6
7
  });
7
8
  /**
8
- * Creates a function to run Dagger commands with Infisical integration
9
- * @param config Configuration for the command runner
10
- * @returns Function to execute Dagger commands
9
+ * Fetches secrets from Infisical via `infisical export`
11
10
  */
11
+ async function fetchInfisicalSecrets(config) {
12
+ const exportedSecrets = InfisicalSecret.array().parse(await $ `infisical export --silent --format=json --projectId ${config.projectId} --env ${config.env} --path ${config.path}`.json());
13
+ return exportedSecrets.reduce((acc, s) => {
14
+ acc[s.key] = s.value;
15
+ return acc;
16
+ }, {});
17
+ }
18
+ /**
19
+ * Fetches secrets from a 1Password item, filtered to the configured sections
20
+ */
21
+ async function fetchOnePasswordSecrets(config) {
22
+ const opItem = OPItem.parse(await $ `op item get ${config.opItem} --vault ${config.opVault} --format json`.json());
23
+ const sectionsToInclude = OPSection.array().parse(config.opSections);
24
+ return opItem.fields
25
+ .filter((f) => sectionsToInclude.some((s) => s.id === f.section?.id))
26
+ .reduce((acc, f) => {
27
+ acc[f.label] = f.value;
28
+ return acc;
29
+ }, {});
30
+ }
12
31
  export function createDaggerCommandRunner(config) {
13
32
  return async function runDaggerCommand(commandName, options) {
14
33
  const { args = {}, env = {}, extraArgs = [] } = options ?? {};
@@ -16,7 +35,7 @@ export function createDaggerCommandRunner(config) {
16
35
  if (config.beforeCommand) {
17
36
  await config.beforeCommand();
18
37
  }
19
- // Environment variables to pass to the `dagger` command
38
+ // Environment variables to pass to the spawned command
20
39
  const envVars = {};
21
40
  const commandArgs = [...extraArgs];
22
41
  // Add Docker socket for specific commands if available
@@ -30,16 +49,22 @@ export function createDaggerCommandRunner(config) {
30
49
  // Ignore if fs is not available or docker socket doesn't exist
31
50
  }
32
51
  }
33
- // Fetch secrets from Infisical
34
- const exportedSecrets = InfisicalSecret.array().parse(await $ `infisical export --silent --format=json --projectId ${config.projectId} --env ${config.env} --path ${config.path}`.json());
35
- // Extract secrets into a key/value map
36
- const secrets = exportedSecrets.reduce((acc, s) => {
37
- acc[s.key] = s.value;
38
- return acc;
39
- }, {});
40
- // Pass dagger cloud token in CI because we don't have user auth
41
- if (process.env.CI !== undefined && secrets.DAGGER_CLOUD_TOKEN !== undefined) {
42
- envVars.DAGGER_CLOUD_TOKEN = secrets.DAGGER_CLOUD_TOKEN;
52
+ // Fetch secrets from the configured provider
53
+ let secrets;
54
+ if ('opVault' in config) {
55
+ secrets = await fetchOnePasswordSecrets(config);
56
+ // Pass dagger cloud token in CI because we don't have user auth.
57
+ // The op:// reference is resolved by the `op run` wrapper below.
58
+ if (process.env.CI !== undefined) {
59
+ envVars.DAGGER_CLOUD_TOKEN = `op://${config.opVault}/${config.opItem}/DAGGER_CLOUD_TOKEN`;
60
+ }
61
+ }
62
+ else {
63
+ secrets = await fetchInfisicalSecrets(config);
64
+ // Pass dagger cloud token in CI because we don't have user auth
65
+ if (process.env.CI !== undefined && secrets.DAGGER_CLOUD_TOKEN !== undefined) {
66
+ envVars.DAGGER_CLOUD_TOKEN = secrets.DAGGER_CLOUD_TOKEN;
67
+ }
43
68
  }
44
69
  // Build environment variables for Dagger
45
70
  const daggerEnv = { ...env };
@@ -56,8 +81,10 @@ export function createDaggerCommandRunner(config) {
56
81
  secrets,
57
82
  });
58
83
  envVars.DAGGER_OPTIONS = JSON.stringify(daggerOptions);
59
- // Construct the command
84
+ // Construct the command. 1Password wraps `dagger call` in `op run`
85
+ // so that op:// references in the environment are resolved.
60
86
  const cmd = [
87
+ ...('opVault' in config ? ['op', 'run', '--no-masking', '--'] : []),
61
88
  'dagger',
62
89
  'call',
63
90
  commandName,
@@ -1 +1 @@
1
- {"version":3,"file":"run-dagger-cmd.js","sourceRoot":"","sources":["../src/run-dagger-cmd.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from 'zod/v4'\nimport { $, fs } from 'zx'\n\nimport type { DaggerEnv, DaggerEnvConfig } from './dagger-env.js'\n\n/**\n * A single secret returned by `infisical export --format=json`\n */\nexport type InfisicalSecret = z.infer<typeof InfisicalSecret>\nexport const InfisicalSecret = z.object({\n\tkey: z.string(),\n\tvalue: z.string(),\n})\n\n/**\n * Configuration for running Dagger commands with Infisical integration\n */\nexport interface RunDaggerCommandConfig<T extends DaggerEnvConfig = DaggerEnvConfig> {\n\t/** Infisical project ID */\n\tprojectId: string\n\t/** Infisical environment slug (e.g. `prod`) */\n\tenv: string\n\t/** Infisical folder path to fetch secrets from (e.g. `/ci/my-repo`) */\n\tpath: string\n\t/** Commands that should include Docker socket if available */\n\tdockerCommands?: string[]\n\t/** Hook to run before executing the command (e.g., vendor file setup) */\n\tbeforeCommand?: () => Promise<void>\n\t/** DaggerEnv instance for schema validation and type safety */\n\tdaggerEnv: DaggerEnv<T>\n}\n\n/**\n * Options for individual command execution\n */\nexport interface RunDaggerCommandOptions {\n\t/** Arguments to pass to the Dagger command */\n\targs?: Record<string, any>\n\t/** Additional environment variables */\n\tenv?: Record<string, string>\n\t/** Additional command-line arguments */\n\textraArgs?: string[]\n}\n\n/**\n * Creates a function to run Dagger commands with Infisical integration\n * @param config Configuration for the command runner\n * @returns Function to execute Dagger commands\n */\nexport function createDaggerCommandRunner<T extends DaggerEnvConfig>(\n\tconfig: RunDaggerCommandConfig<T>\n) {\n\treturn async function runDaggerCommand(\n\t\tcommandName: string,\n\t\toptions?: RunDaggerCommandOptions\n\t): Promise<void> {\n\t\tconst { args = {}, env = {}, extraArgs = [] } = options ?? {}\n\n\t\t// Run any pre-command setup\n\t\tif (config.beforeCommand) {\n\t\t\tawait config.beforeCommand()\n\t\t}\n\n\t\t// Environment variables to pass to the `dagger` command\n\t\tconst envVars: Record<string, string> = {}\n\n\t\tconst commandArgs: string[] = [...extraArgs]\n\n\t\t// Add Docker socket for specific commands if available\n\t\tif (config.dockerCommands?.includes(commandName)) {\n\t\t\ttry {\n\t\t\t\tif (await fs.exists('/var/run/docker.sock')) {\n\t\t\t\t\tcommandArgs.push('--docker-socket=/var/run/docker.sock')\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Ignore if fs is not available or docker socket doesn't exist\n\t\t\t}\n\t\t}\n\n\t\t// Fetch secrets from Infisical\n\t\tconst exportedSecrets = InfisicalSecret.array().parse(\n\t\t\tawait $`infisical export --silent --format=json --projectId ${config.projectId} --env ${config.env} --path ${config.path}`.json()\n\t\t)\n\n\t\t// Extract secrets into a key/value map\n\t\tconst secrets = exportedSecrets.reduce(\n\t\t\t(acc, s) => {\n\t\t\t\tacc[s.key] = s.value\n\t\t\t\treturn acc\n\t\t\t},\n\t\t\t{} as Record<string, string>\n\t\t)\n\n\t\t// Pass dagger cloud token in CI because we don't have user auth\n\t\tif (process.env.CI !== undefined && secrets.DAGGER_CLOUD_TOKEN !== undefined) {\n\t\t\tenvVars.DAGGER_CLOUD_TOKEN = secrets.DAGGER_CLOUD_TOKEN\n\t\t}\n\n\t\t// Build environment variables for Dagger\n\t\tconst daggerEnv: Record<string, string> = { ...env }\n\t\tif (process.env.CI !== undefined) {\n\t\t\tdaggerEnv.CI = process.env.CI\n\t\t}\n\t\tif (process.env.GITHUB_ACTIONS !== undefined) {\n\t\t\tdaggerEnv.GITHUB_ACTIONS = process.env.GITHUB_ACTIONS\n\t\t}\n\n\t\t// Validate and serialize dagger options\n\t\tconst daggerOptions = config.daggerEnv.getOptionsSchema().parse({\n\t\t\targs,\n\t\t\tenv: daggerEnv,\n\t\t\tsecrets,\n\t\t})\n\t\tenvVars.DAGGER_OPTIONS = JSON.stringify(daggerOptions)\n\n\t\t// Construct the command\n\t\tconst cmd: string[] = [\n\t\t\t'dagger',\n\t\t\t'call',\n\t\t\tcommandName,\n\t\t\t...commandArgs,\n\t\t\t'--options=env://DAGGER_OPTIONS',\n\t\t]\n\n\t\t// Execute the command\n\t\tawait $({\n\t\t\tenv: {\n\t\t\t\t...process.env,\n\t\t\t\t...envVars,\n\t\t\t},\n\t\t\tstdio: 'inherit',\n\t\t})`${cmd}`\n\t}\n}\n"]}
1
+ {"version":3,"file":"run-dagger-cmd.js","sourceRoot":"","sources":["../src/run-dagger-cmd.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAA;AAC1B,OAAO,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,IAAI,CAAA;AAE1B,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAQ3C,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IACvC,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;IACf,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;CACjB,CAAC,CAAA;AAwDF;;GAEG;AACH,KAAK,UAAU,qBAAqB,CACnC,MAA+B;IAE/B,MAAM,eAAe,GAAG,eAAe,CAAC,KAAK,EAAE,CAAC,KAAK,CACpD,MAAM,CAAC,CAAA,uDAAuD,MAAM,CAAC,SAAS,UAAU,MAAM,CAAC,GAAG,WAAW,MAAM,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CACjI,CAAA;IAED,OAAO,eAAe,CAAC,MAAM,CAC5B,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE;QACV,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,CAAA;QACpB,OAAO,GAAG,CAAA;IACX,CAAC,EACD,EAA4B,CAC5B,CAAA;AACF,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,uBAAuB,CACrC,MAAiC;IAEjC,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAC1B,MAAM,CAAC,CAAA,eAAe,MAAM,CAAC,MAAM,YAAY,MAAM,CAAC,OAAO,gBAAgB,CAAC,IAAI,EAAE,CACpF,CAAA;IAED,MAAM,iBAAiB,GAAG,SAAS,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,CAAA;IAEpE,OAAO,MAAM,CAAC,MAAM;SAClB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;SACpE,MAAM,CACN,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE;QACV,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAA;QACtB,OAAO,GAAG,CAAA;IACX,CAAC,EACD,EAA4B,CAC5B,CAAA;AACH,CAAC;AAaD,MAAM,UAAU,yBAAyB,CACxC,MAAiC;IAEjC,OAAO,KAAK,UAAU,gBAAgB,CACrC,WAAmB,EACnB,OAAiC;QAEjC,MAAM,EAAE,IAAI,GAAG,EAAE,EAAE,GAAG,GAAG,EAAE,EAAE,SAAS,GAAG,EAAE,EAAE,GAAG,OAAO,IAAI,EAAE,CAAA;QAE7D,4BAA4B;QAC5B,IAAI,MAAM,CAAC,aAAa,EAAE,CAAC;YAC1B,MAAM,MAAM,CAAC,aAAa,EAAE,CAAA;QAC7B,CAAC;QAED,uDAAuD;QACvD,MAAM,OAAO,GAA2B,EAAE,CAAA;QAE1C,MAAM,WAAW,GAAa,CAAC,GAAG,SAAS,CAAC,CAAA;QAE5C,uDAAuD;QACvD,IAAI,MAAM,CAAC,cAAc,EAAE,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;YAClD,IAAI,CAAC;gBACJ,IAAI,MAAM,EAAE,CAAC,MAAM,CAAC,sBAAsB,CAAC,EAAE,CAAC;oBAC7C,WAAW,CAAC,IAAI,CAAC,sCAAsC,CAAC,CAAA;gBACzD,CAAC;YACF,CAAC;YAAC,MAAM,CAAC;gBACR,+DAA+D;YAChE,CAAC;QACF,CAAC;QAED,6CAA6C;QAC7C,IAAI,OAA+B,CAAA;QACnC,IAAI,SAAS,IAAI,MAAM,EAAE,CAAC;YACzB,OAAO,GAAG,MAAM,uBAAuB,CAAC,MAAM,CAAC,CAAA;YAE/C,iEAAiE;YACjE,iEAAiE;YACjE,IAAI,OAAO,CAAC,GAAG,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;gBAClC,OAAO,CAAC,kBAAkB,GAAG,QAAQ,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,MAAM,qBAAqB,CAAA;YAC1F,CAAC;QACF,CAAC;aAAM,CAAC;YACP,OAAO,GAAG,MAAM,qBAAqB,CAAC,MAAM,CAAC,CAAA;YAE7C,gEAAgE;YAChE,IAAI,OAAO,CAAC,GAAG,CAAC,EAAE,KAAK,SAAS,IAAI,OAAO,CAAC,kBAAkB,KAAK,SAAS,EAAE,CAAC;gBAC9E,OAAO,CAAC,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAA;YACxD,CAAC;QACF,CAAC;QAED,yCAAyC;QACzC,MAAM,SAAS,GAA2B,EAAE,GAAG,GAAG,EAAE,CAAA;QACpD,IAAI,OAAO,CAAC,GAAG,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;YAClC,SAAS,CAAC,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,EAAE,CAAA;QAC9B,CAAC;QACD,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,KAAK,SAAS,EAAE,CAAC;YAC9C,SAAS,CAAC,cAAc,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAA;QACtD,CAAC;QAED,wCAAwC;QACxC,MAAM,aAAa,GAAG,MAAM,CAAC,SAAS,CAAC,gBAAgB,EAAE,CAAC,KAAK,CAAC;YAC/D,IAAI;YACJ,GAAG,EAAE,SAAS;YACd,OAAO;SACP,CAAC,CAAA;QACF,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,CAAA;QAEtD,mEAAmE;QACnE,4DAA4D;QAC5D,MAAM,GAAG,GAAa;YACrB,GAAG,CAAC,SAAS,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACnE,QAAQ;YACR,MAAM;YACN,WAAW;YACX,GAAG,WAAW;YACd,gCAAgC;SAChC,CAAA;QAED,sBAAsB;QACtB,MAAM,CAAC,CAAC;YACP,GAAG,EAAE;gBACJ,GAAG,OAAO,CAAC,GAAG;gBACd,GAAG,OAAO;aACV;YACD,KAAK,EAAE,SAAS;SAChB,CAAC,CAAA,GAAG,GAAG,EAAE,CAAA;IACX,CAAC,CAAA;AACF,CAAC","sourcesContent":["import { z } from 'zod/v4'\nimport { $, fs } from 'zx'\n\nimport { OPItem, OPSection } from './op.js'\n\nimport type { DaggerEnv, DaggerEnvConfig } from './dagger-env.js'\n\n/**\n * A single secret returned by `infisical export --format=json`\n */\nexport type InfisicalSecret = z.infer<typeof InfisicalSecret>\nexport const InfisicalSecret = z.object({\n\tkey: z.string(),\n\tvalue: z.string(),\n})\n\n/**\n * Infisical secrets provider configuration\n */\nexport interface InfisicalProviderConfig {\n\t/** Infisical project ID */\n\tprojectId: string\n\t/** Infisical environment slug (e.g. `prod`) */\n\tenv: string\n\t/** Infisical folder path to fetch secrets from (e.g. `/ci/my-repo`) */\n\tpath: string\n}\n\n/**\n * 1Password secrets provider configuration\n */\nexport interface OnePasswordProviderConfig {\n\t/** 1Password vault ID */\n\topVault: string\n\t/** 1Password item ID */\n\topItem: string\n\t/** 1Password sections to include for secrets */\n\topSections: Array<{ label: string; id: string }>\n}\n\n/**\n * Configuration shared by all secrets providers\n */\nexport interface RunDaggerCommandBaseConfig<T extends DaggerEnvConfig = DaggerEnvConfig> {\n\t/** Commands that should include Docker socket if available */\n\tdockerCommands?: string[]\n\t/** Hook to run before executing the command (e.g., vendor file setup) */\n\tbeforeCommand?: () => Promise<void>\n\t/** DaggerEnv instance for schema validation and type safety */\n\tdaggerEnv: DaggerEnv<T>\n}\n\n/**\n * Configuration for running Dagger commands with Infisical or 1Password integration\n */\nexport type RunDaggerCommandConfig<T extends DaggerEnvConfig = DaggerEnvConfig> =\n\tRunDaggerCommandBaseConfig<T> & (InfisicalProviderConfig | OnePasswordProviderConfig)\n\n/**\n * Options for individual command execution\n */\nexport interface RunDaggerCommandOptions {\n\t/** Arguments to pass to the Dagger command */\n\targs?: Record<string, any>\n\t/** Additional environment variables */\n\tenv?: Record<string, string>\n\t/** Additional command-line arguments */\n\textraArgs?: string[]\n}\n\n/**\n * Fetches secrets from Infisical via `infisical export`\n */\nasync function fetchInfisicalSecrets(\n\tconfig: InfisicalProviderConfig\n): Promise<Record<string, string>> {\n\tconst exportedSecrets = InfisicalSecret.array().parse(\n\t\tawait $`infisical export --silent --format=json --projectId ${config.projectId} --env ${config.env} --path ${config.path}`.json()\n\t)\n\n\treturn exportedSecrets.reduce(\n\t\t(acc, s) => {\n\t\t\tacc[s.key] = s.value\n\t\t\treturn acc\n\t\t},\n\t\t{} as Record<string, string>\n\t)\n}\n\n/**\n * Fetches secrets from a 1Password item, filtered to the configured sections\n */\nasync function fetchOnePasswordSecrets(\n\tconfig: OnePasswordProviderConfig\n): Promise<Record<string, string>> {\n\tconst opItem = OPItem.parse(\n\t\tawait $`op item get ${config.opItem} --vault ${config.opVault} --format json`.json()\n\t)\n\n\tconst sectionsToInclude = OPSection.array().parse(config.opSections)\n\n\treturn opItem.fields\n\t\t.filter((f) => sectionsToInclude.some((s) => s.id === f.section?.id))\n\t\t.reduce(\n\t\t\t(acc, f) => {\n\t\t\t\tacc[f.label] = f.value\n\t\t\t\treturn acc\n\t\t\t},\n\t\t\t{} as Record<string, string>\n\t\t)\n}\n\n/**\n * Creates a function to run Dagger commands with Infisical or 1Password integration\n * @param config Configuration for the command runner\n * @returns Function to execute Dagger commands\n */\nexport function createDaggerCommandRunner<T extends DaggerEnvConfig>(\n\tconfig: RunDaggerCommandBaseConfig<T> & InfisicalProviderConfig\n): (commandName: string, options?: RunDaggerCommandOptions) => Promise<void>\nexport function createDaggerCommandRunner<T extends DaggerEnvConfig>(\n\tconfig: RunDaggerCommandBaseConfig<T> & OnePasswordProviderConfig\n): (commandName: string, options?: RunDaggerCommandOptions) => Promise<void>\nexport function createDaggerCommandRunner<T extends DaggerEnvConfig>(\n\tconfig: RunDaggerCommandConfig<T>\n) {\n\treturn async function runDaggerCommand(\n\t\tcommandName: string,\n\t\toptions?: RunDaggerCommandOptions\n\t): Promise<void> {\n\t\tconst { args = {}, env = {}, extraArgs = [] } = options ?? {}\n\n\t\t// Run any pre-command setup\n\t\tif (config.beforeCommand) {\n\t\t\tawait config.beforeCommand()\n\t\t}\n\n\t\t// Environment variables to pass to the spawned command\n\t\tconst envVars: Record<string, string> = {}\n\n\t\tconst commandArgs: string[] = [...extraArgs]\n\n\t\t// Add Docker socket for specific commands if available\n\t\tif (config.dockerCommands?.includes(commandName)) {\n\t\t\ttry {\n\t\t\t\tif (await fs.exists('/var/run/docker.sock')) {\n\t\t\t\t\tcommandArgs.push('--docker-socket=/var/run/docker.sock')\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Ignore if fs is not available or docker socket doesn't exist\n\t\t\t}\n\t\t}\n\n\t\t// Fetch secrets from the configured provider\n\t\tlet secrets: Record<string, string>\n\t\tif ('opVault' in config) {\n\t\t\tsecrets = await fetchOnePasswordSecrets(config)\n\n\t\t\t// Pass dagger cloud token in CI because we don't have user auth.\n\t\t\t// The op:// reference is resolved by the `op run` wrapper below.\n\t\t\tif (process.env.CI !== undefined) {\n\t\t\t\tenvVars.DAGGER_CLOUD_TOKEN = `op://${config.opVault}/${config.opItem}/DAGGER_CLOUD_TOKEN`\n\t\t\t}\n\t\t} else {\n\t\t\tsecrets = await fetchInfisicalSecrets(config)\n\n\t\t\t// Pass dagger cloud token in CI because we don't have user auth\n\t\t\tif (process.env.CI !== undefined && secrets.DAGGER_CLOUD_TOKEN !== undefined) {\n\t\t\t\tenvVars.DAGGER_CLOUD_TOKEN = secrets.DAGGER_CLOUD_TOKEN\n\t\t\t}\n\t\t}\n\n\t\t// Build environment variables for Dagger\n\t\tconst daggerEnv: Record<string, string> = { ...env }\n\t\tif (process.env.CI !== undefined) {\n\t\t\tdaggerEnv.CI = process.env.CI\n\t\t}\n\t\tif (process.env.GITHUB_ACTIONS !== undefined) {\n\t\t\tdaggerEnv.GITHUB_ACTIONS = process.env.GITHUB_ACTIONS\n\t\t}\n\n\t\t// Validate and serialize dagger options\n\t\tconst daggerOptions = config.daggerEnv.getOptionsSchema().parse({\n\t\t\targs,\n\t\t\tenv: daggerEnv,\n\t\t\tsecrets,\n\t\t})\n\t\tenvVars.DAGGER_OPTIONS = JSON.stringify(daggerOptions)\n\n\t\t// Construct the command. 1Password wraps `dagger call` in `op run`\n\t\t// so that op:// references in the environment are resolved.\n\t\tconst cmd: string[] = [\n\t\t\t...('opVault' in config ? ['op', 'run', '--no-masking', '--'] : []),\n\t\t\t'dagger',\n\t\t\t'call',\n\t\t\tcommandName,\n\t\t\t...commandArgs,\n\t\t\t'--options=env://DAGGER_OPTIONS',\n\t\t]\n\n\t\t// Execute the command\n\t\tawait $({\n\t\t\tenv: {\n\t\t\t\t...process.env,\n\t\t\t\t...envVars,\n\t\t\t},\n\t\t\tstdio: 'inherit',\n\t\t})`${cmd}`\n\t}\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dagger-env",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "private": false,
5
5
  "description": "A type-safe, reusable environment configuration abstraction for Dagger modules.",
6
6
  "keywords": [
@@ -28,6 +28,12 @@
28
28
  "default": "./dist/index.js"
29
29
  }
30
30
  },
31
+ "./op": {
32
+ "import": {
33
+ "types": "./dist/op.d.ts",
34
+ "default": "./dist/op.js"
35
+ }
36
+ },
31
37
  "./run": {
32
38
  "import": {
33
39
  "types": "./dist/run-dagger-cmd.d.ts",
@@ -59,7 +65,7 @@
59
65
  "access": "public"
60
66
  },
61
67
  "scripts": {
62
- "build": "runx build tsc ./src/index.ts ./src/run-dagger-cmd.ts",
68
+ "build": "runx build tsc ./src/index.ts ./src/op.ts ./src/run-dagger-cmd.ts",
63
69
  "check:exports": "runx check --exports",
64
70
  "check:lint": "run-eslint",
65
71
  "check:types": "run-tsc",
@@ -2,7 +2,7 @@
2
2
  * Example usage of the runDaggerCommand abstraction
3
3
  *
4
4
  * This example shows a realistic configuration for a project that uses Dagger
5
- * with Infisical integration for secret management.
5
+ * with Infisical or 1Password integration for secret management.
6
6
  *
7
7
  * Import pattern:
8
8
  * - Main DaggerEnv functionality: import { createDaggerEnv } from 'dagger-env'
@@ -74,3 +74,24 @@ export const runDaggerCommand = createDaggerCommandRunner({
74
74
  // Pass the DaggerEnv instance directly
75
75
  daggerEnv,
76
76
  })
77
+
78
+ // Alternatively, create a command runner with 1Password configuration
79
+ export const runDaggerCommandWithOnePassword = createDaggerCommandRunner({
80
+ // 1Password configuration
81
+ opVault: 'your-vault-id', // Replace with your actual vault ID
82
+ opItem: 'your-item-id', // Replace with your actual item ID
83
+ opSections: [
84
+ {
85
+ id: 'shared-section-id',
86
+ label: 'Shared',
87
+ },
88
+ {
89
+ id: 'production-section-id',
90
+ label: 'Production',
91
+ },
92
+ ],
93
+ // Commands that need Docker socket access
94
+ dockerCommands: ['build', 'test', 'deploy', 'migrate', 'seed'],
95
+ // Pass the DaggerEnv instance directly
96
+ daggerEnv,
97
+ })
package/src/op.ts ADDED
@@ -0,0 +1,58 @@
1
+ import { z } from 'zod/v4'
2
+
3
+ /**
4
+ * 1Password vault information
5
+ */
6
+ export type OPVault = z.infer<typeof OPVault>
7
+ export const OPVault = z.object({
8
+ id: z.string(),
9
+ name: z.string(),
10
+ })
11
+
12
+ /**
13
+ * 1Password item section information
14
+ */
15
+ export type OPSection = z.infer<typeof OPSection>
16
+ export const OPSection = z.object({
17
+ id: z.string(),
18
+ label: z.string().optional(),
19
+ })
20
+
21
+ /**
22
+ * 1Password item field
23
+ */
24
+ export type OPField = z.infer<typeof OPField>
25
+ export const OPField = z.object({
26
+ id: z.string(),
27
+ section: OPSection.optional(),
28
+ type: z.enum(['STRING', 'CONCEALED', 'MONTH_YEAR']),
29
+ purpose: z.enum(['NOTES']).optional(),
30
+ label: z.string(),
31
+ value: z.string(),
32
+ reference: z.string(),
33
+ })
34
+
35
+ /**
36
+ * 1Password item category
37
+ */
38
+ export type OPCategory = z.infer<typeof OPCategory>
39
+ export const OPCategory = z.enum(['SECURE_NOTE'])
40
+
41
+ /**
42
+ * Complete 1Password item structure
43
+ */
44
+ export type OPItem = z.infer<typeof OPItem>
45
+ export const OPItem = z.object({
46
+ id: z.string(),
47
+ title: z.string(),
48
+ favorite: z.boolean(),
49
+ version: z.int(),
50
+ vault: OPVault,
51
+ category: OPCategory,
52
+ last_edited_by: z.string(),
53
+ created_at: z.iso.datetime(),
54
+ updated_at: z.iso.datetime(),
55
+ additional_information: z.string(),
56
+ sections: z.array(OPSection),
57
+ fields: z.array(OPField),
58
+ })
@@ -4,13 +4,14 @@ import { z } from 'zod/v4'
4
4
  import { createDaggerEnv } from './dagger-env'
5
5
  import { createDaggerCommandRunner } from './run-dagger-cmd'
6
6
 
7
+ import type { OPItem } from './op'
7
8
  import type { RunDaggerCommandConfig } from './run-dagger-cmd'
8
9
 
9
10
  const mocks = vi.hoisted(() => ({
10
- /** Resolves the JSON payload returned by `infisical export` */
11
- exportJson: vi.fn(),
12
- /** Records the rendered `infisical export` command string */
13
- exportCmd: vi.fn(),
11
+ /** Resolves the JSON payload returned by the secrets CLI (`infisical export` / `op item get`) */
12
+ fetchJson: vi.fn(),
13
+ /** Records the rendered secrets fetch command string */
14
+ fetchCmd: vi.fn(),
14
15
  /** Records the final command execution: (options, argv) */
15
16
  spawn: vi.fn(
16
17
  async (_opts: { env: Record<string, string | undefined> }, _argv: string[]) => undefined
@@ -22,14 +23,14 @@ const mocks = vi.hoisted(() => ({
22
23
  vi.mock('zx', () => {
23
24
  const $ = (firstArg: unknown, ...vals: unknown[]) => {
24
25
  if (Array.isArray(firstArg)) {
25
- // Direct template call: $`infisical export ...`
26
+ // Direct template call: $`infisical export ...` / $`op item get ...`
26
27
  const pieces = firstArg as readonly string[]
27
28
  const cmd = pieces.reduce(
28
29
  (acc, piece, i) => acc + piece + (i < vals.length ? String(vals[i]) : ''),
29
30
  ''
30
31
  )
31
- mocks.exportCmd(cmd)
32
- return { json: mocks.exportJson }
32
+ mocks.fetchCmd(cmd)
33
+ return { json: mocks.fetchJson }
33
34
  }
34
35
  // Options call: $({ env, stdio }) returns a template executor
35
36
  return (_pieces: TemplateStringsArray, ...templateVals: unknown[]) =>
@@ -56,8 +57,8 @@ function getSpawnCall(): { env: SpawnEnv; argv: string[] } {
56
57
  return { env: opts.env, argv }
57
58
  }
58
59
 
59
- function createRunner(overrides?: Partial<Pick<RunDaggerCommandConfig, 'dockerCommands'>>) {
60
- const daggerEnv = createDaggerEnv({
60
+ function createTestDaggerEnv() {
61
+ return createDaggerEnv({
61
62
  args: z.object({
62
63
  push: z.string().optional(),
63
64
  }),
@@ -74,83 +75,205 @@ function createRunner(overrides?: Partial<Pick<RunDaggerCommandConfig, 'dockerCo
74
75
  },
75
76
  derivedEnvVars: {},
76
77
  })
78
+ }
77
79
 
80
+ function createRunner(overrides?: Partial<Pick<RunDaggerCommandConfig, 'dockerCommands'>>) {
78
81
  return createDaggerCommandRunner({
79
82
  projectId: 'test-project-id',
80
83
  env: 'prod',
81
84
  path: '/ci/test',
82
- daggerEnv,
85
+ daggerEnv: createTestDaggerEnv(),
83
86
  ...overrides,
84
87
  })
85
88
  }
86
89
 
90
+ function createOnePasswordRunner(
91
+ overrides?: Partial<Pick<RunDaggerCommandConfig, 'dockerCommands'>>
92
+ ) {
93
+ return createDaggerCommandRunner({
94
+ opVault: 'test-vault-id',
95
+ opItem: 'test-item-id',
96
+ opSections: [{ id: 'shared-section-id', label: 'Shared' }],
97
+ daggerEnv: createTestDaggerEnv(),
98
+ ...overrides,
99
+ })
100
+ }
101
+
102
+ /**
103
+ * A realistic `op item get --format json` payload with one field in the
104
+ * configured section and one field in an excluded section
105
+ */
106
+ const testOPItem: OPItem = {
107
+ id: 'test-item-id',
108
+ title: 'Test Item',
109
+ favorite: false,
110
+ version: 1,
111
+ vault: { id: 'test-vault-id', name: 'Test Vault' },
112
+ category: 'SECURE_NOTE',
113
+ last_edited_by: 'test-user',
114
+ created_at: '2024-01-01T00:00:00Z',
115
+ updated_at: '2024-01-01T00:00:00Z',
116
+ additional_information: '',
117
+ sections: [
118
+ { id: 'shared-section-id', label: 'Shared' },
119
+ { id: 'other-section-id', label: 'Other' },
120
+ ],
121
+ fields: [
122
+ {
123
+ id: 'field-1',
124
+ section: { id: 'shared-section-id', label: 'Shared' },
125
+ type: 'CONCEALED',
126
+ label: 'API_TOKEN',
127
+ value: 'test-api-token',
128
+ reference: 'op://test-vault-id/test-item-id/API_TOKEN',
129
+ },
130
+ {
131
+ id: 'field-2',
132
+ section: { id: 'other-section-id', label: 'Other' },
133
+ type: 'CONCEALED',
134
+ label: 'EXCLUDED_SECRET',
135
+ value: 'excluded-value',
136
+ reference: 'op://test-vault-id/test-item-id/EXCLUDED_SECRET',
137
+ },
138
+ ],
139
+ }
140
+
87
141
  describe('createDaggerCommandRunner()', () => {
88
142
  beforeEach(() => {
89
143
  vi.clearAllMocks()
90
144
  vi.stubEnv('CI', undefined)
91
145
  vi.stubEnv('GITHUB_ACTIONS', undefined)
92
146
  vi.stubEnv('DAGGER_CLOUD_TOKEN', undefined)
93
- mocks.exportJson.mockResolvedValue([
94
- { key: 'API_TOKEN', value: 'test-api-token', comment: '' },
95
- { key: 'DAGGER_CLOUD_TOKEN', value: 'test-dagger-cloud-token', comment: '' },
96
- ])
97
147
  })
98
148
 
99
149
  afterEach(() => {
100
150
  vi.unstubAllEnvs()
101
151
  })
102
152
 
103
- it('builds the expected DAGGER_OPTIONS payload from the infisical export', async () => {
104
- const runDaggerCommand = createRunner()
105
- await runDaggerCommand('test-cmd', {
106
- args: { push: 'true' },
107
- env: { NODE_ENV: 'production' },
153
+ describe('infisical', () => {
154
+ beforeEach(() => {
155
+ mocks.fetchJson.mockResolvedValue([
156
+ { key: 'API_TOKEN', value: 'test-api-token', comment: '' },
157
+ { key: 'DAGGER_CLOUD_TOKEN', value: 'test-dagger-cloud-token', comment: '' },
158
+ ])
108
159
  })
109
160
 
110
- expect(mocks.exportCmd).toHaveBeenCalledWith(
111
- 'infisical export --silent --format=json --projectId test-project-id --env prod --path /ci/test'
112
- )
161
+ it('builds the expected DAGGER_OPTIONS payload from the infisical export', async () => {
162
+ const runDaggerCommand = createRunner()
163
+ await runDaggerCommand('test-cmd', {
164
+ args: { push: 'true' },
165
+ env: { NODE_ENV: 'production' },
166
+ })
167
+
168
+ expect(mocks.fetchCmd).toHaveBeenCalledWith(
169
+ 'infisical export --silent --format=json --projectId test-project-id --env prod --path /ci/test'
170
+ )
113
171
 
114
- const { env } = getSpawnCall()
115
- expect(JSON.parse(env.DAGGER_OPTIONS as string)).toStrictEqual({
116
- args: { push: 'true' },
117
- env: { NODE_ENV: 'production' },
118
- secrets: { API_TOKEN: 'test-api-token' },
172
+ const { env } = getSpawnCall()
173
+ expect(JSON.parse(env.DAGGER_OPTIONS as string)).toStrictEqual({
174
+ args: { push: 'true' },
175
+ env: { NODE_ENV: 'production' },
176
+ secrets: { API_TOKEN: 'test-api-token' },
177
+ })
178
+ expect(env.DAGGER_CLOUD_TOKEN).toBeUndefined()
119
179
  })
120
- expect(env.DAGGER_CLOUD_TOKEN).toBeUndefined()
121
- })
122
180
 
123
- it('passes DAGGER_CLOUD_TOKEN to the runner env (but not DAGGER_OPTIONS) in CI', async () => {
124
- vi.stubEnv('CI', 'true')
125
- vi.stubEnv('GITHUB_ACTIONS', 'true')
181
+ it('passes DAGGER_CLOUD_TOKEN to the runner env (but not DAGGER_OPTIONS) in CI', async () => {
182
+ vi.stubEnv('CI', 'true')
183
+ vi.stubEnv('GITHUB_ACTIONS', 'true')
126
184
 
127
- const runDaggerCommand = createRunner()
128
- await runDaggerCommand('test-cmd')
185
+ const runDaggerCommand = createRunner()
186
+ await runDaggerCommand('test-cmd')
129
187
 
130
- const { env } = getSpawnCall()
131
- const daggerOptions = JSON.parse(env.DAGGER_OPTIONS as string)
132
- expect(daggerOptions.secrets).not.toHaveProperty('DAGGER_CLOUD_TOKEN')
133
- expect(daggerOptions.env).toStrictEqual({ CI: 'true', GITHUB_ACTIONS: 'true' })
134
- expect(env.DAGGER_CLOUD_TOKEN).toBe('test-dagger-cloud-token')
188
+ const { env } = getSpawnCall()
189
+ const daggerOptions = JSON.parse(env.DAGGER_OPTIONS as string)
190
+ expect(daggerOptions.secrets).not.toHaveProperty('DAGGER_CLOUD_TOKEN')
191
+ expect(daggerOptions.env).toStrictEqual({ CI: 'true', GITHUB_ACTIONS: 'true' })
192
+ expect(env.DAGGER_CLOUD_TOKEN).toBe('test-dagger-cloud-token')
193
+ })
194
+
195
+ it('spawns dagger directly without an op wrapper', async () => {
196
+ const runDaggerCommand = createRunner()
197
+ await runDaggerCommand('test-cmd', { extraArgs: ['--verbose'] })
198
+
199
+ const { argv } = getSpawnCall()
200
+ expect(argv[0]).toBe('dagger')
201
+ expect(argv).not.toContain('op')
202
+ expect(argv).toStrictEqual([
203
+ 'dagger',
204
+ 'call',
205
+ 'test-cmd',
206
+ '--verbose',
207
+ '--options=env://DAGGER_OPTIONS',
208
+ ])
209
+ })
135
210
  })
136
211
 
137
- it('spawns dagger directly without an op wrapper', async () => {
138
- const runDaggerCommand = createRunner()
139
- await runDaggerCommand('test-cmd', { extraArgs: ['--verbose'] })
140
-
141
- const { argv } = getSpawnCall()
142
- expect(argv[0]).toBe('dagger')
143
- expect(argv).not.toContain('op')
144
- expect(argv).toStrictEqual([
145
- 'dagger',
146
- 'call',
147
- 'test-cmd',
148
- '--verbose',
149
- '--options=env://DAGGER_OPTIONS',
150
- ])
212
+ describe('1password', () => {
213
+ beforeEach(() => {
214
+ mocks.fetchJson.mockResolvedValue(testOPItem)
215
+ })
216
+
217
+ it('builds the expected DAGGER_OPTIONS payload from the op item sections', async () => {
218
+ const runDaggerCommand = createOnePasswordRunner()
219
+ await runDaggerCommand('test-cmd', {
220
+ args: { push: 'true' },
221
+ env: { NODE_ENV: 'production' },
222
+ })
223
+
224
+ expect(mocks.fetchCmd).toHaveBeenCalledWith(
225
+ 'op item get test-item-id --vault test-vault-id --format json'
226
+ )
227
+
228
+ const { env } = getSpawnCall()
229
+ expect(JSON.parse(env.DAGGER_OPTIONS as string)).toStrictEqual({
230
+ args: { push: 'true' },
231
+ env: { NODE_ENV: 'production' },
232
+ secrets: { API_TOKEN: 'test-api-token' },
233
+ })
234
+ expect(env.DAGGER_CLOUD_TOKEN).toBeUndefined()
235
+ })
236
+
237
+ it('wraps dagger call in op run --no-masking', async () => {
238
+ const runDaggerCommand = createOnePasswordRunner()
239
+ await runDaggerCommand('test-cmd', { extraArgs: ['--verbose'] })
240
+
241
+ const { argv } = getSpawnCall()
242
+ expect(argv).toStrictEqual([
243
+ 'op',
244
+ 'run',
245
+ '--no-masking',
246
+ '--',
247
+ 'dagger',
248
+ 'call',
249
+ 'test-cmd',
250
+ '--verbose',
251
+ '--options=env://DAGGER_OPTIONS',
252
+ ])
253
+ })
254
+
255
+ it('passes DAGGER_CLOUD_TOKEN as an op:// reference in CI', async () => {
256
+ vi.stubEnv('CI', 'true')
257
+ vi.stubEnv('GITHUB_ACTIONS', 'true')
258
+
259
+ const runDaggerCommand = createOnePasswordRunner()
260
+ await runDaggerCommand('test-cmd')
261
+
262
+ const { env } = getSpawnCall()
263
+ const daggerOptions = JSON.parse(env.DAGGER_OPTIONS as string)
264
+ expect(daggerOptions.secrets).not.toHaveProperty('DAGGER_CLOUD_TOKEN')
265
+ expect(daggerOptions.env).toStrictEqual({ CI: 'true', GITHUB_ACTIONS: 'true' })
266
+ expect(env.DAGGER_CLOUD_TOKEN).toBe('op://test-vault-id/test-item-id/DAGGER_CLOUD_TOKEN')
267
+ })
151
268
  })
152
269
 
153
270
  describe('docker socket', () => {
271
+ beforeEach(() => {
272
+ mocks.fetchJson.mockResolvedValue([
273
+ { key: 'API_TOKEN', value: 'test-api-token', comment: '' },
274
+ ])
275
+ })
276
+
154
277
  it('appends --docker-socket for dockerCommands when the socket exists', async () => {
155
278
  mocks.exists.mockResolvedValue(true)
156
279
 
@@ -1,6 +1,8 @@
1
1
  import { z } from 'zod/v4'
2
2
  import { $, fs } from 'zx'
3
3
 
4
+ import { OPItem, OPSection } from './op.js'
5
+
4
6
  import type { DaggerEnv, DaggerEnvConfig } from './dagger-env.js'
5
7
 
6
8
  /**
@@ -13,15 +15,33 @@ export const InfisicalSecret = z.object({
13
15
  })
14
16
 
15
17
  /**
16
- * Configuration for running Dagger commands with Infisical integration
18
+ * Infisical secrets provider configuration
17
19
  */
18
- export interface RunDaggerCommandConfig<T extends DaggerEnvConfig = DaggerEnvConfig> {
20
+ export interface InfisicalProviderConfig {
19
21
  /** Infisical project ID */
20
22
  projectId: string
21
23
  /** Infisical environment slug (e.g. `prod`) */
22
24
  env: string
23
25
  /** Infisical folder path to fetch secrets from (e.g. `/ci/my-repo`) */
24
26
  path: string
27
+ }
28
+
29
+ /**
30
+ * 1Password secrets provider configuration
31
+ */
32
+ export interface OnePasswordProviderConfig {
33
+ /** 1Password vault ID */
34
+ opVault: string
35
+ /** 1Password item ID */
36
+ opItem: string
37
+ /** 1Password sections to include for secrets */
38
+ opSections: Array<{ label: string; id: string }>
39
+ }
40
+
41
+ /**
42
+ * Configuration shared by all secrets providers
43
+ */
44
+ export interface RunDaggerCommandBaseConfig<T extends DaggerEnvConfig = DaggerEnvConfig> {
25
45
  /** Commands that should include Docker socket if available */
26
46
  dockerCommands?: string[]
27
47
  /** Hook to run before executing the command (e.g., vendor file setup) */
@@ -30,6 +50,12 @@ export interface RunDaggerCommandConfig<T extends DaggerEnvConfig = DaggerEnvCon
30
50
  daggerEnv: DaggerEnv<T>
31
51
  }
32
52
 
53
+ /**
54
+ * Configuration for running Dagger commands with Infisical or 1Password integration
55
+ */
56
+ export type RunDaggerCommandConfig<T extends DaggerEnvConfig = DaggerEnvConfig> =
57
+ RunDaggerCommandBaseConfig<T> & (InfisicalProviderConfig | OnePasswordProviderConfig)
58
+
33
59
  /**
34
60
  * Options for individual command execution
35
61
  */
@@ -43,10 +69,58 @@ export interface RunDaggerCommandOptions {
43
69
  }
44
70
 
45
71
  /**
46
- * Creates a function to run Dagger commands with Infisical integration
72
+ * Fetches secrets from Infisical via `infisical export`
73
+ */
74
+ async function fetchInfisicalSecrets(
75
+ config: InfisicalProviderConfig
76
+ ): Promise<Record<string, string>> {
77
+ const exportedSecrets = InfisicalSecret.array().parse(
78
+ await $`infisical export --silent --format=json --projectId ${config.projectId} --env ${config.env} --path ${config.path}`.json()
79
+ )
80
+
81
+ return exportedSecrets.reduce(
82
+ (acc, s) => {
83
+ acc[s.key] = s.value
84
+ return acc
85
+ },
86
+ {} as Record<string, string>
87
+ )
88
+ }
89
+
90
+ /**
91
+ * Fetches secrets from a 1Password item, filtered to the configured sections
92
+ */
93
+ async function fetchOnePasswordSecrets(
94
+ config: OnePasswordProviderConfig
95
+ ): Promise<Record<string, string>> {
96
+ const opItem = OPItem.parse(
97
+ await $`op item get ${config.opItem} --vault ${config.opVault} --format json`.json()
98
+ )
99
+
100
+ const sectionsToInclude = OPSection.array().parse(config.opSections)
101
+
102
+ return opItem.fields
103
+ .filter((f) => sectionsToInclude.some((s) => s.id === f.section?.id))
104
+ .reduce(
105
+ (acc, f) => {
106
+ acc[f.label] = f.value
107
+ return acc
108
+ },
109
+ {} as Record<string, string>
110
+ )
111
+ }
112
+
113
+ /**
114
+ * Creates a function to run Dagger commands with Infisical or 1Password integration
47
115
  * @param config Configuration for the command runner
48
116
  * @returns Function to execute Dagger commands
49
117
  */
118
+ export function createDaggerCommandRunner<T extends DaggerEnvConfig>(
119
+ config: RunDaggerCommandBaseConfig<T> & InfisicalProviderConfig
120
+ ): (commandName: string, options?: RunDaggerCommandOptions) => Promise<void>
121
+ export function createDaggerCommandRunner<T extends DaggerEnvConfig>(
122
+ config: RunDaggerCommandBaseConfig<T> & OnePasswordProviderConfig
123
+ ): (commandName: string, options?: RunDaggerCommandOptions) => Promise<void>
50
124
  export function createDaggerCommandRunner<T extends DaggerEnvConfig>(
51
125
  config: RunDaggerCommandConfig<T>
52
126
  ) {
@@ -61,7 +135,7 @@ export function createDaggerCommandRunner<T extends DaggerEnvConfig>(
61
135
  await config.beforeCommand()
62
136
  }
63
137
 
64
- // Environment variables to pass to the `dagger` command
138
+ // Environment variables to pass to the spawned command
65
139
  const envVars: Record<string, string> = {}
66
140
 
67
141
  const commandArgs: string[] = [...extraArgs]
@@ -77,23 +151,23 @@ export function createDaggerCommandRunner<T extends DaggerEnvConfig>(
77
151
  }
78
152
  }
79
153
 
80
- // Fetch secrets from Infisical
81
- const exportedSecrets = InfisicalSecret.array().parse(
82
- await $`infisical export --silent --format=json --projectId ${config.projectId} --env ${config.env} --path ${config.path}`.json()
83
- )
154
+ // Fetch secrets from the configured provider
155
+ let secrets: Record<string, string>
156
+ if ('opVault' in config) {
157
+ secrets = await fetchOnePasswordSecrets(config)
84
158
 
85
- // Extract secrets into a key/value map
86
- const secrets = exportedSecrets.reduce(
87
- (acc, s) => {
88
- acc[s.key] = s.value
89
- return acc
90
- },
91
- {} as Record<string, string>
92
- )
159
+ // Pass dagger cloud token in CI because we don't have user auth.
160
+ // The op:// reference is resolved by the `op run` wrapper below.
161
+ if (process.env.CI !== undefined) {
162
+ envVars.DAGGER_CLOUD_TOKEN = `op://${config.opVault}/${config.opItem}/DAGGER_CLOUD_TOKEN`
163
+ }
164
+ } else {
165
+ secrets = await fetchInfisicalSecrets(config)
93
166
 
94
- // Pass dagger cloud token in CI because we don't have user auth
95
- if (process.env.CI !== undefined && secrets.DAGGER_CLOUD_TOKEN !== undefined) {
96
- envVars.DAGGER_CLOUD_TOKEN = secrets.DAGGER_CLOUD_TOKEN
167
+ // Pass dagger cloud token in CI because we don't have user auth
168
+ if (process.env.CI !== undefined && secrets.DAGGER_CLOUD_TOKEN !== undefined) {
169
+ envVars.DAGGER_CLOUD_TOKEN = secrets.DAGGER_CLOUD_TOKEN
170
+ }
97
171
  }
98
172
 
99
173
  // Build environment variables for Dagger
@@ -113,8 +187,10 @@ export function createDaggerCommandRunner<T extends DaggerEnvConfig>(
113
187
  })
114
188
  envVars.DAGGER_OPTIONS = JSON.stringify(daggerOptions)
115
189
 
116
- // Construct the command
190
+ // Construct the command. 1Password wraps `dagger call` in `op run`
191
+ // so that op:// references in the environment are resolved.
117
192
  const cmd: string[] = [
193
+ ...('opVault' in config ? ['op', 'run', '--no-masking', '--'] : []),
118
194
  'dagger',
119
195
  'call',
120
196
  commandName,