I've done a bit of a deep dive into how we produce our env. The flow is slightly complicated as some environment comes from the shell that the agent runs in and some comes from buildkite.com and then some is generated each job.
For context, the code that generates the job environment is here: https://github.com/buildkite/agent/blob/2f3d6935996d45877071c01f7b19ae07db302d69/agent/job_runner.go#L307-L426
Paraphrased, the process is:
- A build is triggered on buildkite.com, with user-provided environment (step or pipeline level) and bk specific env
- Write out base job environment to an env file for future reference and set BUILDKITE_ENV_FILE
- The agent job runner merges in it's env (overwriting anything set above, creating "protected" env)