UNPKG

@puls-atlas/cli

Version:

The Puls Atlas CLI tool for managing Atlas projects

105 lines 5.16 kB
import fs from 'fs'; import os from 'os'; import path from 'path'; import { execFileSync } from 'child_process'; import { parseGitHubTerraformModuleSource, resolveSiblingTerraformModuleSource } from '../search/terraformRootTemplates.js'; const SYNC_TERRAFORM_ROOT_TEMPLATE_FILES = ['main.tf.tpl', 'variables.tf.tpl']; const SYNC_TERRAFORM_ROOT_TEMPLATE_DIRECTORY = path.join('templates', 'sync-root'); const normalizeTemplateFileName = templateFileName => templateFileName.replace(/\.tpl$/u, ''); const hasTemplateDirectory = (templateDirectory, templateFiles = SYNC_TERRAFORM_ROOT_TEMPLATE_FILES, existsSyncImpl = fs.existsSync) => templateFiles.every(templateFileName => existsSyncImpl(path.join(templateDirectory, templateFileName))); const readTemplateDirectory = (templateDirectory, templateFiles = SYNC_TERRAFORM_ROOT_TEMPLATE_FILES, readFileSyncImpl = fs.readFileSync) => Object.fromEntries(templateFiles.map(templateFileName => [normalizeTemplateFileName(templateFileName), readFileSyncImpl(path.join(templateDirectory, templateFileName), 'utf-8')])); const walkParentDirectories = startPath => { const directories = []; let currentPath = startPath; while (true) { directories.push(currentPath); const parentPath = path.dirname(currentPath); if (parentPath === currentPath) { return directories; } currentPath = parentPath; } }; const resolveLocalTemplateDirectoryFromModuleSource = (moduleSource, rootPath, templateRootDirectory, options = {}) => { const { existsSyncImpl = fs.existsSync, readdirSyncImpl = fs.readdirSync, templateFiles = SYNC_TERRAFORM_ROOT_TEMPLATE_FILES } = options; if (typeof moduleSource === 'string' && !moduleSource.includes('::')) { const absoluteModulePath = path.resolve(rootPath, moduleSource); for (const candidatePath of walkParentDirectories(absoluteModulePath)) { const templateDirectory = path.join(candidatePath, templateRootDirectory); if (hasTemplateDirectory(templateDirectory, templateFiles, existsSyncImpl)) { return templateDirectory; } } } const gitHubSource = parseGitHubTerraformModuleSource(moduleSource); if (!gitHubSource) { return null; } for (const candidatePath of walkParentDirectories(rootPath)) { const templateDirectory = path.join(candidatePath, gitHubSource.repo, templateRootDirectory); if (hasTemplateDirectory(templateDirectory, templateFiles, existsSyncImpl)) { return templateDirectory; } let nestedDirectories = []; try { nestedDirectories = readdirSyncImpl(candidatePath, { withFileTypes: true }).filter(entry => entry.isDirectory()).map(entry => entry.name); } catch { nestedDirectories = []; } for (const nestedDirectory of nestedDirectories) { const nestedTemplateDirectory = path.join(candidatePath, nestedDirectory, gitHubSource.repo, templateRootDirectory); if (hasTemplateDirectory(nestedTemplateDirectory, templateFiles, existsSyncImpl)) { return nestedTemplateDirectory; } } } return null; }; const loadTaggedTemplateFiles = async (moduleSource, templateRootDirectory, options = {}) => { const { execFileSyncImpl = execFileSync, mkdtempSyncImpl = fs.mkdtempSync, readFileSyncImpl = fs.readFileSync, rmSyncImpl = fs.rmSync, templateFiles = SYNC_TERRAFORM_ROOT_TEMPLATE_FILES, tmpdirImpl = os.tmpdir } = options; const gitHubSource = parseGitHubTerraformModuleSource(moduleSource); if (!gitHubSource) { throw new Error('Tagged Terraform root templates require a git::https://github.com/... module source with a ref query parameter.'); } const cloneUrl = `https://github.com/${gitHubSource.owner}/${gitHubSource.repo}.git`; const cloneDirectory = mkdtempSyncImpl(path.join(tmpdirImpl(), 'atlas-sync-terraform-templates-')); try { execFileSyncImpl('git', ['clone', '--depth', '1', '--branch', gitHubSource.ref, cloneUrl, cloneDirectory], { stdio: 'pipe' }); const templateDirectory = path.join(cloneDirectory, templateRootDirectory); if (!hasTemplateDirectory(templateDirectory, templateFiles, fs.existsSync)) { throw new Error(`Could not find Terraform root templates under ${templateRootDirectory} ` + `in ${cloneUrl} at ref ${gitHubSource.ref}.`); } return readTemplateDirectory(templateDirectory, templateFiles, readFileSyncImpl); } finally { rmSyncImpl(cloneDirectory, { force: true, recursive: true }); } }; export const loadTaggedSyncTerraformRootFiles = async (moduleSource, rootPath, options = {}) => { const localTemplateDirectory = resolveLocalTemplateDirectoryFromModuleSource(moduleSource, rootPath, SYNC_TERRAFORM_ROOT_TEMPLATE_DIRECTORY, options); if (localTemplateDirectory) { return readTemplateDirectory(localTemplateDirectory, SYNC_TERRAFORM_ROOT_TEMPLATE_FILES, options.readFileSyncImpl ?? fs.readFileSync); } return loadTaggedTemplateFiles(moduleSource, SYNC_TERRAFORM_ROOT_TEMPLATE_DIRECTORY, options); }; export { parseGitHubTerraformModuleSource, resolveSiblingTerraformModuleSource }; export default { loadTaggedSyncTerraformRootFiles };