dagger-env
Version:
A type-safe, reusable environment configuration abstraction for Dagger modules.
109 lines • 4.29 kB
JavaScript
import { z } from 'zod/v4';
import { $, fs } from 'zx';
import { OPItem, OPSection } from './op.js';
export const InfisicalSecret = z.object({
key: z.string(),
value: z.string(),
});
/**
* Fetches secrets from Infisical via `infisical export`
*/
async function fetchInfisicalSecrets(config) {
const exportedSecrets = InfisicalSecret.array().parse(await $ `infisical export --silent --format=json --projectId ${config.projectId} --env ${config.env} --path ${config.path}`.json());
return exportedSecrets.reduce((acc, s) => {
acc[s.key] = s.value;
return acc;
}, {});
}
/**
* Fetches secrets from a 1Password item, filtered to the configured sections
*/
async function fetchOnePasswordSecrets(config) {
const opItem = OPItem.parse(await $ `op item get ${config.opItem} --vault ${config.opVault} --format json`.json());
const sectionsToInclude = OPSection.array().parse(config.opSections);
return opItem.fields
.filter((f) => sectionsToInclude.some((s) => s.id === f.section?.id))
.reduce((acc, f) => {
acc[f.label] = f.value;
return acc;
}, {});
}
export function createDaggerCommandRunner(config) {
return async function runDaggerCommand(commandName, options) {
const { args = {}, env = {}, extraArgs = [] } = options ?? {};
// Run any pre-command setup
if (config.beforeCommand) {
await config.beforeCommand();
}
// Environment variables to pass to the spawned command
const envVars = {};
const commandArgs = [...extraArgs];
// Add Docker socket for specific commands if available
if (config.dockerCommands?.includes(commandName)) {
try {
if (await fs.exists('/var/run/docker.sock')) {
commandArgs.push('--docker-socket=/var/run/docker.sock');
}
}
catch {
// Ignore if fs is not available or docker socket doesn't exist
}
}
// Fetch secrets from the configured provider
let secrets;
if ('opVault' in config) {
secrets = await fetchOnePasswordSecrets(config);
// Pass dagger cloud token in CI because we don't have user auth.
// The op:// reference is resolved by the `op run` wrapper below.
if (process.env.CI !== undefined) {
envVars.DAGGER_CLOUD_TOKEN = `op://${config.opVault}/${config.opItem}/DAGGER_CLOUD_TOKEN`;
}
}
else {
secrets = await fetchInfisicalSecrets(config);
// Pass dagger cloud token in CI because we don't have user auth
if (process.env.CI !== undefined && secrets.DAGGER_CLOUD_TOKEN !== undefined) {
envVars.DAGGER_CLOUD_TOKEN = secrets.DAGGER_CLOUD_TOKEN;
}
}
for (const [name, value] of Object.entries(config.additionalSecrets ?? {})) {
if (value !== undefined) {
secrets[name] = value;
}
}
// Build environment variables for Dagger
const daggerEnv = { ...env };
if (process.env.CI !== undefined) {
daggerEnv.CI = process.env.CI;
}
if (process.env.GITHUB_ACTIONS !== undefined) {
daggerEnv.GITHUB_ACTIONS = process.env.GITHUB_ACTIONS;
}
// Validate and serialize dagger options
const daggerOptions = config.daggerEnv.getOptionsSchema().parse({
args,
env: daggerEnv,
secrets,
});
envVars.DAGGER_OPTIONS = JSON.stringify(daggerOptions);
// Construct the command. 1Password wraps `dagger call` in `op run`
// so that op:// references in the environment are resolved.
const cmd = [
...('opVault' in config ? ['op', 'run', '--no-masking', '--'] : []),
'dagger',
'call',
commandName,
...commandArgs,
'--options=env://DAGGER_OPTIONS',
];
// Execute the command
await $({
env: {
...process.env,
...envVars,
},
stdio: 'inherit',
}) `${cmd}`;
};
}
//# sourceMappingURL=run-dagger-cmd.js.map