Read sentry_dsn from SENTRY_DSN environment variable
What
The runner reads the Sentry DSN from the SENTRY_DSN environment variable when sentry_dsn is not set in config.toml. If both are set, the config file wins.
Why
The DSN was only configurable through config.toml. In containerized deployments the config is often templated or read-only, and operators want to point error reporting at a Sentry project through an environment variable without editing the file.
Why not the env: struct tag
The runner already has an env: annotation, so the obvious question is why this doesn't use it.
That tag isn't a generic "read from the environment" mechanism. It's consumed only by GetFlagsFromStruct in golang-cli-helpers, which turns command structs into urfave/cli flags, and urfave/cli reads the variable. It applies to fields that become command-line flags, like the ones on DockerConfig and RunnerSettings.
SentryDSN lives on the global common.Config, which the TOML decoder populates in LoadConfig. The decoder ignores env: tags, so annotating the field would do nothing. Using the tag would mean adding a --sentry-dsn flag and then merging it back into the loaded config with its own precedence rules against config.toml. That's a larger change and a different feature: a flag plus a variable, rather than environment support for the existing config field.
Reading the variable in LoadConfig keeps precedence explicit and re-evaluates on every config reload. The cost is that the variable doesn't show up in --help the way a flag would.
Notes
The fallback uses os.LookupEnv, so an explicitly empty SENTRY_DSN="" still counts as set. That preserves the nil versus non-nil contract commands/multi.go relies on: an unset variable leaves SentryDSN nil and Sentry stays off, an empty string is a deliberate override.
The value is used as-is and is not expanded. References like $OTHER_VAR inside it are not interpolated.
Replaces !6882 (closed), which was opened from a feature/ branch. That ref name matches the .if-runner-or-security-runner-feature-ref rule in .gitlab/ci/_rules.gitlab-ci.yml, which has no merge request exclusion, so the real release jobs (bleeding edge docker images) ran in the MR pipeline. They failed only because the protected DOCKER_HUB_* credentials aren't available on the MR ref, which happened to stop an unintended release. This branch drops the feature/ prefix.