pushscript
Version:
AI-powered Git workflow automation with conventional commits, vulnerability scanning, and multi-provider LLM support
165 lines (137 loc) • 5.18 kB
JavaScript
/**
* Configuration for pushscript
* Centralizes environment variable loading and common configurations
*/
import fs from 'fs';
import path from 'path';
import dotenv from 'dotenv';
import { fileURLToPath } from 'url';
import { dirname } from 'path';
// Setup for ESM
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// Define a function to find the project root
function findProjectRoot(startPath) {
let currentPath = startPath;
// Safety limit for directory traversal
const maxLevels = 10;
let level = 0;
while (level < maxLevels) {
// Check if pnpm-workspace.yaml exists at this level
if (fs.existsSync(path.join(currentPath, 'pnpm-workspace.yaml'))) {
return currentPath;
}
// Move up one directory
const parentPath = path.dirname(currentPath);
// If we can't go up anymore, we've reached the root of the filesystem
if (parentPath === currentPath) {
return null;
}
currentPath = parentPath;
level++;
}
return null;
}
// Logger for config setup
const logConfig = (message, level = 'info') => {
const timestamp = new Date().toISOString();
switch (level) {
case 'error':
console.error(`[${timestamp}] [PushScript-Config] ERROR: ${message}`);
break;
case 'warn':
console.warn(`[${timestamp}] [PushScript-Config] WARNING: ${message}`);
break;
default:
console.log(`[${timestamp}] [PushScript-Config] ${message}`);
}
};
// Start from the current working directory to find the project root
const projectRoot = findProjectRoot(process.cwd());
// Get the root directory of the project
const rootDir = process.cwd().split('/node_modules')[0]; // This ensures we get the true root even if called from within node_modules
// Define patterns to search for
const envFilePatterns = [
'.env',
'.env.local',
'.env.development',
'.env.production',
'.env.test'
];
// Always look in the root directory first, then fall back to other locations
const possibleEnvFiles = [
// First priority: Files directly in the root directory
...envFilePatterns.map(pattern => path.join(rootDir, pattern)),
// Second priority: Project root from findProjectRoot (if different from rootDir)
...(projectRoot && projectRoot !== rootDir
? envFilePatterns.map(pattern => path.join(projectRoot, pattern))
: []),
// Third priority: Current working directory
...envFilePatterns.map(pattern => path.resolve(process.cwd(), pattern)),
// Fourth priority: Parent directory of current working directory
...envFilePatterns.map(pattern => path.resolve(process.cwd(), '../', pattern)),
// Fifth priority: Two directories up from current working directory
...envFilePatterns.map(pattern => path.resolve(process.cwd(), '../../', pattern)),
// Sixth priority: Three directories up from current working directory
...envFilePatterns.map(pattern => path.resolve(process.cwd(), '../../../', pattern))
].filter(Boolean);
let envFileLoaded = false;
// Add debug info about where we're looking
logConfig(`Looking for environment files in these locations (in order of priority):`);
// Try each possible env file location
for (const envPath of possibleEnvFiles) {
if (fs.existsSync(envPath)) {
logConfig(`Loading environment variables from: ${envPath}`);
dotenv.config({ path: envPath });
envFileLoaded = true;
break;
}
}
if (!envFileLoaded) {
logConfig('No environment files found, using system environment variables', 'error');
}
// Provider-specific API key environment variables
const PROVIDER_KEYS = {
groq: 'GROQ_API_KEY',
openai: 'OPENAI_API_KEY',
anthropic: 'ANTHROPIC_API_KEY',
gemini: 'GEMINI_API_KEY'
};
// Provider-specific model environment variables
const PROVIDER_MODELS = {
groq: 'GROQ_PUSHSCRIPT_MODEL',
openai: 'OPENAI_PUSHSCRIPT_MODEL',
anthropic: 'ANTHROPIC_PUSHSCRIPT_MODEL',
gemini: 'GEMINI_PUSHSCRIPT_MODEL'
};
// Default models for each provider
const DEFAULT_MODELS = {
groq: 'llama-3.3-70b-versatile',
openai: 'gpt-4o',
anthropic: 'claude-3.7-sonnet',
gemini: 'gemini-2.0-pro'
};
// Initialize environment variables with fallbacks
const ENV = {
LLM_PROVIDER: process.env.PUSHSCRIPT_LLM_PROVIDER || 'groq',
LLM_API_KEY: process.env.PUSHSCRIPT_LLM_API_KEY || null,
LLM_MODEL: process.env.PUSHSCRIPT_LLM_MODEL || null,
// JSON size limit in bytes (default: 250KB)
JSON_SIZE_LIMIT: process.env.PUSHSCRIPT_JSON_SIZE_LIMIT ?
parseInt(process.env.PUSHSCRIPT_JSON_SIZE_LIMIT) :
250 * 1024,
// Auto-gitignore large JSON files
AUTO_GITIGNORE_JSON: process.env.PUSHSCRIPT_AUTO_GITIGNORE_JSON === 'true'
};
// Export configuration
export default {
provider: ENV.LLM_PROVIDER,
apiKey: ENV.LLM_API_KEY || process.env[PROVIDER_KEYS[ENV.LLM_PROVIDER]] || null,
model: ENV.LLM_MODEL || process.env[PROVIDER_MODELS[ENV.LLM_PROVIDER]] || DEFAULT_MODELS[ENV.LLM_PROVIDER],
providerKeys: PROVIDER_KEYS,
providerModels: PROVIDER_MODELS,
defaultModels: DEFAULT_MODELS,
// JSON size limiting configuration
jsonSizeLimit: ENV.JSON_SIZE_LIMIT,
autoGitignoreJson: ENV.AUTO_GITIGNORE_JSON
};