Skip to content

Commit 124a7bd

Browse files
committed
fix: require env vars to be set and fail otherwise
1 parent 6a6d782 commit 124a7bd

8 files changed

Lines changed: 144 additions & 202 deletions

File tree

apps/cow-fi/util/environment.ts

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@ function isEnvironmentName(value: string): value is EnvironmentName {
77
return ALL_ENVIRONMENTS.includes(value as EnvironmentName)
88
}
99

10-
function getConfiguredEnvironmentName(): EnvironmentName | undefined {
10+
function getConfiguredEnvironmentName(): EnvironmentName {
1111
const env = process.env[ENVIRONMENT_VAR_NAME]?.trim().toLowerCase()
1212

1313
if (!env) {
14-
return undefined
14+
throw new Error(`Missing ${ENVIRONMENT_VAR_NAME}. Expected one of: ${ALL_ENVIRONMENTS.join(', ')}`)
1515
}
1616

1717
if (!isEnvironmentName(env)) {
18-
return undefined
18+
throw new Error(`Invalid ${ENVIRONMENT_VAR_NAME}="${env}". Expected one of: ${ALL_ENVIRONMENTS.join(', ')}`)
1919
}
2020

2121
return env
@@ -37,23 +37,10 @@ function getEnvironmentChecks(environmentName: EnvironmentName): EnvironmentChec
3737
}
3838
}
3939

40-
function getDefaultEnvironmentChecks(): EnvironmentChecks {
41-
return {
42-
isLocal: false,
43-
isDev: false,
44-
isPr: false,
45-
isProd: false,
46-
}
47-
}
40+
const configuredEnvironmentName = getConfiguredEnvironmentName()
4841

4942
export function checkEnvironment(): EnvironmentChecks {
50-
const configuredEnvironmentName = getConfiguredEnvironmentName()
51-
52-
if (configuredEnvironmentName) {
53-
return getEnvironmentChecks(configuredEnvironmentName)
54-
}
55-
56-
return getDefaultEnvironmentChecks()
43+
return getEnvironmentChecks(configuredEnvironmentName)
5744
}
5845

5946
const { isLocal, isDev, isPr, isProd } = checkEnvironmentUsingWindow()
@@ -62,7 +49,7 @@ function checkEnvironmentUsingWindow(): EnvironmentChecks {
6249
return checkEnvironment()
6350
}
6451

65-
function getEnvironmentName(): EnvironmentName | undefined {
52+
function getEnvironmentName(): EnvironmentName {
6653
if (isProd) {
6754
return 'production'
6855
} else if (isPr) {
@@ -72,10 +59,10 @@ function getEnvironmentName(): EnvironmentName | undefined {
7259
} else if (isLocal) {
7360
return 'local'
7461
} else {
75-
return undefined
62+
return configuredEnvironmentName
7663
}
7764
}
7865

79-
export const environmentName: EnvironmentName | undefined = getEnvironmentName()
66+
export const environmentName: EnvironmentName = getEnvironmentName()
8067

8168
export { isLocal, isDev, isPr, isProd }
Lines changed: 34 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,69 @@
1-
import { checkEnvironment, EnvironmentChecks } from './environment'
1+
const ENVIRONMENT_VAR_NAME = 'NEXT_PUBLIC_ENVIRONMENT'
2+
const originalEnvironment = process.env[ENVIRONMENT_VAR_NAME]
23

3-
const DEFAULT_ENVIRONMENTS_CHECKS: EnvironmentChecks = {
4+
const DEFAULT_ENVIRONMENTS_CHECKS = {
45
isProd: false,
56
isPr: false,
67
isDev: false,
78
isLocal: false,
89
}
910

1011
describe('Detect environments using configured env var', () => {
11-
const ENVIRONMENT_VAR_NAME = 'NEXT_PUBLIC_ENVIRONMENT'
12-
const originalEnvironment = process.env[ENVIRONMENT_VAR_NAME]
13-
1412
afterEach(() => {
13+
jest.resetModules()
14+
1515
if (typeof originalEnvironment === 'undefined') {
1616
delete process.env[ENVIRONMENT_VAR_NAME]
1717
} else {
1818
process.env[ENVIRONMENT_VAR_NAME] = originalEnvironment
1919
}
2020
})
2121

22-
describe('No configured environment', () => {
23-
it('returns no active envs', () => {
24-
expect(checkEnvironment()).toEqual(DEFAULT_ENVIRONMENTS_CHECKS)
25-
})
26-
})
22+
it('throws when the env var is missing', async () => {
23+
delete process.env[ENVIRONMENT_VAR_NAME]
2724

28-
describe('Is production', () => {
29-
const isProduction = { ...DEFAULT_ENVIRONMENTS_CHECKS, isProd: true }
25+
await expect(import('./environment')).rejects.toThrow(`Missing ${ENVIRONMENT_VAR_NAME}`)
26+
})
3027

31-
it('uses env var override', () => {
32-
process.env[ENVIRONMENT_VAR_NAME] = 'production'
28+
it('throws when the env var is invalid', async () => {
29+
process.env[ENVIRONMENT_VAR_NAME] = 'invalid-environment'
3330

34-
expect(checkEnvironment()).toEqual(isProduction)
35-
})
31+
await expect(import('./environment')).rejects.toThrow(`Invalid ${ENVIRONMENT_VAR_NAME}="invalid-environment"`)
3632
})
3733

38-
describe('Is PR', () => {
39-
const isPr = { ...DEFAULT_ENVIRONMENTS_CHECKS, isPr: true }
34+
it('uses production env var override', async () => {
35+
process.env[ENVIRONMENT_VAR_NAME] = 'production'
4036

41-
it('uses env var override', () => {
42-
process.env[ENVIRONMENT_VAR_NAME] = 'pr'
37+
const { checkEnvironment, environmentName } = await import('./environment')
4338

44-
expect(checkEnvironment()).toEqual(isPr)
45-
})
39+
expect(environmentName).toBe('production')
40+
expect(checkEnvironment()).toEqual({ ...DEFAULT_ENVIRONMENTS_CHECKS, isProd: true })
4641
})
4742

48-
describe('Is Development', () => {
49-
const isDevelopment = { ...DEFAULT_ENVIRONMENTS_CHECKS, isDev: true }
43+
it('uses pr env var override', async () => {
44+
process.env[ENVIRONMENT_VAR_NAME] = 'pr'
5045

51-
it('uses env var override', () => {
52-
process.env[ENVIRONMENT_VAR_NAME] = 'development'
46+
const { checkEnvironment, environmentName } = await import('./environment')
5347

54-
expect(checkEnvironment()).toEqual(isDevelopment)
55-
})
48+
expect(environmentName).toBe('pr')
49+
expect(checkEnvironment()).toEqual({ ...DEFAULT_ENVIRONMENTS_CHECKS, isPr: true })
5650
})
5751

58-
describe('Is Local', () => {
59-
const isLocal = { ...DEFAULT_ENVIRONMENTS_CHECKS, isLocal: true }
52+
it('uses development env var override', async () => {
53+
process.env[ENVIRONMENT_VAR_NAME] = 'development'
54+
55+
const { checkEnvironment, environmentName } = await import('./environment')
6056

61-
it('uses env var override', () => {
62-
process.env[ENVIRONMENT_VAR_NAME] = 'local'
57+
expect(environmentName).toBe('development')
58+
expect(checkEnvironment()).toEqual({ ...DEFAULT_ENVIRONMENTS_CHECKS, isDev: true })
59+
})
6360

64-
expect(checkEnvironment()).toEqual(isLocal)
65-
})
61+
it('uses local env var override', async () => {
62+
process.env[ENVIRONMENT_VAR_NAME] = 'local'
6663

67-
it('returns no active envs when env var is invalid', () => {
68-
process.env[ENVIRONMENT_VAR_NAME] = 'invalid-environment'
64+
const { checkEnvironment, environmentName } = await import('./environment')
6965

70-
expect(checkEnvironment()).toEqual(DEFAULT_ENVIRONMENTS_CHECKS)
71-
})
66+
expect(environmentName).toBe('local')
67+
expect(checkEnvironment()).toEqual({ ...DEFAULT_ENVIRONMENTS_CHECKS, isLocal: true })
7268
})
7369
})

apps/cowswap-frontend/jest.setup.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,12 @@ beforeEach(() => {
6969
__resetGtmInstance()
7070
})
7171

72-
// eslint-disable-next-line @nx/enforce-module-boundaries
73-
import '../../jest.setup'
7472
import { i18n } from '@lingui/core'
7573

74+
process.env.REACT_APP_ENVIRONMENT = 'production'
75+
76+
require('../../jest.setup')
77+
7678
i18n.load('en-US', {})
7779
i18n.activate('en-US')
7880

apps/explorer/src/test/utils/env.test.ts

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,18 @@ describe('explorer env detection', () => {
1212
}
1313
})
1414

15+
it('throws when the env var is missing', async () => {
16+
delete process.env[ENVIRONMENT_VAR_NAME]
17+
18+
await expect(import('../../utils/env')).rejects.toThrow(`Missing ${ENVIRONMENT_VAR_NAME}`)
19+
})
20+
21+
it('throws when the env var is invalid', async () => {
22+
process.env[ENVIRONMENT_VAR_NAME] = 'invalid-environment'
23+
24+
await expect(import('../../utils/env')).rejects.toThrow(`Invalid ${ENVIRONMENT_VAR_NAME}="invalid-environment"`)
25+
})
26+
1527
it('uses the configured environment name', async () => {
1628
process.env[ENVIRONMENT_VAR_NAME] = 'production'
1729

@@ -41,19 +53,4 @@ describe('explorer env detection', () => {
4153
isProd: false,
4254
})
4355
})
44-
45-
it('falls back when the configured environment is invalid', async () => {
46-
process.env[ENVIRONMENT_VAR_NAME] = 'invalid-environment'
47-
48-
const { environmentName, checkEnvironment } = await import('../../utils/env')
49-
50-
expect(environmentName).toBeUndefined()
51-
expect(checkEnvironment()).toEqual({
52-
isLocal: false,
53-
isDev: false,
54-
isPr: false,
55-
isStaging: false,
56-
isProd: false,
57-
})
58-
})
5956
})

apps/explorer/src/utils/env.ts

Lines changed: 11 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,17 @@ function isEnvironmentName(value: string): value is Envs {
1515
return ALL_ENVIRONMENTS.includes(value as Envs)
1616
}
1717

18-
function getConfiguredEnvironmentName(): Envs | undefined {
18+
function getConfiguredEnvironmentName(): Envs {
1919
const env = process.env[EXPLORER_ENVIRONMENT_VAR_NAME]?.trim().toLowerCase()
2020

2121
if (!env) {
22-
return undefined
22+
throw new Error(`Missing ${EXPLORER_ENVIRONMENT_VAR_NAME}. Expected one of: ${ALL_ENVIRONMENTS.join(', ')}`)
2323
}
2424

2525
if (!isEnvironmentName(env)) {
26-
return undefined
26+
throw new Error(
27+
`Invalid ${EXPLORER_ENVIRONMENT_VAR_NAME}="${env}". Expected one of: ${ALL_ENVIRONMENTS.join(', ')}`,
28+
)
2729
}
2830

2931
return env
@@ -39,34 +41,16 @@ function getEnvironmentChecks(environmentName: Envs): EnvsFlags {
3941
}
4042
}
4143

42-
function getDefaultEnvironmentChecks(): EnvsFlags {
43-
return {
44-
isLocal: false,
45-
isDev: false,
46-
isPr: false,
47-
isStaging: false,
48-
isProd: false,
49-
}
50-
}
44+
const configuredEnvironmentName = getConfiguredEnvironmentName()
5145

5246
export function checkEnvironment(): EnvsFlags {
53-
const configuredEnvironmentName = getConfiguredEnvironmentName()
54-
55-
if (configuredEnvironmentName) {
56-
return getEnvironmentChecks(configuredEnvironmentName)
57-
}
58-
59-
return getDefaultEnvironmentChecks()
47+
return getEnvironmentChecks(configuredEnvironmentName)
6048
}
6149

6250
const { isLocal, isDev, isPr, isStaging, isProd } = checkEnvironment()
6351

64-
function getEnvironmentName(): Envs | undefined {
65-
const configuredEnvironmentName = getConfiguredEnvironmentName()
66-
67-
if (configuredEnvironmentName) {
68-
return configuredEnvironmentName
69-
} else if (isProd) {
52+
function getEnvironmentName(): Envs {
53+
if (isProd) {
7054
return 'production'
7155
} else if (isStaging) {
7256
return 'staging'
@@ -77,10 +61,10 @@ function getEnvironmentName(): Envs | undefined {
7761
} else if (isLocal) {
7862
return 'local'
7963
} else {
80-
return undefined
64+
return configuredEnvironmentName
8165
}
8266
}
8367

84-
export const environmentName: Envs | undefined = getEnvironmentName()
68+
export const environmentName: Envs = getEnvironmentName()
8569

8670
export { isLocal, isDev, isPr, isStaging, isProd }

jest.setup.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ import { TextDecoder, TextEncoder } from 'util'
77

88
// For simplicity, we will use CowSwap default .env for all projects and libs
99
config({ path: __dirname + '/apps/cowswap-frontend/.env' })
10+
config({ path: __dirname + '/apps/cowswap-frontend/.env.development' })
11+
config({ path: __dirname + '/apps/explorer/.env' })
12+
config({ path: __dirname + '/apps/cow-fi/.env' })
1013

1114
if (typeof global.TextEncoder === 'undefined') {
1215
global.ReadableStream = Readable as unknown as typeof globalThis.ReadableStream

0 commit comments

Comments
 (0)