@puls-atlas/cli
Version:
The Puls Atlas CLI tool for managing Atlas projects
105 lines • 5.16 kB
JavaScript
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
};