All files / src index.ts

95.65% Statements 66/69
90% Branches 18/20
100% Functions 6/6
95.65% Lines 66/69

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 891x       1x   5x 5x 5x   5x   4x 4x 4x   5x 5x 5x 5x 5x       5x   5x 5x   5x   2x 2x 2x   2x 2x   5x 5x 5x 5x 5x   5x 8x 8x 8x 6x 6x 6x 6x 8x 1x 1x 8x 1x 1x 1x 8x   5x 1x 1x   4x 4x   8x 8x 8x 8x 8x   8x 7x 8x   4x 4x 4x 7x 7x 4x 4x   4x 4x  
import * as fs from 'node:fs';
import { GetParameterCommand, SSM } from '@aws-sdk/client-ssm';
import * as dotenv from 'dotenv';
 
const ssm = new SSM({});
 
export async function run(mapPath: string, envFilePath: string) {
  const paramMap = loadParamMap(mapPath);
  const existingEnvVariables = loadExistingEnvVariables(envFilePath);
 
  const updatedEnvVariables = await fetchAndUpdateEnvVariables(paramMap, existingEnvVariables);
 
  writeEnvFile(envFilePath, updatedEnvVariables);
  console.log(`Environment File generated at '${envFilePath}'`);
}
 
function loadParamMap(mapPath: string): Record<string, string> {
  const content = fs.readFileSync(mapPath, 'utf-8');
  try {
    return JSON.parse(content);
  } catch (error) {
    console.error(`Error parsing JSON from ${mapPath}`);
    throw new Error(`Invalid JSON in parameter map file: ${mapPath}`);
  }
}
 
function loadExistingEnvVariables(envFilePath: string): Record<string, string> {
  const envVariables: Record<string, string> = {};
 
  if (!fs.existsSync(envFilePath)) return envVariables;
 
  const existingEnvContent = fs.readFileSync(envFilePath, 'utf-8');
  const parsedEnv = dotenv.parse(existingEnvContent);
  Object.assign(envVariables, parsedEnv);
 
  return envVariables;
}
 
async function fetchAndUpdateEnvVariables(
  paramMap: Record<string, string>,
  existingEnvVariables: Record<string, string>,
): Promise<Record<string, string>> {
  const errors: string[] = [];
 
  for (const [envVar, ssmName] of Object.entries(paramMap)) {
    try {
      const value = await fetchSSMParameter(ssmName);
      if (value) {
        existingEnvVariables[envVar] = value;
        console.log(
          `${envVar}=${value.length > 3 ? '*'.repeat(value.length - 3) + value.slice(-3) : '*'.repeat(value.length)}`,
        );
      } else {
        console.error(`Warning: No value found for: '${ssmName}'`);
      }
    } catch (error) {
      console.error(`Error fetching parameter: '${ssmName}'`);
      errors.push(`ParameterNotFound: ${ssmName}`);
    }
  }
 
  if (errors.length > 0) {
    throw new Error(`Some parameters could not be fetched:\n${errors.join('\n')}`);
  }
 
  return existingEnvVariables;
}
 
async function fetchSSMParameter(ssmName: string): Promise<string | undefined> {
  const command = new GetParameterCommand({
    Name: ssmName,
    WithDecryption: true,
  });
 
  const { Parameter } = await ssm.send(command);
  return Parameter?.Value;
}
 
function writeEnvFile(envFilePath: string, envVariables: Record<string, string>): void {
  const envContent = Object.entries(envVariables)
    .map(([key, value]) => {
      const escapedValue = value.replace(/(\n|\r|\n\r)/g, '\\n').replace(/"/g, '\\"');
      return `${key}=${escapedValue}`;
    })
    .join('\n');
 
  fs.writeFileSync(envFilePath, envContent);
}