Skip to content

feat: add write_output_files_only to keep file lists out of GITHUB_OUTPUT - #2937

Open
diogokiss wants to merge 1 commit into
tj-actions:mainfrom
diogokiss:feat/write-output-files-only
Open

feat: add write_output_files_only to keep file lists out of GITHUB_OUTPUT#2937
diogokiss wants to merge 1 commit into
tj-actions:mainfrom
diogokiss:feat/write-output-files-only

Conversation

@diogokiss

@diogokiss diogokiss commented Aug 18, 2026

Copy link
Copy Markdown

Note

This Pull Request was opened by an AI agent, which has been previously reviewed and green-lit by a human, @diogokiss.

Addresses cause 3 of #2933.

The name and shape here are a suggestion, not a request. I have picked something that seemed
reasonable and made it work end to end, so there is something concrete to react to rather than a
proposal to discuss in the abstract. Happy to rename it, narrow it, widen it, or take a completely
different approach — say which and I will redo it.

The problem

write_output_files: true writes the changed file lists to output_dir, but it does not stop them
also being set as step outputs
(src/utils.ts#L1504
runs unconditionally, before the file-writing branch). A workflow that only ever reads the files still
pays for the outputs, and no input turns that off.

That cost is not always small. Every filter key produces fourteen file lists, and on a large diff each
holds the whole set of paths. The runner reads the output file in one piece with File.ReadAllText and
applies no size limit to it, unlike step summaries which are capped at 1 MB. A big enough diff can
exhaust the runner while it processes the file and take the job down.

The change

write_output_files_only. When enabled, the fourteen file lists go to output_dir and are left out of
$GITHUB_OUTPUT. Everything small stays an output, so existing gating keeps working:

Output With write_output_files_only
the 14 *_files lists written to output_dir only
*_count still an output
any_*, only_* still an output
changed_keys, modified_keys still an output
- uses: tj-actions/changed-files@v47
  with:
    json: true
    write_output_files_only: true
    files_yaml_from_source_file: .github/filters.yaml

Enabling it implies write_output_files, so it works on its own:

    write_output_files_only: true    # this alone is enough

Setting both is deliberately not an error. An action cannot tell an input explicitly set to false
from one left at its declared default, so a real contradiction is not detectable, and refusing the
harmless redundant case would only get in the way of anyone adding this to a step that already sets
write_output_files: true.

Measured

Generated repository, one filter key matching every changed file, 40,000 changed files, same build,
only the input differs:

$GITHUB_OUTPUT Keys File lists in it
default 6,300,409 bytes 37 14 of 14
write_output_files_only: true 3,040 bytes 23 0 of 14

Both runs write the same 37 files to output_dir, and content_all_changed_files.json holds all
40,000 paths either way. The twenty-one counting and gating outputs are present in both, as are
changed_keys and modified_keys.

Notes on the implementation

  • The discriminator is explicit. setOutput gained a skipGithubOutput flag, and the fourteen
    path-list call sites pass it. I did not key off setArrayOutput, because only three of the fourteen
    lists go through it and it also carries changed_keys and modified_keys, which must not be
    suppressed.
  • Unit tests cover setOutput with and without the flag, and that the input implies write_output_files.
  • yarn all passes: build, prettier, eslint with --max-warnings 0, ncc package, jest with coverage
    (68 tests, 4 suites). dist/ is rebuilt in the same commit.
  • The README input table is generated from action.yml by auto-doc, so it is left untouched here.

Relationship to the other Pull Requests

Independent of both. #2934 makes processing a diff cheaper, this stops the result being copied where it
is not wanted, and #2936 is about which two commits get compared in the first place. They apply to the
same failure but none of them needs the others.

@codacy-production

codacy-production Bot commented Aug 18, 2026

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 71 complexity

Metric Results
Complexity 71

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@diogokiss
diogokiss force-pushed the feat/write-output-files-only branch from 9f9852f to fbd0bd3 Compare August 18, 2026 12:17
…TPUT

Setting `write_output_files: true` writes the changed file lists to files in
`output_dir`, but it does not stop them also being set as step outputs. There
is no way to ask for the files alone, so a workflow that only ever reads the
files still pays for the outputs.

That cost is not always small. Every filter key produces fourteen file lists,
and on a large diff each one holds the whole set of paths. The runner reads
the output file in one piece and applies no size limit to it, so a big enough
diff can exhaust it while it processes the file, taking the job down with it.

This adds `write_output_files_only`. When enabled, the fourteen file lists are
written to `output_dir` and left out of the output file. Counts, the `any_*`
and `only_*` flags, and the `changed_keys` and `modified_keys` lists are still
set as outputs, so steps that gate on them keep working unchanged.

Enabling it implies `write_output_files`, so it works on its own and does not
have to be paired with another input. Setting both is deliberately not an
error: an action cannot tell an input explicitly set to `false` from one left
at its declared default, so a real contradiction is not detectable, and
refusing the harmless redundant case would only get in the way.

Measured on a generated repository with one filter matching every changed
file, at 40,000 changed files:

    default                     6,300,409 bytes, 37 keys, 14 of 14 lists
    write_output_files_only         3,040 bytes, 23 keys,  0 of 14 lists

Both runs write the same 37 files to `output_dir`, and the changed file list
read back from disk holds all 40,000 paths either way. The twenty-one counting
and gating outputs are present in both.

Nothing changes for anyone who does not enable it.

The README input table is generated from `action.yml` by auto-doc, so it is
left untouched here.

References:
tj-actions#2933
https://github.com/tj-actions/changed-files/blob/934b2d2c7e653bb8c968afed5a0428617f09aa24/src/utils.ts#L1504
https://github.com/actions/runner/blob/main/src/Runner.Worker/FileCommandManager.cs
@diogokiss
diogokiss force-pushed the feat/write-output-files-only branch from fbd0bd3 to 352fd2a Compare August 18, 2026 12:33
@diogokiss
diogokiss marked this pull request as ready for review August 18, 2026 12:39
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.

1 participant