Environments and configuration

The generation chain

Configuration is generated, not hand-written. Four files are involved:

deployment/setenv.sh                      ← you run this
  │  ENV_NAME [DOMAIN] [CONFIG_DIR] [ENV_FILE_NAME]
  │
  ├─ if ENV_NAME == "release":
  │     THIS_NODE_ENV=production
  │     source deployment/setup_template-production.cfg
  │  else:
  │     GCI = python3 deployment/generate_gci.py     (from ENV_NAME)
  │     THIS_NODE_ENV = staging | development
  │     source deployment/setup_template-dev.cfg
  │
  ├─ ts-node deployment/set-env.ts
  │     reads the exported FRONTEND_* / MARKETPLACE_API / EVA_API / … variables
  │     + `git rev-parse HEAD`
  │     writes src/environments/environment.{prod|test}.ts
  │     writes deployment/lastPath.txt  (LAST_PATH=<that file>)
  │
  ├─ source deployment/lastPath.txt
  ├─ cp $LAST_PATH src/environments/$ENV_FILE_NAME.ts    (default: environment.ts)
  └─ echo "$ENV_NAME" > deployment/lastEnv.txt           (read by checkenv.sh)

src/environments/environment.ts is generated output. Hand-editing it works locally and is then silently overwritten by the next setenv.sh run — and never takes effect in CI, which regenerates from the templates. All the environment files are `.gitignore`d:

/src/environments/environment.test.ts
/src/environments/environment.ts
/src/environments/environment.production.ts
/src/environments/environment.prod.ts
/src/environments/environment.with-eva.ts

Only environment.interface.ts is tracked.

setenv.sh arguments

Argument Default Meaning

ENV_NAME

required

The environment name, e.g. exp, staging, apps, or release for production.

DOMAIN

dev.payability.com

Top-level domain for integration URLs. Use payability.com for production.

CONFIG_DIR

$PWD/deployment

Where the .cfg templates live.

ENV_FILE_NAME

environment

Which file under src/environments to write (without extension). npm run prepare-env-with-eva passes environment.with-eva.

It is a bash script with positional arguments — you cannot supply the third without the second. The script’s own message puts it plainly: "bash is too silly for this".

ENV_NAME also decides which template is sourced and what envName ends up as:

ENV_NAME Template THIS_NODE_ENV generated envName

release

setup_template-production.cfg

production

prod

staging

setup_template-dev.cfg

staging

test

anything else

setup_template-dev.cfg

development

test

Only release produces envName: 'prod'. Everything else — including staging — produces 'test'. Code that branches on envName (the theme colour option, the stage banner, showStoreDebugger) therefore treats staging as a test environment.

environment.production.ts in a checkout can show production: true alongside envName: 'test', because production is derived from the template while envName comes from the same release-or-not check at a different point. Read deployment/set-env.ts rather than inferring.

The GCI

For non-release environments setenv.sh computes a GCI — a workflow correlation id for the B2B task manager:

# deployment/generate_gci.py
sha1 = hashlib.sha1(env_name.encode())
decimals = re.findall('\d', sha1.hexdigest())[:6]
print(int(''.join(decimals)))

It takes the first six digits of the SHA-1 hex digest of ENV_NAME, so each environment name maps deterministically to a number. Production hardcodes FRONTEND_TASK_MANAGER_GCI=1.

This is why Python 3 is a build prerequisite. See real-time streams for what the GCI is used for.

The templates

Two live templates plus one dead one.

setup_template-dev.cfg

FRONTEND_SUPPLIER_URL="https://${ENV_NAME}-supplier.${DOMAIN}"
FRONTEND_TASK_MANAGER="https://${ENV_NAME}.${DOMAIN}/b2btaskmanager"
FRONTEND_TASK_MANAGER_GCI="${GCI}"
FRONTEND_INTERNAL_URL="https://${ENV_NAME}.${DOMAIN}/database_v2"
FRONTEND_INTERNAL_API_URL="/internal"

FRONTEND_ACTION_TOOLS_API_URL="https://vn1s47jzkd.execute-api.us-east-1.amazonaws.com/stage"
FRONTEND_B2B_DOCUMENTS_API_URL="https://${ENV_NAME}.${DOMAIN}/b2bdocument"
FRONTEND_PYB_DOCUMENTS_API_URL="https://${ENV_NAME}.${DOMAIN}/pyb-documents"
FRONTEND_DOCUMENTS_API_URL="https://ajwk785vec.execute-api.us-east-1.amazonaws.com/dev"
FRONTEND_EVENT_LOGGER_API_URL="https://epu44ua492.execute-api.us-east-1.amazonaws.com/test_rules2"
FRONTEND_EVENT_RULES_OLD_API_URL="https://drkrpkdxe4.execute-api.us-east-1.amazonaws.com/stage"
FRONTEND_MESSAGING_API_URL="https://argumexh08.execute-api.us-east-1.amazonaws.com/stage"
FRONTEND_AUTH_TOOLS_API_URL="https://0vd7bg72e4.execute-api.us-east-1.amazonaws.com/stage"
FRONTEND_REFERRAL_URL="https://go-test.payability.com"
FRONTEND_SEGMENTIO_CODE="qQtIVnv1eERcK7bsQs8ITA48ak8kzxA3"
NODE_ENV=dev

MARKETPLACE_API="https://marketplace-api-dev2.payability.dev"
AUTOMATIC_UW_TOOL_API="https://${ENV_NAME}.${DOMAIN}/automatic-uw-tool-web-api"
S3_FILES_PROVIDER_API="https://${ENV_NAME}.${DOMAIN}/s3-files-provider"
EVA_API="https://${ENV_NAME}-eva-fe.${DOMAIN}"

Note the pattern: services hosted per environment interpolate ${ENV_NAME}.${DOMAIN}; shared Lambda-backed services are fixed API Gateway URLs and are therefore shared across every dev environment.

setup_template-production.cfg

FRONTEND_SUPPLIER_URL="https://supplier.payability.com"
FRONTEND_TASK_MANAGER="https://workflow.payability.com/b2btaskmanager"
FRONTEND_TASK_MANAGER_GCI=1
FRONTEND_INTERNAL_URL="https://dbapi-internal.payability.com"
FRONTEND_INTERNAL_API_URL="/bin/internal"

FRONTEND_ACTION_TOOLS_API_URL="https://api.payability.com/actionstools"
FRONTEND_B2B_DOCUMENTS_API_URL="https://doc-api.payability.com/b2bdocument"
FRONTEND_PYB_DOCUMENTS_API_URL="https://pyb-documents.payability.com"
FRONTEND_DOCUMENTS_API_URL="https://api.payability.com/documents"
FRONTEND_EVENT_LOGGER_API_URL="https://api.payability.com/eventlogger_v2"
FRONTEND_EVENT_RULES_OLD_API_URL="https://api.payability.com/eventlogger"
FRONTEND_MESSAGING_API_URL="https://api.payability.com/messaging"
FRONTEND_AUTH_TOOLS_API_URL="https://api.payability.com/authtools"
FRONTEND_REFERRAL_URL="https://goto.payability.com"
FRONTEND_SEGMENTIO_CODE="J8O1DVU1ToteAHLWlfsqlD2uosWt5zhA"
NODE_ENV=prod

MARKETPLACE_API="https://marketplace-api.payability.com"
AUTOMATIC_UW_TOOL_API="https://automatic-uw-tool.payability.com"
S3_FILES_PROVIDER_API="https://automatic…"   # see the file
EVA_API="https://prod-eva-fe.payability.com"

Structural differences from dev:

  • The internal API base path is /bin/internal, not /internal, and the host is dbapi-internal.payability.com rather than <env>.<domain>/database_v2.

  • The event logger is eventlogger_v2 on production while eventRulesOldUrl is eventlogger — the two generations are clearly separated there, and less so on dev.

  • A different Segment write key.

setup_template-legacy.cfg

Not sourced by setenv.sh. It references test2-api.payability.com, workflow-test, b2btaskmanager2 and a hardcoded GCI=12 — an older environment shape. Dead file; do not use it as a reference for current URLs.

Values set-env.ts adds itself

Not from the templates:

Key Source

production

environment === 'prod'.

envName

'prod' or 'test'.

VERSION

require('../../package.json').version — evaluated at runtime in the browser, not baked in as a literal.

googleAuthClientId

Hardcoded in set-env.ts — the same client id for every environment.

sentryUrl

Also hardcoded, and unused by any code. See observability.

instanceId

Hardcoded 8001.

supplierSiteVersions

A hardcoded array: c2fo, morecommerce, neweggcapital, neweggcapital2, onboarding, onboarding1, supplier, supplier2, supplier3, supplier4, volusion.

displayErrorsInConsole / displayErrorsPopup

Hardcoded true / false.

featureFlags

process.env.FEATURE_FLAGS or ''. Neither template exports it, so build flags are empty unless the pipeline sets it. Flags are managed through global settings instead — see feature flags.

gitHash

git rev-parse HEAD, first 40 characters.

lastBuildDate

new Date().toISOString().substr(0, 16) — ISO to the minute.

domain

process.env.DOMAIN. Used for the EVA cookie and iframe origin checks.

The double mechanism for production builds

Two things swap the environment file, and knowing both avoids confusion:

  1. setenv.sh copies the generated file over src/environments/environment.ts.

  2. angular.json’s `production configuration also declares a file replacement:

    "fileReplacements": [
      { "replace": "src/environments/environment.ts", "with": "src/environments/environment.prod.ts" }
    ]

So a production build reads environment.prod.ts — which set-env.ts wrote — while environment.ts holds a copy of the same content. The staging and development configurations declare no replacement and use environment.ts directly.

IEnvironment

src/environments/environment.interface.ts is the tracked contract — 33 keys. The full table with dev and production values is in environment configuration keys; what each backend is for is in the integration landscape.

Reading configuration in code

Always through AppConfigService:

constructor(private appConfig: AppConfigService, private http: HttpClient) {
  this.apiUrl = appConfig.env.internalApiUrl;
}

AppConfigService is provided by factory in AppModule:

export const initAppConfig = () =>
  new AppConfigService('0.0.1', themeConfigData(environment.envName), environment);

The '0.0.1' version argument is vestigial — the user-visible version comes from environment.VERSION via VersionService, not from AppConfigService.version.

A handful of services import environment directly instead — EventsService, EvaService, LookupRepository, PendingReviewService, FeatureFlagsService, GatewayService, MarketplaceRepository, VpnTesterService, CommunicationService, VersionService. New code should take AppConfigService.

Adding a configuration key

All four steps, or it breaks in CI while working locally:

  1. Add the key to src/environments/environment.interface.ts.

  2. Export the shell variable in both deployment/setup_template-dev.cfg and deployment/setup_template-production.cfg.

  3. Add the field to the generated template literal in deployment/set-env.ts.

  4. Re-run ./deployment/setenv.sh <env> locally to regenerate.

Then read it through AppConfigService, and document it in the environment reference and the integration landscape.

Skipping step 2 or 3 produces the classic failure: the key is populated in your local file (because you edited it) and undefined in every deployed build. TypeScript will not catch it — the interface is satisfied by the generated file, which simply omits the property.

Checking which environment you are on

./deployment/checkenv.sh      # prints deployment/lastEnv.txt

At runtime, shared/layout/app-stage-info/ is designed to show the environment, connected backend, git hash and build date — but is currently disabled (getStageName() returns of('')). The sidebar footer still shows front-end and back-end versions. See versions.