diff --git a/CHANGELOG.md b/CHANGELOG.md index 8848a3a8..979e97cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,9 @@ This project follows [Keep a Changelog](https://keepachangelog.com/) and [Semant - ## [2.2.7] - 2025-09-27 +### Added +- Added warning on .env not ignored by .gitignore on default. + ### Fixed - Fixed `--strict` error output to console when no warnings are found. diff --git a/src/services/scanOutputToConsole.ts b/src/services/scanOutputToConsole.ts index d1b7cf25..b9a06a99 100644 --- a/src/services/scanOutputToConsole.ts +++ b/src/services/scanOutputToConsole.ts @@ -1,4 +1,5 @@ import chalk from 'chalk'; +import { warnIfEnvNotIgnored, isEnvIgnoredByGit } from './git.js'; import type { ScanUsageOptions, ScanResult, @@ -229,12 +230,23 @@ export function outputToConsole( console.log(); } + let envNotIgnored = false; + if (!opts.json) { + warnIfEnvNotIgnored({ cwd: opts.cwd, envFile: '.env' }); + + const ignored = isEnvIgnoredByGit({ cwd: opts.cwd, envFile: '.env' }); + if (ignored === false || ignored === null) { + envNotIgnored = true; + } + } + if (opts.strict) { const hasWarnings = scanResult.unused.length > 0 || (scanResult.duplicates?.env?.length ?? 0) > 0 || (scanResult.duplicates?.example?.length ?? 0) > 0 || - (scanResult.secrets?.length ?? 0) > 0; + (scanResult.secrets?.length ?? 0) > 0 || + envNotIgnored; if (hasWarnings) { exitWithError = true; @@ -247,6 +259,7 @@ export function outputToConsole( warnings.push('duplicate keys in example'); if ((scanResult.secrets?.length ?? 0) > 0) warnings.push('potential secrets'); + if (envNotIgnored) warnings.push('.env not ignored by git'); console.log( chalk.red(`💥 Strict mode: Error on warnings → ${warnings.join(', ')}`), diff --git a/test/e2e/cli.autoscan.e2e.test.ts b/test/e2e/cli.autoscan.e2e.test.ts index 4158fa12..74af8eda 100644 --- a/test/e2e/cli.autoscan.e2e.test.ts +++ b/test/e2e/cli.autoscan.e2e.test.ts @@ -38,7 +38,30 @@ describe('no-flag autoscan', () => { const res = runCli(cwd, ['--compare']); expect(res.status).toBe(1); expect(res.stdout).toContain('Comparing .env ↔ .env.example'); - expect(res.stdout).toContain('Comparing .env.staging ↔ .env.example.staging'); + expect(res.stdout).toContain( + 'Comparing .env.staging ↔ .env.example.staging', + ); expect(res.stdout).toContain('Missing keys'); }); + it('will warn about .env not ignored by .gitignore', () => { + const cwd = tmpDir(); + + fs.mkdirSync(path.join(cwd, '.git')); + fs.writeFileSync(path.join(cwd, '.env'), 'API_KEY=test\n'); + + fs.writeFileSync(path.join(cwd, '.gitignore'), 'node_modules\n'); + + fs.mkdirSync(path.join(cwd, 'src'), { recursive: true }); + fs.writeFileSync( + path.join(cwd, 'src', 'index.ts'), + `const apiKey = process.env.API_KEY;`.trimStart(), + ); + + const res = runCli(cwd, []); + console.log('stdout:', res.stdout); + console.log('stderr:', res.stderr); + + expect(res.status).toBe(0); + expect(res.stdout).toContain('.env is not ignored by Git'); + }); }); diff --git a/test/e2e/cli.strict.e2e.test.ts b/test/e2e/cli.strict.e2e.test.ts index 94a079eb..375dfd21 100644 --- a/test/e2e/cli.strict.e2e.test.ts +++ b/test/e2e/cli.strict.e2e.test.ts @@ -61,6 +61,7 @@ describe('--strict mode', () => { it('succeeds when there are no warnings', () => { const cwd = tmpDir(); fs.writeFileSync(path.join(cwd, '.env'), ''); + fs.writeFileSync(path.join(cwd, '.gitignore'), '.env'); const res = runCli(cwd, ['--strict']); expect(res.status).toBe(0);