@htekdev/actions-debugger 1.0.127 → 1.0.128

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.
@@ -0,0 +1,147 @@
1
+ id: known-unsolved-075
2
+ title: '`matrix` context unavailable in job-level `if:` condition when matrix is dynamically generated from upstream job outputs'
3
+ category: known-unsolved
4
+ severity: limitation
5
+ tags:
6
+ - matrix
7
+ - dynamic-matrix
8
+ - if-condition
9
+ - job-level
10
+ - fromJSON
11
+ - needs-outputs
12
+ patterns:
13
+ - regex: 'Unrecognized named-value.*matrix.*Located at position'
14
+ flags: 'i'
15
+ - regex: 'matrix.*context.*not.*available.*if|if.*matrix.*unrecognized'
16
+ flags: 'i'
17
+ error_messages:
18
+ - "Unrecognized named-value: 'matrix'. Located at position 26 within expression: contains(inputs.SCHEMAS, matrix.customer.schema)"
19
+ - "The workflow is not valid. ... Unrecognized named-value: 'matrix'."
20
+ root_cause: |
21
+ When a job uses `strategy.matrix` with values derived from an upstream job's outputs
22
+ (e.g. `fromJson(needs.set-matrix.outputs.matrix)`), the `matrix` context is **not**
23
+ available in the job's own `if:` condition.
24
+
25
+ The root cause is evaluation ordering: GitHub Actions evaluates job-level `if:`
26
+ conditions **before** resolving the dynamic matrix values from upstream job outputs.
27
+ At the time `if:` is checked, the specific matrix combination (e.g. `matrix.schema`,
28
+ `matrix.os`) has not yet been bound.
29
+
30
+ This limitation does NOT affect:
31
+ - Step-level `if:` conditions inside the job (matrix IS available there)
32
+ - Static matrices defined inline with literal values (matrix IS available in job-level if)
33
+ - Downstream jobs reading this job's outputs (normal needs chain)
34
+
35
+ **Workaround does not exist at job level**: There is no supported way to filter
36
+ individual matrix combinations at the job `if:` level when the matrix is dynamic.
37
+ GitHub's matrix `include`/`exclude` keys do not support expressions.
38
+
39
+ The only workaround is to move the filtering logic inside a step using
40
+ `if: condition` at the step level, or to generate a pre-filtered matrix in the
41
+ upstream job so that no filtering is needed at the consumer job level.
42
+
43
+ Source: actions/runner#1985 (64 reactions, open since 2022)
44
+ Community discussion: https://github.community/t/matrix-cannot-be-used-in-jobs-level-if/17177
45
+ fix: |
46
+ **Option 1 (recommended): Filter inside the upstream matrix-generation job**
47
+
48
+ Produce a matrix JSON that only includes the combinations you want to run. This is
49
+ the cleanest approach — no filtering needed in the consumer job.
50
+
51
+ **Option 2: Use step-level `if:` instead of job-level `if:`**
52
+
53
+ Move the filtering logic into the first step of the job. The job itself runs for
54
+ every matrix entry but exits cleanly. This wastes a job slot but works.
55
+
56
+ **Option 3: Use `continue-on-error: true` with an early-exit pattern**
57
+
58
+ Not recommended — harder to distinguish real failures from filtered runs.
59
+ fix_code:
60
+ - language: yaml
61
+ label: "Broken — matrix context in job-level if with dynamic matrix"
62
+ code: |
63
+ jobs:
64
+ set-matrix:
65
+ runs-on: ubuntu-latest
66
+ outputs:
67
+ matrix: ${{ steps.gen.outputs.matrix }}
68
+ steps:
69
+ - id: gen
70
+ run: |
71
+ echo 'matrix={"customer":[{"schema":"prod"},{"schema":"staging"}]}' >> "$GITHUB_OUTPUT"
72
+
73
+ deploy:
74
+ needs: set-matrix
75
+ runs-on: ubuntu-latest
76
+ strategy:
77
+ matrix: ${{ fromJson(needs.set-matrix.outputs.matrix) }}
78
+ # ❌ FAILS: "Unrecognized named-value: 'matrix'"
79
+ if: contains(inputs.SCHEMAS, matrix.customer.schema)
80
+ steps:
81
+ - run: echo "Deploying ${{ matrix.customer.schema }}"
82
+
83
+ - language: yaml
84
+ label: "Fixed Option 1 — pre-filter in the matrix-generation job"
85
+ code: |
86
+ jobs:
87
+ set-matrix:
88
+ runs-on: ubuntu-latest
89
+ outputs:
90
+ matrix: ${{ steps.gen.outputs.matrix }}
91
+ steps:
92
+ - id: gen
93
+ # ✅ Generate only the matrix entries that should run
94
+ run: |
95
+ # Filter based on inputs.SCHEMAS inside the script
96
+ SCHEMAS="${{ inputs.SCHEMAS }}"
97
+ MATRIX=$(jq -n --arg schemas "$SCHEMAS" \
98
+ '[{"schema":"prod"},{"schema":"staging"}] |
99
+ map(select(.schema | IN($schemas | split(","))))' \
100
+ | jq -c '{customer:.}')
101
+ echo "matrix=$MATRIX" >> "$GITHUB_OUTPUT"
102
+
103
+ deploy:
104
+ needs: set-matrix
105
+ runs-on: ubuntu-latest
106
+ strategy:
107
+ matrix: ${{ fromJson(needs.set-matrix.outputs.matrix) }}
108
+ # ✅ No job-level if needed — matrix is already filtered
109
+ steps:
110
+ - run: echo "Deploying ${{ matrix.customer.schema }}"
111
+
112
+ - language: yaml
113
+ label: "Fixed Option 2 — move filtering to step-level if"
114
+ code: |
115
+ jobs:
116
+ deploy:
117
+ needs: set-matrix
118
+ runs-on: ubuntu-latest
119
+ strategy:
120
+ matrix: ${{ fromJson(needs.set-matrix.outputs.matrix) }}
121
+ # ✅ No job-level if — allow all matrix entries through
122
+ steps:
123
+ # Early exit for matrix entries that don't match
124
+ - name: Check if this schema should deploy
125
+ # ✅ matrix context IS available in step-level if conditions
126
+ if: "!contains(inputs.SCHEMAS, matrix.customer.schema)"
127
+ run: |
128
+ echo "Skipping schema ${{ matrix.customer.schema }}"
129
+ exit 0
130
+
131
+ - name: Deploy
132
+ if: contains(inputs.SCHEMAS, matrix.customer.schema)
133
+ run: echo "Deploying ${{ matrix.customer.schema }}"
134
+
135
+ prevention:
136
+ - "When you need per-combination filtering, pre-filter the matrix JSON in the generation step rather than relying on job-level `if:` with matrix context."
137
+ - "Use step-level `if:` conditions (not job-level) when you must reference `matrix.*` context for filtering — step-level conditions evaluate after matrix expansion."
138
+ - "Check GitHub docs for 'Context availability' to confirm which contexts are available at each level before authoring complex conditional logic."
139
+ docs:
140
+ - url: 'https://github.com/actions/runner/issues/1985'
141
+ label: 'actions/runner#1985 — Unrecognized named-value: matrix in job if conditional (64 reactions)'
142
+ - url: 'https://github.community/t/matrix-cannot-be-used-in-jobs-level-if/17177'
143
+ label: 'GitHub Community: matrix cannot be used in jobs level if'
144
+ - url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/using-a-matrix-for-your-jobs'
145
+ label: 'GitHub Docs: Using a matrix for your jobs'
146
+ - url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/contexts#context-availability'
147
+ label: 'GitHub Docs: Context availability table'
@@ -0,0 +1,146 @@
1
+ id: runner-environment-237
2
+ title: 'Container job `$HOME` is hardcoded to `/github/home` — Docker images built with `/root` home break silently'
3
+ category: runner-environment
4
+ severity: error
5
+ tags:
6
+ - container
7
+ - HOME
8
+ - docker
9
+ - self-hosted
10
+ - environment-variable
11
+ - tool-cache
12
+ patterns:
13
+ - regex: '\$HOME.*github/home'
14
+ flags: 'i'
15
+ - regex: 'Could not find.*\$HOME.*plugin|command not found.*HOME.*github'
16
+ flags: 'i'
17
+ - regex: 'no such file.*github/home|Permission denied.*github/home'
18
+ flags: 'i'
19
+ error_messages:
20
+ - "The reason `bluebase plugins` doesn't work is because it depends on `$HOME` pointing to `/root` but now GitHub Actions has changed it to `/github/home`."
21
+ - "Error: Could not find plugin at /github/home/.cache/@bluebase"
22
+ - "rust: command not found"
23
+ - "cargo: command not found"
24
+ - "go: command not found"
25
+ root_cause: |
26
+ When using `jobs.<name>.container`, the GitHub Actions runner mounts a host volume at
27
+ `/github/home` and unconditionally overrides the `HOME` environment variable to point there,
28
+ regardless of:
29
+ - The Docker image's configured user or home directory
30
+ - Any `env: HOME:` value set in the container spec
31
+ - The container image's `ENV HOME` instruction
32
+
33
+ This is hard-coded in ContainerOperationProvider.cs:
34
+ `-v "/runner/work/_temp/_github_home":"/github/home"`
35
+ `HOME=/github/home`
36
+
37
+ The volume mount overwrites whatever was at `/github/home` inside the container, and
38
+ the forced HOME env var points to that empty/host-controlled directory.
39
+
40
+ **Impact on pre-installed tools**: Any CLI tool or plugin manager that stores
41
+ state relative to `$HOME` during the Docker image build (e.g. `npm global`, Rust's
42
+ `cargo`, Go binaries in `~/go/bin`, Homebrew cellar, Python user installs at
43
+ `~/.local`) will no longer find its data because HOME now points to `/github/home`
44
+ instead of `/root` or the image user's home.
45
+
46
+ Source: actions/runner#863 (124 reactions, open since 2021)
47
+ fix: |
48
+ **Option 1 (recommended): Prefix affected commands with `HOME=/root`**
49
+
50
+ Temporarily reset HOME to the original image home for each step that relies on
51
+ pre-installed tools. Do NOT set HOME permanently in the workflow — the runner
52
+ depends on `/github/home` for internal state.
53
+
54
+ **Option 2: Rebuild Docker image with `/github/home` as the home directory**
55
+
56
+ Set `ENV HOME /github/home` in the Dockerfile before installing tools.
57
+ Note: if `/github/home` is empty at build time (it is), tools install there, and
58
+ the volume mount at runtime will still OVERWRITE the directory. This does not work.
59
+
60
+ **Option 3: Copy tool state in a setup step**
61
+
62
+ Add an entrypoint or a workflow step that copies `/root/.config`, `/root/.cache`,
63
+ `/root/go`, etc. to `/github/home` before the steps that need them run.
64
+
65
+ **Option 4: Avoid container jobs for images with pre-installed tools**
66
+
67
+ Use a Docker action (`uses: docker://image`) instead of `jobs.<name>.container`
68
+ — Docker actions do not override HOME.
69
+ fix_code:
70
+ - language: yaml
71
+ label: "Broken — pre-installed cargo/rust not found in container job"
72
+ code: |
73
+ jobs:
74
+ build:
75
+ runs-on: ubuntu-latest
76
+ container:
77
+ image: my-rust-tools:latest # Built with cargo installed at /root/.cargo
78
+ steps:
79
+ - uses: actions/checkout@v4
80
+ - name: Build
81
+ run: cargo build --release # ❌ FAILS: cargo not found — HOME=/github/home
82
+
83
+ - language: yaml
84
+ label: "Fixed — reset HOME per-step for pre-built tool invocations"
85
+ code: |
86
+ jobs:
87
+ build:
88
+ runs-on: ubuntu-latest
89
+ container:
90
+ image: my-rust-tools:latest
91
+ steps:
92
+ - uses: actions/checkout@v4
93
+ - name: Build
94
+ run: HOME=/root cargo build --release # ✅ HOME temporarily reset to where cargo is
95
+
96
+ # Or use env: at the step level:
97
+ - name: Run tests
98
+ env:
99
+ HOME: /root
100
+ run: cargo test
101
+
102
+ - language: yaml
103
+ label: "Fixed — rebuild Docker image with tools installed at github/home path"
104
+ code: |
105
+ # Dockerfile — install tools where GHA will point HOME
106
+ FROM ubuntu:22.04
107
+
108
+ # Install Rust to /github/home/.cargo (matches runtime HOME)
109
+ RUN mkdir -p /github/home
110
+ ENV HOME=/github/home
111
+ RUN curl https://sh.rustup.rs -sSf | sh -s -- -y
112
+
113
+ # In workflow, no HOME override needed:
114
+ # cargo is at /github/home/.cargo/bin — BUT runtime mount overwrites!
115
+ # This approach ONLY works if you add the PATH explicitly:
116
+ ENV PATH="/github/home/.cargo/bin:${PATH}"
117
+
118
+ # Better: install to /usr/local/bin which is not affected by HOME mount
119
+ RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain stable
120
+ RUN cp -r /github/home/.cargo/bin/* /usr/local/bin/
121
+
122
+ - language: yaml
123
+ label: "Alternative — use Docker action instead of container job (HOME not overridden)"
124
+ code: |
125
+ jobs:
126
+ build:
127
+ runs-on: ubuntu-latest
128
+ steps:
129
+ - uses: actions/checkout@v4
130
+ - name: Build in custom container
131
+ uses: docker://my-rust-tools:latest # ✅ HOME not overridden by runner
132
+ with:
133
+ args: cargo build --release
134
+
135
+ prevention:
136
+ - "When building Docker images for use in `jobs.<name>.container`, install binaries to `/usr/local/bin` or another PATH location not dependent on `$HOME`."
137
+ - "Test container images locally by running `docker run -e HOME=/github/home <image> <command>` to reproduce the GHA HOME override before using them in workflows."
138
+ - "Prefer Docker actions (`uses: docker://image`) over container jobs if your image relies on a specific HOME directory — Docker actions do not override HOME."
139
+ - "Read actions/runner#863 before publishing a Docker image intended for use as a GHA container job."
140
+ docs:
141
+ - url: 'https://github.com/actions/runner/issues/863'
142
+ label: 'actions/runner#863 — HOME is overridden for containers (124 reactions, open since 2021)'
143
+ - url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/running-jobs-in-a-container'
144
+ label: 'GitHub Docs: Running jobs in a container'
145
+ - url: 'https://stackoverflow.com/questions/58516181/missing-installed-dependencies-when-docker-image-is-used'
146
+ label: 'Stack Overflow: Missing installed dependencies when Docker image is used (Score: 3)'
@@ -0,0 +1,151 @@
1
+ id: runner-environment-238
2
+ title: 'Self-hosted runner with Docker container steps creates root-owned files in workspace — `git clean` fails on next run'
3
+ category: runner-environment
4
+ severity: error
5
+ tags:
6
+ - self-hosted
7
+ - docker
8
+ - container
9
+ - workspace
10
+ - permissions
11
+ - checkout
12
+ - root-owned
13
+ patterns:
14
+ - regex: 'warning: could not open directory.*Permission denied'
15
+ flags: 'i'
16
+ - regex: 'failed to remove.*Directory not empty|rm.*cannot remove.*Permission denied'
17
+ flags: 'i'
18
+ - regex: 'Unable to clean or reset the repository.*recreated'
19
+ flags: 'i'
20
+ error_messages:
21
+ - "warning: could not open directory 'foo/': Permission denied"
22
+ - "warning: failed to remove foo/: Directory not empty"
23
+ - "##[warning]Unable to clean or reset the repository. The repository will be recreated instead."
24
+ - "##[error]Command failed: rm -rf \"/home/runner/_work/repo/repo/foo\""
25
+ - "rm: cannot remove '/home/runner/_work/repo/repo/foo': Permission denied"
26
+ - "error: failed to run 'git clean -ffdx': exit code 1"
27
+ root_cause: |
28
+ When a GitHub Actions workflow on a **self-hosted runner** uses a Docker-based step
29
+ (either `container:` jobs or `uses: docker://` actions), files written to the
30
+ workspace during that step are owned by `root:root` — because most Docker containers
31
+ run as root by default.
32
+
33
+ The self-hosted runner process runs as a non-root user (e.g., `github-runner`, `ubuntu`).
34
+ On the **next workflow run**, `actions/checkout` attempts to clean the workspace by
35
+ running `git clean -ffdx`. Git's clean-up calls `stat()` on root-owned directories.
36
+ Because stat succeeds (files are visible) but `rm` is blocked (not root), `git clean`
37
+ reports the directory but cannot remove it. Git then falls back to `rm -rf` via the
38
+ runner, which also fails with `Permission denied`.
39
+
40
+ The checkout action logs a warning "Unable to clean or reset the repository. The
41
+ repository will be recreated instead." — then `rm -rf` of the entire workspace also
42
+ fails because root-owned directories are nested inside. The job fails permanently
43
+ until a human manually removes the root-owned files on the runner host.
44
+
45
+ **Why GitHub-hosted runners don't hit this**: GitHub-hosted runners are ephemeral —
46
+ the workspace is destroyed after every job, so there's no cross-run persistence of
47
+ root-owned files. Self-hosted runners persist the workspace by default.
48
+
49
+ Source: actions/runner#434 (131 reactions, open since 2020)
50
+ fix: |
51
+ **Option 1 (recommended): Run the container as the same UID as the host runner user**
52
+
53
+ Pass `--user $(id -u):$(id -g)` to Docker options so files are written with the
54
+ runner's UID/GID. Most images support this without modification.
55
+
56
+ **Option 2: Add a cleanup step before checkout**
57
+
58
+ Add a step at the start of the workflow that removes workspace contents using sudo.
59
+ Requires configuring passwordless sudo for the runner user.
60
+
61
+ **Option 3: Configure the container to run as root but add a post-step chown**
62
+
63
+ After container steps, add a step that runs `sudo chown -R $USER:$USER .` to
64
+ reclaim ownership. Still requires sudo access.
65
+
66
+ **Option 4: Use ephemeral (one-shot) self-hosted runners**
67
+
68
+ Ephemeral runners create a fresh workspace per job. No cross-run file persistence
69
+ means root-owned files from Docker steps never accumulate.
70
+
71
+ **Option 5: Set `clean: false` on checkout and handle cleanup yourself**
72
+
73
+ Disable automatic workspace cleaning in checkout and add your own robust cleanup
74
+ step that can handle permission errors.
75
+ fix_code:
76
+ - language: yaml
77
+ label: "Fixed — run Docker container as host runner's UID"
78
+ code: |
79
+ jobs:
80
+ build:
81
+ runs-on: [self-hosted, linux]
82
+ container:
83
+ image: node:20
84
+ options: '--user 1001:1001' # ✅ Match runner UID — no root-owned files
85
+
86
+ steps:
87
+ - uses: actions/checkout@v4
88
+ - run: npm ci && npm run build
89
+
90
+ - language: yaml
91
+ label: "Fixed — run container as current user dynamically"
92
+ code: |
93
+ jobs:
94
+ build:
95
+ runs-on: [self-hosted, linux]
96
+ steps:
97
+ - uses: actions/checkout@v4
98
+
99
+ - name: Build in container (current user)
100
+ run: |
101
+ docker run --rm \
102
+ --user "$(id -u):$(id -g)" \
103
+ -v "${{ github.workspace }}:/work" \
104
+ -w /work \
105
+ node:20 \
106
+ npm ci && npm run build
107
+ # ✅ Files written inside container owned by host runner user
108
+
109
+ - language: yaml
110
+ label: "Fixed — cleanup step before checkout to handle pre-existing root-owned files"
111
+ code: |
112
+ jobs:
113
+ build:
114
+ runs-on: [self-hosted, linux]
115
+ steps:
116
+ # ✅ Cleanup root-owned files before checkout
117
+ - name: Cleanup workspace
118
+ run: |
119
+ if [ -d "${{ github.workspace }}" ]; then
120
+ sudo chown -R "$USER:$USER" "${{ github.workspace }}" || true
121
+ sudo rm -rf "${{ github.workspace }}"
122
+ fi
123
+ # Requires: echo "runner ALL=(ALL) NOPASSWD: /bin/chown, /bin/rm" | sudo tee /etc/sudoers.d/runner
124
+
125
+ - uses: actions/checkout@v4
126
+
127
+ - language: yaml
128
+ label: "Fixed — register self-hosted runner as ephemeral (--ephemeral flag)"
129
+ code: |
130
+ # Register the runner with --ephemeral so each job gets a fresh workspace:
131
+ # ./config.sh --url https://github.com/org/repo --token TOKEN --ephemeral
132
+ #
133
+ # Or for ARC (Actions Runner Controller):
134
+ # spec:
135
+ # ephemeral: true # Each job pod is destroyed after completion
136
+
137
+ prevention:
138
+ - "Always run Docker container steps with `--user $(id -u):$(id -g)` on self-hosted runners to match the host runner's UID/GID."
139
+ - "Use ephemeral self-hosted runners to eliminate cross-run workspace persistence entirely."
140
+ - "Add a workspace cleanup step at the start of workflows that use Docker container steps to proactively clear root-owned files."
141
+ - "After setting up a self-hosted runner, test with a workflow that uses a `container:` job and verify subsequent runs can clean up without errors."
142
+ - "When using ARC (Actions Runner Controller), enable `ephemeral: true` on the RunnerDeployment spec."
143
+ docs:
144
+ - url: 'https://github.com/actions/runner/issues/434'
145
+ label: 'actions/runner#434 — Self-hosted runner with Docker step creates root-owned files (131 reactions)'
146
+ - url: 'https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners'
147
+ label: 'GitHub Docs: About self-hosted runners'
148
+ - url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/running-jobs-in-a-container'
149
+ label: 'GitHub Docs: Running jobs in a container'
150
+ - url: 'https://stackoverflow.com/questions/76407664/files-owned-by-rootroot-when-using-actions-checkout-on-self-hosted-runner'
151
+ label: 'Stack Overflow: Files owned by root:root when using actions/checkout on self-hosted runner'
@@ -0,0 +1,162 @@
1
+ id: silent-failures-119
2
+ title: '`github.ref` and `GITHUB_REF` intermittently empty on `release` event — workflows that tag-check silently fail or exit 1'
3
+ category: silent-failures
4
+ severity: silent-failure
5
+ tags:
6
+ - github.ref
7
+ - GITHUB_REF
8
+ - release
9
+ - release-event
10
+ - intermittent
11
+ - tag
12
+ - empty
13
+ patterns:
14
+ - regex: 'github\.ref.*release|GITHUB_REF.*release.*empty'
15
+ flags: 'i'
16
+ - regex: 'refs/tags.*empty.*release|release.*github_ref.*blank'
17
+ flags: 'i'
18
+ - regex: 'startsWith\(github\.ref.*refs/tags.*false.*release'
19
+ flags: 'i'
20
+ error_messages:
21
+ - "if [[ \"\" == refs/tags/release/* ]] ; then"
22
+ - "::error ::Could not determine the version number using ref "
23
+ - "GITHUB_REF: ''"
24
+ - "github.ref evaluates to empty string on release event"
25
+ root_cause: |
26
+ When a workflow is triggered by an `on: release` event (e.g. `published`, `created`,
27
+ `prereleased`), the `github.ref` context value and `GITHUB_REF` environment variable
28
+ are **intermittently empty** — sometimes populated correctly, sometimes empty string.
29
+
30
+ The bug is non-deterministic: re-running the same workflow sometimes succeeds (ref
31
+ populated) and sometimes fails (ref empty). It is more frequent when:
32
+ - The release is created immediately after tagging
33
+ - Multiple releases fire in quick succession
34
+ - The workflow is called as a reusable workflow with `github.ref` forwarded
35
+
36
+ **Root cause**: A race condition or internal state propagation issue in the GitHub
37
+ Actions platform where the `release` event payload is dispatched before the ref
38
+ metadata is fully resolved in all runtime components. The `github.event.release`
39
+ object IS populated correctly in the same runs that have an empty `github.ref`.
40
+
41
+ **Impact**: Workflows that use `github.ref` to extract tag names (e.g.
42
+ `startsWith(github.ref, 'refs/tags/')`, or `echo $GITHUB_REF | cut -c11-`) will
43
+ silently get an empty string, causing:
44
+ - Conditional steps to be skipped (release-only deployments don't run)
45
+ - Version parsing to produce empty strings (publishing incorrect artifacts)
46
+ - Shell scripts to `exit 1` on empty-ref checks
47
+
48
+ This issue was introduced/regressed in August 2023 (runner ~v2.307) and remains
49
+ intermittently reproducible as of April/August 2025.
50
+
51
+ Source: actions/runner#2788 (65 reactions, open since 2023)
52
+ fix: |
53
+ **Always use `github.event.release.tag_name` for release tag extraction** instead of
54
+ parsing `github.ref`. The `github.event.release` object is consistently populated
55
+ even when `github.ref` is empty.
56
+
57
+ For step-level `if:` conditions that check whether a workflow was triggered by a
58
+ release event, use `github.event_name == 'release'` instead of
59
+ `startsWith(github.ref, 'refs/tags/')`.
60
+
61
+ If you need the full `refs/tags/v1.2.3` ref format, construct it from the event:
62
+ `refs/tags/${{ github.event.release.tag_name }}`
63
+ fix_code:
64
+ - language: yaml
65
+ label: "Broken — version extraction via github.ref (intermittently empty)"
66
+ code: |
67
+ on:
68
+ release:
69
+ types: [published]
70
+
71
+ jobs:
72
+ publish:
73
+ runs-on: ubuntu-latest
74
+ steps:
75
+ - name: Extract version
76
+ run: |
77
+ # ❌ BROKEN: github.ref is intermittently empty on release events
78
+ VERSION="${{ github.ref }}"
79
+ if [[ "$VERSION" == refs/tags/v* ]]; then
80
+ TAG="${VERSION#refs/tags/}"
81
+ else
82
+ echo "::error ::Could not determine version from ref: $VERSION"
83
+ exit 1
84
+ fi
85
+
86
+ # Also fragile:
87
+ - if: startsWith(github.ref, 'refs/tags/') # ❌ sometimes false when ref is empty
88
+ run: echo "Publishing release"
89
+
90
+ - language: yaml
91
+ label: "Fixed — use github.event.release.tag_name (always populated)"
92
+ code: |
93
+ on:
94
+ release:
95
+ types: [published]
96
+
97
+ jobs:
98
+ publish:
99
+ runs-on: ubuntu-latest
100
+ steps:
101
+ - name: Extract version
102
+ run: |
103
+ # ✅ FIXED: event.release.tag_name is always populated correctly
104
+ TAG="${{ github.event.release.tag_name }}"
105
+ echo "Publishing version: $TAG"
106
+ echo "VERSION=$TAG" >> "$GITHUB_ENV"
107
+
108
+ # For if: conditions, use event_name check instead of ref check:
109
+ - if: github.event_name == 'release' # ✅ Always correct
110
+ run: echo "Publishing release ${{ github.event.release.tag_name }}"
111
+
112
+ # If you need the refs/tags/... format, construct it:
113
+ - name: Construct full ref
114
+ run: |
115
+ FULL_REF="refs/tags/${{ github.event.release.tag_name }}"
116
+ echo "Full ref: $FULL_REF"
117
+
118
+ - language: yaml
119
+ label: "Fixed — defensive workaround using both sources"
120
+ code: |
121
+ on:
122
+ release:
123
+ types: [published]
124
+
125
+ jobs:
126
+ publish:
127
+ runs-on: ubuntu-latest
128
+ steps:
129
+ - name: Determine tag with fallback
130
+ id: tag
131
+ run: |
132
+ # Primary: event object (always populated)
133
+ TAG="${{ github.event.release.tag_name }}"
134
+
135
+ # Fallback: parse from github.ref if event is empty (unlikely)
136
+ if [ -z "$TAG" ] && [ -n "${{ github.ref }}" ]; then
137
+ TAG="${{ github.ref }}"
138
+ TAG="${TAG#refs/tags/}"
139
+ fi
140
+
141
+ if [ -z "$TAG" ]; then
142
+ echo "::error ::Could not determine release tag from either source"
143
+ exit 1
144
+ fi
145
+ echo "tag=$TAG" >> "$GITHUB_OUTPUT"
146
+
147
+ - run: echo "Publishing ${{ steps.tag.outputs.tag }}"
148
+
149
+ prevention:
150
+ - "Never parse release tags from `github.ref` — always use `github.event.release.tag_name` in release-triggered workflows."
151
+ - "Use `github.event_name == 'release'` in `if:` conditions rather than `startsWith(github.ref, 'refs/tags/')` to avoid empty-ref false negatives."
152
+ - "Add explicit error messages when tag extraction returns empty string so intermittent failures are visible rather than silent."
153
+ - "If a release workflow fails unexpectedly, check whether `github.ref` was empty by adding `run: echo \"ref=${{ github.ref }}\"; echo \"tag=${{ github.event.release.tag_name }}\"`."
154
+ docs:
155
+ - url: 'https://github.com/actions/runner/issues/2788'
156
+ label: 'actions/runner#2788 — github.ref is empty for workflows triggered by release (65 reactions)'
157
+ - url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/events-that-trigger-workflows#release'
158
+ label: 'GitHub Docs: Events that trigger workflows — release'
159
+ - url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/contexts#github-context'
160
+ label: 'GitHub Docs: github context'
161
+ - url: 'https://github.com/orgs/community/discussions/64528'
162
+ label: 'GitHub Community: github.ref empty on release event (discussion)'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@htekdev/actions-debugger",
3
- "version": "1.0.127",
3
+ "version": "1.0.128",
4
4
  "description": "65+ real GitHub Actions errors, queryable by agents. CLI + MCP server + Copilot skills + error database.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",