Skip to content

Commit 6e22310

Browse files
authored
Add content linter rule for third-party actions disclaimer enforcement (GHD054) (#56133)
1 parent 946ebf0 commit 6e22310

File tree

5 files changed

+511
-1
lines changed

5 files changed

+511
-1
lines changed

data/reusables/contributing/content-linter-rules.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,5 @@
7373
| GHD048 | british-english-quotes | Periods and commas should be placed inside quotation marks (American English style) | warning | punctuation, quotes, style, consistency |
7474
| GHD050 | multiple-emphasis-patterns | Do not use more than one emphasis/strong, italics, or uppercase for a string | warning | formatting, emphasis, style |
7575
| GHD049 | note-warning-formatting | Note and warning tags should be formatted according to style guide | warning | formatting, callouts, notes, warnings, style |
76-
| GHD051 | frontmatter-versions-whitespace | Versions frontmatter should not contain unnecessary whitespace | warning | frontmatter, versions |
76+
| GHD051 | frontmatter-versions-whitespace | Versions frontmatter should not contain unnecessary whitespace | warning | frontmatter, versions |
77+
| GHD054 | third-party-actions-reusable | Code examples with third-party actions must include disclaimer reusable | warning | actions, reusable, third-party |

src/content-linter/lib/linting-rules/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ import { britishEnglishQuotes } from '@/content-linter/lib/linting-rules/british
5151
import { multipleEmphasisPatterns } from '@/content-linter/lib/linting-rules/multiple-emphasis-patterns'
5252
import { noteWarningFormatting } from '@/content-linter/lib/linting-rules/note-warning-formatting'
5353
import { frontmatterVersionsWhitespace } from '@/content-linter/lib/linting-rules/frontmatter-versions-whitespace'
54+
import { thirdPartyActionsReusable } from '@/content-linter/lib/linting-rules/third-party-actions-reusable'
5455

5556
const noDefaultAltText = markdownlintGitHub.find((elem) =>
5657
elem.names.includes('no-default-alt-text'),
@@ -107,5 +108,6 @@ export const gitHubDocsMarkdownlint = {
107108
multipleEmphasisPatterns,
108109
noteWarningFormatting,
109110
frontmatterVersionsWhitespace,
111+
thirdPartyActionsReusable,
110112
],
111113
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import { addError, filterTokens } from 'markdownlint-rule-helpers'
2+
3+
export const thirdPartyActionsReusable = {
4+
names: ['GHD054', 'third-party-actions-reusable'],
5+
description: 'Code examples with third-party actions must include disclaimer reusable',
6+
tags: ['actions', 'reusable', 'third-party'],
7+
function: (params, onError) => {
8+
// Find all code fence blocks
9+
filterTokens(params, 'fence', (token) => {
10+
// Only check YAML code blocks (GitHub Actions workflows)
11+
if (token.info !== 'yaml' && token.info !== 'yaml copy') return
12+
13+
const codeContent = token.content
14+
const lineNumber = token.lineNumber
15+
16+
// Find third-party actions in the code block
17+
const thirdPartyActions = findThirdPartyActions(codeContent)
18+
19+
if (thirdPartyActions.length === 0) return
20+
21+
// Check if the required disclaimer reusable is present before this code block or inside it
22+
const hasDisclaimer = checkForDisclaimer(params.lines, lineNumber, codeContent)
23+
24+
if (!hasDisclaimer) {
25+
const actionList = thirdPartyActions.map((action) => `'${action}'`).join(', ')
26+
addError(
27+
onError,
28+
lineNumber,
29+
`Code examples with third-party actions must include the disclaimer reusable. Found third-party actions: ${actionList}. Add '{% data reusables.actions.actions-not-certified-by-github-comment %}' before or inside this code block.`,
30+
token.line,
31+
null, // No specific range within the line
32+
null, // No fix possible - requires manual addition of reusable
33+
)
34+
}
35+
})
36+
},
37+
}
38+
39+
/**
40+
* Find third-party actions in YAML content
41+
* Third-party actions are identified by the pattern: owner/action@version
42+
* where owner is not 'actions' or 'github'
43+
*/
44+
function findThirdPartyActions(yamlContent) {
45+
const thirdPartyActions = []
46+
47+
// Pattern to match 'uses: owner/action@version' where owner is not actions or github
48+
const actionPattern = /uses:\s+([^{\s]+\/[^@\s]+@[^\s]+)/g
49+
50+
let match
51+
while ((match = actionPattern.exec(yamlContent)) !== null) {
52+
const actionRef = match[1]
53+
54+
// Extract owner from action reference
55+
const parts = actionRef.split('/')
56+
if (parts.length >= 2) {
57+
const owner = parts[0]
58+
59+
// Skip GitHub-owned actions (actions/* and github/*)
60+
if (owner !== 'actions' && owner !== 'github') {
61+
thirdPartyActions.push(actionRef)
62+
}
63+
}
64+
}
65+
66+
return thirdPartyActions
67+
}
68+
69+
/**
70+
* Check if the disclaimer reusable is present before the given line number or inside the code block
71+
* Looks backward from the code block and also inside the code block content
72+
*/
73+
function checkForDisclaimer(lines, codeBlockLineNumber, codeContent) {
74+
const disclaimerPattern = /{% data reusables\.actions\.actions-not-certified-by-github-comment %}/
75+
76+
// First, check inside the code block content
77+
if (disclaimerPattern.test(codeContent)) {
78+
return true
79+
}
80+
81+
// Convert from 1-based line number to 0-based array index
82+
const codeBlockIndex = codeBlockLineNumber - 1
83+
84+
// Search backwards from the code block (up to 10 lines before)
85+
// This is reasonable since disclaimers are typically right before code blocks
86+
const searchStart = Math.max(0, codeBlockIndex - 10)
87+
88+
for (let i = codeBlockIndex - 1; i >= searchStart; i--) {
89+
const line = lines[i]
90+
91+
if (disclaimerPattern.test(line)) {
92+
return true
93+
}
94+
}
95+
96+
return false
97+
}

src/content-linter/style/github-docs.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,12 @@ const githubDocsConfig = {
237237
'partial-markdown-files': true,
238238
'yml-files': true,
239239
},
240+
'third-party-actions-reusable': {
241+
// GHD054
242+
severity: 'warning',
243+
'partial-markdown-files': true,
244+
'yml-files': true,
245+
},
240246
}
241247

242248
export const githubDocsFrontmatterConfig = {

0 commit comments

Comments
 (0)