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 1/6] 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(); From 2690d416f8b73c748597cbba3783ded5babedab4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Tue, 24 Feb 2026 21:30:27 +0100 Subject: [PATCH 2/6] Update scripts/check-docs-quality.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Fredrik Adelöw --- scripts/check-docs-quality.js | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/check-docs-quality.js b/scripts/check-docs-quality.js index 4769496ba2..057d4580df 100755 --- a/scripts/check-docs-quality.js +++ b/scripts/check-docs-quality.js @@ -142,6 +142,7 @@ async function ciCheck(prFilesPath) { const severityLevels = { error: 'error', warning: 'warning', + suggestion: 'notice', }; const level = severityLevels[alert.Severity] ?? 'notice'; const col = alert.Span ? alert.Span[0] : 1; From 0c0f46de09f85d8a40098393a781031a0bc301dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Tue, 24 Feb 2026 21:30:33 +0100 Subject: [PATCH 3/6] Update .github/workflows/verify_docs-quality.yml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Fredrik Adelöw --- .github/workflows/verify_docs-quality.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/verify_docs-quality.yml b/.github/workflows/verify_docs-quality.yml index 7aa855e189..6f9798169e 100644 --- a/.github/workflows/verify_docs-quality.yml +++ b/.github/workflows/verify_docs-quality.yml @@ -21,7 +21,9 @@ jobs: - 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 + mkdir -p "$HOME/.local/bin" + tar -xzf /tmp/vale_*_Linux_64-bit.tar.gz -C "$HOME/.local/bin" vale + echo "$HOME/.local/bin" >> "$GITHUB_PATH" vale --version env: GH_TOKEN: ${{ github.token }} From 356d1662d833989327e9ccde0773ab8673d9b70d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Tue, 24 Feb 2026 21:40:56 +0100 Subject: [PATCH 4/6] Update .github/workflows/verify_docs-quality.yml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Fredrik Adelöw --- .github/workflows/verify_docs-quality.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/verify_docs-quality.yml b/.github/workflows/verify_docs-quality.yml index 6f9798169e..1989b39814 100644 --- a/.github/workflows/verify_docs-quality.yml +++ b/.github/workflows/verify_docs-quality.yml @@ -8,6 +8,9 @@ on: jobs: check-docs: + permissions: + contents: read + pull-requests: read runs-on: ubuntu-latest steps: From 4e85e7b0b3692316075d12ff9d5f07df227b5290 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Tue, 24 Feb 2026 21:42:10 +0100 Subject: [PATCH 5/6] Update .github/workflows/verify_docs-quality.yml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Fredrik Adelöw --- .github/workflows/verify_docs-quality.yml | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/.github/workflows/verify_docs-quality.yml b/.github/workflows/verify_docs-quality.yml index 1989b39814..ef8bb40d18 100644 --- a/.github/workflows/verify_docs-quality.yml +++ b/.github/workflows/verify_docs-quality.yml @@ -23,9 +23,21 @@ jobs: - name: Install vale run: | - gh release download --repo errata-ai/vale -p 'vale_*_Linux_64-bit.tar.gz' -D /tmp + VALE_VERSION="3.7.1" + VALE_REPO="errata-ai/vale" + VALE_DIST_DIR="/tmp/vale-dist" + + mkdir -p "$VALE_DIST_DIR" + + # Download pinned Vale binary and corresponding checksums file + gh release download "v${VALE_VERSION}" --repo "$VALE_REPO" --pattern "vale_${VALE_VERSION}_Linux_64-bit.tar.gz" --dir "$VALE_DIST_DIR" + gh release download "v${VALE_VERSION}" --repo "$VALE_REPO" --pattern "vale_${VALE_VERSION}_checksums.txt" --dir "$VALE_DIST_DIR" + + # Verify the integrity of the downloaded archive + (cd "$VALE_DIST_DIR" && sha256sum --check --ignore-missing "vale_${VALE_VERSION}_checksums.txt") + mkdir -p "$HOME/.local/bin" - tar -xzf /tmp/vale_*_Linux_64-bit.tar.gz -C "$HOME/.local/bin" vale + tar -xzf "$VALE_DIST_DIR/vale_${VALE_VERSION}_Linux_64-bit.tar.gz" -C "$HOME/.local/bin" vale echo "$HOME/.local/bin" >> "$GITHUB_PATH" vale --version env: From a22c0a368e6bb5a89ad669034ba0390a94a9243d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Thu, 26 Feb 2026 14:44:02 +0100 Subject: [PATCH 6/6] use a distinct hash instead 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 | 12 ++++++------ scripts/check-docs-quality.js | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/verify_docs-quality.yml b/.github/workflows/verify_docs-quality.yml index ef8bb40d18..6ace7d72a3 100644 --- a/.github/workflows/verify_docs-quality.yml +++ b/.github/workflows/verify_docs-quality.yml @@ -24,20 +24,20 @@ jobs: - name: Install vale run: | VALE_VERSION="3.7.1" - VALE_REPO="errata-ai/vale" + VALE_CHECKSUM="ba4924bf2c5884499f09b02a6eb3318b29df40a3e81701c0804b9b1aefcd9483" VALE_DIST_DIR="/tmp/vale-dist" + VALE_ARCHIVE="vale_${VALE_VERSION}_Linux_64-bit.tar.gz" mkdir -p "$VALE_DIST_DIR" - # Download pinned Vale binary and corresponding checksums file - gh release download "v${VALE_VERSION}" --repo "$VALE_REPO" --pattern "vale_${VALE_VERSION}_Linux_64-bit.tar.gz" --dir "$VALE_DIST_DIR" - gh release download "v${VALE_VERSION}" --repo "$VALE_REPO" --pattern "vale_${VALE_VERSION}_checksums.txt" --dir "$VALE_DIST_DIR" + # Download pinned Vale binary + curl -fsSL -H "Authorization: token $GH_TOKEN" "https://github.com/errata-ai/vale/releases/download/v${VALE_VERSION}/${VALE_ARCHIVE}" -o "$VALE_DIST_DIR/$VALE_ARCHIVE" # Verify the integrity of the downloaded archive - (cd "$VALE_DIST_DIR" && sha256sum --check --ignore-missing "vale_${VALE_VERSION}_checksums.txt") + echo "$VALE_CHECKSUM $VALE_DIST_DIR/$VALE_ARCHIVE" | sha256sum --check mkdir -p "$HOME/.local/bin" - tar -xzf "$VALE_DIST_DIR/vale_${VALE_VERSION}_Linux_64-bit.tar.gz" -C "$HOME/.local/bin" vale + tar -xzf "$VALE_DIST_DIR/$VALE_ARCHIVE" -C "$HOME/.local/bin" vale echo "$HOME/.local/bin" >> "$GITHUB_PATH" vale --version env: diff --git a/scripts/check-docs-quality.js b/scripts/check-docs-quality.js index 057d4580df..06383539c3 100755 --- a/scripts/check-docs-quality.js +++ b/scripts/check-docs-quality.js @@ -36,6 +36,7 @@ const IGNORED_WHEN_LISTING = [ const IGNORED_WHEN_EXPLICIT = [ /^ADOPTERS\.md$/, /^OWNERS\.md$/, + /^.*[/\\]CHANGELOG\.md$/, // generated from changesets anyway - THOSE should have been checked earlier /^.*[/\\]knip-report\.md$/, ];