-
Notifications
You must be signed in to change notification settings - Fork 1
Read VITE config from runtime env in Docker image #82
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 7 commits
6ef597c
6d7f776
783861a
7ecdc8b
8c5199f
362de10
2771e1f
e918e3d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,15 @@ | ||||||||||||||||
| #!/usr/bin/env sh | ||||||||||||||||
| set -e | ||||||||||||||||
|
|
||||||||||||||||
| JSON_ENV=$(jq -n \ | ||||||||||||||||
| --arg VITE_API_BASE_URL "$VITE_API_BASE_URL" \ | ||||||||||||||||
| --arg VITE_API_MOCKING_ENABLED "$VITE_API_MOCKING_ENABLED" \ | ||||||||||||||||
| --arg VITE_KEYCLOAK_URL "$VITE_KEYCLOAK_URL" \ | ||||||||||||||||
| --arg VITE_KEYCLOAK_REALM "$VITE_KEYCLOAK_REALM" \ | ||||||||||||||||
| --arg VITE_KEYCLOAK_CLIENT_ID "$VITE_KEYCLOAK_CLIENT_ID" \ | ||||||||||||||||
| --arg VITE_BITCR_DEV_INCLUDE_CROWDIN_IN_CONTEXT_TOOLING "$VITE_BITCR_DEV_INCLUDE_CROWDIN_IN_CONTEXT_TOOLING" \ | ||||||||||||||||
| '{VITE_API_BASE_URL: $VITE_API_BASE_URL, VITE_API_MOCKING_ENABLED: $VITE_API_MOCKING_ENABLED, VITE_KEYCLOAK_URL: $VITE_KEYCLOAK_URL, VITE_KEYCLOAK_REALM: $VITE_KEYCLOAK_REALM, VITE_KEYCLOAK_CLIENT_ID: $VITE_KEYCLOAK_CLIENT_ID, VITE_BITCR_DEV_INCLUDE_CROWDIN_IN_CONTEXT_TOOLING: $VITE_BITCR_DEV_INCLUDE_CROWDIN_IN_CONTEXT_TOOLING}') | ||||||||||||||||
|
|
||||||||||||||||
| cat <<EOF > /usr/share/nginx/html/config.js | ||||||||||||||||
| window.__ENV__ = ${JSON_ENV}; | ||||||||||||||||
| EOF | ||||||||||||||||
|
||||||||||||||||
| EOF | |
| EOF | |
| if [ ! -f /usr/share/nginx/html/config.js ]; then | |
| echo "Error: Failed to generate config.js" | |
| exit 1 | |
| fi |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| window.__ENV__ = window.__ENV__ || {} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,8 @@ | ||
| import { env } from "@/lib/env" | ||
|
|
||
| export default { | ||
| devModeEnabled: import.meta.env.DEV, | ||
| apiBaseUrl: import.meta.env.VITE_API_BASE_URL as string, | ||
| apiMocksEnabled: import.meta.env.VITE_API_MOCKING_ENABLED === "true", | ||
| crowdinInContextToolingEnabled: import.meta.env.VITE_BITCR_DEV_INCLUDE_CROWDIN_IN_CONTEXT_TOOLING === "true", | ||
| devModeEnabled: env.devModeEnabled, | ||
| apiBaseUrl: env.apiBaseUrl, | ||
| apiMocksEnabled: env.apiMocksEnabled, | ||
| crowdinInContextToolingEnabled: env.crowdinInContextToolingEnabled, | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| import { afterEach, describe, expect, it, vi } from "vitest" | ||
|
|
||
| const loadEnv = async () => { | ||
| const module = await import("./env") | ||
| return module.env | ||
| } | ||
|
|
||
| afterEach(() => { | ||
| vi.resetModules() | ||
| vi.unstubAllGlobals() | ||
| vi.unstubAllEnvs() | ||
| }) | ||
|
|
||
| describe("env runtime resolution", () => { | ||
| it("prefers runtime env values when provided", async () => { | ||
| vi.stubEnv("VITE_API_BASE_URL", "https://fallback.example.com") | ||
| vi.stubEnv("VITE_KEYCLOAK_URL", "https://fallback-keycloak.example.com") | ||
| vi.stubEnv("VITE_KEYCLOAK_REALM", "fallback-realm") | ||
| vi.stubEnv("VITE_KEYCLOAK_CLIENT_ID", "fallback-client") | ||
|
|
||
| vi.stubGlobal("window", { | ||
| __ENV__: { | ||
| VITE_API_BASE_URL: "https://runtime.example.com", | ||
| VITE_API_MOCKING_ENABLED: "true", | ||
| VITE_KEYCLOAK_URL: "https://runtime-keycloak.example.com", | ||
| VITE_KEYCLOAK_REALM: "runtime-realm", | ||
| VITE_KEYCLOAK_CLIENT_ID: "runtime-client", | ||
| VITE_BITCR_DEV_INCLUDE_CROWDIN_IN_CONTEXT_TOOLING: "true", | ||
| }, | ||
| }) | ||
|
|
||
| const env = await loadEnv() | ||
|
|
||
| expect(env.apiBaseUrl).toBe("https://runtime.example.com") | ||
| expect(env.apiMocksEnabled).toBe(true) | ||
| expect(env.keycloakUrl).toBe("https://runtime-keycloak.example.com") | ||
| expect(env.keycloakRealm).toBe("runtime-realm") | ||
| expect(env.keycloakClientId).toBe("runtime-client") | ||
| expect(env.crowdinInContextToolingEnabled).toBe(true) | ||
| }) | ||
|
|
||
| it("falls back to build-time env when runtime values are empty", async () => { | ||
| vi.stubEnv("VITE_API_BASE_URL", "https://fallback.example.com") | ||
| vi.stubEnv("VITE_API_MOCKING_ENABLED", "true") | ||
| vi.stubEnv("VITE_KEYCLOAK_URL", "https://fallback-keycloak.example.com") | ||
| vi.stubEnv("VITE_KEYCLOAK_REALM", "fallback-realm") | ||
| vi.stubEnv("VITE_KEYCLOAK_CLIENT_ID", "fallback-client") | ||
| vi.stubEnv("VITE_BITCR_DEV_INCLUDE_CROWDIN_IN_CONTEXT_TOOLING", "false") | ||
|
|
||
| vi.stubGlobal("window", { | ||
| __ENV__: { | ||
| VITE_API_BASE_URL: "", | ||
| VITE_API_MOCKING_ENABLED: "", | ||
| VITE_KEYCLOAK_URL: "", | ||
| VITE_KEYCLOAK_REALM: "", | ||
| VITE_KEYCLOAK_CLIENT_ID: "", | ||
| VITE_BITCR_DEV_INCLUDE_CROWDIN_IN_CONTEXT_TOOLING: "", | ||
| }, | ||
| }) | ||
|
|
||
| const env = await loadEnv() | ||
|
|
||
| expect(env.apiBaseUrl).toBe("https://fallback.example.com") | ||
| expect(env.apiMocksEnabled).toBe(true) | ||
| expect(env.keycloakUrl).toBe("https://fallback-keycloak.example.com") | ||
| expect(env.keycloakRealm).toBe("fallback-realm") | ||
| expect(env.keycloakClientId).toBe("fallback-client") | ||
| expect(env.crowdinInContextToolingEnabled).toBe(false) | ||
| }) | ||
|
|
||
| it("handles SSR where window is undefined", async () => { | ||
| vi.stubEnv("VITE_API_BASE_URL", "https://fallback.example.com") | ||
| vi.stubEnv("VITE_KEYCLOAK_URL", "https://fallback-keycloak.example.com") | ||
| vi.stubEnv("VITE_KEYCLOAK_REALM", "fallback-realm") | ||
| vi.stubEnv("VITE_KEYCLOAK_CLIENT_ID", "fallback-client") | ||
|
|
||
| vi.stubGlobal("window", undefined) | ||
|
|
||
| const env = await loadEnv() | ||
|
|
||
| expect(env.apiBaseUrl).toBe("https://fallback.example.com") | ||
| expect(env.keycloakUrl).toBe("https://fallback-keycloak.example.com") | ||
| expect(env.keycloakRealm).toBe("fallback-realm") | ||
| expect(env.keycloakClientId).toBe("fallback-client") | ||
| }) | ||
| }) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| type RuntimeEnv = Partial<{ | ||
| VITE_API_BASE_URL: string | ||
| VITE_API_MOCKING_ENABLED: string | ||
| VITE_KEYCLOAK_URL: string | ||
| VITE_KEYCLOAK_REALM: string | ||
| VITE_KEYCLOAK_CLIENT_ID: string | ||
| VITE_BITCR_DEV_INCLUDE_CROWDIN_IN_CONTEXT_TOOLING: string | ||
| }> | ||
|
|
||
| const runtimeEnv: RuntimeEnv = | ||
| typeof window !== "undefined" ? (window as { __ENV__?: RuntimeEnv }).__ENV__ ?? {} : {} | ||
|
|
||
| const fallbackEnv = import.meta.env as ImportMetaEnv & RuntimeEnv | ||
|
|
||
| const getEnvValue = <K extends keyof RuntimeEnv>(key: K): RuntimeEnv[K] | undefined => { | ||
| const value = runtimeEnv[key] | ||
|
|
||
| if (value !== undefined && value !== null && value !== "") { | ||
| return value | ||
| } | ||
|
|
||
| return fallbackEnv[key] | ||
| } | ||
|
|
||
| export const env = { | ||
| devModeEnabled: fallbackEnv.DEV, | ||
| apiBaseUrl: getEnvValue("VITE_API_BASE_URL")!, | ||
| apiMocksEnabled: (getEnvValue("VITE_API_MOCKING_ENABLED") ?? "false") === "true", | ||
| keycloakUrl: getEnvValue("VITE_KEYCLOAK_URL")!, | ||
| keycloakRealm: getEnvValue("VITE_KEYCLOAK_REALM")!, | ||
| keycloakClientId: getEnvValue("VITE_KEYCLOAK_CLIENT_ID")!, | ||
| crowdinInContextToolingEnabled: | ||
| (getEnvValue("VITE_BITCR_DEV_INCLUDE_CROWDIN_IN_CONTEXT_TOOLING") ?? "false") === "true", | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -58,3 +58,5 @@ void prepare().then(() => { | |
| </StrictMode>, | ||
| ) | ||
| }) | ||
|
|
||
| export { App } | ||
Uh oh!
There was an error while loading. Please reload this page.