@@ -12,6 +12,7 @@ import { createJsonOutput } from '../core/scanJsonOutput.js';
1212import { findDuplicateKeys } from '../services/duplicates.js' ;
1313import { compareWithEnvFiles } from '../core/compareScan.js' ;
1414import { applyFixes } from '../core/fixEnv.js' ;
15+ import { isEnvIgnoredByGit } from '../services/git.js' ;
1516
1617/**
1718 * Scans codebase for environment variable usage and compares with .env file
@@ -46,9 +47,9 @@ export async function scanUsage(
4647 // Recalculate stats after filtering out commented usages
4748 const uniqueVariables = new Set ( scanResult . used . map ( ( u ) => u . variable ) ) . size ;
4849 scanResult . stats = {
49- filesScanned : scanResult . stats . filesScanned , // Keep original files scanned count
50+ filesScanned : scanResult . stats . filesScanned ,
5051 totalUsages : scanResult . used . length ,
51- uniqueVariables : uniqueVariables ,
52+ uniqueVariables,
5253 } ;
5354
5455 // If user explicitly passed --example but the file doesn't exist:
@@ -59,7 +60,6 @@ export async function scanUsage(
5960 if ( missing ) {
6061 const msg = `❌ Missing specified example file: ${ opts . examplePath } ` ;
6162 if ( opts . isCiMode ) {
62- // IMPORTANT: stdout (console.log), not stderr, to satisfy the test
6363 console . log ( chalk . red ( msg ) ) ;
6464 return { exitWithError : true } ;
6565 } else if ( ! opts . json ) {
@@ -81,6 +81,7 @@ export async function scanUsage(
8181 let fixApplied = false ;
8282 let fixedKeys : string [ ] = [ ] ;
8383 let removedDuplicates : string [ ] = [ ] ;
84+ let gitignoreUpdated = false ;
8485
8586 if ( compareFile ) {
8687 try {
@@ -131,14 +132,15 @@ export async function scanUsage(
131132 if ( changed ) {
132133 fixApplied = true ;
133134 removedDuplicates = result . removedDuplicates ;
135+ gitignoreUpdated = gitignoreUpdated || result . gitignoreUpdated ;
134136 // Clear duplicates after fix
135137 duplicatesFound = false ;
136138 dupsEnv = [ ] ;
137139 dupsExample = [ ] ;
138140 }
139141 }
140142
141- // Add to scan result for both JSON and console output (only if not fixed)
143+ // Keep duplicates for output if not fixed
142144 if (
143145 ( dupsEnv . length > 0 || dupsExample . length > 0 ) &&
144146 ( ! opts . fix || ! fixApplied )
@@ -151,65 +153,55 @@ export async function scanUsage(
151153 }
152154 } catch ( error ) {
153155 const errorMessage = `⚠️ Could not read ${ compareFile . name } : ${ compareFile . path } - ${ error } ` ;
154-
155156 if ( opts . isCiMode ) {
156- // In CI mode, exit with error if file doesn't exist
157157 console . log ( chalk . red ( `❌ ${ errorMessage } ` ) ) ;
158158 return { exitWithError : true } ;
159159 }
160-
161- if ( ! opts . json ) {
162- console . log ( chalk . yellow ( errorMessage ) ) ;
163- }
160+ if ( ! opts . json ) console . log ( chalk . yellow ( errorMessage ) ) ;
164161 }
165162 }
166163
167- // Apply missing keys fix if --fix is enabled (but don't show message yet )
164+ // Apply missing keys fix with applyFixes (so gitignore is handled too )
168165 if ( opts . fix && compareFile ) {
169166 const missingKeys = scanResult . missing ;
170-
171167 if ( missingKeys . length > 0 ) {
172168 const envFilePath = compareFile . path ;
173169 const exampleFilePath = opts . examplePath
174170 ? resolveFromCwd ( opts . cwd , opts . examplePath )
175- : null ;
171+ : '' ;
176172
177- // Append missing keys to .env
178- const content = fs . readFileSync ( envFilePath , 'utf-8' ) ;
179- const newContent =
180- content +
181- ( content . endsWith ( '\n' ) ? '' : '\n' ) +
182- missingKeys . map ( ( k ) => `${ k } =` ) . join ( '\n' ) +
183- '\n' ;
184- fs . writeFileSync ( envFilePath , newContent ) ;
173+ const { changed, result } = applyFixes ( {
174+ envPath : envFilePath ,
175+ examplePath : exampleFilePath ,
176+ missingKeys,
177+ duplicateKeys : [ ] ,
178+ } ) ;
185179
186- // Append to .env.example if it exists
187- if ( exampleFilePath && fs . existsSync ( exampleFilePath ) ) {
188- const exContent = fs . readFileSync ( exampleFilePath , 'utf-8' ) ;
189- const existingExKeys = new Set (
190- exContent
191- . split ( '\n' )
192- . map ( ( l ) => l . trim ( ) . split ( '=' ) [ 0 ] )
193- . filter ( Boolean ) ,
194- ) ;
195- const newKeys = missingKeys . filter ( ( k ) => ! existingExKeys . has ( k ) ) ;
196- if ( newKeys . length ) {
197- const newExContent =
198- exContent +
199- ( exContent . endsWith ( '\n' ) ? '' : '\n' ) +
200- newKeys . join ( '\n' ) +
201- '\n' ;
202- fs . writeFileSync ( exampleFilePath , newExContent ) ;
203- }
180+ if ( changed ) {
181+ fixApplied = true ;
182+ fixedKeys = result . addedEnv ;
183+ gitignoreUpdated = gitignoreUpdated || result . gitignoreUpdated ;
184+ scanResult . missing = [ ] ;
204185 }
186+ }
187+ }
205188
189+ // Always run a gitignore-only fix when --fix is set (even if no missing/duplicates)
190+ if ( opts . fix && compareFile ) {
191+ const { result } = applyFixes ( {
192+ envPath : compareFile . path ,
193+ examplePath : '' ,
194+ missingKeys : [ ] ,
195+ duplicateKeys : [ ] ,
196+ } ) ;
197+ if ( result . gitignoreUpdated ) {
206198 fixApplied = true ;
207- fixedKeys = missingKeys ;
208- scanResult . missing = [ ] ;
199+ gitignoreUpdated = true ;
209200 }
210201 }
211202
212- // Prepare JSON output
203+
204+ // JSON output
213205 if ( opts . json ) {
214206 const jsonOutput = createJsonOutput (
215207 scanResult ,
@@ -235,7 +227,7 @@ export async function scanUsage(
235227 // Console output
236228 const result = outputToConsole ( scanResult , opts , comparedAgainst ) ;
237229
238- // Show consolidated fix message at the bottom (after all other output)
230+ // Consolidated fix message
239231 if ( opts . fix && ! opts . json ) {
240232 if ( fixApplied ) {
241233 console . log ( chalk . green ( '✅ Auto-fix applied:' ) ) ;
@@ -265,7 +257,9 @@ export async function scanUsage(
265257 ) ;
266258 }
267259 }
268-
260+ if ( gitignoreUpdated ) {
261+ console . log ( chalk . green ( ' - Added .env ignore rules to .gitignore' ) ) ;
262+ }
269263 console . log ( ) ;
270264 } else {
271265 console . log ( chalk . green ( '✅ Auto-fix applied: no changes needed.' ) ) ;
@@ -274,6 +268,8 @@ export async function scanUsage(
274268 }
275269
276270 if ( ! opts . json && ! opts . fix ) {
271+ const ignored = isEnvIgnoredByGit ( { cwd : opts . cwd , envFile : '.env' } ) ;
272+ const envNotIgnored = ignored === false || ignored === null ;
277273 if ( scanResult . missing . length > 0 && duplicatesFound ) {
278274 console . log (
279275 chalk . gray (
@@ -289,6 +285,13 @@ export async function scanUsage(
289285 chalk . gray ( '💡 Tip: Run with `--fix` to remove duplicate keys' ) ,
290286 ) ;
291287 console . log ( ) ;
288+ } else if ( envNotIgnored ) {
289+ console . log (
290+ chalk . gray (
291+ '💡 Tip: Run with `--fix` to ensure .env is added to .gitignore' ,
292+ ) ,
293+ ) ;
294+ console . log ( ) ;
292295 }
293296 }
294297
0 commit comments