@sigmapark/op-deploy-cli
v1.1.0
Published
The command-line interface for op-deploy. Materializes files from 1Password, runs deploy commands, and manages secrets.
Readme
@sigmapark/op-deploy-cli
The command-line interface for op-deploy. Materializes files from 1Password, runs deploy commands, and manages secrets.
Installation
npm install -D @sigmapark/op-deploy-cli
# or
pnpm add -D @sigmapark/op-deploy-cliQuick Start
- Initialize your config:
pnpm op-deploy setupThis creates op-deploy.config.ts at your repo root.
- Set up an environment:
# Sign in to 1Password
eval $(op signin)
# Create 1Password items and scaffold attachments for your environment
pnpm op-deploy init --env production- Edit and populate files:
pnpm op-deploy edit --env production my-config-file- Run your first deploy:
pnpm op-deploy materialize --env production -- bash -c 'your-deploy-command'Commands
op-deploy (interactive)
Run with no arguments for an interactive menu:
op-deploysetup
Scaffold op-deploy.config.ts in the current directory. Run this once when setting up op-deploy in your repo.
op-deploy setupinit
Create missing 1Password items and initialize attachment files for an environment. Run this once per environment to set up the Secure Note items and example attachments in 1Password.
op-deploy init --env productionedit
Open a file in your $EDITOR for interactive round-trip editing in 1Password:
op-deploy edit --env production config-file-nameThe file is fetched from 1Password, opened in your editor, and saved back when you close the editor.
materialize
Fetch files from 1Password into a secure temporary directory, optionally create symlinks, run your command, then securely clean up.
op-deploy materialize --env production --filter 'tag:deploy' -- your-command --with argsFiles are exposed as environment variables: OP_DEPLOY_FILE_<FILENAME> (uppercase, underscores). Use these in your command.
upload
Run per-line commands for each line in matched files. Useful for uploading secrets line-by-line to a remote service.
op-deploy upload --env production --filter 'tag:secrets'Each line in the matched files is passed to the configured upload command.
inject
Wrap op inject for inline secret substitution:
op-deploy inject input-file output-fileConfiguration
Create op-deploy.config.ts at your repo root (or run op-deploy setup to scaffold it):
import { defineConfig } from "@sigmapark/op-deploy-cli";
export default defineConfig({
environments: {
staging: {
files: {
"app-wrangler": {
// Where to fetch this file from in 1Password
source: {
vault: "Deploy",
item: "staging",
file: "app-wrangler.jsonc",
},
// Optional: symlink to this repo-relative path
path: "wrangler.staging.jsonc",
// Optional: tags for filtering with --filter
tags: ["app", "kind:wrangler"],
// Optional: example file used by `op-deploy init`
example: "deploy/wrangler.staging.jsonc.example",
},
"app-secrets": {
source: {
vault: "Deploy",
item: "staging",
file: "app-secrets.env",
},
tags: ["app", "kind:secrets"],
example: "deploy/app-secrets.env.example",
// Optional: per-line upload command (e.g., for secret managers)
upload: {
// Spawned for each line: KEY=VALUE
forEachLine: [
"wrangler",
"secret",
"put",
{ var: "key" },
"-c",
"wrangler.staging.jsonc",
],
// Pipe the value to stdin
stdin: { var: "value" },
},
},
},
},
production: {
files: {
// ... similar structure for production
},
},
},
// Enforce that symlinked files are gitignored (optional)
materializedPathPatterns: ["wrangler.*.jsonc"],
});Configuration Reference
Environments
Top-level environments (e.g., staging, production). Each environment contains a set of files to manage.
Files
Named files to materialize. Names must be alphanumeric with hyphens/underscores (1Password-compatible attachment names).
Each file entry has:
| Field | Required | Purpose |
| -------------- | -------- | ----------------------------------------------------------------------------------------- |
| source.vault | yes | 1Password vault name |
| source.item | yes | 1Password Secure Note item name |
| source.file | yes | Attachment file name within the item |
| path | no | Repo-relative path for a symlink (must be gitignored if using materializedPathPatterns) |
| tags | no | Free-form labels for filtering (e.g., app, kind:secrets, region:us) |
| example | no | Repo-relative example file to seed new attachments during op-deploy init |
| upload | no | Command template to run for each line (see "Upload Actions" below) |
Upload Actions
When upload is configured, op-deploy runs the command for each line in the file:
upload: {
// Command to spawn, with placeholder objects for dynamic values
forEachLine: [
'wrangler', 'secret', 'put',
{ var: 'key' }, // Replaced with line's key
'-c', 'wrangler.jsonc',
],
// Optional: pipe one of the variables to stdin
stdin: { var: 'value' },
// Optional: working directory for the command
cwd: 'apps/my-app',
}Lines are parsed as KEY=VALUE. Both key and value placeholders are available.
Filtering
Use --filter with a Lucene-lite query to match files:
# Match by tag
op-deploy materialize --env production --filter 'tag:kind:secrets' -- ...
# Match by file name
op-deploy materialize --env production --filter 'name:app-wrangler' -- ...
# Combine with AND/OR/NOT
op-deploy materialize --env production --filter 'tag:app AND -tag:internal' -- ...Examples
Multi-app Deployment Setup
For a monorepo with multiple apps, configure each app with its own wrangler config and secrets:
files: {
'dashboard-wrangler': {
source: { vault: 'Deploy', item: 'production', file: 'dashboard-wrangler.jsonc' },
path: 'wrangler.dashboard.jsonc',
tags: ['app:dashboard', 'kind:wrangler'],
example: 'deploy/dashboard-wrangler.example.jsonc',
},
'dashboard-secrets': {
source: { vault: 'Deploy', item: 'production', file: 'dashboard-secrets.env' },
tags: ['app:dashboard', 'kind:secrets'],
upload: {
forEachLine: ['wrangler', 'secret', 'put', { var: 'key' }, '-c', 'wrangler.dashboard.jsonc'],
stdin: { var: 'value' },
},
},
'api-wrangler': {
source: { vault: 'Deploy', item: 'production', file: 'api-wrangler.jsonc' },
path: 'wrangler.api.jsonc',
tags: ['app:api', 'kind:wrangler'],
},
'api-secrets': {
source: { vault: 'Deploy', item: 'production', file: 'api-secrets.env' },
tags: ['app:api', 'kind:secrets'],
upload: {
forEachLine: ['wrangler', 'secret', 'put', { var: 'key' }, '-c', 'wrangler.api.jsonc'],
stdin: { var: 'value' },
},
},
}Then in your package.json:
{
"scripts": {
"deploy:dashboard": "op-deploy materialize --env production --filter 'app:dashboard AND kind:wrangler' -- bash -c 'pnpm build && wrangler deploy -c $OP_DEPLOY_FILE_DASHBOARD_WRANGLER'",
"deploy:dashboard:secrets": "op-deploy upload --env production --filter 'app:dashboard AND kind:secrets'",
"deploy:api": "op-deploy materialize --env production --filter 'app:api AND kind:wrangler' -- bash -c 'pnpm build && wrangler deploy -c $OP_DEPLOY_FILE_API_WRANGLER'",
"deploy:api:secrets": "op-deploy upload --env production --filter 'app:api AND kind:secrets'",
"deploy:migrations": "op-deploy materialize --env production --filter 'app:api AND kind:wrangler' -- bash -c 'wrangler d1 migrations apply my-database --remote -c $OP_DEPLOY_FILE_API_WRANGLER'"
}
}Per-Environment Configuration
Use separate 1Password items for each environment:
{
staging: {
files: {
'config': {
source: { vault: 'Deploy', item: 'staging-config', file: 'config.json' },
},
},
},
production: {
files: {
'config': {
source: { vault: 'Deploy', item: 'production-config', file: 'config.json' },
},
},
},
}Symlinked Configuration Files
Optionally symlink materialized files into your repo for use by tools:
{
'wrangler-config': {
source: { vault: 'Deploy', item: 'production', file: 'wrangler.jsonc' },
path: 'wrangler.production.jsonc', // Creates a symlink here
tags: ['wrangler'],
},
}The symlink persists only for the duration of your command. Add the path to .gitignore:
wrangler.*.jsoncSecrets Upload with Custom Services
If you use a different secret service, customize the upload command:
{
'api-secrets': {
source: { vault: 'Deploy', item: 'production', file: 'api-secrets.env' },
tags: ['secrets', 'api'],
upload: {
// Example: upload to a custom API
forEachLine: ['curl', '-X', 'POST', 'https://secrets.company.internal/set', '-d', { string: ['key=', { var: 'key' }, '&value=', { var: 'value' }] }],
},
},
}Workflow
Typical development + deployment workflow:
# One-time setup
eval $(op signin)
pnpm op-deploy init --env production
pnpm op-deploy edit --env production config-file-name # fill in real values
# On each deploy
pnpm deploy:app # materialize + run build + deploy
pnpm deploy:app:secrets # upload secrets
# Edit files as needed
pnpm op-deploy edit --env production config-file-nameHelp
op-deploy --help
op-deploy <command> --help