@htekdev/actions-debugger 1.0.76 → 1.0.77
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/errors/runner-environment/ubuntu-24-glibc-binary-incompatibility.yml +117 -0
- package/errors/silent-failures/inputs-context-empty-on-non-dispatch-event.yml +96 -0
- package/errors/silent-failures/vars-context-undefined-variable-empty-string.yml +84 -0
- package/errors/yaml-syntax/env-variable-name-dot-expression-subproperty-access.yml +101 -0
- package/package.json +1 -1
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
id: runner-environment-139
|
|
2
|
+
title: "Binaries Built on ubuntu-24.04 Require GLIBC 2.39 — Fail to Execute on ubuntu-22.04 Targets"
|
|
3
|
+
category: runner-environment
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- ubuntu-24.04
|
|
7
|
+
- ubuntu-latest
|
|
8
|
+
- glibc
|
|
9
|
+
- binary-compatibility
|
|
10
|
+
- linker
|
|
11
|
+
- distribution
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'version ''GLIBC_2\.3[6-9]'' not found'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
- regex: 'libc\.so\.[0-9]+: version ''GLIBC_'
|
|
16
|
+
flags: 'i'
|
|
17
|
+
- regex: 'GLIBC_2\.3[6-9] not found \(required by'
|
|
18
|
+
flags: 'i'
|
|
19
|
+
error_messages:
|
|
20
|
+
- "/lib/x86_64-linux-gnu/libc.so.6: version 'GLIBC_2.38' not found (required by ./myapp)"
|
|
21
|
+
- "/lib/x86_64-linux-gnu/libc.so.6: version 'GLIBC_2.39' not found (required by ./myapp)"
|
|
22
|
+
- "GLIBC_2.39 not found (required by /usr/local/bin/app)"
|
|
23
|
+
- "version 'GLIBC_2.38' not found"
|
|
24
|
+
root_cause: |
|
|
25
|
+
GitHub Actions ubuntu-24.04 runners ship with GLIBC 2.39 (GNU C Library). When a
|
|
26
|
+
binary is compiled or dynamically linked on ubuntu-24.04, the resulting ELF binary
|
|
27
|
+
records minimum GLIBC version requirements based on the newest symbol version used —
|
|
28
|
+
either directly or via a linked shared library. If any symbol first introduced in
|
|
29
|
+
GLIBC 2.36–2.39 is referenced, the binary will fail to load on older systems.
|
|
30
|
+
|
|
31
|
+
GLIBC versions by runner image:
|
|
32
|
+
- ubuntu-20.04: GLIBC 2.31
|
|
33
|
+
- ubuntu-22.04: GLIBC 2.35
|
|
34
|
+
- ubuntu-24.04: GLIBC 2.39
|
|
35
|
+
|
|
36
|
+
This affects workflows that:
|
|
37
|
+
1. Build release binaries on ubuntu-latest (which resolved to ubuntu-24.04 in mid-2025)
|
|
38
|
+
and distribute them for Linux users on ubuntu-22.04 or older.
|
|
39
|
+
2. Produce binaries in one job on ubuntu-24.04 and run them in a matrix job on
|
|
40
|
+
ubuntu-22.04 — the artifact fails to execute in the older-runner job.
|
|
41
|
+
3. Build Docker images using ubuntu-24.04 as the build stage but target a
|
|
42
|
+
FROM ubuntu:22.04 or FROM debian:bookworm base image in the runtime stage.
|
|
43
|
+
|
|
44
|
+
The error only surfaces at execution time, not at compile or link time. The
|
|
45
|
+
binary appears healthy; only when it is actually run on the older system does
|
|
46
|
+
the dynamic linker report the missing GLIBC version.
|
|
47
|
+
fix: |
|
|
48
|
+
Option 1 — Pin the build runner to the oldest supported OS:
|
|
49
|
+
Use ubuntu-22.04 as the build runner to ensure binaries link against GLIBC 2.35,
|
|
50
|
+
which is compatible with both ubuntu-22.04 and ubuntu-24.04 targets.
|
|
51
|
+
|
|
52
|
+
Option 2 — Build inside a container matching the target OS:
|
|
53
|
+
Use a Docker container (e.g., ubuntu:22.04) in the build job so the compiler
|
|
54
|
+
links against the correct GLIBC version regardless of the host runner.
|
|
55
|
+
|
|
56
|
+
Option 3 — Cross-compile with musl-libc (static, no GLIBC dependency):
|
|
57
|
+
For Rust, use the x86_64-unknown-linux-musl target. For C/C++, use musl-gcc or
|
|
58
|
+
musl-cross. The resulting binary has no GLIBC dependency and runs anywhere.
|
|
59
|
+
|
|
60
|
+
Option 4 — Verify GLIBC requirement in CI:
|
|
61
|
+
Add a validation step that runs objdump or readelf to assert the binary's
|
|
62
|
+
minimum GLIBC requirement before it reaches distribution.
|
|
63
|
+
fix_code:
|
|
64
|
+
- language: yaml
|
|
65
|
+
label: "Pin build runner to ubuntu-22.04 for GLIBC 2.35 compatibility"
|
|
66
|
+
code: |
|
|
67
|
+
jobs:
|
|
68
|
+
build:
|
|
69
|
+
runs-on: ubuntu-22.04 # GLIBC 2.35 — compatible with ubuntu-22.04 targets
|
|
70
|
+
steps:
|
|
71
|
+
- uses: actions/checkout@v4
|
|
72
|
+
- name: Build binary
|
|
73
|
+
run: cargo build --release
|
|
74
|
+
- uses: actions/upload-artifact@v4
|
|
75
|
+
with:
|
|
76
|
+
name: my-binary
|
|
77
|
+
path: target/release/myapp
|
|
78
|
+
- language: yaml
|
|
79
|
+
label: "Build inside a Docker container targeting ubuntu:22.04"
|
|
80
|
+
code: |
|
|
81
|
+
jobs:
|
|
82
|
+
build:
|
|
83
|
+
runs-on: ubuntu-latest
|
|
84
|
+
container:
|
|
85
|
+
image: ubuntu:22.04
|
|
86
|
+
steps:
|
|
87
|
+
- name: Install build tools
|
|
88
|
+
run: |
|
|
89
|
+
apt-get update
|
|
90
|
+
apt-get install -y build-essential curl
|
|
91
|
+
- uses: actions/checkout@v4
|
|
92
|
+
- name: Build binary
|
|
93
|
+
run: make release
|
|
94
|
+
- language: yaml
|
|
95
|
+
label: "Rust: build static binary with musl (no GLIBC dependency)"
|
|
96
|
+
code: |
|
|
97
|
+
jobs:
|
|
98
|
+
build:
|
|
99
|
+
runs-on: ubuntu-latest
|
|
100
|
+
steps:
|
|
101
|
+
- uses: actions/checkout@v4
|
|
102
|
+
- name: Add musl target
|
|
103
|
+
run: rustup target add x86_64-unknown-linux-musl
|
|
104
|
+
- name: Build static binary
|
|
105
|
+
run: cargo build --release --target x86_64-unknown-linux-musl
|
|
106
|
+
prevention:
|
|
107
|
+
- "Pin the build runner to the oldest Linux OS your users run when distributing compiled binaries."
|
|
108
|
+
- "Test binary artifacts in a matrix that includes the oldest supported runner (ubuntu-22.04) as a separate job."
|
|
109
|
+
- "Use `objdump -T myapp | grep -o 'GLIBC_[0-9.]*' | sort -V | tail -1` to check the minimum GLIBC version before release."
|
|
110
|
+
- "For maximum portability, use musl-libc or static linking to eliminate the GLIBC runtime dependency entirely."
|
|
111
|
+
docs:
|
|
112
|
+
- url: "https://docs.github.com/en/actions/using-github-hosted-runners/using-github-hosted-runners/about-github-hosted-runners"
|
|
113
|
+
label: "GitHub-hosted runners: available images"
|
|
114
|
+
- url: "https://wiki.ubuntu.com/Releases"
|
|
115
|
+
label: "Ubuntu release versions and GLIBC versions"
|
|
116
|
+
- url: "https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2404-Readme.md"
|
|
117
|
+
label: "ubuntu-24.04 runner image readme"
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
id: silent-failures-072
|
|
2
|
+
title: "inputs Context Empty on Non-Dispatch Events — Multi-Trigger Workflows Leave inputs.* as Empty String"
|
|
3
|
+
category: silent-failures
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- inputs
|
|
7
|
+
- workflow-dispatch
|
|
8
|
+
- push
|
|
9
|
+
- multi-trigger
|
|
10
|
+
- context
|
|
11
|
+
- empty-string
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'inputs\.[a-zA-Z_][a-zA-Z0-9_-]* (?:is empty|not set|undefined|was null)'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
error_messages:
|
|
16
|
+
- "Deploy target is empty — expected inputs.environment to be non-empty"
|
|
17
|
+
- "Error: Input required and not supplied: environment"
|
|
18
|
+
root_cause: |
|
|
19
|
+
The `inputs` context in GitHub Actions is ONLY populated when a workflow is triggered
|
|
20
|
+
by a `workflow_dispatch` or `workflow_call` event. When the same workflow is triggered
|
|
21
|
+
by any other event — push, pull_request, schedule, release, workflow_run, etc. — the
|
|
22
|
+
entire `inputs` context is empty. Every `${{ inputs.anything }}` expression evaluates
|
|
23
|
+
to `""` (empty string) without raising an error or printing a warning.
|
|
24
|
+
|
|
25
|
+
This commonly affects developers who:
|
|
26
|
+
1. Add `on: workflow_dispatch` with inputs to an existing push-triggered workflow
|
|
27
|
+
to enable manual override capability.
|
|
28
|
+
2. Reference `${{ inputs.environment }}` or `${{ inputs.deploy_target }}` in `run:`
|
|
29
|
+
steps, `with:` parameters, or `env:` blocks — expecting a sensible default when
|
|
30
|
+
triggered automatically by push.
|
|
31
|
+
3. Use `if: inputs.skip_tests == 'true'` — on push events this is effectively
|
|
32
|
+
`if: '' == 'true'` which evaluates to false silently.
|
|
33
|
+
4. Pass `${{ inputs.version }}` to an action's `with: version:` parameter — the
|
|
34
|
+
action receives an empty string and may silently use its own default or fail
|
|
35
|
+
with an opaque "invalid version" message.
|
|
36
|
+
|
|
37
|
+
The `inputs` context is distinct from `github.event.inputs` (legacy alias, same
|
|
38
|
+
empty behavior on non-dispatch events) and from the `vars` context (org/repo
|
|
39
|
+
configuration variables which are always populated).
|
|
40
|
+
fix: |
|
|
41
|
+
Use the `||` fallback operator to provide defaults for all inputs in workflows
|
|
42
|
+
that support multiple triggers. This guarantees non-dispatch triggers receive
|
|
43
|
+
a sensible default instead of an empty string.
|
|
44
|
+
|
|
45
|
+
Guard dispatch-only logic blocks with `if: github.event_name == 'workflow_dispatch'`
|
|
46
|
+
to explicitly skip input-dependent steps on automated trigger runs.
|
|
47
|
+
fix_code:
|
|
48
|
+
- language: yaml
|
|
49
|
+
label: "Provide fallback defaults for all inputs in multi-event workflows"
|
|
50
|
+
code: |
|
|
51
|
+
on:
|
|
52
|
+
push:
|
|
53
|
+
branches: [main]
|
|
54
|
+
workflow_dispatch:
|
|
55
|
+
inputs:
|
|
56
|
+
environment:
|
|
57
|
+
type: choice
|
|
58
|
+
options: [staging, production]
|
|
59
|
+
default: staging
|
|
60
|
+
skip_tests:
|
|
61
|
+
type: boolean
|
|
62
|
+
default: false
|
|
63
|
+
|
|
64
|
+
jobs:
|
|
65
|
+
deploy:
|
|
66
|
+
runs-on: ubuntu-latest
|
|
67
|
+
env:
|
|
68
|
+
# Use || to fall back to defaults when triggered by push
|
|
69
|
+
DEPLOY_ENV: ${{ inputs.environment || 'staging' }}
|
|
70
|
+
SKIP_TESTS: ${{ inputs.skip_tests || 'false' }}
|
|
71
|
+
steps:
|
|
72
|
+
- name: Deploy
|
|
73
|
+
run: echo "Deploying to $DEPLOY_ENV (skip_tests=$SKIP_TESTS)"
|
|
74
|
+
- language: yaml
|
|
75
|
+
label: "Guard input-dependent logic with event_name check"
|
|
76
|
+
code: |
|
|
77
|
+
steps:
|
|
78
|
+
- name: Apply manual override (dispatch only)
|
|
79
|
+
if: github.event_name == 'workflow_dispatch'
|
|
80
|
+
run: echo "Manual dispatch with environment=${{ inputs.environment }}"
|
|
81
|
+
|
|
82
|
+
- name: Default automated deploy
|
|
83
|
+
if: github.event_name != 'workflow_dispatch'
|
|
84
|
+
run: echo "Automated push deploy to staging"
|
|
85
|
+
prevention:
|
|
86
|
+
- "Always use `${{ inputs.param || 'default' }}` when a workflow supports more than one trigger — never assume inputs will be populated."
|
|
87
|
+
- "Log `github.event_name` at the start of the workflow to trace which trigger fired and aid debugging of empty-input behavior."
|
|
88
|
+
- "Use the 'Context availability' table in GitHub Actions documentation to verify which contexts are populated for each event type before referencing them."
|
|
89
|
+
- "Consider splitting dispatch workflows (with inputs) from automated trigger workflows — combining both in one file with shared input-referencing code is a common source of subtle bugs."
|
|
90
|
+
docs:
|
|
91
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/contexts#inputs-context"
|
|
92
|
+
label: "GitHub Actions: inputs context"
|
|
93
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#workflow_dispatch"
|
|
94
|
+
label: "workflow_dispatch event"
|
|
95
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/contexts#context-availability"
|
|
96
|
+
label: "Context availability by event type"
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
id: silent-failures-073
|
|
2
|
+
title: "vars. Context Returns Empty String for Undefined Organization or Repository Variables — No Error Raised"
|
|
3
|
+
category: silent-failures
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- vars
|
|
7
|
+
- configuration-variables
|
|
8
|
+
- context
|
|
9
|
+
- empty-string
|
|
10
|
+
- organization
|
|
11
|
+
- repository
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'vars\.[A-Z_][A-Z0-9_]* (?:is empty|not set|missing|undefined|not defined)'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
error_messages:
|
|
16
|
+
- "Error: REGISTRY_URL is empty — check that vars.REGISTRY_URL is defined in repository variables"
|
|
17
|
+
- "Error: docker tag '' is not valid"
|
|
18
|
+
- "fatal: repository '' does not exist"
|
|
19
|
+
root_cause: |
|
|
20
|
+
GitHub Actions configuration variables (the `vars` context, introduced 2023) provide a
|
|
21
|
+
non-secret key/value store at the organization, repository, or environment level. When
|
|
22
|
+
a `${{ vars.MY_VARIABLE }}` expression references a variable that has not been defined
|
|
23
|
+
at any applicable scope, the expression evaluates to `""` (empty string) without
|
|
24
|
+
raising any error, warning, or annotation in the Actions run UI.
|
|
25
|
+
|
|
26
|
+
Unlike missing secrets (which also silently return empty string), `vars` variables are
|
|
27
|
+
user-visible and frequently forgotten when:
|
|
28
|
+
1. A workflow is used in a new repository that hasn't had its variables configured.
|
|
29
|
+
2. A variable is defined at the repository level but the job targets an environment
|
|
30
|
+
that doesn't inherit repo-level variables (environment-scoped variables override
|
|
31
|
+
but do not merge with repo-scoped variables of the same name).
|
|
32
|
+
3. A variable is defined only at the organization level but the repository is
|
|
33
|
+
a fork — forked repositories cannot access parent org variables.
|
|
34
|
+
4. The variable name has a typo in the workflow file.
|
|
35
|
+
|
|
36
|
+
Silent empty values cascade into confusing downstream errors: Docker tags become `":latest"`
|
|
37
|
+
instead of `"ghcr.io/org/image:latest"`, API endpoints receive empty base URLs, and
|
|
38
|
+
notification webhooks silently fail with HTTP 400.
|
|
39
|
+
|
|
40
|
+
Environment-scoped variables are ONLY available when the job specifies `environment:`.
|
|
41
|
+
If a job has no `environment:` key, env-scoped variables are not available even if
|
|
42
|
+
the env-scoped variable has the same name as a repo-scoped variable.
|
|
43
|
+
fix: |
|
|
44
|
+
Use the `||` fallback operator to provide safe defaults, and add an explicit
|
|
45
|
+
validation step for variables that are required for the workflow to succeed.
|
|
46
|
+
fix_code:
|
|
47
|
+
- language: yaml
|
|
48
|
+
label: "Use || fallback for optional vars"
|
|
49
|
+
code: |
|
|
50
|
+
env:
|
|
51
|
+
REGISTRY: ${{ vars.REGISTRY_URL || 'ghcr.io' }}
|
|
52
|
+
LOG_LEVEL: ${{ vars.LOG_LEVEL || 'info' }}
|
|
53
|
+
API_BASE: ${{ vars.API_BASE_URL || 'https://api.example.com' }}
|
|
54
|
+
- language: yaml
|
|
55
|
+
label: "Validate required vars at workflow start with fail-fast"
|
|
56
|
+
code: |
|
|
57
|
+
jobs:
|
|
58
|
+
preflight:
|
|
59
|
+
runs-on: ubuntu-latest
|
|
60
|
+
steps:
|
|
61
|
+
- name: Validate required configuration variables
|
|
62
|
+
env:
|
|
63
|
+
REGISTRY_URL: ${{ vars.REGISTRY_URL }}
|
|
64
|
+
DEPLOY_ENV: ${{ vars.DEPLOY_ENV }}
|
|
65
|
+
run: |
|
|
66
|
+
missing=()
|
|
67
|
+
[ -z "$REGISTRY_URL" ] && missing+=("vars.REGISTRY_URL")
|
|
68
|
+
[ -z "$DEPLOY_ENV" ] && missing+=("vars.DEPLOY_ENV")
|
|
69
|
+
if [ ${#missing[@]} -gt 0 ]; then
|
|
70
|
+
echo "::error::Missing required variables: ${missing[*]}"
|
|
71
|
+
echo "Set them at: Settings → Secrets and Variables → Variables"
|
|
72
|
+
exit 1
|
|
73
|
+
fi
|
|
74
|
+
prevention:
|
|
75
|
+
- "Document all required `vars.*` variables in the repository README or CONTRIBUTING guide — unlike required inputs, they have no built-in validation."
|
|
76
|
+
- "Add a preflight validation job that checks for required variables and fails immediately with a clear error message."
|
|
77
|
+
- "Use `${{ vars.SETTING || 'sensible-default' }}` consistently to make empty-variable behavior explicit."
|
|
78
|
+
- "For environment-scoped variables, ensure the job specifies `environment:` — variables defined only on an environment are not available to jobs that omit the environment key."
|
|
79
|
+
- "Use repository variable naming conventions (UPPER_SNAKE_CASE) and check for typos in workflow YAML against the variables defined in Settings."
|
|
80
|
+
docs:
|
|
81
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/store-information-in-variables"
|
|
82
|
+
label: "GitHub Actions: configuration variables (vars context)"
|
|
83
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/contexts#vars-context"
|
|
84
|
+
label: "vars context reference"
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
id: yaml-syntax-049
|
|
2
|
+
title: "env Variable Name with Dots — ${{ env.MY.VAR }} Silently Reads Undefined Subproperty"
|
|
3
|
+
category: yaml-syntax
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- env-context
|
|
7
|
+
- expression-syntax
|
|
8
|
+
- dots
|
|
9
|
+
- property-access
|
|
10
|
+
- empty-string
|
|
11
|
+
- naming
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: '\$\{\{\s*env\.[A-Za-z_][A-Za-z0-9_]*\.[A-Za-z_]'
|
|
14
|
+
flags: ''
|
|
15
|
+
- regex: '\$\{\{\s*vars\.[A-Za-z_][A-Za-z0-9_]*\.[A-Za-z_]'
|
|
16
|
+
flags: ''
|
|
17
|
+
error_messages:
|
|
18
|
+
- "Error: Expected non-empty value but got '' (env.MY.VAR resolved to empty string)"
|
|
19
|
+
- "::error::Variable 'app.version' is empty — check expression syntax"
|
|
20
|
+
root_cause: |
|
|
21
|
+
GitHub Actions expression syntax uses the dot (`.`) character as a property accessor
|
|
22
|
+
for traversing nested context objects. When an environment variable name contains a
|
|
23
|
+
dot (e.g., `MY.VAR`, `app.version`, `node.options`), the expression `${{ env.MY.VAR }}`
|
|
24
|
+
is evaluated by the expression parser as two sequential property dereferences:
|
|
25
|
+
|
|
26
|
+
1. `env.MY` — look up the property named exactly `MY` on the env object
|
|
27
|
+
2. `.VAR` — look up the property named `VAR` on the result of step 1
|
|
28
|
+
|
|
29
|
+
If no environment variable named exactly `MY` exists (only `MY.VAR` does), then
|
|
30
|
+
`env.MY` returns `""` (empty string), and the entire expression silently evaluates
|
|
31
|
+
to `""` with no error or warning.
|
|
32
|
+
|
|
33
|
+
This is legal YAML — `env: MY.VAR: 'hello'` is valid YAML syntax and DOES set the
|
|
34
|
+
shell environment variable `MY.VAR` in `run:` steps. Linux and macOS allow dots in
|
|
35
|
+
env var names. The problem is exclusively in the expression layer: `${{ env.MY.VAR }}`
|
|
36
|
+
cannot reach the variable because `.` is an operator, not part of the identifier.
|
|
37
|
+
|
|
38
|
+
The same issue affects the `vars` context: `${{ vars.app.version }}` attempts to
|
|
39
|
+
read a sub-property `version` of a variable named `app`, not a variable named
|
|
40
|
+
`app.version`.
|
|
41
|
+
|
|
42
|
+
actionlint does not currently warn on dotted env/vars expressions because the
|
|
43
|
+
expression syntax is technically valid — it just resolves to empty string at runtime.
|
|
44
|
+
fix: |
|
|
45
|
+
Option 1 — Rename environment variables to use underscores instead of dots (recommended):
|
|
46
|
+
Change `MY.VAR` to `MY_VAR`. This is the POSIX-conventional approach and works
|
|
47
|
+
reliably with all GitHub Actions expression syntax.
|
|
48
|
+
|
|
49
|
+
Option 2 — Use bracket property accessor syntax:
|
|
50
|
+
`${{ env['MY.VAR'] }}` uses the array/bracket notation which treats the entire
|
|
51
|
+
string `MY.VAR` as a single property key, correctly reading the dotted variable.
|
|
52
|
+
|
|
53
|
+
Option 3 — Access the variable via shell syntax in run: steps:
|
|
54
|
+
In `run:` steps the shell receives the full env var with its dotted name. Use
|
|
55
|
+
the shell variable reference directly in shell scripts, not in expressions.
|
|
56
|
+
fix_code:
|
|
57
|
+
- language: yaml
|
|
58
|
+
label: "Rename to underscores (recommended)"
|
|
59
|
+
code: |
|
|
60
|
+
env:
|
|
61
|
+
MY_VAR: 'hello' # was MY.VAR — underscore works in expressions
|
|
62
|
+
APP_VERSION: '1.2.3' # was app.version
|
|
63
|
+
|
|
64
|
+
steps:
|
|
65
|
+
- name: Read variable
|
|
66
|
+
run: echo "Version is ${{ env.APP_VERSION }}"
|
|
67
|
+
- language: yaml
|
|
68
|
+
label: "Bracket syntax for dotted env var names"
|
|
69
|
+
code: |
|
|
70
|
+
env:
|
|
71
|
+
MY.VAR: 'hello' # dotted name is set in the shell env
|
|
72
|
+
|
|
73
|
+
steps:
|
|
74
|
+
- name: Read via bracket syntax
|
|
75
|
+
run: echo "Value is ${{ env['MY.VAR'] }}"
|
|
76
|
+
# BAD: ${{ env.MY.VAR }} → silently reads undefined subproperty
|
|
77
|
+
# GOOD: ${{ env['MY.VAR'] }} → reads the full dotted variable name
|
|
78
|
+
- language: yaml
|
|
79
|
+
label: "Access dotted env var directly in shell (run: steps only)"
|
|
80
|
+
code: |
|
|
81
|
+
env:
|
|
82
|
+
MY.VAR: 'hello'
|
|
83
|
+
|
|
84
|
+
steps:
|
|
85
|
+
- name: Use shell variable directly
|
|
86
|
+
run: |
|
|
87
|
+
# The shell receives the env var with its dotted name
|
|
88
|
+
# Access it via indirect parameter expansion
|
|
89
|
+
VAR_NAME='MY.VAR'
|
|
90
|
+
echo "${!VAR_NAME}" # bash indirect expansion
|
|
91
|
+
prevention:
|
|
92
|
+
- "Never use dots in environment variable names in `env:` blocks — always use UPPER_SNAKE_CASE with underscores."
|
|
93
|
+
- "When consuming third-party tooling that sets dotted env var names, immediately re-export them with underscore names for safe use in expressions."
|
|
94
|
+
- "Review any expression containing two dots in the same context path (e.g., `env.a.b`) — this almost always indicates a naming mistake."
|
|
95
|
+
docs:
|
|
96
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/contexts#env-context"
|
|
97
|
+
label: "GitHub Actions: env context"
|
|
98
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/evaluate-expressions-in-workflows-and-actions#property-dereference"
|
|
99
|
+
label: "Property dereference in expressions"
|
|
100
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/evaluate-expressions-in-workflows-and-actions"
|
|
101
|
+
label: "Evaluate expressions in workflows and actions"
|
package/package.json
CHANGED