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 +36 -8
- package/dist/op.d.ts +90 -0
- package/dist/op.d.ts.map +1 -0
- package/dist/op.js +34 -0
- package/dist/op.js.map +1 -0
- package/dist/run-dagger-cmd.d.ts +28 -4
- package/dist/run-dagger-cmd.d.ts.map +1 -1
- package/dist/run-dagger-cmd.js +42 -15
- package/dist/run-dagger-cmd.js.map +1 -1
- package/package.json +8 -2
- package/src/examples/run-dagger-cmd-usage.ts +22 -1
- package/src/op.ts +58 -0
- package/src/run-dagger-cmd.spec.ts +176 -53
- package/src/run-dagger-cmd.ts +96 -20
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
|
-
|
|
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
|
-
**
|
|
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
|
package/dist/op.d.ts.map
ADDED
|
@@ -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"]}
|
package/dist/run-dagger-cmd.d.ts
CHANGED
|
@@ -9,15 +9,34 @@ export declare const InfisicalSecret: z.ZodObject<{
|
|
|
9
9
|
value: z.ZodString;
|
|
10
10
|
}, z.core.$strip>;
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
12
|
+
* Infisical secrets provider configuration
|
|
13
13
|
*/
|
|
14
|
-
export interface
|
|
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:
|
|
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;
|
|
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"}
|
package/dist/run-dagger-cmd.js
CHANGED
|
@@ -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
|
-
*
|
|
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
|
|
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
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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":"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","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.
|
|
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
|
-
|
|
12
|
-
/** Records the rendered
|
|
13
|
-
|
|
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.
|
|
32
|
-
return { json: mocks.
|
|
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
|
|
60
|
-
|
|
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
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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
|
-
|
|
111
|
-
|
|
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
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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
|
-
|
|
128
|
-
|
|
185
|
+
const runDaggerCommand = createRunner()
|
|
186
|
+
await runDaggerCommand('test-cmd')
|
|
129
187
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
|
package/src/run-dagger-cmd.ts
CHANGED
|
@@ -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
|
-
*
|
|
18
|
+
* Infisical secrets provider configuration
|
|
17
19
|
*/
|
|
18
|
-
export interface
|
|
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
|
-
*
|
|
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
|
|
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
|
|
81
|
-
|
|
82
|
-
|
|
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
|
-
|
|
86
|
-
|
|
87
|
-
(
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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,
|