Skip to content

Commit 9f02212

Browse files
authored
Replace JSON string I/O with file-based approach in find, file, fix actions (#179)
## Summary Extends the file-based approach from #177 to the `find`, `file`, and `fix` sub-actions, eliminating all large JSON string interpolation in the composite workflow. ## Problem The `file` action was failing with `Argument list too long` when the findings list was too long. ``` ##[error]An error occurred trying to start process 'node' ... Argument list too long ``` See failing run [Staff only]: https://github.com/github/accessibility-scorecard/actions/runs/23802863081/job/69367961031 ## Changes ### Sub-actions (find, file, fix) Each action now: 1. **Writes output to a temp file** (`$RUNNER_TEMP/*.json`) in addition to setting the string output 2. **Exposes a `*_file` output** with the file path (e.g., `findings_file`, `filings_file`, `fixings_file`) 3. **Requires `*_file` inputs** (e.g., `findings_file` instead of `findings`) ### Composite action (`action.yml`) - **Normalize cache step**: Reads directly from the cache file on disk instead of interpolating `${{ steps.restore.outputs.value }}` through the shell - **File step**: Passes `findings_file` and `cached_filings_file` instead of raw JSON strings - **Get issues step**: Reads from `filings_file` instead of interpolating the output string - **Fix step**: Passes `issues_file` instead of raw JSON string - **Set results step**: Reads from file outputs via environment variables instead of pre-written temp files - **Removed** the now-unnecessary "Write filings and fixings to temp files" step
2 parents e1d8f99 + e91522e commit 9f02212

File tree

7 files changed

+61
-48
lines changed

7 files changed

+61
-48
lines changed

.github/actions/file/action.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,17 @@ name: "File"
22
description: "Files GitHub issues to track potential accessibility gaps."
33

44
inputs:
5-
findings:
6-
description: "List of potential accessibility gaps, as stringified JSON"
5+
findings_file:
6+
description: "Path to a JSON file containing the list of potential accessibility gaps"
77
required: true
88
repository:
99
description: "Repository (with owner) to file issues in"
1010
required: true
1111
token:
1212
description: "Token with fine-grained permission 'issues: write'"
1313
required: true
14-
cached_filings:
15-
description: "Cached filings from previous runs, as stringified JSON. Without this, duplicate issues may be filed."
14+
cached_filings_file:
15+
description: "Path to a JSON file containing cached filings from previous runs. Without this, duplicate issues may be filed."
1616
required: false
1717
screenshot_repository:
1818
description: "Repository (with owner) where screenshots are stored on the gh-cache branch. Defaults to the 'repository' input if not set. Required if issues are open in a different repo to construct proper screenshot URLs."
@@ -23,8 +23,8 @@ inputs:
2323
default: "false"
2424

2525
outputs:
26-
filings:
27-
description: "List of issues filed (and their associated finding(s)), as stringified JSON"
26+
filings_file:
27+
description: "Path to a JSON file containing the list of issues filed (and their associated finding(s))"
2828

2929
runs:
3030
using: "node24"

.github/actions/file/src/index.ts

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import type {Finding, ResolvedFiling, RepeatedFiling, FindingGroupIssue, Filing, IssueResponse} from './types.d.js'
2+
import fs from 'node:fs'
3+
import path from 'node:path'
24
import process from 'node:process'
35
import * as core from '@actions/core'
46
import {Octokit} from '@octokit/core'
@@ -16,18 +18,20 @@ const OctokitWithThrottling = Octokit.plugin(throttling)
1618

1719
export default async function () {
1820
core.info("Started 'file' action")
19-
const findings: Finding[] = JSON.parse(core.getInput('findings', {required: true}))
21+
const findingsFile = core.getInput('findings_file', {required: true})
22+
const findings: Finding[] = JSON.parse(fs.readFileSync(findingsFile, 'utf8'))
2023
const repoWithOwner = core.getInput('repository', {required: true})
2124
const token = core.getInput('token', {required: true})
2225
const screenshotRepo = core.getInput('screenshot_repository', {required: false}) || repoWithOwner
23-
const cachedFilings: (ResolvedFiling | RepeatedFiling)[] = JSON.parse(
24-
core.getInput('cached_filings', {required: false}) || '[]',
25-
)
26+
const cachedFilingsFile = core.getInput('cached_filings_file', {required: false})
27+
const cachedFilings: (ResolvedFiling | RepeatedFiling)[] = cachedFilingsFile
28+
? JSON.parse(fs.readFileSync(cachedFilingsFile, 'utf8'))
29+
: []
2630
const shouldOpenGroupedIssues = core.getBooleanInput('open_grouped_issues')
27-
core.debug(`Input: 'findings: ${JSON.stringify(findings)}'`)
31+
core.debug(`Input: 'findings_file: ${findingsFile}'`)
2832
core.debug(`Input: 'repository: ${repoWithOwner}'`)
2933
core.debug(`Input: 'screenshot_repository: ${screenshotRepo}'`)
30-
core.debug(`Input: 'cached_filings: ${JSON.stringify(cachedFilings)}'`)
34+
core.debug(`Input: 'cached_filings_file: ${cachedFilingsFile}'`)
3135
core.debug(`Input: 'open_grouped_issues: ${shouldOpenGroupedIssues}'`)
3236

3337
const octokit = new OctokitWithThrottling({
@@ -131,7 +135,10 @@ export default async function () {
131135
}
132136
}
133137

134-
core.setOutput('filings', JSON.stringify(filings))
135-
core.debug(`Output: 'filings: ${JSON.stringify(filings)}'`)
138+
const filingsPath = path.join(process.env.RUNNER_TEMP || '/tmp', `filings-${crypto.randomUUID()}.json`)
139+
fs.writeFileSync(filingsPath, JSON.stringify(filings))
140+
core.setOutput('filings_file', filingsPath)
141+
142+
core.debug(`Output: 'filings_file: ${filingsPath}'`)
136143
core.info("Finished 'file' action")
137144
}

.github/actions/find/action.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ inputs:
2424
required: false
2525

2626
outputs:
27-
findings:
28-
description: 'List of potential accessibility gaps, as stringified JSON'
27+
findings_file:
28+
description: 'Path to a JSON file containing the list of potential accessibility gaps'
2929

3030
runs:
3131
using: 'node24'

.github/actions/find/src/index.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import type {AuthContextInput, ColorSchemePreference, ReducedMotionPreference} from './types.js'
2+
import fs from 'node:fs'
3+
import path from 'node:path'
24
import * as core from '@actions/core'
35
import {AuthContext} from './AuthContext.js'
46
import {findForUrl} from './findForUrl.js'
@@ -44,8 +46,11 @@ export default async function () {
4446
core.info(`Found ${findingsForUrl.length} findings for ${url}`)
4547
}
4648

47-
core.setOutput('findings', JSON.stringify(findings))
48-
core.debug(`Output: 'findings: ${JSON.stringify(findings)}'`)
49+
const findingsPath = path.join(process.env.RUNNER_TEMP || '/tmp', `findings-${crypto.randomUUID()}.json`)
50+
fs.writeFileSync(findingsPath, JSON.stringify(findings))
51+
core.setOutput('findings_file', findingsPath)
52+
53+
core.debug(`Output: 'findings_file: ${findingsPath}'`)
4954
core.info(`Found ${findings.length} findings in total`)
5055
core.info("Finished 'find' action")
5156
}

.github/actions/fix/action.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ name: "Fix"
22
description: "Attempts to fix issues with Copilot."
33

44
inputs:
5-
issues:
6-
description: "List of issues to attempt to fix, as stringified JSON"
5+
issues_file:
6+
description: "Path to a JSON file containing the list of issues to attempt to fix"
77
required: true
88
repository:
99
description: "Repository (with owner) containing issues"
@@ -13,8 +13,8 @@ inputs:
1313
required: true
1414

1515
outputs:
16-
fixings:
17-
description: "List of pull requests filed (and their associated issues), as stringified JSON"
16+
fixings_file:
17+
description: "Path to a JSON file containing the list of pull requests filed (and their associated issues)"
1818

1919
runs:
2020
using: "node24"

.github/actions/fix/src/index.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import type {Issue as IssueInput, Fixing} from './types.d.js'
2+
import fs from 'node:fs'
3+
import path from 'node:path'
24
import process from 'node:process'
35
import * as core from '@actions/core'
46
import {Octokit} from '@octokit/core'
@@ -11,10 +13,11 @@ const OctokitWithThrottling = Octokit.plugin(throttling)
1113

1214
export default async function () {
1315
core.info("Started 'fix' action")
14-
const issues: IssueInput[] = JSON.parse(core.getInput('issues', {required: true}) || '[]')
16+
const issuesFile = core.getInput('issues_file', {required: true})
17+
const issues: IssueInput[] = JSON.parse(fs.readFileSync(issuesFile, 'utf8'))
1518
const repoWithOwner = core.getInput('repository', {required: true})
1619
const token = core.getInput('token', {required: true})
17-
core.debug(`Input: 'issues: ${JSON.stringify(issues)}'`)
20+
core.debug(`Input: 'issues_file: ${issuesFile}'`)
1821
core.debug(`Input: 'repository: ${repoWithOwner}'`)
1922

2023
const octokit = new OctokitWithThrottling({
@@ -56,7 +59,10 @@ export default async function () {
5659
}
5760
}
5861

59-
core.setOutput('fixings', JSON.stringify(fixings))
60-
core.debug(`Output: 'fixings: ${JSON.stringify(fixings)}'`)
62+
const fixingsPath = path.join(process.env.RUNNER_TEMP || '/tmp', `fixings-${crypto.randomUUID()}.json`)
63+
fs.writeFileSync(fixingsPath, JSON.stringify(fixings))
64+
core.setOutput('fixings_file', fixingsPath)
65+
66+
core.debug(`Output: 'fixings_file: ${fixingsPath}'`)
6167
core.info("Finished 'fix' action")
6268
}

action.yml

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,9 @@ runs:
9090
# If cached data is a list of Finding objects, each with 'issueUrl' keys (i.e. v1),
9191
# convert to a list of (partial) Result objects, each with 'findings' and 'issue' keys (i.e. v2).
9292
# Otherwise, re-output as-is.
93-
printf '%s' "value=$(printf '%s' '${{ steps.restore.outputs.value }}' | jq -c 'if (type == "array" and length > 0 and (.[0] | has("issueUrl"))) then map({findings: [del(.issueUrl)], issue: {url: .issueUrl}}) else . end' )" >> $GITHUB_OUTPUT
93+
# Read directly from the cache file to avoid shell interpolation of large JSON.
94+
jq -c 'if (type == "array" and length > 0 and (.[0] | has("issueUrl"))) then map({findings: [del(.issueUrl)], issue: {url: .issueUrl}}) else . end' "${{ inputs.cache_key }}" > "$RUNNER_TEMP/cached_filings.json"
95+
echo "cached_filings_file=$RUNNER_TEMP/cached_filings.json" >> $GITHUB_OUTPUT
9496
- if: ${{ inputs.login_url && inputs.username && inputs.password && !inputs.auth_context }}
9597
name: Authenticate
9698
id: auth
@@ -113,49 +115,42 @@ runs:
113115
id: file
114116
uses: ./../../_actions/github/accessibility-scanner/current/.github/actions/file
115117
with:
116-
findings: ${{ steps.find.outputs.findings }}
118+
findings_file: ${{ steps.find.outputs.findings_file }}
117119
repository: ${{ inputs.repository }}
118120
token: ${{ inputs.token }}
119-
cached_filings: ${{ steps.normalize_cache.outputs.value }}
121+
cached_filings_file: ${{ steps.normalize_cache.outputs.cached_filings_file }}
120122
screenshot_repository: ${{ github.repository }}
121123
open_grouped_issues: ${{ inputs.open_grouped_issues }}
122-
- if: ${{ steps.file.outputs.filings }}
124+
- if: ${{ steps.file.outputs.filings_file }}
123125
name: Get issues from filings
124126
id: get_issues_from_filings
125127
shell: bash
126128
run: |
127-
# Extract open issues from Filing objects and output as a single-line JSON array
128-
issues=$(jq -c '[.[] | select(.issue.state == "open") | .issue]' <<< '${{ steps.file.outputs.filings }}')
129-
echo "issues=$issues" >> "$GITHUB_OUTPUT"
129+
# Extract open issues from Filing objects and write to a file
130+
jq -c '[.[] | select(.issue.state == "open") | .issue]' "${{ steps.file.outputs.filings_file }}" > "$RUNNER_TEMP/issues.json"
131+
echo "issues_file=$RUNNER_TEMP/issues.json" >> "$GITHUB_OUTPUT"
130132
- if: ${{ inputs.skip_copilot_assignment != 'true' }}
131133
name: Fix
132134
id: fix
133135
uses: ./../../_actions/github/accessibility-scanner/current/.github/actions/fix
134136
with:
135-
issues: ${{ steps.get_issues_from_filings.outputs.issues }}
137+
issues_file: ${{ steps.get_issues_from_filings.outputs.issues_file }}
136138
repository: ${{ inputs.repository }}
137139
token: ${{ inputs.token }}
138-
- name: Write filings and fixings to temp files
139-
shell: bash
140-
run: |
141-
cat > "$RUNNER_TEMP/filings.json" << 'FILINGS_HEREDOC_DELIMITER'
142-
${{ steps.file.outputs.filings || '[]' }}
143-
FILINGS_HEREDOC_DELIMITER
144-
145-
cat > "$RUNNER_TEMP/fixings.json" << 'FIXINGS_HEREDOC_DELIMITER'
146-
${{ steps.fix.outputs.fixings || '[]' }}
147-
FIXINGS_HEREDOC_DELIMITER
148-
149140
- name: Set results output
150141
id: results
151142
shell: bash
143+
env:
144+
FILINGS_FILE: ${{ steps.file.outputs.filings_file }}
145+
FIXINGS_FILE: ${{ steps.fix.outputs.fixings_file }}
152146
run: |
153147
node << 'NODE_SCRIPT'
154148
const fs = require('fs');
155149
const path = require('path');
156-
const runnerTemp = process.env.RUNNER_TEMP;
157-
const filings = JSON.parse(fs.readFileSync(path.join(runnerTemp, 'filings.json'), 'utf8')) || [];
158-
const fixings = JSON.parse(fs.readFileSync(path.join(runnerTemp, 'fixings.json'), 'utf8')) || [];
150+
const filingsFile = process.env.FILINGS_FILE;
151+
const fixingsFile = process.env.FIXINGS_FILE;
152+
const filings = filingsFile && fs.existsSync(filingsFile) ? JSON.parse(fs.readFileSync(filingsFile, 'utf8')) : [];
153+
const fixings = fixingsFile && fs.existsSync(fixingsFile) ? JSON.parse(fs.readFileSync(fixingsFile, 'utf8')) : [];
159154
const fixingsByIssueUrl = fixings.reduce((acc, fixing) => {
160155
if (fixing.issue && fixing.issue.url) acc[fixing.issue.url] = fixing;
161156
return acc;

0 commit comments

Comments
 (0)