UNPKG

@catladder/pipeline

Version:

Panter workflow for cloud CI/CD and DevOps

196 lines (171 loc) • 6.06 kB
import { DEPLOY_TYPES } from "../deploy"; import type { ComponentContext, EnvironmentEnvVarPart as EnvironmentVariables, } from "../types"; import type { DevLocalEnvConfig } from "../types/config"; import type { CreateComponentContextContext, UnspecifiedEnvVars } from ".."; import type { StringOrBashExpression } from "../bash/BashExpression"; import { joinBashExpressions } from "../bash/BashExpression"; import { isStandaloneBuildConfig } from "../build/types"; import type { EnvironmentContext } from "../types/environmentContext"; import { createVariableValueContainingReferencesFromString } from "../variables/VariableValueContainingReferences"; import { resolveAllReferences } from "../variables/resolveAllReferences"; import { getBuildInfoVariables } from "./getBuildInfoVariables"; import { getEnvironmentContext } from "./getEnvironmentContext"; import { transformJobOnlyVars } from "./transformJobOnlyVars"; import { makeSecretEnvVarMapping, stringListToSecreteEnvVarList, stringifyValues, } from "./utils/envVars"; export type SecretEnvVar = { key: string; // hidden env vars are not shown in config-secrets hidden?: boolean; }; const getBasePredefinedVariables = (ctx: EnvironmentContext) => { return { ENV_SHORT: ctx.env, APP_DIR: ctx.envConfigRaw.dir, ENV_TYPE: ctx.envType, ...(ctx.envType !== "local" ? getBuildInfoVariables(ctx) : {}), }; }; type BasePredefinedVariables = ReturnType<typeof getBasePredefinedVariables>; // we export so that we have later nice autocomplete export type PredefinedVariables = BasePredefinedVariables & { HOSTNAME: StringOrBashExpression; ROOT_URL: StringOrBashExpression; HOSTNAME_INTERNAL: StringOrBashExpression; ROOT_URL_INTERNAL: StringOrBashExpression; }; export const getEnvironmentVariables = async ( ctx: CreateComponentContextContext, options: { shouldResolveReferences?: boolean } = {}, ): Promise<EnvironmentVariables> => { const environmentContext = getEnvironmentContext(ctx); const { config, env, componentName } = ctx; const { envConfigRaw, deployConfigRaw, buildConfigRaw, envType } = environmentContext; const basePredefinedVariables = getBasePredefinedVariables(environmentContext); let predefinedVariables: PredefinedVariables & UnspecifiedEnvVars; let host: StringOrBashExpression; let url: StringOrBashExpression; if (envType === "local") { const devLocalConfig: DevLocalEnvConfig = envConfigRaw; const port = devLocalConfig.port ?? 3000; host = "localhost:" + port.toString(); url = "http://" + host; predefinedVariables = { ...basePredefinedVariables, ENV_SHORT: "local", ROOT_URL: url, HOSTNAME: host, HOSTNAME_INTERNAL: host, ROOT_URL_INTERNAL: "http://" + host, PORT: port.toString(), }; } else { const additionalEnvVars = deployConfigRaw ? DEPLOY_TYPES[deployConfigRaw.type].getAdditionalEnvVars( environmentContext as never, ) : {}; const HOSTNAME_INTERNAL = additionalEnvVars.HOSTNAME_INTERNAL ?? "unknown-host.example.com"; host = envConfigRaw?.host ?? HOSTNAME_INTERNAL; url = joinBashExpressions(["https://", host]); predefinedVariables = { ...basePredefinedVariables, HOSTNAME: host, ROOT_URL: url, HOSTNAME_INTERNAL, ROOT_URL_INTERNAL: joinBashExpressions(["https://", HOSTNAME_INTERNAL]), ...additionalEnvVars, }; } const publicEnvVarsRaw = envConfigRaw.vars?.public ?? {}; const additionalSecretKeys = deployConfigRaw ? DEPLOY_TYPES[deployConfigRaw.type].additionalSecretKeys( environmentContext as never, ) : []; const secretEnvVarKeys: SecretEnvVar[] = [ ...stringListToSecreteEnvVarList(envConfigRaw.vars?.secret ?? []), ...additionalSecretKeys, ]; const secretEnvVars = makeSecretEnvVarMapping( env, componentName, secretEnvVarKeys, ); const publicEnvVarsRawSanitized = stringifyValues(publicEnvVarsRaw); const publicEnvVarsUnresolved = Object.fromEntries( Object.entries(publicEnvVarsRawSanitized).map(([key, value]) => [ key, createVariableValueContainingReferencesFromString(value, { componentName: ctx.componentName, }), ]), ); const publicEnvVars = (options.shouldResolveReferences ?? true) ? await resolveAllReferences( publicEnvVarsUnresolved, async (otherComponentName) => { const { envVars: otherEnvVars } = await getEnvironmentVariables( { ...ctx, componentName: otherComponentName, }, { shouldResolveReferences: false, // we already do this here with replaceAllReferences recursivly until re replaced all }, ); return otherEnvVars; }, ) : publicEnvVarsUnresolved; const envVars = addIndexVar({ ...predefinedVariables, ...secretEnvVars, ...publicEnvVars, }); return { envVars, secretEnvVarKeys, jobOnlyVars: { build: await transformJobOnlyVars( env, componentName, (buildConfigRaw && isStandaloneBuildConfig(buildConfigRaw) && buildConfigRaw.jobVars) || null, ), deploy: await transformJobOnlyVars( env, componentName, (deployConfigRaw && deployConfigRaw.jobVars) || null, ), }, }; }; const sanitizeForEnVar = (s: string) => s.replace(/-/g, "_"); export const getSecretVarName = ( env: string, componentName: string, key: string, ) => `CL_${sanitizeForEnVar(env)}_${sanitizeForEnVar(componentName)}_${key}`; // remove dash from component name const addIndexVar = <V extends Record<string, unknown>>( vars: V, ): V & { _ALL_ENV_VAR_KEYS: string } => ({ ...vars, _ALL_ENV_VAR_KEYS: JSON.stringify(Object.keys(vars)), }); export const getSecretVarNameForContext = ( context: ComponentContext, key: string, ) => getSecretVarName(context.env, context.name, key);