dagger-env 0.6.8 → 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 +40 -20
- package/dist/run-dagger-cmd.d.ts +34 -4
- package/dist/run-dagger-cmd.d.ts.map +1 -1
- package/dist/run-dagger-cmd.js +47 -25
- package/dist/run-dagger-cmd.js.map +1 -1
- package/package.json +1 -1
- package/src/examples/run-dagger-cmd-usage.ts +21 -9
- package/src/run-dagger-cmd.spec.ts +314 -0
- package/src/run-dagger-cmd.ts +105 -32
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 1Password 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
|
-
- 🔐 **1Password Integration**: Built-in command runner
|
|
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 1Password CLI (`op`) to be installed.
|
|
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 (1Password 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,16 +100,11 @@ 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
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
{
|
|
109
|
-
id: 'your-section-id',
|
|
110
|
-
label: 'Shared'
|
|
111
|
-
}
|
|
112
|
-
],
|
|
105
|
+
projectId: 'your-project-id',
|
|
106
|
+
env: 'prod',
|
|
107
|
+
path: '/ci/my-repo',
|
|
113
108
|
dockerCommands: ['build', 'deploy', 'test'],
|
|
114
109
|
daggerEnv: myDaggerEnv
|
|
115
110
|
})
|
|
@@ -121,18 +116,34 @@ await runDaggerCommand('test', {
|
|
|
121
116
|
})
|
|
122
117
|
```
|
|
123
118
|
|
|
124
|
-
###
|
|
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:
|
|
125
122
|
|
|
126
123
|
```typescript
|
|
127
|
-
// Advanced configuration with multiple sections and pre-command setup
|
|
128
124
|
const runDaggerCommand = createDaggerCommandRunner({
|
|
129
125
|
opVault: 'your-vault-id',
|
|
130
126
|
opItem: 'your-item-id',
|
|
131
127
|
opSections: [
|
|
132
|
-
{
|
|
133
|
-
|
|
128
|
+
{
|
|
129
|
+
id: 'your-section-id',
|
|
130
|
+
label: 'Shared'
|
|
131
|
+
}
|
|
134
132
|
],
|
|
135
133
|
dockerCommands: ['build', 'deploy', 'test'],
|
|
134
|
+
daggerEnv: myDaggerEnv
|
|
135
|
+
})
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Advanced Configuration
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
// Advanced configuration with pre-command setup
|
|
142
|
+
const runDaggerCommand = createDaggerCommandRunner({
|
|
143
|
+
projectId: 'your-project-id',
|
|
144
|
+
env: 'prod',
|
|
145
|
+
path: '/ci/my-repo',
|
|
146
|
+
dockerCommands: ['build', 'deploy', 'test'],
|
|
136
147
|
beforeCommand: async () => {
|
|
137
148
|
// Setup vendor files, modules, etc.
|
|
138
149
|
console.log('Setting up environment...')
|
|
@@ -208,13 +219,22 @@ Returns array of secret names for a specific preset.
|
|
|
208
219
|
|
|
209
220
|
#### `createDaggerCommandRunner(config)`
|
|
210
221
|
|
|
211
|
-
Creates a function to run Dagger commands with 1Password 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.
|
|
212
223
|
|
|
213
|
-
**
|
|
224
|
+
**Infisical parameters:**
|
|
225
|
+
|
|
226
|
+
- `config.projectId`: Infisical project ID
|
|
227
|
+
- `config.env`: Infisical environment slug (e.g. `prod`)
|
|
228
|
+
- `config.path`: Infisical folder path to fetch secrets from (e.g. `/ci/my-repo`)
|
|
229
|
+
|
|
230
|
+
**1Password parameters:**
|
|
214
231
|
|
|
215
232
|
- `config.opVault`: 1Password vault ID
|
|
216
233
|
- `config.opItem`: 1Password item ID
|
|
217
234
|
- `config.opSections`: Array of 1Password sections to include for secrets
|
|
235
|
+
|
|
236
|
+
**Shared parameters:**
|
|
237
|
+
|
|
218
238
|
- `config.dockerCommands`: Optional array of command names that should include Docker socket
|
|
219
239
|
- `config.beforeCommand`: Optional async function to run before executing the command
|
|
220
240
|
- `config.daggerEnv`: DaggerEnv instance for schema validation and type safety
|
package/dist/run-dagger-cmd.d.ts
CHANGED
|
@@ -1,8 +1,28 @@
|
|
|
1
|
+
import { z } from 'zod/v4';
|
|
1
2
|
import type { DaggerEnv, DaggerEnvConfig } from './dagger-env.js';
|
|
2
3
|
/**
|
|
3
|
-
*
|
|
4
|
+
* A single secret returned by `infisical export --format=json`
|
|
4
5
|
*/
|
|
5
|
-
export
|
|
6
|
+
export type InfisicalSecret = z.infer<typeof InfisicalSecret>;
|
|
7
|
+
export declare const InfisicalSecret: z.ZodObject<{
|
|
8
|
+
key: z.ZodString;
|
|
9
|
+
value: z.ZodString;
|
|
10
|
+
}, z.core.$strip>;
|
|
11
|
+
/**
|
|
12
|
+
* Infisical secrets provider configuration
|
|
13
|
+
*/
|
|
14
|
+
export interface InfisicalProviderConfig {
|
|
15
|
+
/** Infisical project ID */
|
|
16
|
+
projectId: string;
|
|
17
|
+
/** Infisical environment slug (e.g. `prod`) */
|
|
18
|
+
env: string;
|
|
19
|
+
/** Infisical folder path to fetch secrets from (e.g. `/ci/my-repo`) */
|
|
20
|
+
path: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* 1Password secrets provider configuration
|
|
24
|
+
*/
|
|
25
|
+
export interface OnePasswordProviderConfig {
|
|
6
26
|
/** 1Password vault ID */
|
|
7
27
|
opVault: string;
|
|
8
28
|
/** 1Password item ID */
|
|
@@ -12,6 +32,11 @@ export interface RunDaggerCommandConfig<T extends DaggerEnvConfig = DaggerEnvCon
|
|
|
12
32
|
label: string;
|
|
13
33
|
id: string;
|
|
14
34
|
}>;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Configuration shared by all secrets providers
|
|
38
|
+
*/
|
|
39
|
+
export interface RunDaggerCommandBaseConfig<T extends DaggerEnvConfig = DaggerEnvConfig> {
|
|
15
40
|
/** Commands that should include Docker socket if available */
|
|
16
41
|
dockerCommands?: string[];
|
|
17
42
|
/** Hook to run before executing the command (e.g., vendor file setup) */
|
|
@@ -19,6 +44,10 @@ export interface RunDaggerCommandConfig<T extends DaggerEnvConfig = DaggerEnvCon
|
|
|
19
44
|
/** DaggerEnv instance for schema validation and type safety */
|
|
20
45
|
daggerEnv: DaggerEnv<T>;
|
|
21
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);
|
|
22
51
|
/**
|
|
23
52
|
* Options for individual command execution
|
|
24
53
|
*/
|
|
@@ -31,9 +60,10 @@ export interface RunDaggerCommandOptions {
|
|
|
31
60
|
extraArgs?: string[];
|
|
32
61
|
}
|
|
33
62
|
/**
|
|
34
|
-
* Creates a function to run Dagger commands with 1Password integration
|
|
63
|
+
* Creates a function to run Dagger commands with Infisical or 1Password integration
|
|
35
64
|
* @param config Configuration for the command runner
|
|
36
65
|
* @returns Function to execute Dagger commands
|
|
37
66
|
*/
|
|
38
|
-
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>;
|
|
39
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":"
|
|
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,24 +1,42 @@
|
|
|
1
|
+
import { z } from 'zod/v4';
|
|
1
2
|
import { $, fs } from 'zx';
|
|
2
3
|
import { OPItem, OPSection } from './op.js';
|
|
4
|
+
export const InfisicalSecret = z.object({
|
|
5
|
+
key: z.string(),
|
|
6
|
+
value: z.string(),
|
|
7
|
+
});
|
|
3
8
|
/**
|
|
4
|
-
*
|
|
5
|
-
* @param config Configuration for the command runner
|
|
6
|
-
* @returns Function to execute Dagger commands
|
|
9
|
+
* Fetches secrets from Infisical via `infisical export`
|
|
7
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
|
+
}
|
|
8
31
|
export function createDaggerCommandRunner(config) {
|
|
9
|
-
const opItemUri = `op://${config.opVault}/${config.opItem}`;
|
|
10
32
|
return async function runDaggerCommand(commandName, options) {
|
|
11
33
|
const { args = {}, env = {}, extraArgs = [] } = options ?? {};
|
|
12
34
|
// Run any pre-command setup
|
|
13
35
|
if (config.beforeCommand) {
|
|
14
36
|
await config.beforeCommand();
|
|
15
37
|
}
|
|
16
|
-
// Environment variables to pass to the
|
|
38
|
+
// Environment variables to pass to the spawned command
|
|
17
39
|
const envVars = {};
|
|
18
|
-
// Pass dagger cloud token in CI because we don't have user auth
|
|
19
|
-
if (process.env.CI !== undefined) {
|
|
20
|
-
envVars.DAGGER_CLOUD_TOKEN = `${opItemUri}/DAGGER_CLOUD_TOKEN`;
|
|
21
|
-
}
|
|
22
40
|
const commandArgs = [...extraArgs];
|
|
23
41
|
// Add Docker socket for specific commands if available
|
|
24
42
|
if (config.dockerCommands?.includes(commandName)) {
|
|
@@ -31,17 +49,23 @@ export function createDaggerCommandRunner(config) {
|
|
|
31
49
|
// Ignore if fs is not available or docker socket doesn't exist
|
|
32
50
|
}
|
|
33
51
|
}
|
|
34
|
-
// Fetch
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
+
}
|
|
68
|
+
}
|
|
45
69
|
// Build environment variables for Dagger
|
|
46
70
|
const daggerEnv = { ...env };
|
|
47
71
|
if (process.env.CI !== undefined) {
|
|
@@ -57,12 +81,10 @@ export function createDaggerCommandRunner(config) {
|
|
|
57
81
|
secrets,
|
|
58
82
|
});
|
|
59
83
|
envVars.DAGGER_OPTIONS = JSON.stringify(daggerOptions);
|
|
60
|
-
// Construct the command
|
|
84
|
+
// Construct the command. 1Password wraps `dagger call` in `op run`
|
|
85
|
+
// so that op:// references in the environment are resolved.
|
|
61
86
|
const cmd = [
|
|
62
|
-
'op',
|
|
63
|
-
'run',
|
|
64
|
-
'--no-masking',
|
|
65
|
-
'--',
|
|
87
|
+
...('opVault' in config ? ['op', 'run', '--no-masking', '--'] : []),
|
|
66
88
|
'dagger',
|
|
67
89
|
'call',
|
|
68
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 { $, fs } from 'zx'\n\nimport { OPItem, OPSection } from './op.js'\n\nimport type { DaggerEnv, DaggerEnvConfig } from './dagger-env.js'\n\n/**\n * Configuration for running Dagger commands with 1Password integration\n */\nexport interface RunDaggerCommandConfig<T extends DaggerEnvConfig = DaggerEnvConfig> {\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\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 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: RunDaggerCommandConfig<T>\n) {\n\tconst opItemUri = `op://${config.opVault}/${config.opItem}`\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 `op run` command\n\t\tconst envVars: Record<string, string> = {}\n\n\t\t// Pass dagger cloud token in CI because we don't have user auth\n\t\tif (process.env.CI !== undefined) {\n\t\t\tenvVars.DAGGER_CLOUD_TOKEN = `${opItemUri}/DAGGER_CLOUD_TOKEN`\n\t\t}\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 1Password item\n\t\tconst opItem = OPItem.parse(\n\t\t\tawait $`op item get ${config.opItem} --vault ${config.opVault} --format json`.json()\n\t\t)\n\n\t\t// Parse sections to include\n\t\tconst sectionsToInclude = OPSection.array().parse(config.opSections)\n\n\t\t// Extract secrets from specified sections\n\t\tconst secrets = opItem.fields\n\t\t\t.filter((f) => sectionsToInclude.some((s) => s.id === f.section?.id))\n\t\t\t.reduce(\n\t\t\t\t(acc, f) => {\n\t\t\t\t\tacc[f.label] = f.value\n\t\t\t\t\treturn acc\n\t\t\t\t},\n\t\t\t\t{} as Record<string, string>\n\t\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'op',\n\t\t\t'run',\n\t\t\t'--no-masking',\n\t\t\t'--',\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
|
@@ -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 1Password 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'
|
|
@@ -56,8 +56,27 @@ const daggerEnv = createDaggerEnv({
|
|
|
56
56
|
derivedEnvVars: {} as const,
|
|
57
57
|
})
|
|
58
58
|
|
|
59
|
-
// Create the command runner with
|
|
59
|
+
// Create the command runner with Infisical configuration
|
|
60
60
|
export const runDaggerCommand = createDaggerCommandRunner({
|
|
61
|
+
// Infisical configuration
|
|
62
|
+
projectId: 'your-project-id', // Replace with your actual Infisical project ID
|
|
63
|
+
env: 'prod', // Infisical environment slug
|
|
64
|
+
path: '/ci/my-repo', // Infisical folder path to fetch secrets from
|
|
65
|
+
// Commands that need Docker socket access
|
|
66
|
+
dockerCommands: ['build', 'test', 'deploy', 'migrate', 'seed'],
|
|
67
|
+
// Optional: Run setup before executing commands
|
|
68
|
+
beforeCommand: async () => {
|
|
69
|
+
// Example: Set up vendor files, download dependencies, etc.
|
|
70
|
+
console.log('Preparing Dagger environment...')
|
|
71
|
+
// const modules = await getDaggerModules()
|
|
72
|
+
// await setupDaggerVendorFiles(modules)
|
|
73
|
+
},
|
|
74
|
+
// Pass the DaggerEnv instance directly
|
|
75
|
+
daggerEnv,
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
// Alternatively, create a command runner with 1Password configuration
|
|
79
|
+
export const runDaggerCommandWithOnePassword = createDaggerCommandRunner({
|
|
61
80
|
// 1Password configuration
|
|
62
81
|
opVault: 'your-vault-id', // Replace with your actual vault ID
|
|
63
82
|
opItem: 'your-item-id', // Replace with your actual item ID
|
|
@@ -73,13 +92,6 @@ export const runDaggerCommand = createDaggerCommandRunner({
|
|
|
73
92
|
],
|
|
74
93
|
// Commands that need Docker socket access
|
|
75
94
|
dockerCommands: ['build', 'test', 'deploy', 'migrate', 'seed'],
|
|
76
|
-
// Optional: Run setup before executing commands
|
|
77
|
-
beforeCommand: async () => {
|
|
78
|
-
// Example: Set up vendor files, download dependencies, etc.
|
|
79
|
-
console.log('Preparing Dagger environment...')
|
|
80
|
-
// const modules = await getDaggerModules()
|
|
81
|
-
// await setupDaggerVendorFiles(modules)
|
|
82
|
-
},
|
|
83
95
|
// Pass the DaggerEnv instance directly
|
|
84
96
|
daggerEnv,
|
|
85
97
|
})
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
2
|
+
import { z } from 'zod/v4'
|
|
3
|
+
|
|
4
|
+
import { createDaggerEnv } from './dagger-env'
|
|
5
|
+
import { createDaggerCommandRunner } from './run-dagger-cmd'
|
|
6
|
+
|
|
7
|
+
import type { OPItem } from './op'
|
|
8
|
+
import type { RunDaggerCommandConfig } from './run-dagger-cmd'
|
|
9
|
+
|
|
10
|
+
const mocks = vi.hoisted(() => ({
|
|
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(),
|
|
15
|
+
/** Records the final command execution: (options, argv) */
|
|
16
|
+
spawn: vi.fn(
|
|
17
|
+
async (_opts: { env: Record<string, string | undefined> }, _argv: string[]) => undefined
|
|
18
|
+
),
|
|
19
|
+
/** Mock for fs.exists() (docker socket probe) */
|
|
20
|
+
exists: vi.fn(async () => false),
|
|
21
|
+
}))
|
|
22
|
+
|
|
23
|
+
vi.mock('zx', () => {
|
|
24
|
+
const $ = (firstArg: unknown, ...vals: unknown[]) => {
|
|
25
|
+
if (Array.isArray(firstArg)) {
|
|
26
|
+
// Direct template call: $`infisical export ...` / $`op item get ...`
|
|
27
|
+
const pieces = firstArg as readonly string[]
|
|
28
|
+
const cmd = pieces.reduce(
|
|
29
|
+
(acc, piece, i) => acc + piece + (i < vals.length ? String(vals[i]) : ''),
|
|
30
|
+
''
|
|
31
|
+
)
|
|
32
|
+
mocks.fetchCmd(cmd)
|
|
33
|
+
return { json: mocks.fetchJson }
|
|
34
|
+
}
|
|
35
|
+
// Options call: $({ env, stdio }) returns a template executor
|
|
36
|
+
return (_pieces: TemplateStringsArray, ...templateVals: unknown[]) =>
|
|
37
|
+
mocks.spawn(
|
|
38
|
+
firstArg as { env: Record<string, string | undefined> },
|
|
39
|
+
templateVals[0] as string[]
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
return { $, fs: { exists: mocks.exists } }
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Environment variables passed to the spawned process
|
|
47
|
+
*/
|
|
48
|
+
type SpawnEnv = Record<string, string | undefined>
|
|
49
|
+
|
|
50
|
+
function getSpawnCall(): { env: SpawnEnv; argv: string[] } {
|
|
51
|
+
expect(mocks.spawn).toHaveBeenCalledTimes(1)
|
|
52
|
+
const call = mocks.spawn.mock.calls.at(0)
|
|
53
|
+
if (!call) {
|
|
54
|
+
throw new Error('spawn was not called')
|
|
55
|
+
}
|
|
56
|
+
const [opts, argv] = call
|
|
57
|
+
return { env: opts.env, argv }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function createTestDaggerEnv() {
|
|
61
|
+
return createDaggerEnv({
|
|
62
|
+
args: z.object({
|
|
63
|
+
push: z.string().optional(),
|
|
64
|
+
}),
|
|
65
|
+
env: z.object({
|
|
66
|
+
CI: z.string().optional(),
|
|
67
|
+
GITHUB_ACTIONS: z.string().optional(),
|
|
68
|
+
NODE_ENV: z.string().optional(),
|
|
69
|
+
}),
|
|
70
|
+
secrets: z.object({
|
|
71
|
+
API_TOKEN: z.string(),
|
|
72
|
+
}),
|
|
73
|
+
secretPresets: {
|
|
74
|
+
api: ['API_TOKEN'],
|
|
75
|
+
},
|
|
76
|
+
derivedEnvVars: {},
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function createRunner(overrides?: Partial<Pick<RunDaggerCommandConfig, 'dockerCommands'>>) {
|
|
81
|
+
return createDaggerCommandRunner({
|
|
82
|
+
projectId: 'test-project-id',
|
|
83
|
+
env: 'prod',
|
|
84
|
+
path: '/ci/test',
|
|
85
|
+
daggerEnv: createTestDaggerEnv(),
|
|
86
|
+
...overrides,
|
|
87
|
+
})
|
|
88
|
+
}
|
|
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
|
+
|
|
141
|
+
describe('createDaggerCommandRunner()', () => {
|
|
142
|
+
beforeEach(() => {
|
|
143
|
+
vi.clearAllMocks()
|
|
144
|
+
vi.stubEnv('CI', undefined)
|
|
145
|
+
vi.stubEnv('GITHUB_ACTIONS', undefined)
|
|
146
|
+
vi.stubEnv('DAGGER_CLOUD_TOKEN', undefined)
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
afterEach(() => {
|
|
150
|
+
vi.unstubAllEnvs()
|
|
151
|
+
})
|
|
152
|
+
|
|
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
|
+
])
|
|
159
|
+
})
|
|
160
|
+
|
|
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
|
+
)
|
|
171
|
+
|
|
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()
|
|
179
|
+
})
|
|
180
|
+
|
|
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')
|
|
184
|
+
|
|
185
|
+
const runDaggerCommand = createRunner()
|
|
186
|
+
await runDaggerCommand('test-cmd')
|
|
187
|
+
|
|
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
|
+
})
|
|
210
|
+
})
|
|
211
|
+
|
|
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
|
+
})
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
describe('docker socket', () => {
|
|
271
|
+
beforeEach(() => {
|
|
272
|
+
mocks.fetchJson.mockResolvedValue([
|
|
273
|
+
{ key: 'API_TOKEN', value: 'test-api-token', comment: '' },
|
|
274
|
+
])
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
it('appends --docker-socket for dockerCommands when the socket exists', async () => {
|
|
278
|
+
mocks.exists.mockResolvedValue(true)
|
|
279
|
+
|
|
280
|
+
const runDaggerCommand = createRunner({ dockerCommands: ['build'] })
|
|
281
|
+
await runDaggerCommand('build')
|
|
282
|
+
|
|
283
|
+
const { argv } = getSpawnCall()
|
|
284
|
+
expect(argv).toStrictEqual([
|
|
285
|
+
'dagger',
|
|
286
|
+
'call',
|
|
287
|
+
'build',
|
|
288
|
+
'--docker-socket=/var/run/docker.sock',
|
|
289
|
+
'--options=env://DAGGER_OPTIONS',
|
|
290
|
+
])
|
|
291
|
+
})
|
|
292
|
+
|
|
293
|
+
it('does not append --docker-socket for non-docker commands', async () => {
|
|
294
|
+
mocks.exists.mockResolvedValue(true)
|
|
295
|
+
|
|
296
|
+
const runDaggerCommand = createRunner({ dockerCommands: ['build'] })
|
|
297
|
+
await runDaggerCommand('test-cmd')
|
|
298
|
+
|
|
299
|
+
const { argv } = getSpawnCall()
|
|
300
|
+
expect(argv).not.toContain('--docker-socket=/var/run/docker.sock')
|
|
301
|
+
expect(mocks.exists).not.toHaveBeenCalled()
|
|
302
|
+
})
|
|
303
|
+
|
|
304
|
+
it('does not append --docker-socket when the socket does not exist', async () => {
|
|
305
|
+
mocks.exists.mockResolvedValue(false)
|
|
306
|
+
|
|
307
|
+
const runDaggerCommand = createRunner({ dockerCommands: ['build'] })
|
|
308
|
+
await runDaggerCommand('build')
|
|
309
|
+
|
|
310
|
+
const { argv } = getSpawnCall()
|
|
311
|
+
expect(argv).not.toContain('--docker-socket=/var/run/docker.sock')
|
|
312
|
+
})
|
|
313
|
+
})
|
|
314
|
+
})
|
package/src/run-dagger-cmd.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { z } from 'zod/v4'
|
|
1
2
|
import { $, fs } from 'zx'
|
|
2
3
|
|
|
3
4
|
import { OPItem, OPSection } from './op.js'
|
|
@@ -5,15 +6,42 @@ import { OPItem, OPSection } from './op.js'
|
|
|
5
6
|
import type { DaggerEnv, DaggerEnvConfig } from './dagger-env.js'
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
|
-
*
|
|
9
|
+
* A single secret returned by `infisical export --format=json`
|
|
9
10
|
*/
|
|
10
|
-
export
|
|
11
|
+
export type InfisicalSecret = z.infer<typeof InfisicalSecret>
|
|
12
|
+
export const InfisicalSecret = z.object({
|
|
13
|
+
key: z.string(),
|
|
14
|
+
value: z.string(),
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Infisical secrets provider configuration
|
|
19
|
+
*/
|
|
20
|
+
export interface InfisicalProviderConfig {
|
|
21
|
+
/** Infisical project ID */
|
|
22
|
+
projectId: string
|
|
23
|
+
/** Infisical environment slug (e.g. `prod`) */
|
|
24
|
+
env: string
|
|
25
|
+
/** Infisical folder path to fetch secrets from (e.g. `/ci/my-repo`) */
|
|
26
|
+
path: string
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 1Password secrets provider configuration
|
|
31
|
+
*/
|
|
32
|
+
export interface OnePasswordProviderConfig {
|
|
11
33
|
/** 1Password vault ID */
|
|
12
34
|
opVault: string
|
|
13
35
|
/** 1Password item ID */
|
|
14
36
|
opItem: string
|
|
15
37
|
/** 1Password sections to include for secrets */
|
|
16
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> {
|
|
17
45
|
/** Commands that should include Docker socket if available */
|
|
18
46
|
dockerCommands?: string[]
|
|
19
47
|
/** Hook to run before executing the command (e.g., vendor file setup) */
|
|
@@ -22,6 +50,12 @@ export interface RunDaggerCommandConfig<T extends DaggerEnvConfig = DaggerEnvCon
|
|
|
22
50
|
daggerEnv: DaggerEnv<T>
|
|
23
51
|
}
|
|
24
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
|
+
|
|
25
59
|
/**
|
|
26
60
|
* Options for individual command execution
|
|
27
61
|
*/
|
|
@@ -35,15 +69,61 @@ export interface RunDaggerCommandOptions {
|
|
|
35
69
|
}
|
|
36
70
|
|
|
37
71
|
/**
|
|
38
|
-
*
|
|
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
|
|
39
115
|
* @param config Configuration for the command runner
|
|
40
116
|
* @returns Function to execute Dagger commands
|
|
41
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>
|
|
42
124
|
export function createDaggerCommandRunner<T extends DaggerEnvConfig>(
|
|
43
125
|
config: RunDaggerCommandConfig<T>
|
|
44
126
|
) {
|
|
45
|
-
const opItemUri = `op://${config.opVault}/${config.opItem}`
|
|
46
|
-
|
|
47
127
|
return async function runDaggerCommand(
|
|
48
128
|
commandName: string,
|
|
49
129
|
options?: RunDaggerCommandOptions
|
|
@@ -55,14 +135,9 @@ export function createDaggerCommandRunner<T extends DaggerEnvConfig>(
|
|
|
55
135
|
await config.beforeCommand()
|
|
56
136
|
}
|
|
57
137
|
|
|
58
|
-
// Environment variables to pass to the
|
|
138
|
+
// Environment variables to pass to the spawned command
|
|
59
139
|
const envVars: Record<string, string> = {}
|
|
60
140
|
|
|
61
|
-
// Pass dagger cloud token in CI because we don't have user auth
|
|
62
|
-
if (process.env.CI !== undefined) {
|
|
63
|
-
envVars.DAGGER_CLOUD_TOKEN = `${opItemUri}/DAGGER_CLOUD_TOKEN`
|
|
64
|
-
}
|
|
65
|
-
|
|
66
141
|
const commandArgs: string[] = [...extraArgs]
|
|
67
142
|
|
|
68
143
|
// Add Docker socket for specific commands if available
|
|
@@ -76,24 +151,24 @@ export function createDaggerCommandRunner<T extends DaggerEnvConfig>(
|
|
|
76
151
|
}
|
|
77
152
|
}
|
|
78
153
|
|
|
79
|
-
// Fetch
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
154
|
+
// Fetch secrets from the configured provider
|
|
155
|
+
let secrets: Record<string, string>
|
|
156
|
+
if ('opVault' in config) {
|
|
157
|
+
secrets = await fetchOnePasswordSecrets(config)
|
|
83
158
|
|
|
84
|
-
|
|
85
|
-
|
|
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)
|
|
86
166
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
acc[f.label] = f.value
|
|
93
|
-
return acc
|
|
94
|
-
},
|
|
95
|
-
{} as Record<string, string>
|
|
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
|
+
}
|
|
171
|
+
}
|
|
97
172
|
|
|
98
173
|
// Build environment variables for Dagger
|
|
99
174
|
const daggerEnv: Record<string, string> = { ...env }
|
|
@@ -112,12 +187,10 @@ export function createDaggerCommandRunner<T extends DaggerEnvConfig>(
|
|
|
112
187
|
})
|
|
113
188
|
envVars.DAGGER_OPTIONS = JSON.stringify(daggerOptions)
|
|
114
189
|
|
|
115
|
-
// Construct the command
|
|
190
|
+
// Construct the command. 1Password wraps `dagger call` in `op run`
|
|
191
|
+
// so that op:// references in the environment are resolved.
|
|
116
192
|
const cmd: string[] = [
|
|
117
|
-
'op',
|
|
118
|
-
'run',
|
|
119
|
-
'--no-masking',
|
|
120
|
-
'--',
|
|
193
|
+
...('opVault' in config ? ['op', 'run', '--no-masking', '--'] : []),
|
|
121
194
|
'dagger',
|
|
122
195
|
'call',
|
|
123
196
|
commandName,
|