@htekdev/actions-debugger 1.0.86 → 1.0.87

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,93 @@
1
+ id: runner-environment-151
2
+ title: "Docker container action creates root-owned files causing Permission denied in following steps"
3
+ category: runner-environment
4
+ severity: error
5
+ tags:
6
+ - docker
7
+ - container-action
8
+ - file-permissions
9
+ - root-user
10
+ - workspace
11
+ patterns:
12
+ - regex: 'Permission denied'
13
+ flags: i
14
+ - regex: 'EACCES: permission denied'
15
+ flags: i
16
+ - regex: 'cannot open .+ Permission denied'
17
+ flags: i
18
+ - regex: 'error: open .+ permission denied'
19
+ flags: i
20
+ error_messages:
21
+ - "Permission denied"
22
+ - "cannot create directory '...': Permission denied"
23
+ - "EACCES: permission denied, open '/github/workspace/...'"
24
+ - "error: cannot open '.git/COMMIT_EDITMSG': Permission denied"
25
+ - "chown: changing ownership of '...': Operation not permitted"
26
+ root_cause: |
27
+ Docker-based GitHub Actions — both `uses: docker://image:tag` inline steps and
28
+ actions with `runs.using: docker` in their action.yml — run their container process
29
+ as root (UID 0) by default unless the Dockerfile explicitly sets a USER directive.
30
+
31
+ Any files written to `GITHUB_WORKSPACE` (mounted at `/github/workspace` inside the
32
+ container) are created with root ownership (uid=0, gid=0). Subsequent workflow steps
33
+ run as the `runner` user (UID 1001) and cannot read, modify, or delete root-owned
34
+ files, causing "Permission denied" errors.
35
+
36
+ The failure is especially common when:
37
+ - A Docker action writes build artifacts, generated code, or lock files.
38
+ - A subsequent step tries to commit changes or run tools on the generated output.
39
+ - The workflow uses `actions/checkout` after a Docker action and git operations fail.
40
+ fix: |
41
+ Option 1 — Chown the workspace after the Docker action (easiest for third-party actions):
42
+ Add a step after the Docker action: `sudo chown -R "$USER:$(id -gn)" "$GITHUB_WORKSPACE"`
43
+
44
+ Option 2 — Set USER in the Dockerfile (best for actions you control):
45
+ Add `USER 1001` (runner UID) to the action's Dockerfile so all written files
46
+ are owned by the runner user.
47
+
48
+ Option 3 — Use --user flag in the Docker run args:
49
+ Set `args` in the action's action.yml to include `--user=1001:127`.
50
+ fix_code:
51
+ - language: yaml
52
+ label: "Fix: chown workspace after Docker action (works for any Docker-based action)"
53
+ code: |
54
+ steps:
55
+ - uses: actions/checkout@v4
56
+
57
+ - name: Run Docker-based action
58
+ uses: docker://my-build-image:latest
59
+ with:
60
+ args: '--output /github/workspace/dist'
61
+
62
+ - name: Fix file ownership after Docker action
63
+ run: sudo chown -R "$USER:$(id -gn)" "$GITHUB_WORKSPACE"
64
+
65
+ - name: Use build output (now accessible as runner user)
66
+ run: ls -la dist/ && cat dist/output.txt
67
+ - language: yaml
68
+ label: "Fix: set non-root USER in Dockerfile (preferred when controlling the action)"
69
+ code: |
70
+ # In your Docker action's Dockerfile — run as UID 1001 to match GitHub runner
71
+ # FROM ubuntu:22.04
72
+ # RUN useradd -u 1001 -g 127 runner
73
+ # WORKDIR /github/workspace
74
+ # USER 1001
75
+ # ENTRYPOINT ["/entrypoint.sh"]
76
+
77
+ # With this Dockerfile, no chown step is needed — files are owned by runner user
78
+ steps:
79
+ - uses: actions/checkout@v4
80
+ - uses: ./ # local Docker action with non-root USER
81
+ - run: cat generated-output.txt # accessible without permission errors
82
+ prevention:
83
+ - "Always set a non-root USER directive in Dockerfiles for actions that write to the workspace."
84
+ - "After any third-party Docker action, add a chown step before accessing created files."
85
+ - "Prefer JavaScript/TypeScript or composite actions over Docker actions when workspace I/O is needed — they run as the runner user by default."
86
+ - "Test Docker actions locally with UID 1001 to catch permission issues before CI."
87
+ docs:
88
+ - url: "https://docs.github.com/en/actions/sharing-automations/creating-actions/dockerfile-support-for-github-actions"
89
+ label: "Dockerfile support for GitHub Actions"
90
+ - url: "https://docs.github.com/en/actions/sharing-automations/creating-actions/creating-a-docker-container-action"
91
+ label: "Creating a Docker container action"
92
+ - url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/store-information-in-variables#default-environment-variables"
93
+ label: "GITHUB_WORKSPACE default environment variable"
@@ -0,0 +1,97 @@
1
+ id: silent-failures-080
2
+ title: "github.event.commits always empty array on pull_request events"
3
+ category: silent-failures
4
+ severity: silent-failure
5
+ tags:
6
+ - pull-request
7
+ - commits
8
+ - event-payload
9
+ - skip-ci
10
+ - multi-trigger
11
+ patterns:
12
+ - regex: 'github\.event\.commits\[0\]'
13
+ flags: i
14
+ - regex: 'Cannot read propert\w+ of undefined.*message'
15
+ flags: i
16
+ error_messages:
17
+ - "Error: Cannot read properties of undefined (reading 'message')"
18
+ - "github.event.commits[0] evaluates to null on pull_request events"
19
+ - "contains(github.event.commits[0].message, '[skip ci]') always false on PRs"
20
+ root_cause: |
21
+ The `github.event.commits` array is only populated in `push` event payloads.
22
+ On `pull_request` events, `github.event.commits` is always an empty array `[]`.
23
+ Accessing `github.event.commits[0]` returns `null`, and
24
+ `github.event.commits[0].message` returns an empty string without throwing an
25
+ expression error.
26
+
27
+ This is the most common footgun when implementing "[skip ci]" commit message
28
+ detection: the condition works correctly for `push` events but silently never
29
+ triggers (never skips) on `pull_request` events. The workflow runs for every PR
30
+ push regardless of the commit message.
31
+
32
+ The issue affects any multi-trigger workflow with both `push:` and `pull_request:`
33
+ that reads commit data from the event payload.
34
+ fix: |
35
+ Guard `github.event.commits` access by event type, or use PR-specific context:
36
+ 1. Use `if: github.event_name == 'push'` before accessing commits.
37
+ 2. For PR events, check `github.event.pull_request.title` or `github.event.pull_request.body`
38
+ for skip conditions.
39
+ 3. Use the `actions/github-script` action to query commits via the REST API on PR events.
40
+ 4. Consider separate workflows for push and pull_request to avoid cross-event payload confusion.
41
+ fix_code:
42
+ - language: yaml
43
+ label: "Wrong: commit message check silently fails on pull_request events"
44
+ code: |
45
+ on: [push, pull_request]
46
+
47
+ jobs:
48
+ build:
49
+ runs-on: ubuntu-latest
50
+ # github.event.commits[0].message is null on pull_request — condition always false
51
+ if: "!contains(github.event.commits[0].message, '[skip ci]')"
52
+ steps:
53
+ - uses: actions/checkout@v4
54
+ - run: npm test
55
+ - language: yaml
56
+ label: "Fix: guard by event type and use PR-appropriate skip signal"
57
+ code: |
58
+ on: [push, pull_request]
59
+
60
+ jobs:
61
+ build:
62
+ runs-on: ubuntu-latest
63
+ steps:
64
+ - uses: actions/checkout@v4
65
+
66
+ - name: Check skip condition
67
+ id: skip
68
+ run: |
69
+ SKIP=false
70
+ if [[ "${{ github.event_name }}" == "push" ]]; then
71
+ # Commits array is available on push events
72
+ if echo "${{ github.event.commits[0].message }}" | grep -q '\[skip ci\]'; then
73
+ SKIP=true
74
+ fi
75
+ elif [[ "${{ github.event_name }}" == "pull_request" ]]; then
76
+ # Check PR title or body for skip signal on PR events
77
+ if echo "${{ github.event.pull_request.title }}" | grep -q '\[skip ci\]'; then
78
+ SKIP=true
79
+ fi
80
+ fi
81
+ echo "skip=$SKIP" >> "$GITHUB_OUTPUT"
82
+
83
+ - name: Run tests
84
+ if: steps.skip.outputs.skip != 'true'
85
+ run: npm test
86
+ prevention:
87
+ - "Never access github.event.commits on workflows with pull_request triggers — it will always be empty."
88
+ - "Use separate workflows for push and pull_request if commit-message-based conditionals are required."
89
+ - "For PR skip conditions, use the PR title or body rather than commit messages."
90
+ - "Check which fields are populated for each event type in the GitHub webhook event payload documentation."
91
+ docs:
92
+ - url: "https://docs.github.com/en/webhooks/webhook-events-and-payloads#push"
93
+ label: "Push webhook event payload (commits array)"
94
+ - url: "https://docs.github.com/en/webhooks/webhook-events-and-payloads#pull_request"
95
+ label: "Pull request webhook event payload"
96
+ - url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/using-conditions-to-control-job-execution"
97
+ label: "Using conditions to control job execution"
@@ -0,0 +1,106 @@
1
+ id: triggers-059
2
+ title: "workflow_call required: true inputs silently receive empty string when omitted by caller"
3
+ category: triggers
4
+ severity: silent-failure
5
+ tags:
6
+ - reusable-workflow
7
+ - workflow-call
8
+ - inputs
9
+ - required
10
+ - validation
11
+ patterns:
12
+ - regex: 'required.*input.*not provided'
13
+ flags: i
14
+ - regex: 'Input .+ is required but was not provided'
15
+ flags: i
16
+ error_messages:
17
+ - "Input 'environment' is required but was not provided"
18
+ - "required: true input silently receives empty string when omitted from caller with:"
19
+ root_cause: |
20
+ When a reusable workflow declares `on.workflow_call.inputs` with `required: true`,
21
+ GitHub does NOT enforce this requirement at call time via the REST API or from other
22
+ calling workflows. The `required: true` flag is informational documentation only —
23
+ it is not validated before the workflow runs.
24
+
25
+ When a calling workflow omits a required input from its `with:` block, the reusable
26
+ workflow receives an empty string `""` for the missing input with no error or warning.
27
+ The workflow starts and runs to whatever conclusion the empty input causes.
28
+
29
+ This leads to silent failures such as deploying to an undefined environment, running
30
+ against an empty target URL, or passing `""` to shell commands that produce incorrect
31
+ results. The bug is especially hard to find because the calling workflow shows a green
32
+ "success" until the silent empty input causes a downstream failure.
33
+ fix: |
34
+ Explicitly validate all required inputs at the start of the reusable workflow:
35
+ 1. Add an input-validation step as the FIRST step of the FIRST job that checks each
36
+ required input for emptiness and fails with a descriptive error.
37
+ 2. Use `default:` values in `on.workflow_call.inputs` to handle omitted inputs
38
+ gracefully with a fallback rather than silently using empty string.
39
+ 3. In calling workflows, always pass all inputs explicitly with `with:` — never
40
+ rely on `required: true` to catch missing inputs.
41
+ fix_code:
42
+ - language: yaml
43
+ label: "Reusable workflow: validate required inputs at startup"
44
+ code: |
45
+ # .github/workflows/deploy.yml (reusable workflow)
46
+ on:
47
+ workflow_call:
48
+ inputs:
49
+ environment:
50
+ required: true # documentation only — NOT enforced by GitHub
51
+ type: string
52
+ target-url:
53
+ required: true
54
+ type: string
55
+
56
+ jobs:
57
+ validate-inputs:
58
+ runs-on: ubuntu-latest
59
+ steps:
60
+ - name: Validate required inputs
61
+ run: |
62
+ ERRORS=()
63
+ if [[ -z "${{ inputs.environment }}" ]]; then
64
+ ERRORS+=("Required input 'environment' was not provided")
65
+ fi
66
+ if [[ -z "${{ inputs.target-url }}" ]]; then
67
+ ERRORS+=("Required input 'target-url' was not provided")
68
+ fi
69
+ if [[ ${#ERRORS[@]} -gt 0 ]]; then
70
+ for ERR in "${ERRORS[@]}"; do
71
+ echo "::error::$ERR"
72
+ done
73
+ exit 1
74
+ fi
75
+
76
+ deploy:
77
+ needs: validate-inputs
78
+ runs-on: ubuntu-latest
79
+ steps:
80
+ - run: echo "Deploying to ${{ inputs.environment }}"
81
+ - language: yaml
82
+ label: "Fix: use default: values to avoid silent empty string behavior"
83
+ code: |
84
+ on:
85
+ workflow_call:
86
+ inputs:
87
+ environment:
88
+ required: false
89
+ default: 'staging' # explicit default instead of relying on required: true
90
+ type: string
91
+
92
+ jobs:
93
+ deploy:
94
+ runs-on: ubuntu-latest
95
+ steps:
96
+ - run: echo "Deploying to ${{ inputs.environment }}" # always 'staging' if omitted
97
+ prevention:
98
+ - "Treat required: true on workflow_call inputs as documentation only — always validate inside the reusable workflow."
99
+ - "Add a dedicated input-validation job as the first job in every reusable workflow with required inputs."
100
+ - "Prefer default: values over required: true when a sensible fallback exists."
101
+ - "In calling workflows, always explicitly pass all inputs in the with: block."
102
+ docs:
103
+ - url: "https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#onworkflow_callinputs"
104
+ label: "on.workflow_call.inputs syntax reference"
105
+ - url: "https://docs.github.com/en/actions/sharing-automations/reusing-workflows#using-inputs-and-secrets-in-a-called-workflow"
106
+ label: "Using inputs in a called workflow"
@@ -0,0 +1,94 @@
1
+ id: yaml-syntax-054
2
+ title: "env context silently empty in reusable workflow with: inputs"
3
+ category: yaml-syntax
4
+ severity: silent-failure
5
+ tags:
6
+ - reusable-workflow
7
+ - env-context
8
+ - with-inputs
9
+ - workflow-call
10
+ - context-availability
11
+ patterns:
12
+ - regex: '"env" context is not available in this context'
13
+ flags: i
14
+ - regex: 'env context.*not available.*reusable'
15
+ flags: i
16
+ error_messages:
17
+ - '"env" context is not available in this context'
18
+ - "Input evaluates to empty string when env context used in reusable workflow with:"
19
+ root_cause: |
20
+ The `env` context is NOT available inside `jobs.<job_id>.with:` blocks used to
21
+ call reusable workflows. Unlike `jobs.<job_id>.steps.with:` (regular action steps,
22
+ which support `env` context), reusable workflow `with:` inputs are evaluated before
23
+ job-level environment variables are injected.
24
+
25
+ Using `${{ env.MY_VAR }}` in a reusable workflow `with:` block silently evaluates
26
+ to an empty string. No error is thrown and the reusable workflow receives `""` for
27
+ that input. The actionlint static linter reports:
28
+ `"env" context is not available in this context`.
29
+
30
+ This is explicitly documented in GitHub's context availability table — `env` is
31
+ available in `jobs.<job_id>.steps.with` but NOT in `jobs.<job_id>.with`.
32
+ fix: |
33
+ Replace `env` context references in reusable workflow `with:` blocks with one of:
34
+ 1. `vars` context — for repository or organization-level configuration variables
35
+ (Settings → Variables). Available in reusable workflow `with:`.
36
+ 2. Inline literal values directly in `with:`.
37
+ 3. Workflow-level `inputs:` context if the calling workflow is itself a reusable
38
+ workflow (chain input passing).
39
+ 4. A prior job that captures the value and passes it via `needs.<job>.outputs`.
40
+ fix_code:
41
+ - language: yaml
42
+ label: "Wrong: env context in reusable workflow with: (silently evaluates to empty string)"
43
+ code: |
44
+ env:
45
+ BASE_URL: 'https://api.example.com'
46
+ DEPLOY_ENV: 'production'
47
+
48
+ jobs:
49
+ call-deploy:
50
+ uses: ./.github/workflows/deploy.yml
51
+ with:
52
+ api-url: ${{ env.BASE_URL }} # silently empty — env unavailable in reusable with:
53
+ environment: ${{ env.DEPLOY_ENV }} # also silently empty
54
+ - language: yaml
55
+ label: "Fix: use vars context or inline value"
56
+ code: |
57
+ # Set BASE_URL as a repository variable in Settings → Secrets and variables → Variables
58
+ jobs:
59
+ call-deploy:
60
+ uses: ./.github/workflows/deploy.yml
61
+ with:
62
+ api-url: ${{ vars.BASE_URL }} # vars IS available in reusable with:
63
+ environment: 'production' # inline literal also works
64
+ - language: yaml
65
+ label: "Fix: pass via prior job output if value is dynamic"
66
+ code: |
67
+ jobs:
68
+ resolve-config:
69
+ runs-on: ubuntu-latest
70
+ outputs:
71
+ api-url: ${{ steps.config.outputs.url }}
72
+ steps:
73
+ - id: config
74
+ run: echo "url=$BASE_URL" >> "$GITHUB_OUTPUT"
75
+ env:
76
+ BASE_URL: ${{ env.BASE_URL }} # env IS available in step env:
77
+
78
+ call-deploy:
79
+ needs: resolve-config
80
+ uses: ./.github/workflows/deploy.yml
81
+ with:
82
+ api-url: ${{ needs.resolve-config.outputs.api-url }} # needs context available
83
+ prevention:
84
+ - "Consult the GitHub Actions context availability table before using contexts in reusable workflow with: blocks."
85
+ - "Prefer vars context (repository/org variables) over env for values shared across workflows."
86
+ - "Run actionlint in CI — it detects unavailable context usage and catches this before runtime."
87
+ - "If a reusable workflow input is silently empty, check context availability as the first debugging step."
88
+ docs:
89
+ - url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/contexts#context-availability"
90
+ label: "GitHub Actions context availability table"
91
+ - url: "https://docs.github.com/en/actions/sharing-automations/reusing-workflows#passing-inputs-and-secrets-to-a-called-workflow"
92
+ label: "Passing inputs and secrets to a called workflow"
93
+ - url: "https://rhysd.github.io/actionlint/"
94
+ label: "actionlint — static checker for GitHub Actions"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@htekdev/actions-debugger",
3
- "version": "1.0.86",
3
+ "version": "1.0.87",
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",