dagger-env
v0.6.5
Published
A type-safe, reusable environment configuration abstraction for Dagger modules.
Readme
dagger-env
A type-safe, reusable environment configuration abstraction for Dagger modules with full Zod v4 validation and 1Password integration.
Features
- 🔒 Type-safe: Full TypeScript support with Zod v4 validation
- 🔄 Reusable: Create multiple environment configurations for different projects
- 🎯 Consistent: Standardized API across all Dagger modules
- 🛡️ Validated: Runtime validation of arguments, environment variables, and secrets
- 📦 Modular: Secret presets and derived environment variables
- 🔐 1Password Integration: Built-in command runner with
op runsupport - 🚀 Easy to use: Simple configuration-based setup
Installation
npm install dagger-env zodNote: The command runner functionality (dagger-env/run) requires the 1Password CLI (op) to be installed.
Quick Start
import { createDaggerEnv } from 'dagger-env'
import { z } from 'zod/v4'
// Define your environment configuration
const myDaggerEnv = createDaggerEnv({
args: z.object({
push: z.string().optional(),
environment: z.enum(['dev', 'staging', 'prod']).optional()
}),
env: z.object({
CI: z.string().optional(),
NODE_ENV: z.string().optional()
}),
secrets: z.object({
API_TOKEN: z.string(),
DATABASE_URL: z.string(),
REDIS_URL: z.string()
}),
secretPresets: {
api: ['API_TOKEN', 'DATABASE_URL'],
cache: ['REDIS_URL']
},
derivedEnvVars: {
API_TOKEN: {
API_BASE_URL: 'https://api.example.com',
API_VERSION: 'v1'
},
DATABASE_URL: {
DB_POOL_SIZE: '10'
}
}
})// Use in your Dagger module
import { Container, dag, func, object, Secret } from '@dagger.io/dagger'
@object()
export class MyModule {
@func()
async build(options: Secret): Promise<Container> {
const opts = await myDaggerEnv.parseDaggerOptions(options)
const { withEnv } = await myDaggerEnv.getWithEnv(options, ['api'], ['REDIS_URL'])
return withEnv(dag.container().from('node:18')).withExec(['npm', 'run', 'build']).sync()
}
}Command Runner (1Password Integration)
For projects using 1Password for secret management, dagger-env provides a convenient command runner that integrates with op run:
import { createDaggerEnv } from 'dagger-env'
import { createDaggerCommandRunner } from 'dagger-env/run'
import { z } from 'zod/v4'
// Create your DaggerEnv configuration
const myDaggerEnv = createDaggerEnv({
args: z.object({
environment: z.enum(['dev', 'staging', 'prod']).optional()
}),
env: z.object({
CI: z.string().optional(),
NODE_ENV: z.string().optional()
}),
secrets: z.object({
API_TOKEN: z.string()
}),
secretPresets: {
api: ['API_TOKEN']
},
derivedEnvVars: {}
})
// Create a command runner - simply pass your DaggerEnv instance
const runDaggerCommand = createDaggerCommandRunner({
opVault: 'your-vault-id',
opItem: 'your-item-id',
opSections: [
{
id: 'your-section-id',
label: 'Shared'
}
],
dockerCommands: ['build', 'deploy', 'test'],
daggerEnv: myDaggerEnv
})
// Run a Dagger command
await runDaggerCommand('test', {
args: { environment: 'dev' },
env: { NODE_ENV: 'development' }
})Advanced Configuration
// Advanced configuration with multiple sections and pre-command setup
const runDaggerCommand = createDaggerCommandRunner({
opVault: 'your-vault-id',
opItem: 'your-item-id',
opSections: [
{ id: 'shared-section-id', label: 'Shared' },
{ id: 'project-section-id', label: 'Project Specific' }
],
dockerCommands: ['build', 'deploy', 'test'],
beforeCommand: async () => {
// Setup vendor files, modules, etc.
console.log('Setting up environment...')
// await setupDaggerVendorFiles()
},
daggerEnv: myDaggerEnv
})API Reference
Environment Configuration
createDaggerEnv(config)
Creates a new DaggerEnv instance with the provided configuration.
Parameters:
config.args: Zod schema for command-line argumentsconfig.env: Zod schema for environment variablesconfig.secrets: Zod schema for secretsconfig.secretPresets: Object mapping preset names to arrays of secret namesconfig.derivedEnvVars: Object mapping secret names to derived environment variables
Returns: DaggerEnv<T> instance
daggerEnv.parseDaggerOptions(options: Secret)
Parses and validates dagger options from a Secret containing JSON.
Parameters:
options: Dagger Secret containing JSON options
Returns: Promise<DaggerOptionsFromConfig<T>> - Parsed and typed options object
daggerEnv.getWithEnv(options, secretPresets, secretNames?)
Creates a function that applies environment variables and secrets to a container.
Parameters:
options: Secret or parsed options objectsecretPresets: Array of preset names to include (e.g.,['api', 'cache'])secretNames: Optional array of additional individual secret names
Returns: Promise<(con: Container) => Container> - Function that applies env vars and secrets
daggerEnv.getOptionsSchema()
Returns the Zod schema for the complete options object. Primarily used internally by the command runner, but available for advanced use cases.
Returns: ZodObject - The combined schema for args, env, and secrets
daggerEnv.getSecretPresets()
Returns array of available secret preset names.
Returns: Array<string> - Available preset names
daggerEnv.getPresetSecrets(preset)
Returns array of secret names for a specific preset.
Parameters:
preset: Name of the preset
Returns: readonly string[] - Secret names in the preset
Command Runner
createDaggerCommandRunner(config)
Creates a function to run Dagger commands with 1Password integration.
Parameters:
config.opVault: 1Password vault IDconfig.opItem: 1Password item IDconfig.opSections: Array of 1Password sections to include for secretsconfig.dockerCommands: Optional array of command names that should include Docker socketconfig.beforeCommand: Optional async function to run before executing the commandconfig.daggerEnv: DaggerEnv instance for schema validation and type safety
Returns: (commandName: string, options?: RunDaggerCommandOptions) => Promise<void> - Function to execute Dagger commands
RunDaggerCommandOptions
Options for individual command execution:
args: Optional record of arguments to pass to the Dagger commandenv: Optional record of additional environment variablesextraArgs: Optional array of additional command-line arguments
Configuration Examples
Simple API Service
const apiServiceEnv = createDaggerEnv({
args: z.object({
push: z.string().optional()
}),
env: z.object({
CI: z.string().optional()
}),
secrets: z.object({
API_TOKEN: z.string(),
DATABASE_URL: z.string()
}),
secretPresets: {
api: ['API_TOKEN', 'DATABASE_URL']
} as const,
derivedEnvVars: {
API_TOKEN: {
API_BASE_URL: 'https://api.example.com'
}
} as const
})Multi-Environment Setup
const multiEnvDaggerEnv = createDaggerEnv({
args: z.object({
environment: z.enum(['dev', 'staging', 'prod']),
push: z.string().optional()
}),
env: z.object({
CI: z.string().optional()
}),
secrets: z.object({
DEV_API_KEY: z.string(),
STAGING_API_KEY: z.string(),
PROD_API_KEY: z.string()
}),
secretPresets: {
dev: ['DEV_API_KEY'],
staging: ['STAGING_API_KEY'],
prod: ['PROD_API_KEY']
} as const,
derivedEnvVars: {
DEV_API_KEY: { API_URL: 'https://dev-api.example.com' },
STAGING_API_KEY: { API_URL: 'https://staging-api.example.com' },
PROD_API_KEY: { API_URL: 'https://api.example.com' }
} as const
})Type Extraction
For advanced use cases where you need to extract TypeScript types:
import { z } from 'zod/v4'
import type { DaggerOptionsFromConfig } from 'dagger-env'
// Extract the options type from your DaggerEnv configuration
type MyDaggerEnvConfig = typeof myDaggerEnv extends DaggerEnv<infer T> ? T : never
type MyOptions = DaggerOptionsFromConfig<MyDaggerEnvConfig>
// Access the schema if needed for validation or type extraction
const schema = myDaggerEnv.getOptionsSchema()
type SchemaOutput = z.output<typeof schema>Best Practices
- Use
as constforsecretPresetsandderivedEnvVarsto ensure proper typing - Group related secrets into logical presets (e.g.,
api,database,cache) - Validate early by calling
parseDaggerOptions()at the start of functions - Reuse configurations across multiple Dagger modules in the same project
- Document your schemas with JSDoc comments for better developer experience
Requirements
- Node.js 18+
- Dagger SDK
- Zod v4+
License
MIT
Contributing
Contributions are welcome! Please read our contributing guidelines and submit pull requests to our GitHub repository.
