This commit is contained in:
197
src/utils/env-utils.ts
Normal file
197
src/utils/env-utils.ts
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
import * as path from 'path';
|
||||||
|
import * as fs from 'fs-extra';
|
||||||
|
import { execa } from 'execa';
|
||||||
|
import * as os from 'os';
|
||||||
|
import { CreateOptions } from '../types/index';
|
||||||
|
import { GitAgentSyncError } from './errors';
|
||||||
|
import { createEnvironmentFile, readPackageJson } from './file-utils';
|
||||||
|
|
||||||
|
const DEFAULT_ENV_TEMPLATE = `# Environment variables for agent workspace
|
||||||
|
AGENT_ID={agentName}
|
||||||
|
WORKSPACE_PATH={workspacePath}
|
||||||
|
AGENT_WORKSPACE={workspacePath}
|
||||||
|
GIT_USER_EMAIL={gitUserEmail}
|
||||||
|
GIT_USER_NAME={gitUserName}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export async function installDependencies(workspacePath: string): Promise<void> {
|
||||||
|
const pkgJson = await readPackageJson(workspacePath);
|
||||||
|
|
||||||
|
if (!pkgJson) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const packageManager = detectPackageManager(workspacePath);
|
||||||
|
|
||||||
|
if (!packageManager) {
|
||||||
|
throw new GitAgentSyncError('No package manager detected (package.json found but no lockfile)');
|
||||||
|
}
|
||||||
|
|
||||||
|
const command = packageManager === 'npm' ? 'npm install' : `${packageManager} install`;
|
||||||
|
|
||||||
|
await execa(command, {
|
||||||
|
cwd: workspacePath,
|
||||||
|
stdio: 'inherit'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function detectPackageManager(workspacePath: string): 'npm' | 'yarn' | 'pnpm' | null {
|
||||||
|
if (fs.existsSync(path.join(workspacePath, 'yarn.lock'))) {
|
||||||
|
return 'yarn';
|
||||||
|
}
|
||||||
|
if (fs.existsSync(path.join(workspacePath, 'pnpm-lock.yaml'))) {
|
||||||
|
return 'pnpm';
|
||||||
|
}
|
||||||
|
if (fs.existsSync(path.join(workspacePath, 'package-lock.json'))) {
|
||||||
|
return 'npm';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function setupEnvironment(
|
||||||
|
workspacePath: string,
|
||||||
|
agentName: string,
|
||||||
|
options: CreateOptions
|
||||||
|
): Promise<void> {
|
||||||
|
const envVars: Record<string, string> = {
|
||||||
|
AGENT_ID: agentName,
|
||||||
|
WORKSPACE_PATH: workspacePath,
|
||||||
|
AGENT_WORKSPACE: workspacePath,
|
||||||
|
...(options.environment || {})
|
||||||
|
};
|
||||||
|
|
||||||
|
const templatePath = options.template
|
||||||
|
? path.resolve(options.template)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
if (templatePath && await fs.pathExists(templatePath)) {
|
||||||
|
await applyEnvironmentTemplate(templatePath, workspacePath, agentName, envVars);
|
||||||
|
} else {
|
||||||
|
await createEnvironmentFile(workspacePath, envVars);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function applyEnvironmentTemplate(
|
||||||
|
templatePath: string,
|
||||||
|
workspacePath: string,
|
||||||
|
agentName: string,
|
||||||
|
additionalVars: Record<string, string>
|
||||||
|
): Promise<void> {
|
||||||
|
const templateEnvPath = path.join(templatePath, '.env.template');
|
||||||
|
const templateReadmePath = path.join(templatePath, 'README.md');
|
||||||
|
|
||||||
|
if (await fs.pathExists(templateEnvPath)) {
|
||||||
|
const templateContent = await fs.readFile(templateEnvPath, 'utf-8');
|
||||||
|
const processedContent = processTemplate(templateContent, {
|
||||||
|
agentName,
|
||||||
|
workspacePath,
|
||||||
|
...additionalVars
|
||||||
|
});
|
||||||
|
await fs.writeFile(path.join(workspacePath, '.env'), processedContent, 'utf-8');
|
||||||
|
} else {
|
||||||
|
const defaultContent = DEFAULT_ENV_TEMPLATE
|
||||||
|
.replace(/{agentName}/g, agentName)
|
||||||
|
.replace(/{workspacePath}/g, workspacePath)
|
||||||
|
.replace(/{gitUserEmail}/g, '')
|
||||||
|
.replace(/{gitUserName}/g, '');
|
||||||
|
await fs.writeFile(path.join(workspacePath, '.env'), defaultContent, 'utf-8');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await fs.pathExists(templateReadmePath)) {
|
||||||
|
const readmeContent = await fs.readFile(templateReadmePath, 'utf-8');
|
||||||
|
await fs.writeFile(path.join(workspacePath, 'SETUP.md'), readmeContent, 'utf-8');
|
||||||
|
}
|
||||||
|
|
||||||
|
const otherFiles = await fs.readdir(templatePath);
|
||||||
|
for (const file of otherFiles) {
|
||||||
|
if (file !== '.env.template' && file !== 'README.md') {
|
||||||
|
const src = path.join(templatePath, file);
|
||||||
|
const dest = path.join(workspacePath, file);
|
||||||
|
if ((await fs.stat(src)).isFile()) {
|
||||||
|
await fs.copy(src, dest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function processTemplate(
|
||||||
|
content: string,
|
||||||
|
variables: Record<string, string>
|
||||||
|
): string {
|
||||||
|
let result = content;
|
||||||
|
for (const [key, value] of Object.entries(variables)) {
|
||||||
|
result = result.replace(new RegExp(`\\{${key}\\}`, 'g'), value);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function runCustomSetupScript(
|
||||||
|
workspacePath: string,
|
||||||
|
scriptPath: string
|
||||||
|
): Promise<void> {
|
||||||
|
if (!(await fs.pathExists(scriptPath))) {
|
||||||
|
throw new GitAgentSyncError(`Setup script not found: ${scriptPath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const absoluteScriptPath = path.resolve(scriptPath);
|
||||||
|
const extension = path.extname(absoluteScriptPath);
|
||||||
|
|
||||||
|
if (extension === '.sh') {
|
||||||
|
await execa('bash', [absoluteScriptPath], {
|
||||||
|
cwd: workspacePath,
|
||||||
|
stdio: 'inherit'
|
||||||
|
});
|
||||||
|
} else if (extension === '.js' || extension === '.mjs') {
|
||||||
|
await execa(process.execPath, [absoluteScriptPath], {
|
||||||
|
cwd: workspacePath,
|
||||||
|
stdio: 'inherit'
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw new GitAgentSyncError(`Unsupported setup script: ${extension}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getGitUserInfo(): Promise<{ name: string; email: string }> {
|
||||||
|
try {
|
||||||
|
const { stdout: name } = await execa('git', ['config', '--global', 'user.name'], {
|
||||||
|
reject: false
|
||||||
|
});
|
||||||
|
const { stdout: email } = await execa('git', ['config', '--global', 'user.email'], {
|
||||||
|
reject: false
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: name || os.userInfo().username,
|
||||||
|
email: email || `${os.userInfo().username}@localhost`
|
||||||
|
};
|
||||||
|
} catch {
|
||||||
|
return {
|
||||||
|
name: os.userInfo().username,
|
||||||
|
email: `${os.userInfo().username}@localhost`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function checkCommandAvailability(command: string): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
await execa(command, ['--version'], { reject: false });
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateAgentEnvironment(
|
||||||
|
agentName: string,
|
||||||
|
workspacePath: string,
|
||||||
|
gitUserInfo: { name: string; email: string }
|
||||||
|
): Record<string, string> {
|
||||||
|
return {
|
||||||
|
AGENT_ID: agentName,
|
||||||
|
WORKSPACE_PATH: workspacePath,
|
||||||
|
AGENT_WORKSPACE: workspacePath,
|
||||||
|
GIT_USER_EMAIL: gitUserInfo.email,
|
||||||
|
GIT_USER_NAME: gitUserInfo.name,
|
||||||
|
AGENT_CREATED_AT: new Date().toISOString()
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user