From b35f3595aa31c8ae8ea0b91d42a91555c2063639 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Tue, 24 Feb 2026 21:15:41 +0100 Subject: [PATCH] run vale directly instead of through the action MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .github/workflows/verify_docs-quality.yml | 32 +++++---- scripts/check-docs-quality.js | 86 ++++++++++++++++++++++- 2 files changed, 102 insertions(+), 16 deletions(-) diff --git a/.github/workflows/verify_docs-quality.yml b/.github/workflows/verify_docs-quality.yml index af42f25390..7aa855e189 100644 --- a/.github/workflows/verify_docs-quality.yml +++ b/.github/workflows/verify_docs-quality.yml @@ -7,7 +7,7 @@ on: - '**.md' jobs: - check-all-files: + check-docs: runs-on: ubuntu-latest steps: @@ -18,18 +18,20 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - # Vale does not support file excludes, so we use the script to generate a list of files instead - # The action also does not allow args or a local config file to be passed in, so the files array - # also contains an "--config=.github/vale/config.ini" option - - name: generate vale args - id: generate - run: echo "args=$(node scripts/check-docs-quality.js --ci-args)" >> $GITHUB_OUTPUT - - - name: documentation quality check - uses: errata-ai/vale-action@d89dee975228ae261d22c15adcd03578634d429c # v2.1.1 - with: - # This also contains --config=.github/vale/config.ini ... :/ - files: '${{ steps.generate.outputs.args }}' - version: latest + - name: Install vale + run: | + gh release download --repo errata-ai/vale -p 'vale_*_Linux_64-bit.tar.gz' -D /tmp + tar -xzf /tmp/vale_*_Linux_64-bit.tar.gz -C /usr/local/bin vale + vale --version env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ github.token }} + + - name: Get changed files + run: | + gh api "repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/files" \ + --paginate -q '.[] | select(.status != "removed") | .filename' > /tmp/pr-files.txt + env: + GH_TOKEN: ${{ github.token }} + + - name: Documentation quality check + run: node scripts/check-docs-quality.js --ci /tmp/pr-files.txt diff --git a/scripts/check-docs-quality.js b/scripts/check-docs-quality.js index fbedcfe077..4769496ba2 100755 --- a/scripts/check-docs-quality.js +++ b/scripts/check-docs-quality.js @@ -71,7 +71,7 @@ async function exitIfMissingVale() { try { // eslint-disable-next-line @backstage/no-undeclared-imports await require('command-exists')('vale'); - } catch (e) { + } catch { console.log( `Language linter (vale) was not found. Please install vale linter (https://vale.sh/docs/vale-cli/installation/).\n`, ); @@ -101,7 +101,91 @@ async function runVale(files) { return true; } +async function ciCheck(prFilesPath) { + const content = await fs.readFile(prFilesPath, 'utf8'); + const prFiles = content.split('\n').filter(f => f.trim()); + + const mdFiles = prFiles + .filter(f => f.endsWith('.md')) + .filter(f => !IGNORED_WHEN_LISTING.some(p => p.test(f))); + + if (mdFiles.length === 0) { + console.log('No documentation files to check.'); + return; + } + + console.log(`Checking ${mdFiles.length} changed documentation file(s)...`); + + const result = spawnSync( + 'vale', + [ + '--config', + resolvePath(rootDir, '.vale.ini'), + '--output=JSON', + ...mdFiles, + ], + { encoding: 'utf8', maxBuffer: 50 * 1024 * 1024 }, + ); + + if (result.error) { + console.error('Failed to run vale:', result.error.message); + process.exit(1); + } + + let issueCount = 0; + + if (result.stdout && result.stdout.trim()) { + try { + const data = JSON.parse(result.stdout); + for (const [file, alerts] of Object.entries(data)) { + for (const alert of alerts) { + const severityLevels = { + error: 'error', + warning: 'warning', + }; + const level = severityLevels[alert.Severity] ?? 'notice'; + const col = alert.Span ? alert.Span[0] : 1; + const endCol = alert.Span ? `,endColumn=${alert.Span[1] + 1}` : ''; + console.log( + `::${level} file=${file},line=${alert.Line},col=${col}${endCol},title=${alert.Check}::${alert.Message}`, + ); + issueCount++; + } + } + } catch { + console.error('Failed to parse vale output:'); + console.error(result.stdout); + process.exit(1); + } + } + + if (result.stderr && result.stderr.trim()) { + console.error(result.stderr); + } + + if (issueCount > 0) { + console.log( + `\nFound ${issueCount} documentation quality issue(s). Please review the annotations above.`, + ); + } + + if (result.status !== 0) { + process.exit(1); + } +} + async function main() { + if (process.argv.includes('--ci')) { + const idx = process.argv.indexOf('--ci'); + const prFilesPath = process.argv[idx + 1]; + if (!prFilesPath) { + console.error('Usage: check-docs-quality.js --ci '); + process.exit(1); + } + await ciCheck(prFilesPath); + return; + } + if (process.argv.includes('--ci-args')) { const files = await listFiles();