diff --git a/.github/workflows/content-linter-rules-docs.yml b/.github/workflows/content-linter-rules-docs.yml new file mode 100644 index 000000000000..eab891e9aa30 --- /dev/null +++ b/.github/workflows/content-linter-rules-docs.yml @@ -0,0 +1,48 @@ +name: 'Check content-linter rules docs' + +# **What it does**: Makes sure the content-linter-rules.md is up-to-date. +# **Why we have it**: So what's automated doesn't fall behind +# **Who does it impact**: Docs content. + +on: + workflow_dispatch: + pull_request: + paths: + - 'src/content-linter/**' + # In case imported markdownlint rules are updated + - package-lock.json + # In case manual changes are made to the content-linter-rules.md file + - data/reusables/contributing/content-linter-rules.md + # Self-test + - .github/workflows/content-linter-rules-docs.yml + +permissions: + contents: read + +jobs: + check-content-linter-rules-docs: + runs-on: ${{ fromJSON('["ubuntu-latest", "ubuntu-20.04-xl"]')[github.repository == 'github/docs-internal'] }} + if: github.repository == 'github/docs-internal' || github.repository == 'github/docs' + steps: + - name: Checkout + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + + - uses: ./.github/actions/node-npm-setup + + - name: Check that content-linter-rules.md is up-to-date + run: npm run generate-content-linter-docs + + - name: Fail if it isn't up-to-date + run: | + if [ -n "$(git status --porcelain)" ]; then + git status + git diff + + # Some whitespace for the sake of the message below + echo "" + echo "" + + echo "content-linter-rules.md is out of date." + echo "Please run 'npm run generate-content-linter-docs' and commit the changes." + exit 1; + fi diff --git a/data/reusables/contributing/content-linter-rules.md b/data/reusables/contributing/content-linter-rules.md index 9b7157e32b19..797848d1577a 100644 --- a/data/reusables/contributing/content-linter-rules.md +++ b/data/reusables/contributing/content-linter-rules.md @@ -25,7 +25,7 @@ | [MD049](https://github.com/DavidAnson/markdownlint/blob/v0.28.2/doc/md049.md) | emphasis-style | Emphasis style should be consistent | error | emphasis | | [MD050](https://github.com/DavidAnson/markdownlint/blob/v0.28.2/doc/md050.md) | strong-style | Strong style should be consistent | error | emphasis | | [search-replace](https://github.com/OnkarRuikar/markdownlint-rule-search-replace) | todocs-placeholder | Catch occurrences of TODOCS placeholder. | error | | -| [search-replace](https://github.com/OnkarRuikar/markdownlint-rule-search-replace) | docs-domain | Catch occurrences of docs.gitub.com domain. | warning | | +| [search-replace](https://github.com/OnkarRuikar/markdownlint-rule-search-replace) | docs-domain | Catch occurrences of docs.gitub.com domain. | error | | | [search-replace](https://github.com/OnkarRuikar/markdownlint-rule-search-replace) | help-domain | Catch occurrences of help.github.com domain. | error | | | [search-replace](https://github.com/OnkarRuikar/markdownlint-rule-search-replace) | preview-domain | Catch occurrences of preview.ghdocs.com domain. | error | | | [search-replace](https://github.com/OnkarRuikar/markdownlint-rule-search-replace) | developer-domain | Catch occurrences of developer.github.com domain. | error | | @@ -58,4 +58,6 @@ | GHD017 | frontmatter-liquid-syntax | Frontmatter properties must use valid Liquid | error | liquid, frontmatter | | GHD018 | liquid-syntax | Markdown content must use valid Liquid | error | liquid | | GHD019 | liquid-if-tags | Liquid `ifversion` tags should be used instead of `if` tags when the argument is a valid version | error | liquid, versioning | -| GHD020 | liquid-ifversion-tags | Liquid `ifversion` tags should contain valid version names as arguments | error | liquid, versioning | \ No newline at end of file +| GHD020 | liquid-ifversion-tags | Liquid `ifversion` tags should contain valid version names as arguments | error | liquid, versioning | +| GHD035 | rai-reusable-usage | RAI articles and reusables can only reference reusable content in the data/reusables/rai directory | error | feature, rai | +| GHD036 | image-no-gif | Image must not be a gif, styleguide reference: contributing/style-guide-and-content-model/style-guide.md#images | error | images | \ No newline at end of file diff --git a/package.json b/package.json index 2b91c778a5f9..347d13f870b7 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "lint-content": "node src/content-linter/scripts/lint-content.js", "lint-translation": "cross-env NODE_OPTIONS=--experimental-vm-modules jest src/content-linter/tests/lint-files.js", "generate-code-scanning-query-list": "tsx src/code-scanning/scripts/generate-code-scanning-query-list.ts", + "generate-content-linter-docs": "tsx src/content-linter/scripts/generate-docs.ts", "move-content": "node src/content-render/scripts/move-content.js", "openapi-docs": "node src/rest/docs.js", "playwright-test": "playwright test --config src/fixtures/playwright.config.ts --project=\"Google Chrome\"", diff --git a/src/content-linter/lib/helpers/get-rules.d.ts b/src/content-linter/lib/helpers/get-rules.d.ts new file mode 100644 index 000000000000..524cc84f5b00 --- /dev/null +++ b/src/content-linter/lib/helpers/get-rules.d.ts @@ -0,0 +1,8 @@ +// get-rules.d.ts +// This is a declaration file for get-rules.js + +import type { Rule, RuleConfig } from '../../types.ts' + +export const customRules: Rule[] +export const allRules: Rule[] +export const allConfig: RuleConfig diff --git a/src/content-linter/scripts/generate-docs.js b/src/content-linter/scripts/generate-docs.ts old mode 100755 new mode 100644 similarity index 79% rename from src/content-linter/scripts/generate-docs.js rename to src/content-linter/scripts/generate-docs.ts index f63bc61bcee0..4e7f0c27d047 --- a/src/content-linter/scripts/generate-docs.js +++ b/src/content-linter/scripts/generate-docs.ts @@ -1,5 +1,6 @@ #!/usr/bin/env node import { writeFileSync } from 'fs' +import type { Rule, Config } from '../types.ts' import { allRules, allConfig } from '../lib/helpers/get-rules.js' main() @@ -13,11 +14,12 @@ function main() { ) markdown.push('| Rule ID | Rule Name(s) | Description | Severity | Tags |') markdown.push('| ------- | ------------ | ----------- | -------- | ---- |') - for (const rule of allRules) { - const ruleName = rule.names.find((name) => allConfig[name] !== undefined) - if (allConfig[ruleName] === undefined) continue + + for (const rule of allRules as Rule[]) { + const ruleName = rule.names.find((name) => name in allConfig) + if (!ruleName) continue if (rule.names.includes('search-replace')) { - markdown.push(...getSearchRepolaceRules(rule, allConfig[ruleName])) + markdown.push(...getSearchReplaceRules(rule, allConfig[ruleName])) continue } const row = [] @@ -34,10 +36,10 @@ function main() { // The search-replace rule configures multiple psuedo-rules // under the rules key. -function getSearchRepolaceRules(srRule, ruleConfig) { +function getSearchReplaceRules(srRule: Rule, ruleConfig: Config) { const name = srRule.information ? `[search-replace](${srRule.information})` : 'search-replace' const markdown = [] - for (const rule of ruleConfig.rules) { + for (const rule of ruleConfig.rules || []) { const row = [] row.push(name) row.push(rule.name) diff --git a/src/content-linter/types.ts b/src/content-linter/types.ts new file mode 100644 index 000000000000..894e86915016 --- /dev/null +++ b/src/content-linter/types.ts @@ -0,0 +1,29 @@ +export type Rule = { + names: string[] + description: string + tags: string[] + information?: URL + function: Function[] +} + +type RuleDetail = Rule & { + name: string + 'partial-markdown-files': boolean + message: string + severity: string + searchPattern: string + searchScope: string + precommitSeverity?: string +} + +export type Config = { + severity: string + 'partial-markdown-files': boolean + allowed_languages?: string[] + style?: string + rules?: RuleDetail[] +} + +export type RuleConfig = { + [name: string]: Config +}