Refactor .env file copying script to implement custom gitignore handling

This update enhances the script by replacing the glob-based search for .env files with a custom directory traversal method that respects .gitignore rules. The new implementation allows for more precise control over which files are included, improving the script's functionality in various project structures.
This commit is contained in:
dal 2025-07-25 19:20:48 -06:00
parent 9aed7a612e
commit 83952f4554
No known key found for this signature in database
GPG Key ID: 16F4B0E1E9F61122
1 changed files with 114 additions and 8 deletions

View File

@ -1,22 +1,128 @@
#!/usr/bin/env tsx #!/usr/bin/env tsx
import { promises as fs } from 'node:fs'; import { promises as fs } from 'node:fs';
import { join, relative, dirname } from 'node:path'; import { join, relative, dirname, basename } from 'node:path';
import { glob } from 'glob';
const SOURCE_REPO = join(process.env.HOME!, 'buster', 'buster'); const SOURCE_REPO = join(process.env.HOME!, 'buster', 'buster');
const TARGET_REPO = process.cwd(); const TARGET_REPO = process.cwd();
interface GitignoreRules {
patterns: string[];
directory: string;
}
// Simple gitignore pattern matcher
function matchesGitignorePattern(path: string, pattern: string): boolean {
// Remove leading/trailing slashes
pattern = pattern.trim();
if (pattern.startsWith('#') || pattern === '') return false;
const isNegation = pattern.startsWith('!');
if (isNegation) pattern = pattern.slice(1);
// Handle directory-only patterns (ending with /)
const isDirPattern = pattern.endsWith('/');
if (isDirPattern) pattern = pattern.slice(0, -1);
// Simple glob matching (basic implementation)
// Convert pattern to regex
let regexPattern = pattern
.replace(/\./g, '\\.')
.replace(/\*/g, '[^/]*')
.replace(/\?/g, '[^/]')
.replace(/\*\*/g, '.*');
// If pattern doesn't start with /, it can match anywhere
if (!pattern.startsWith('/')) {
regexPattern = `(^|/)${regexPattern}`;
} else {
regexPattern = `^${regexPattern.slice(1)}`;
}
if (isDirPattern) {
regexPattern += '(/|$)';
} else {
regexPattern += '(/|$)';
}
const regex = new RegExp(regexPattern);
const matches = regex.test(path);
return isNegation ? !matches : matches;
}
async function loadGitignoreRules(dir: string): Promise<string[]> {
try {
const gitignorePath = join(dir, '.gitignore');
const content = await fs.readFile(gitignorePath, 'utf-8');
return content
.split('\n')
.map(line => line.trim())
.filter(line => line && !line.startsWith('#'));
} catch {
return [];
}
}
function isIgnored(path: string, gitignoreStack: GitignoreRules[]): boolean {
for (const rules of gitignoreStack) {
const relativePath = relative(rules.directory, path);
if (relativePath && !relativePath.startsWith('..')) {
for (const pattern of rules.patterns) {
if (matchesGitignorePattern(relativePath, pattern)) {
return true;
}
}
}
}
return false;
}
async function findEnvFiles(dir: string, baseDir: string = dir): Promise<string[]> {
const envFiles: string[] = [];
async function walkDir(currentDir: string, gitignoreStack: GitignoreRules[]) {
try {
// Load gitignore rules for current directory
const localRules = await loadGitignoreRules(currentDir);
if (localRules.length > 0) {
gitignoreStack = [...gitignoreStack, { patterns: localRules, directory: currentDir }];
}
const entries = await fs.readdir(currentDir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = join(currentDir, entry.name);
const relativePath = relative(baseDir, fullPath);
// Check if this path is ignored
if (isIgnored(fullPath, gitignoreStack)) {
continue;
}
if (entry.isDirectory()) {
// Always skip .git directory
if (entry.name === '.git') continue;
await walkDir(fullPath, gitignoreStack);
} else if (entry.name.startsWith('.env')) {
envFiles.push(relativePath);
}
}
} catch (error) {
// Skip directories we can't read
console.warn(`Skipping directory: ${currentDir}`);
}
}
await walkDir(dir, []);
return envFiles;
}
async function copyEnvFiles() { async function copyEnvFiles() {
try { try {
console.info(`Searching for .env files in ${SOURCE_REPO}...`); console.info(`Searching for .env files in ${SOURCE_REPO}...`);
const envFiles = await glob('**/.env*', { const envFiles = await findEnvFiles(SOURCE_REPO);
cwd: SOURCE_REPO,
absolute: false,
dot: true,
ignore: ['**/node_modules/**', '**/.git/**']
});
if (envFiles.length === 0) { if (envFiles.length === 0) {
console.warn('No .env files found in source repository'); console.warn('No .env files found in source repository');