@papavault/cli
Version:
CLI tool for Papa Vault Secret Manager
132 lines (131 loc) • 7.32 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.syncCommand = syncCommand;
const chalk_1 = __importDefault(require("chalk"));
const ora_1 = __importDefault(require("ora"));
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const config_1 = require("../utils/config");
const api_1 = require("../utils/api");
// Debug function to log detailed information
function debugLog(message, data) {
const DEBUG = process.env.PAPAVAULT_DEBUG === 'true';
if (DEBUG) {
console.log(chalk_1.default.cyan('[DEBUG] ') + message);
if (data !== undefined) {
console.log(chalk_1.default.cyan('[DEBUG DATA] '), data);
}
}
}
function syncCommand(program) {
program
.command('sync')
.description('Sync secrets from Papa Vault to your local environment')
.option('-p, --project-id <projectId>', 'Project ID (overrides config file)')
.option('-s, --stage <stage>', 'Environment stage (overrides config file)')
.option('-d, --debug', 'Enable debug mode')
.option('-t, --token <token>', 'API token for authentication (overrides stored token)')
.action(async (options) => {
// Set debug mode if flag is provided
if (options.debug) {
process.env.PAPAVAULT_DEBUG = 'true';
debugLog('Debug mode enabled');
}
// Check if authenticated
if (!(0, config_1.isAuthenticated)() && !options.token) {
console.log(chalk_1.default.red('You need to be logged in to sync secrets.'));
console.log(chalk_1.default.yellow('Run `papavault login` to authenticate or provide a token with --token.'));
return;
}
// Get project ID and stage from options or config
const projectId = options.projectId || (0, config_1.getProjectId)();
const stage = options.stage || (0, config_1.getStage)();
const token = options.token;
debugLog(`Using project ID: ${projectId}, stage: ${stage}`);
if (!projectId || !stage) {
console.log(chalk_1.default.red('Project ID and stage are required to sync secrets.'));
console.log(chalk_1.default.yellow('Run `papavault set-project` to configure your project.'));
return;
}
// Get the config file path to use as the base directory
const configFilePath = (0, config_1.getConfigFilePath)();
debugLog(`Config file path: ${configFilePath}`);
if (!configFilePath) {
console.log(chalk_1.default.red('Could not find .papavault.json configuration file.'));
console.log(chalk_1.default.yellow('Run `papavault set-project` to configure your project.'));
return;
}
// Get the directory where the config file is located
const baseDir = path_1.default.dirname(configFilePath);
console.log(chalk_1.default.blue(`Using base directory: ${baseDir}`));
debugLog(`Base directory: ${baseDir}`);
// Fetch credential files
const spinner = (0, ora_1.default)('Fetching credential files...').start();
try {
const credentialFiles = await (0, api_1.getCredentialFiles)(projectId, stage, token);
spinner.succeed(`Found ${credentialFiles.length} credential files.`);
if (credentialFiles.length === 0) {
console.log(chalk_1.default.yellow('No credential files found to sync.'));
return;
}
// Process each credential file
for (const file of credentialFiles) {
try {
// Get the credential file details
const { downloadUrl } = await (0, api_1.getCredentialFile)(projectId, stage, file.id, token);
// Download the file content
const content = await (0, api_1.downloadCredentialFile)(downloadUrl, token);
// Normalize the file path
let normalizedFilePath = file.filePath;
// Special handling for different path formats
if (normalizedFilePath === '/') {
// For root path, use the filename directly in the base directory
normalizedFilePath = file.fileName;
console.log(chalk_1.default.yellow(`Warning: Converting root path '/' to '${normalizedFilePath}'. File will be placed directly in the config directory.`));
}
else if (path_1.default.isAbsolute(normalizedFilePath)) {
// For absolute paths, we need to make them relative
// Get the path parts
const parsedPath = path_1.default.parse(normalizedFilePath);
// On Windows, remove the drive letter if present
if (parsedPath.root.includes(':')) {
normalizedFilePath = normalizedFilePath.replace(/^[A-Za-z]:/, '');
}
// Remove leading slashes to make it relative
normalizedFilePath = normalizedFilePath.replace(/^[\/\\]+/, '');
// Log a warning about the absolute path
console.log(chalk_1.default.yellow(`Warning: Converting absolute path '${file.filePath}' to relative path '${normalizedFilePath}'`));
}
// Replace backslashes with forward slashes for consistency
normalizedFilePath = normalizedFilePath.replace(/\\/g, '/');
debugLog(`Normalized file path: ${normalizedFilePath}`);
// Resolve the file path relative to the config file location
const absoluteFilePath = path_1.default.resolve(baseDir, normalizedFilePath);
debugLog(`Resolved absolute file path: ${absoluteFilePath}`);
debugLog(`Path.parse of resolved path:`, path_1.default.parse(absoluteFilePath));
// Create the directory if it doesn't exist
const dir = path_1.default.dirname(absoluteFilePath);
if (!fs_1.default.existsSync(dir)) {
fs_1.default.mkdirSync(dir, { recursive: true });
debugLog(`Created directory: ${dir}`);
}
// Write the file
fs_1.default.writeFileSync(absoluteFilePath, content);
console.log(chalk_1.default.green(`✓ Synced ${file.fileName} to ${normalizedFilePath}`));
}
catch (error) {
console.error(chalk_1.default.red(`Error syncing ${file.fileName}:`), error);
}
}
console.log(chalk_1.default.green('\nSecrets synced successfully!'));
}
catch (error) {
spinner.fail('Failed to fetch credential files');
console.error(chalk_1.default.red('Error:'), error);
debugLog('Error fetching credential files:', error);
}
});
}