UNPKG

nx

Version:

The core Nx plugin contains the core functionality of Nx like the project graph, nx commands and task orchestration.

156 lines (155 loc) 6.01 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getAiConfigRepoPath = getAiConfigRepoPath; const child_process_1 = require("child_process"); const fs_1 = require("fs"); const os_1 = require("os"); const path_1 = require("path"); const REPO_URL = 'https://github.com/nrwl/nx-ai-agents-config'; const CACHE_DIR = (0, path_1.join)((0, os_1.tmpdir)(), 'nx-ai-agents-config'); /** * Get the latest commit hash from the remote repository. * Uses `git ls-remote` to fetch the HEAD commit hash without cloning. */ function getLatestCommitHash() { try { const output = (0, child_process_1.execSync)(`git ls-remote ${REPO_URL} HEAD`, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 30000, // 30 second timeout windowsHide: true, }); const hash = output.split('\t')[0]; if (!hash || hash.length < 10) { throw new Error('Invalid commit hash received'); } // Return first 10 characters of the commit hash return hash.substring(0, 10); } catch (error) { throw new Error(`Failed to fetch latest commit hash from ${REPO_URL}. Please check your network connection.`); } } /** * Clone the repository to the specified path using shallow clone. */ function cloneRepo(targetPath) { try { // Ensure parent directory exists (0, fs_1.mkdirSync)(CACHE_DIR, { recursive: true }); // Use a temporary path first to avoid race conditions const tempPath = `${targetPath}.tmp.${process.pid}`; // Clean up any leftover temp directory if ((0, fs_1.existsSync)(tempPath)) { (0, fs_1.rmSync)(tempPath, { recursive: true, force: true }); } (0, child_process_1.execSync)(`git clone --depth 1 ${REPO_URL} "${tempPath}"`, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 120000, // 2 minute timeout for clone windowsHide: true, }); // Remove .git directory after clone const gitDir = (0, path_1.join)(tempPath, '.git'); if ((0, fs_1.existsSync)(gitDir)) { (0, fs_1.rmSync)(gitDir, { recursive: true, force: true }); } // Atomically move temp directory to final location // If targetPath already exists (race condition), just clean up temp if ((0, fs_1.existsSync)(targetPath)) { (0, fs_1.rmSync)(tempPath, { recursive: true, force: true }); } else { // Rename is atomic on the same filesystem try { (0, fs_1.renameSync)(tempPath, targetPath); } catch { // Rename failed - check if another process won the race if ((0, fs_1.existsSync)(targetPath)) { // Another process created it, clean up our temp (0, fs_1.rmSync)(tempPath, { recursive: true, force: true }); } else { // targetPath still doesn't exist - retry once try { (0, fs_1.renameSync)(tempPath, targetPath); } catch (retryError) { // Clean up and fail (0, fs_1.rmSync)(tempPath, { recursive: true, force: true }); throw new Error(`Failed to move cloned repository to cache location: ${retryError.message}`); } } } } } catch (error) { // Re-throw if it's already our error (from rename failure) if (error instanceof Error && error.message.startsWith('Failed to move')) { throw error; } throw new Error(`Failed to clone ${REPO_URL}. Please check your network connection.`); } } /** * Clean up old cached versions, keeping only the current one. */ function cleanupOldCaches(currentCommitHash) { if (!(0, fs_1.existsSync)(CACHE_DIR)) { return; } try { const entries = (0, fs_1.readdirSync)(CACHE_DIR, { withFileTypes: true }); for (const entry of entries) { if (entry.isDirectory() && entry.name !== currentCommitHash) { const oldCachePath = (0, path_1.join)(CACHE_DIR, entry.name); (0, fs_1.rmSync)(oldCachePath, { recursive: true, force: true }); } } } catch { // Ignore cleanup errors - not critical } } /** * Get the path to the cached nx-ai-agents-config repository. * Uses a commit-hash based caching strategy: * 1. Fetches the latest commit hash from the remote repository * 2. Checks if a cached version exists for that hash * 3. If not, clones the repository and cleans up old caches * * @returns The path to the cached repository * @throws Error if unable to fetch or clone the repository */ function getAiConfigRepoPath() { // 1. Get latest commit hash (first 10 chars) const commitHash = getLatestCommitHash(); // 2. Reuse cached version if it still has content (macOS may have // swept its files but left the directory tree). const cachedPath = (0, path_1.join)(CACHE_DIR, commitHash); if (hasRootFile(cachedPath)) { return cachedPath; } // 3. Wipe any empty skeleton, then clone fresh if ((0, fs_1.existsSync)(cachedPath)) { (0, fs_1.rmSync)(cachedPath, { recursive: true, force: true }); } cloneRepo(cachedPath); // 4. Clean up old cached versions cleanupOldCaches(commitHash); return cachedPath; } /** * The repo always has at least one regular file at its root (e.g. README). * If everything at the root is a directory, the cache was swept by macOS * tmp cleanup and we should re-clone. */ function hasRootFile(dir) { try { return (0, fs_1.readdirSync)(dir, { withFileTypes: true }).some((e) => e.isFile()); } catch { return false; } }