mattermost-mobile-upstream/detox/maestro/lib/env.ts

96 lines
3.4 KiB
TypeScript

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
/* eslint-disable no-process-env, no-console */
import crypto from 'crypto';
import fs from 'fs';
/**
* Maestro environment helper — consistent env-var access with fallback defaults.
*
* Every Maestro fixture/seed script MUST use these helpers so that:
* • CI can override any value via GitHub Secrets / workflow inputs
* • Local dev gets sensible defaults without hardcoding secrets
*
* Usage:
* import {env, envNum, envBool, maestroEnvFile} from '../lib/env';
* const url = env('SITE_1_URL', 'http://localhost:8065');
*/
/** Read a required env var (exits with error if missing). */
export function required(key: string): string {
const val = process.env[key];
if (!val) {
console.error(`[maestro] Error: ${key} is required`);
process.exit(1);
}
return val;
}
/** Read an optional string env var, returning `fallback` when unset. */
export function env(key: string, fallback: string): string {
return process.env[key] || fallback;
}
/** Read an optional numeric env var, returning `fallback` when unset or NaN. */
export function envNum(key: string, fallback: number): number {
const raw = process.env[key];
if (raw === undefined || raw === '') {
return fallback;
}
const n = Number(raw);
return Number.isNaN(n) ? fallback : n;
}
/** Read an optional boolean env var (truthy: '1'|'true'|'yes'). */
export function envBool(key: string, fallback: boolean): boolean {
const raw = process.env[key];
if (raw === undefined || raw === '') {
return fallback;
}
return ['1', 'true', 'yes'].includes(raw.toLowerCase());
}
/**
* Password for API-created test users. Uses TEST_USER_PASSWORD when set;
* otherwise generates an ephemeral value (never hardcode credentials in source).
*/
export function testUserPassword(): string {
const fromEnv = process.env.TEST_USER_PASSWORD;
if (fromEnv) {
return fromEnv;
}
return crypto.randomBytes(24).toString('base64url');
}
/** Absolute path to the generated .maestro-test-env.sh inside detox/maestro/. */
export const MAESTRO_ENV_FILE = __dirname + '/../.maestro-test-env.sh';
const ENV_VAR_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
const GENERATED_BY = /^[\w./-]+$/;
/** Escape a value for use inside single quotes in a POSIX shell script. */
export function shellEscapeSingleQuoted(value: string): string {
// 'foo'\''bar' closes the quoted string, adds an escaped quote, reopens quoting.
return `'${value.replace(/'/g, '\'\\\'\'')}'`;
}
/** Write seed credentials to .maestro-test-env.sh for `source` in Maestro CI scripts. */
export function writeMaestroEnvFile(vars: Record<string, string>, generatedBy: string): void {
if (!GENERATED_BY.test(generatedBy)) {
throw new Error(`Invalid generatedBy label for Maestro env file: ${generatedBy}`);
}
const lines = Object.entries(vars).map(([key, value]) => {
if (!ENV_VAR_NAME.test(key)) {
throw new Error(`Invalid env var name for Maestro export: ${key}`);
}
return `export ${key}=${shellEscapeSingleQuoted(value)}`;
});
fs.writeFileSync(
MAESTRO_ENV_FILE,
`#!/bin/bash\n# Auto-generated by ${generatedBy} — do not edit\n${lines.join('\n')}\n`,
);
console.log(`[maestro] Wrote env vars to ${MAESTRO_ENV_FILE}`);
}