diff --git a/.changeset/brown-chicken-look.md b/.changeset/brown-chicken-look.md new file mode 100644 index 00000000..cd20fe56 --- /dev/null +++ b/.changeset/brown-chicken-look.md @@ -0,0 +1,7 @@ +--- +'dotenv-diff': patch +'@repo/eslint-config': patch +'@repo/tsconfig': patch +--- + +Added support for lint-staged diff --git a/docs/git_hooks_ci.md b/docs/git_hooks_ci.md index a2ddb14d..fcf3c2fa 100644 --- a/docs/git_hooks_ci.md +++ b/docs/git_hooks_ci.md @@ -10,6 +10,16 @@ Running `dotenv-diff` before each commit helps catch missing, unused, and misuse A common setup is Husky + lint-staged, where `dotenv-diff` runs automatically on commit. +### Example lint-staged config + +```json +{ + "*.{js,ts,tsx,svelte}": [ + "dotenv-diff --example .env.example" + ] +} +``` + ## Running dotenv-diff in GitHub Actions Use `dotenv-diff` in CI to validate environment variable consistency on pull requests. diff --git a/packages/cli/src/cli/program.ts b/packages/cli/src/cli/program.ts index 7aa675af..1a3a9101 100644 --- a/packages/cli/src/cli/program.ts +++ b/packages/cli/src/cli/program.ts @@ -5,85 +5,92 @@ import { Command } from 'commander'; * @returns The configured commander program instance */ export function createProgram() { - return new Command() - .name('dotenv-diff') - .description('Compare .env and .env.example files') - .option('--check-values', 'Compare actual values if example has values') - .option('--ci', 'Run non-interactively and never create files') - .option('-y, --yes', 'Run non-interactively and answer Yes to prompts') - .option('--env ', 'Path to a specific .env file') - .option('--example ', 'Path to a specific .env.example file') - .option( - '--allow-duplicates', - 'Do not warn about duplicate keys in .env* files', - ) - .option('--ignore ', 'Comma-separated list of keys to ignore') - .option('--ignore-regex ', 'Regex pattern to ignore matching keys') - .option( - '--fix', - 'Automatically fix common issues: remove duplicates, add missing keys', - ) - .option('--json', 'Output results in JSON format') - .option('--color', 'Enable colored output') - .option('--no-color', 'Disable colored output') - .option( - '--only ', - 'Comma-separated categories to only run (missing,extra,empty,duplicate,gitignore)', - ) - .option('--scan-usage', 'Scan codebase for environment variable usage') - .option('--compare', 'Compare .env and .env.example files') - .option( - '--include-files ', - 'Comma-separated file patterns to ADD to default scan patterns (extends default)', - ) - .option( - '--files ', - 'Comma-separated file patterns to scan (completely replaces default patterns)', - ) - .option( - '--exclude-files ', - 'Comma-separated file patterns to exclude from scan', - ) - .option( - '--show-unused', - 'List variables that are defined in .env but not used in code', - ) - .option( - '--no-show-unused', - 'Do not list variables that are defined in .env but not used in code', - ) - .option('--show-stats', 'Show statistics') - .option('--no-show-stats', 'Do not show statistics') - .option('--strict', 'Enable fail on warnings') - .option( - '--secrets', - 'Enable secret detection during scan (enabled by default)', - ) - .option( - '--no-secrets', - 'Disable secret detection during scan (enabled by default)', - ) - .option( - '--ignore-urls ', - 'Comma-separated URLs to ignore in secret scan', - ) - .option( - '--uppercase-keys', - 'Enable uppercase key validation (enabled by default)', - ) - .option('--no-uppercase-keys', 'Disable uppercase key validation') - .option( - '--expire-warnings', - 'Enable expiration date warnings for environment variables (enabled by default)', - ) - .option('--no-expire-warnings', 'Disable expiration date warnings') - .option( - '--inconsistent-naming-warnings', - 'Enable inconsistent naming pattern warnings (enabled by default)', - ) - .option( - '--no-inconsistent-naming-warnings', - 'Disable inconsistent naming pattern warnings', - ) - .option('--init', 'Create a sample dotenv-diff.config.json file'); + return ( + new Command() + .name('dotenv-diff') + .description('Compare .env and .env.example files') + // Ignore extra positional args + .allowExcessArguments(true) + .option('--check-values', 'Compare actual values if example has values') + .option('--ci', 'Run non-interactively and never create files') + .option('-y, --yes', 'Run non-interactively and answer Yes to prompts') + .option('--env ', 'Path to a specific .env file') + .option('--example ', 'Path to a specific .env.example file') + .option( + '--allow-duplicates', + 'Do not warn about duplicate keys in .env* files', + ) + .option('--ignore ', 'Comma-separated list of keys to ignore') + .option( + '--ignore-regex ', + 'Regex pattern to ignore matching keys', + ) + .option( + '--fix', + 'Automatically fix common issues: remove duplicates, add missing keys', + ) + .option('--json', 'Output results in JSON format') + .option('--color', 'Enable colored output') + .option('--no-color', 'Disable colored output') + .option( + '--only ', + 'Comma-separated categories to only run (missing,extra,empty,duplicate,gitignore)', + ) + .option('--scan-usage', 'Scan codebase for environment variable usage') + .option('--compare', 'Compare .env and .env.example files') + .option( + '--include-files ', + 'Comma-separated file patterns to ADD to default scan patterns (extends default)', + ) + .option( + '--files ', + 'Comma-separated file patterns to scan (completely replaces default patterns)', + ) + .option( + '--exclude-files ', + 'Comma-separated file patterns to exclude from scan', + ) + .option( + '--show-unused', + 'List variables that are defined in .env but not used in code', + ) + .option( + '--no-show-unused', + 'Do not list variables that are defined in .env but not used in code', + ) + .option('--show-stats', 'Show statistics') + .option('--no-show-stats', 'Do not show statistics') + .option('--strict', 'Enable fail on warnings') + .option( + '--secrets', + 'Enable secret detection during scan (enabled by default)', + ) + .option( + '--no-secrets', + 'Disable secret detection during scan (enabled by default)', + ) + .option( + '--ignore-urls ', + 'Comma-separated URLs to ignore in secret scan', + ) + .option( + '--uppercase-keys', + 'Enable uppercase key validation (enabled by default)', + ) + .option('--no-uppercase-keys', 'Disable uppercase key validation') + .option( + '--expire-warnings', + 'Enable expiration date warnings for environment variables (enabled by default)', + ) + .option('--no-expire-warnings', 'Disable expiration date warnings') + .option( + '--inconsistent-naming-warnings', + 'Enable inconsistent naming pattern warnings (enabled by default)', + ) + .option( + '--no-inconsistent-naming-warnings', + 'Disable inconsistent naming pattern warnings', + ) + .option('--init', 'Create a sample dotenv-diff.config.json file') + ); } diff --git a/packages/cli/test/unit/cli/program.test.ts b/packages/cli/test/unit/cli/program.test.ts index a0a39f3b..c8827ce1 100644 --- a/packages/cli/test/unit/cli/program.test.ts +++ b/packages/cli/test/unit/cli/program.test.ts @@ -34,4 +34,24 @@ describe('createProgram', () => { expect(opts.color).toBe(false); expect(opts.strict).toBe(true); }); + + it('ignores extra positional arguments', () => { + const program = createProgram(); + + expect(() => { + program.parse([ + 'node', + 'dotenv-diff', + '--compare', + '--example', + '.env.example', + 'file1.ts', + 'file2.ts', + ]); + }).not.toThrow(); + + const opts = program.opts(); + expect(opts.compare).toBe(true); + expect(opts.example).toBe('.env.example'); + }); });