UNPKG

bitbucket-env-manager

Version:

Deploys Bitbucket Environment

198 lines (171 loc) 4.79 kB
#!/usr/bin/env node const { select, confirm, isCancel, cancel, log, note } = require('@clack/prompts'); const fs = require('fs-extra'); const path = require('path'); const color = require('picocolors'); const { names } = require('./config'); /** * Recursively find files matching a pattern in a directory */ const findFiles = (dir, filename, results = []) => { try { const items = fs.readdirSync(dir); for (const item of items) { // Skip hidden directories and node_modules if (item.startsWith('.') || item === 'node_modules') continue; const fullPath = path.join(dir, item); try { const stat = fs.statSync(fullPath); if (stat.isDirectory()) { findFiles(fullPath, filename, results); } else if (item === filename) { results.push(fullPath); } } catch (e) { // Skip files we can't access } } } catch (e) { // Skip directories we can't access } return results; }; /** * Find all .env files (matching pattern .env.* ) in a directory */ const findEnvFiles = (dir, results = []) => { try { const items = fs.readdirSync(dir); for (const item of items) { // Skip hidden directories, node_modules, and remote (download output) if (item === 'node_modules' || item === 'remote') continue; const fullPath = path.join(dir, item); try { const stat = fs.statSync(fullPath); if (stat.isDirectory() && !item.startsWith('.')) { findEnvFiles(fullPath, results); } else if (item.startsWith('.env.') && stat.isFile()) { results.push(fullPath); } } catch (e) { // Skip files we can't access } } } catch (e) { // Skip directories we can't access } return results; }; /** * Select an info.json file using Clack */ const selectInfoJson = async (message = 'Select a repository') => { const basePath = process.cwd(); const files = findFiles(basePath, names.infoJsonFile); if (files.length === 0) { log.error(`No ${names.infoJsonFile} files found in current directory`); return null; } const options = files.map((f) => { const relativePath = path.relative(basePath, f); const dirName = path.dirname(relativePath); return { value: f, label: dirName || relativePath, hint: relativePath, }; }); const selected = await select({ message, options, }); if (isCancel(selected)) { cancel('Operation cancelled.'); return null; } return selected; }; /** * Select .env files using Clack (with multi-select simulation) */ const selectEnvFiles = async (message = 'Select .env files to deploy') => { const basePath = process.cwd(); const files = findEnvFiles(basePath); if (files.length === 0) { log.error('No .env files found in current directory'); return null; } // Group files by directory const filesByDir = {}; for (const f of files) { const dir = path.dirname(f); if (!filesByDir[dir]) { filesByDir[dir] = []; } filesByDir[dir].push(f); } const selectedFiles = []; // For each directory with env files, ask which to deploy for (const [dir, dirFiles] of Object.entries(filesByDir)) { const relativeDirPath = path.relative(basePath, dir); note(`Found ${dirFiles.length} env file(s) in ${color.cyan(relativeDirPath || '.')}`, 'Files'); for (const filePath of dirFiles) { const fileName = path.basename(filePath); const shouldInclude = await confirm({ message: `Include ${color.cyan(fileName)}?`, initialValue: true, }); if (isCancel(shouldInclude)) { cancel('Operation cancelled.'); return null; } if (shouldInclude) { selectedFiles.push(filePath); } } } return selectedFiles; }; /** * Parse info.json file and return parameters */ const getInfoJsonParams = (infoJsonPath) => { const infoRaw = fs.readFileSync(infoJsonPath, { encoding: 'utf-8' }); const infoJson = JSON.parse(infoRaw); if ( !infoJson.workspace || !infoJson.repo || !infoJson.secured || !Array.isArray(infoJson.secured) ) { throw new Error( `Please specify {workspace:, repo:, secured:[]} under "${names.infoJsonFile}" file` ); } return { workspace: infoJson.workspace, repo: infoJson.repo, secured: infoJson.secured, envs_config: infoJson.envs_config || {}, }; }; /** * Handle cancellation consistently */ const handleCancel = (value) => { if (isCancel(value)) { cancel('Operation cancelled.'); process.exit(0); } return value; }; module.exports = { findFiles, findEnvFiles, selectInfoJson, selectEnvFiles, getInfoJsonParams, handleCancel, isCancel, cancel, };