Skip to content

Commit 117caea

Browse files
rachmariPeter Bengtsson
andauthored
rai review enforcement (#47237)
Co-authored-by: Peter Bengtsson <[email protected]>
1 parent a0b43ec commit 117caea

File tree

9 files changed

+199
-80
lines changed

9 files changed

+199
-80
lines changed

.github/CODEOWNERS

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,4 @@ src/ghes-releases/lib/enterprise-dates.json @github/docs-content-enterprise
1414
content/actions/deployment/security-hardening-your-deployments/** @github/oidc
1515

1616
# RAI - CELA
17-
data/reusables/copilot/about-copilot-chat.md @github/legal-product
18-
content/copilot/github-copilot-in-the-cli/about-github-copilot-in-the-cli.md @github/legal-product
19-
20-
content/code-security/secret-scanning/about-the-regular-expression-generator-for-custom-patterns @github/legal-product
21-
data/reusables/secret-scanning/beta-custom-pattern-regular-expression-generator.md @github/legal-product
22-
23-
content/code-security/secret-scanning/about-the-detection-of-generic-secrets-with-secret-scanning.md @github/legal-product
24-
data/reusables/secret-scanning/generic-secret-detection-ai.md @github/legal-product
25-
26-
content/code-security/code-scanning/managing-code-scanning-alerts/about-autofix-for-codeql-code-scanning.md @github/legal-product
27-
data/reusables/rai/ @github/legal-product
28-
29-
content/copilot/github-copilot-enterprise/copilot-pull-request-summaries/about-copilot-pull-request-summaries.md @github/legal-product
17+
data/reusables/rai/** @github/legal-product
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
name: Codeowners - Legal
2+
3+
# **What it does**: Enforces reviews of Responsible AI (RAI) content by the GitHub legal team. Because RAI content can live anywhere in the content directory, it becomes a maintenance problem to use CODEOWNERS to enforce review on each article.
4+
# **Why we have it**: RAI content must be reviewed by the GitHub legal team.
5+
# **Who does it impact**: Content writers and the GitHub legal team.
6+
7+
on:
8+
pull_request:
9+
types:
10+
- edited
11+
- opened
12+
- ready_for_review
13+
- reopened
14+
- synchronize
15+
paths:
16+
- 'content/**'
17+
18+
permissions:
19+
contents: read
20+
pull-requests: write
21+
22+
jobs:
23+
codeowners-legal:
24+
if: >-
25+
${{ github.repository == 'github/docs-internal' &&
26+
!github.event.pull_request.draft &&
27+
github.event.pull_request.head.ref != 'repo-sync' }}
28+
runs-on: ubuntu-latest
29+
steps:
30+
- name: Get files changed
31+
uses: dorny/paths-filter@4512585405083f25c027a35db413c2b3b9006d50
32+
id: filter
33+
with:
34+
# Base branch used to get changed files
35+
base: 'main'
36+
37+
# Enables setting an output in the format in `${FILTER_NAME}_files
38+
# with the names of the matching files formatted as JSON array
39+
list-files: json
40+
41+
# Returns list of changed files matching each filter
42+
filters: |
43+
rai:
44+
- 'content/**'
45+
46+
- name: Check out repo
47+
if: ${{ steps.filter.outputs.rai}}
48+
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
49+
50+
- name: Check content type
51+
id: checkContentType
52+
if: ${{ steps.filter.outputs.rai}}
53+
run: npm run check-content-type
54+
env:
55+
FILE_PATHS_CONTENT_TYPES: ${{ steps.filter.outputs.rai_files }}
56+
CONTENT_TYPE: 'rai'
57+
58+
- name: Add Legal team as a reviewer
59+
if: ${{ steps.checkContentType.outputs.contentType }}
60+
env:
61+
# The GH CLI uses a slightly different env name for
62+
# the token than the GITHUB_TOKEN used by actions
63+
GH_TOKEN: ${{ github.token }}
64+
PR: ${{ github.event.pull_request.html_url }}
65+
run: |
66+
has_reviewer=$(
67+
gh pr view $PR --json reviews |
68+
jq 'any(.reviews[]; select(length > 0))'
69+
)
70+
if ! $has_reviewer
71+
then
72+
gh pr edit $PR --add-reviewer github/legal-product
73+
fi

.github/workflows/triage-unallowed-contributions.yml

Lines changed: 12 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,6 @@ name: Check unallowed file changes
77
on:
88
# Needed in lieu of `pull_request` so that PRs from a fork can be notified of unallowed changes.
99
pull_request_target:
10-
paths:
11-
- '.devcontainer/**'
12-
- '.github/workflows/**'
13-
- '.github/CODEOWNERS'
14-
- 'assets/fonts/**'
15-
- 'data/graphql/**'
16-
- 'Dockerfile*'
17-
- 'src/*/scripts/**'
18-
- 'src/**.json'
19-
- 'src/workflows/**'
20-
- 'lib/redirects/**'
21-
- 'package*.json'
22-
- 'content/actions/deployment/security-hardening-your-deployments/**'
2310

2411
permissions:
2512
contents: read
@@ -35,6 +22,9 @@ jobs:
3522
}}
3623
runs-on: ubuntu-latest
3724
steps:
25+
- name: Check out repo
26+
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
27+
3828
- name: Get files changed
3929
uses: dorny/paths-filter@4512585405083f25c027a35db413c2b3b9006d50
4030
id: filter
@@ -47,61 +37,16 @@ jobs:
4737
list-files: json
4838

4939
# Returns list of changed files matching each filter
50-
filters: |
51-
openapi:
52-
- 'src/rest/data/**'
53-
notAllowed:
54-
- '.devcontainer/**'
55-
- '.github/workflows/**'
56-
- '.github/CODEOWNERS'
57-
- 'assets/fonts/**'
58-
- 'data/graphql/**'
59-
- 'Dockerfile*'
60-
- 'src/*/scripts/**'
61-
- 'src/**.json'
62-
- 'src/workflows/**'
63-
- 'lib/redirects/**'
64-
- 'package*.json'
65-
- 'content/actions/deployment/security-hardening-your-deployments/**'
40+
filters: 'src/workflows/unallowed-contribution-filters.yml'
6641

6742
# When there are changes to files we can't accept, leave a comment
6843
# explaining this to the PR author
6944
- name: "Comment about changes we can't accept"
70-
if: ${{ steps.filter.outputs.notAllowed }}
71-
uses: actions/github-script@e69ef5462fd455e02edcaf4dd7708eda96b9eda0
72-
with:
73-
script: |
74-
const badFilesArr = [
75-
'.devcontainer/**',
76-
'.github/workflows/**',
77-
'.github/CODEOWNERS',
78-
'assets/fonts/**',
79-
'data/graphql/**',
80-
'Dockerfile*',
81-
'src/*/scripts/**',
82-
'src/**.json',
83-
'src/workflows/**',
84-
'lib/redirects/**',
85-
'package*.json',
86-
'content/actions/deployment/security-hardening-your-deployments/**',
87-
]
88-
89-
const badFiles = badFilesArr.join('\n')
90-
91-
let reviewMessage = `👋 Hey there spelunker. It looks like you've modified some files that we can't accept as contributions. The complete list of files we can't accept are:\n${badFiles}\n\nYou'll need to revert all of the files you changed in that list using [GitHub Desktop](https://docs.github.com/en/free-pro-team@latest/desktop/contributing-and-collaborating-using-github-desktop/managing-commits/reverting-a-commit-in-github-desktop) or \`git checkout origin/main <file name>\`. Once you get those files reverted, we can continue with the review process. :octocat:`
92-
let workflowFailMessage = "It looks like you've modified some files that we can't accept as contributions."
93-
94-
try {
95-
createdComment = await github.rest.issues.createComment({
96-
owner: context.repo.owner,
97-
repo: context.repo.repo,
98-
issue_number: context.payload.number,
99-
body: reviewMessage,
100-
})
101-
102-
workflowFailMessage = `${workflowFailMessage} Please see ${createdComment.data.html_url} for details.`
103-
} catch(err) {
104-
console.log("Error creating comment.", err)
105-
}
106-
107-
core.setFailed(workflowFailMessage)
45+
if: ${{ steps.filter.outputs.notAllowed || steps.filter.outputs.contentTypes}}
46+
run: npm run unallowed-contributions
47+
env:
48+
REPO_OWNER_AND_NAME: ${{ github.repository }}
49+
PR_NUMBER: ${{ github.event.number }}
50+
FILE_PATHS_NOT_ALLOWED: ${{ steps.filter.outputs.notAllowed_files }}
51+
FILE_PATHS_CONTENT_TYPES: ${{ steps.filter.outputs.contentTypes_files }}
52+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"analyze-text": "node src/search/scripts/analyze-text.js",
2020
"archive-version": "node src/ghes-releases/scripts/archive-version.js",
2121
"build": "next build",
22+
"check-content-type": "node src/workflows/check-content-type.js",
2223
"check-github-github-links": "node src/links/scripts/check-github-github-links.js",
2324
"copy-fixture-data": "node src/tests/scripts/copy-fixture-data.js",
2425
"create-translation-health-report": "node src/languages/scripts/create-translation-health-report.js",
@@ -63,6 +64,7 @@
6364
"test-watch": "cross-env NODE_OPTIONS=--experimental-vm-modules jest --watch --notify --notifyMode=change --coverage",
6465
"toggle-ghae-feature-flags": "node src/versions/scripts/toggle-ghae-feature-flags.js",
6566
"tsc": "tsc --noEmit",
67+
"unallowed-contributions": "node src/workflows/unallowed-contributions.js",
6668
"update-data-and-image-paths": "node src/early-access/scripts/update-data-and-image-paths.js",
6769
"update-internal-links": "node src/links/scripts/update-internal-links.js",
6870
"validate-asset-images": "node src/assets/scripts/validate-asset-images.js",

src/frame/tests/secure-files.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ const secureFiles = [
1515
path: 'content/actions/deployment/security-hardening-your-deployments/**',
1616
requiredCodeOwner: 'github/oidc',
1717
},
18+
{
19+
name: 'RAI transparency note reusable directory',
20+
path: 'data/reusables/rai',
21+
requiredCodeOwner: 'github/legal-product',
22+
},
1823
]
1924

2025
const codeOwnersFile = await fs.readFile('.github/CODEOWNERS', 'utf8')
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#!/usr/bin/env node
2+
3+
import coreLib from '@actions/core'
4+
5+
import { checkContentType } from '#src/workflows/fm-utils.js'
6+
7+
const { FILE_PATHS_CONTENT_TYPES, CONTENT_TYPE } = process.env
8+
9+
main()
10+
11+
async function main() {
12+
const filePaths = JSON.parse(FILE_PATHS_CONTENT_TYPES)
13+
const containsRai = checkContentType(filePaths, CONTENT_TYPE)
14+
if (containsRai.length === 0) {
15+
coreLib.setOutput('contentType', false)
16+
} else {
17+
coreLib.setOutput('contentType', true)
18+
}
19+
}

src/workflows/fm-utils.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { readFileSync } from 'fs'
2+
import matter from 'gray-matter'
3+
4+
// Filters out files from a list of filePaths
5+
// that have a type property in their frontmatter
6+
// where the type value matches the type argument
7+
export function checkContentType(filePaths, type) {
8+
const unallowedChangedFiles = []
9+
for (const filePath of filePaths) {
10+
const { data } = matter(readFileSync(filePath, 'utf8'))
11+
if (data.type === type) {
12+
unallowedChangedFiles.push(filePath)
13+
}
14+
}
15+
return unallowedChangedFiles
16+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
notAllowed:
2+
- '.devcontainer/**'
3+
- '.github/workflows/**'
4+
- '.github/CODEOWNERS'
5+
- 'assets/fonts/**'
6+
- 'data/graphql/**'
7+
- 'Dockerfile*'
8+
- 'src/*/scripts/**'
9+
- 'src/**.json'
10+
- 'src/workflows/**'
11+
- 'lib/redirects/**'
12+
- 'package*.json'
13+
- 'content/actions/deployment/security-hardening-your-deployments/**'
14+
contentTypes:
15+
- 'content/**'
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#!/usr/bin/env node
2+
3+
import coreLib from '@actions/core'
4+
import { readFileSync } from 'fs'
5+
import yaml from 'js-yaml'
6+
7+
import { checkContentType } from '#src/workflows/fm-utils.js'
8+
import github from '#src/workflows/github.js'
9+
10+
const core = coreLib
11+
const octokit = github()
12+
13+
const { PR_NUMBER, REPO_OWNER_AND_NAME, FILE_PATHS_NOT_ALLOWED, FILE_PATHS_CONTENT_TYPES } =
14+
process.env
15+
const [owner, repo] = REPO_OWNER_AND_NAME.split('/')
16+
const filters = yaml.load(readFileSync('src/workflows/unallowed-contribution-filters.yml', 'utf8'))
17+
18+
main()
19+
20+
async function main() {
21+
// Files in the diff that match specific paths we don't allow
22+
const unallowedChangedFiles = [...JSON.parse(FILE_PATHS_NOT_ALLOWED)]
23+
// Any changes to a file in the content directory could potentially
24+
// have `type: rai` so each changed content file's frontmatter needs
25+
// to be checked.
26+
unallowedChangedFiles.push(
27+
...(await checkContentType(JSON.parse(FILE_PATHS_CONTENT_TYPES), 'rai')),
28+
)
29+
30+
if (unallowedChangedFiles.length === 0) return
31+
32+
// Format into Markdown bulleted list to use in the PR comment
33+
const listUnallowedChangedFiles = unallowedChangedFiles.map((file) => `\n - ${file}`).join('')
34+
const listUnallowedFiles = filters.notAllowed.map((file) => `\n - ${file}`).join('')
35+
36+
const reviewMessage = `👋 Hey there spelunker. It looks like you've modified some files that we can't accept as contributions:${listUnallowedChangedFiles}\n\nYou'll need to revert all of the files you changed that match that list using [GitHub Desktop](https://docs.github.com/en/free-pro-team@latest/desktop/contributing-and-collaborating-using-github-desktop/managing-commits/reverting-a-commit-in-github-desktop) or \`git checkout origin/main <file name>\`. Once you get those files reverted, we can continue with the review process. :octocat:\n\nThe complete list of files we can't accept are:${listUnallowedFiles}\n\nWe also can't accept contributions to files in the content directory with frontmatter \`type: rai\`.`
37+
38+
let workflowFailMessage =
39+
"It looks like you've modified some files that we can't accept as contributions."
40+
let createdComment
41+
42+
try {
43+
createdComment = await octokit.rest.issues.createComment({
44+
owner,
45+
repo,
46+
issue_number: PR_NUMBER,
47+
body: reviewMessage,
48+
})
49+
50+
workflowFailMessage = `${workflowFailMessage} Please see ${createdComment.data.html_url} for details.`
51+
} catch (err) {
52+
console.log('Error creating comment.', err)
53+
}
54+
55+
core.setFailed(workflowFailMessage)
56+
}

0 commit comments

Comments
 (0)