diff --git a/.github/workflows/link-check-all.yml b/.github/workflows/link-check-all.yml index 6139618f9f..ad35f5f48d 100644 --- a/.github/workflows/link-check-all.yml +++ b/.github/workflows/link-check-all.yml @@ -22,7 +22,7 @@ concurrency: cancel-in-progress: true jobs: - build: + check-links: runs-on: ${{ fromJSON('["ubuntu-latest", "self-hosted"]')[github.repository == 'github/docs-internal'] }} steps: - name: Checkout @@ -37,26 +37,15 @@ jobs: - name: Install run: npm ci + # Creates file "${{ env.HOME }}/files.json", among others - name: Gather files changed uses: trilom/file-changes-action@a6ca26c14274c33b15e6499323aac178af06ad4b - id: get_diff_files - - # Necessary because trilom/file-changes-action can't escape each file - # name for using in bash. So, we do it ourselves. - # trilom/file-changes-action will, by default produce outputs - # in JSON format. We consume that and set a new output where each - # filename is wrapped in quotation marks. - # Perhaps some day we can rely on this directly based on; - # https://github.com/trilom/file-changes-action/issues/130 - - name: Escape each diff file name - id: get_diff_files_escaped - uses: actions/github-script@2b34a689ec86a68d8ab9478298f91d5401337b7d with: - github-token: ${{ secrets.DOCUBOT_READORG_REPO_WORKFLOW_SCOPES }} - script: | - const input = JSON.parse('${{ steps.get_diff_files.outputs.files }}') - const files = input.map(filename => `"${filename}"`) - core.setOutput('files', files.join(' ')) + fileOutput: 'json' + + # For verification + - name: Show files changed + run: cat $HOME/files.json - name: Link check (warnings, changed files) run: | @@ -66,7 +55,7 @@ jobs: --check-anchors \ --check-images \ --verbose \ - ${{ steps.get_diff_files_escaped.outputs.files }} + --list $HOME/files.json - name: Link check (critical, all files) run: | diff --git a/script/rendered-content-link-checker.mjs b/script/rendered-content-link-checker.mjs index a236edfd73..779b14d2ab 100755 --- a/script/rendered-content-link-checker.mjs +++ b/script/rendered-content-link-checker.mjs @@ -19,6 +19,7 @@ import { languageKeys } from '../lib/languages.js' import warmServer from '../lib/warm-server.js' import renderContent from '../lib/render-content/index.js' import { deprecated } from '../lib/enterprise-server-releases.js' +import readFileAsync from '../lib/readfile-async.js' const STATIC_PREFIXES = { assets: path.resolve('assets'), @@ -65,13 +66,33 @@ program } return parsed }) + .option( + '--list .json', + 'JSON file containing an array of specific files to check (default: none)', + (filePath) => { + const resolvedPath = path.resolve(filePath) + + let stats + try { + stats = fs.statSync(resolvedPath) + } catch (error) { + // Ignore + } + + if (!stats || !stats.isFile()) { + throw new InvalidArgumentError('Not an existing file.') + } + + return resolvedPath + } + ) .arguments('[files...]', 'Specific files to check') .parse(process.argv) main(program.opts(), program.args) async function main(opts, files) { - const { random, language, filter, exit, debug, max, verbose } = opts + const { random, language, filter, exit, debug, max, verbose, list } = opts // Note! The reason we're using `warmServer()` in this script, // even though there's no server involved, is because @@ -89,6 +110,19 @@ async function main(opts, files) { const filters = filter || [] console.assert(Array.isArray(filters), `${filters} is not an array`) + if (list && Array.isArray(files) && files.length > 0) { + throw new InvalidArgumentError('Cannot specify both --list and a file list.') + } + + if (list) { + const fileList = JSON.parse(await readFileAsync(list)) + if (Array.isArray(fileList) && fileList.length > 0) { + files = fileList + } else { + throw new InvalidArgumentError('No files found in --list.') + } + } + if (random) { shuffle(pageList) }