Highlight content linting results as PR annotations (#48954)
This commit is contained in:
12
.github/workflows/content-lint-markdown.yml
vendored
12
.github/workflows/content-lint-markdown.yml
vendored
@@ -40,6 +40,18 @@ jobs:
|
|||||||
content/**
|
content/**
|
||||||
data/**
|
data/**
|
||||||
|
|
||||||
|
- name: Print content linter annotations if changed content/data files
|
||||||
|
if: steps.changed-files.outputs.any_changed == 'true'
|
||||||
|
env:
|
||||||
|
# Make it an environment variable so that its value doesn't need to be escaped.
|
||||||
|
# See https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-an-intermediate-environment-variable
|
||||||
|
CHANGED_FILES: |-
|
||||||
|
${{ steps.changed-files.outputs.all_changed_files }}
|
||||||
|
# If there are errors, using `--print-annotations` will make it
|
||||||
|
# so it does *not* exit non-zero.
|
||||||
|
# This is so that all warnings and errors are printed.
|
||||||
|
run: npm run lint-content -- --print-annotations --paths $CHANGED_FILES
|
||||||
|
|
||||||
- name: Run content linter if changed content/data files
|
- name: Run content linter if changed content/data files
|
||||||
if: steps.changed-files.outputs.any_changed == 'true'
|
if: steps.changed-files.outputs.any_changed == 'true'
|
||||||
env:
|
env:
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ redirect_from:
|
|||||||
versions:
|
versions:
|
||||||
fpt: '*'
|
fpt: '*'
|
||||||
ghes: '*'
|
ghes: '*'
|
||||||
ghae: '*'
|
|
||||||
ghec: '*'
|
ghec: '*'
|
||||||
shortTitle: Events that trigger workflows
|
shortTitle: Events that trigger workflows
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ intro: 'When a workflow is configured to run on the `workflow_dispatch` event, y
|
|||||||
versions:
|
versions:
|
||||||
fpt: '*'
|
fpt: '*'
|
||||||
ghes: '*'
|
ghes: '*'
|
||||||
ghae: '*'
|
|
||||||
ghec: '*'
|
ghec: '*'
|
||||||
shortTitle: Manually run a workflow
|
shortTitle: Manually run a workflow
|
||||||
redirect_from:
|
redirect_from:
|
||||||
|
|||||||
45
src/content-linter/lib/helpers/print-annotations.js
Normal file
45
src/content-linter/lib/helpers/print-annotations.js
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
/**
|
||||||
|
* This function is meant to be used by the code that runs the linter,
|
||||||
|
* but only within Actions workflows. That means, if it works, it
|
||||||
|
* posts all the annotations as inline comments on the PR.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function printAnnotationResults(results, { skippableRules = [] } = {}) {
|
||||||
|
for (const [file, flaws] of Object.entries(results)) {
|
||||||
|
for (const flaw of flaws) {
|
||||||
|
if (intersection(flaw.ruleNames, skippableRules)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
let annotation = `::${flaw.severity === 'error' ? 'error' : 'warning'} `
|
||||||
|
const bits = [`file=${file}`]
|
||||||
|
if (flaw.lineNumber) {
|
||||||
|
bits.push(`line=${flaw.lineNumber}`)
|
||||||
|
// Note: it's possible to use a endLine property
|
||||||
|
// if you can "lump" together the same error description on
|
||||||
|
// consecutive lines.
|
||||||
|
// See https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-error-message
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flaw.ruleDescription) {
|
||||||
|
bits.push(`title=${flaw.ruleDescription}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
annotation += `${bits.join(',')}`
|
||||||
|
|
||||||
|
if (flaw.errorDetail) {
|
||||||
|
annotation += `::${flaw.errorDetail}`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Why console.log and not `core.error()` (from @actions/core)?
|
||||||
|
// Because, this way you can debug this more easily on you own
|
||||||
|
// terminal.
|
||||||
|
console.log(annotation)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function intersection(arr1, arr2) {
|
||||||
|
return arr1.some((item) => arr2.includes(item))
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@ import { allConfig, allRules, customRules } from '../lib/helpers/get-rules.js'
|
|||||||
import { customConfig, githubDocsFrontmatterConfig } from '../style/github-docs.js'
|
import { customConfig, githubDocsFrontmatterConfig } from '../style/github-docs.js'
|
||||||
import { defaultConfig } from '../lib/default-markdownlint-options.js'
|
import { defaultConfig } from '../lib/default-markdownlint-options.js'
|
||||||
import { prettyPrintResults } from './pretty-print-results.js'
|
import { prettyPrintResults } from './pretty-print-results.js'
|
||||||
|
import { printAnnotationResults } from '../lib/helpers/print-annotations.js'
|
||||||
|
|
||||||
program
|
program
|
||||||
.description('Run GitHub Docs Markdownlint rules.')
|
.description('Run GitHub Docs Markdownlint rules.')
|
||||||
@@ -45,6 +46,7 @@ program
|
|||||||
.addOption(
|
.addOption(
|
||||||
new Option('-o, --output-file <filepath>', `Outputs the errors/warnings to the filepath.`),
|
new Option('-o, --output-file <filepath>', `Outputs the errors/warnings to the filepath.`),
|
||||||
)
|
)
|
||||||
|
.option('--print-annotations', 'Print annotations for GitHub Actions check runs.', false)
|
||||||
.parse(process.argv)
|
.parse(process.argv)
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -56,6 +58,7 @@ const {
|
|||||||
outputFile,
|
outputFile,
|
||||||
verbose,
|
verbose,
|
||||||
precommit: isPrecommit,
|
precommit: isPrecommit,
|
||||||
|
printAnnotations,
|
||||||
} = program.opts()
|
} = program.opts()
|
||||||
|
|
||||||
const ALL_CONTENT_DIR = ['content', 'data']
|
const ALL_CONTENT_DIR = ['content', 'data']
|
||||||
@@ -91,7 +94,7 @@ async function main() {
|
|||||||
customRules: configuredRules.data,
|
customRules: configuredRules.data,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Run Markdownlint for data directory
|
// Run Markdownlint for content directory (frontmatter only)
|
||||||
const resultFrontmatter = await markdownlint.promises.markdownlint({
|
const resultFrontmatter = await markdownlint.promises.markdownlint({
|
||||||
frontMatter: null,
|
frontMatter: null,
|
||||||
files: files.content,
|
files: files.content,
|
||||||
@@ -154,6 +157,18 @@ async function main() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (printAnnotations) {
|
||||||
|
printAnnotationResults(formattedResults, {
|
||||||
|
skippableRules: [
|
||||||
|
// As of Feb 2024, this rule is quite noisy. It's present in
|
||||||
|
// many files and is not always a problem. And besides, when it
|
||||||
|
// does warn, it's usually a very long one.
|
||||||
|
'code-fence-line-length', // a.k.a. GHD030
|
||||||
|
],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const end = Date.now()
|
const end = Date.now()
|
||||||
// Ensure previous console logging is not truncated
|
// Ensure previous console logging is not truncated
|
||||||
console.log('\n')
|
console.log('\n')
|
||||||
@@ -214,9 +229,15 @@ async function main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (errorFileCount) {
|
if (errorFileCount) {
|
||||||
process.exit(1)
|
if (printAnnotations) {
|
||||||
|
console.warn('When printing annotations, the exit code is always 0')
|
||||||
|
process.exit(0)
|
||||||
|
} else {
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
spinner.succeed('No errors found')
|
||||||
}
|
}
|
||||||
spinner.succeed('No errors found')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function pluralize(things, word, pluralForm = null) {
|
function pluralize(things, word, pluralForm = null) {
|
||||||
|
|||||||
Reference in New Issue
Block a user