Skip to content

build(workflows): avoid Argument list too long in markdown_links/markdown_src_attributes - #13838

Draft
Planeshifter wants to merge 1 commit into
developfrom
philipp/ci-fix-markdown-workflows-arg-max-2026-08-01
Draft

build(workflows): avoid Argument list too long in markdown_links/markdown_src_attributes#13838
Planeshifter wants to merge 1 commit into
developfrom
philipp/ci-fix-markdown-workflows-arg-max-2026-08-01

Conversation

@Planeshifter

Copy link
Copy Markdown
Member

Description

What is the purpose of this pull request?

This pull request:

  • fixes markdown_links and markdown_src_attributes scheduled workflows, which have failed on every develop run since 2026-07-01 with Argument list too long
  • replaces oversized env:-block payloads with quoted heredocs that write directly into the run script, avoiding the exec-time argument/environment size limit while preserving shell-injection protection
  • fixes a malformed if: condition in markdown_src_attributes.yml that made the "Check status" step always run regardless of actual status

Related Issues

This pull request does not have any related issues.

Questions

None.

Other

Failing runs: 30675870949 (markdown_src_attributes, step "Log results"), 30675418090 (markdown_links, step "Check links against database"). Both fail with:

An error occurred trying to start process '/usr/bin/bash' with working directory '/home/runner/work/stdlib/stdlib'. Argument list too long

Root cause: #12741 (shell-injection hardening) moved broken-link/broken-image JSON payloads, sometimes exceeding 200KB, into step-level env: blocks. env: values are placed into the process environment table when the runner execve()s bash for the step. Linux caps individual argv/environ strings at MAX_ARG_STRLEN (128KB), so any payload larger than that crashes the spawn before the step body runs.

Fix: replace each env: VAR: ${{ steps.results.outputs.X }} + "$VAR" pattern in both workflow files with cat > file <<'DELIM' ... DELIM, writing the payload into the script body instead of the environment or argv. The heredoc delimiter is quoted, which disables parameter and command substitution on the body — untrusted $(...) or backtick content is written literally, never executed, so the injection protection from #12741 is preserved through a size-safe mechanism. Four call sites changed across the two workflow files. .github/workflows/scripts/markdown_links/create_broken_link_issues now takes a file path as its first argument instead of an inline JSON string, since passing the payload as a literal argv element hits the same size limit.

Second fix, same file: markdown_src_attributes.yml's "Check status" step had if: ${{ steps.results.outputs.status }} != 'success' — only the left-hand side is wrapped in ${{ }}, so the whole expression evaluates to a non-empty literal string and is always truthy. The step therefore always ran and always failed, but this was masked by the "Log results" crash happening first. Corrected to ${{ steps.results.outputs.status != 'success' }}, matching the form already used in markdown_links.yml.

Validation: YAML syntax checked for both workflow files (yaml.safe_load), bash syntax checked for the modified script (bash -n). Standalone reproduction confirmed the heredoc write produces byte-identical output to the old echo "$VAR" >> file approach across compact JSON, pretty-printed JSON, empty string, empty array, and trailing-newline cases, and confirmed shell metacharacters, $(), and backticks embedded in a payload are written literally and never executed.

Two residual items for maintainer attention, neither blocking:

  • a payload line exactly matching the heredoc delimiter would truncate it early; not reachable today since the upstream stdlib-js/check-markdown-link-definitions-action always emits single-line JSON. That action is referenced via @main rather than a pinned SHA — pre-existing, not introduced here, but worth a separate look
  • create_broken_link_issues now treats a genuinely empty failures file as "no broken links" (exit 0) instead of erroring; unreachable in the old code since the upstream action always emits at least [ ] for zero failures

Checklist

Please ensure the following tasks are completed before submitting this pull request.

AI Assistance

When authoring the changes proposed in this PR, did you use any kind of AI assistance?

  • Yes
  • No

If you answered "yes" above, how did you use AI assistance?

  • Code generation (e.g., when writing an implementation or fixing a bug)
  • Test/benchmark generation
  • Documentation (including examples)
  • Research and understanding

Disclosure

If you answered "yes" to using AI assistance, please provide a short disclosure indicating how you used AI assistance. This helps reviewers determine how much scrutiny to apply when reviewing your contribution.

This PR was produced by an automated CI-maintenance routine. Root cause was identified via multi-agent investigation of the failing workflow runs, and the fix was independently validated by three separate reviewer agents (correctness, regression scope, style/conventions) before commit. A maintainer should still review before merging.


@stdlib-js/reviewers


Generated by Claude Code

…`markdown_src_attributes`

The jobs `Log results` (workflow `markdown_src_attributes`) and
`Check links against database` (workflow `markdown_links`) failed on
scheduled `develop` runs with `An error occurred trying to start
process '/usr/bin/bash' ... Argument list too long`.

Root cause: PR #12741 (shell-injection hardening) routed large
untrusted JSON payloads (broken-link/broken-image lists, sometimes
over 200 KB) through step-level `env:` blocks instead of interpolating
them into the run script text. Setting such a value as an `env:` var
forces it into the OS process environment table at `execve()` time,
which exceeds Linux's per-string argument/environment size limit
(`MAX_ARG_STRLEN`) and crashes the runner's attempt to spawn `bash`
for the step.

This commit replaces every `env: VAR: ${{ steps.results.outputs.X }}`
+ `"$VAR"` pattern in both workflows with a quoted heredoc that writes
the value directly into the run script body (`cat > file <<'DELIM'
... DELIM`). Because the heredoc body becomes part of the script file
rather than the process environment or argv, it is not subject to the
exec-time size limit. A quoted delimiter disables all shell
parameter/command substitution on the body, so the fix also preserves
the injection protection PR #12741 was introducing, via a size-safe
mechanism instead of `env:`.

`.github/workflows/scripts/markdown_links/create_broken_link_issues`
is updated to accept a file path instead of an inline JSON string as
its first argument, since passing the payload as a literal
command-line argument is subject to the same exec-time limit.

Also fixes a masked pre-existing bug in `markdown_src_attributes.yml`:
the `Check status` step's `if: ${{ steps.results.outputs.status }} !=
'success'` wraps only the left-hand operand in `${{ }}`, so the
condition is always a non-empty (truthy) literal string regardless of
the actual status. This step was unreachable while the `Log results`
step crashed first; fixing the `Argument list too long` failure alone
would have surfaced this bug as the job's new, unconditional failure
point. Corrected to `${{ steps.results.outputs.status != 'success' }}`,
matching the already-correct form in `markdown_links.yml`.

Ref: https://github.com/stdlib-js/stdlib/actions/runs/30675870949
Ref: https://github.com/stdlib-js/stdlib/actions/runs/30675418090
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants