stack/packages/template/scripts/generate-env.ts
Bilal Godil 178b5c5a8c chore: rename STACK_* env vars to HEXCLAVE_* in env templates, with legacy dual-read
Renames every STACK_*-prefixed variable (including NEXT_PUBLIC_STACK_*) to
HEXCLAVE_* across all checked-in .env, .env.development, and .env.example
files, completing the env-var side of the Hexclave rebrand. Legacy STACK_*
names keep working everywhere so existing deployments, .env.local files, and
self-hosted setups don't need immediate migration:

- getEnvVariable already prefers HEXCLAVE_* with STACK_* fallback; fix it to
  treat empty-string values as unset so the empty HEXCLAVE_* placeholders in
  the checked-in templates can't shadow a real value under the legacy name.
- Apply the same empty-as-unset rule (|| instead of ??) to all literal
  process.env dual-reads (dashboard inline env, docs, examples, CLI) and to
  the generated SDK env getter chains via packages/template generate-env.ts.
- Add explicit HEXCLAVE_* || STACK_* dual-reads to direct process.env readers
  fed by the renamed files: prisma seed, e2e tests/helpers, internal-tool
  scripts and app, demo/convex examples.
- docker/server/entrypoint.sh: add a generic two-way HEXCLAVE_/STACK_ env
  mirror (run at startup and again before sentinel replacement), replacing the
  previous URL-trio-only mirror; accept legacy NEXT_PUBLIC_STACK_PORT_PREFIX;
  rotate-secrets.sh falls back to HEXCLAVE_DATABASE_CONNECTION_STRING.
- e2e cross-domain-auth and the internal-feedback-emails in-source test now
  override the canonical HEXCLAVE_* names (the legacy override would be
  shadowed by the renamed env files).
- docs/code-examples snippets renamed outright to the canonical names.
2026-06-11 16:23:50 -07:00

95 lines
2.8 KiB
TypeScript

import { writeFileSyncIfChanged } from "@hexclave/shared/dist/utils/fs";
import { deindent } from "@hexclave/shared/dist/utils/strings";
const envVarsConfig: Record<string, { allowPublic?: boolean, deprecatedLegacyNames?: string[] }> = {
HEXCLAVE_PORT_PREFIX: {
allowPublic: true,
},
HEXCLAVE_PROJECT_ID: {
allowPublic: true,
},
HEXCLAVE_PUBLISHABLE_CLIENT_KEY: {
allowPublic: true,
},
HEXCLAVE_SECRET_SERVER_KEY: {},
HEXCLAVE_SUPER_SECRET_ADMIN_KEY: {},
HEXCLAVE_EXTRA_REQUEST_HEADERS: {
allowPublic: true,
},
HEXCLAVE_API_URL_BROWSER: {
allowPublic: true,
deprecatedLegacyNames: ["BROWSER_STACK_API_URL", "BROWSER_HEXCLAVE_API_URL"],
},
HEXCLAVE_API_URL_SERVER: {
allowPublic: true,
deprecatedLegacyNames: ["SERVER_STACK_API_URL", "SERVER_HEXCLAVE_API_URL"],
},
HEXCLAVE_API_URL: {
allowPublic: true,
deprecatedLegacyNames: ["HEXCLAVE_URL", "STACK_URL"],
},
HEXCLAVE_HOSTED_HANDLER_DOMAIN_SUFFIX: {
allowPublic: true,
},
HEXCLAVE_HOSTED_HANDLER_URL_TEMPLATE: {
allowPublic: true,
},
HEXCLAVE_STRIPE_PUBLISHABLE_KEY: {
allowPublic: true,
},
HEXCLAVE_BOT_CHALLENGE_SITE_KEY: {
allowPublic: true,
},
HEXCLAVE_BOT_CHALLENGE_INVISIBLE_SITE_KEY: {
allowPublic: true,
},
HEXCLAVE_IS_LOCAL_EMULATOR: {
allowPublic: true,
},
HEXCLAVE_POSTHOG_KEY: {
allowPublic: true,
},
HEXCLAVE_SVIX_SERVER_URL: {
allowPublic: true,
},
HEXCLAVE_SENTRY_DSN: {
allowPublic: true,
},
HEXCLAVE_VERSION_ALERTER_SEVERE_ONLY: {
allowPublic: true,
},
NODE_ENV: {
allowPublic: false,
},
};
function generateEnvVarsConstSnippet() {
const getters: string[] = [];
for (const [key, config] of Object.entries(envVarsConfig)) {
const allVariables = [key, ...(config.deprecatedLegacyNames ?? [])]
.flatMap(k => k.startsWith("HEXCLAVE_") ? [k, k.replace("HEXCLAVE_", "STACK_")] : [k])
.flatMap(k => config.allowPublic ? [k, `NEXT_PUBLIC_${k}`, `VITE_${k}`] : [k]);
// Use || (not ??) between candidates: empty-string env vars (e.g. the empty
// HEXCLAVE_* placeholders in checked-in .env templates) must not shadow a
// real value under a legacy STACK_* name further down the chain.
getters.push(deindent`
get ${key}() {
return ${allVariables.map(variableName => deindent`
((typeof process !== "undefined" ? process.env.${variableName} : undefined) || import.meta.env?.${variableName})
`).join("\n || ")} || undefined;
},
`);
}
return deindent`
// THIS FILE IS AUTO-GENERATED BY THE \`generate-env.ts\` SCRIPT.
// DO NOT EDIT IT BY HAND.
/* eslint-disable no-restricted-properties */
export const envVars = {
${getters.join("\n")}
};
` + "\n";
}
writeFileSyncIfChanged("src/generated/env.ts", generateEnvVarsConstSnippet());