rai review enforcement (#47237)
Co-authored-by: Peter Bengtsson <peterbe@github.com>
This commit is contained in:
14
.github/CODEOWNERS
vendored
14
.github/CODEOWNERS
vendored
@@ -14,16 +14,4 @@ src/ghes-releases/lib/enterprise-dates.json @github/docs-content-enterprise
|
||||
content/actions/deployment/security-hardening-your-deployments/** @github/oidc
|
||||
|
||||
# RAI - CELA
|
||||
data/reusables/copilot/about-copilot-chat.md @github/legal-product
|
||||
content/copilot/github-copilot-in-the-cli/about-github-copilot-in-the-cli.md @github/legal-product
|
||||
|
||||
content/code-security/secret-scanning/about-the-regular-expression-generator-for-custom-patterns @github/legal-product
|
||||
data/reusables/secret-scanning/beta-custom-pattern-regular-expression-generator.md @github/legal-product
|
||||
|
||||
content/code-security/secret-scanning/about-the-detection-of-generic-secrets-with-secret-scanning.md @github/legal-product
|
||||
data/reusables/secret-scanning/generic-secret-detection-ai.md @github/legal-product
|
||||
|
||||
content/code-security/code-scanning/managing-code-scanning-alerts/about-autofix-for-codeql-code-scanning.md @github/legal-product
|
||||
data/reusables/rai/ @github/legal-product
|
||||
|
||||
content/copilot/github-copilot-enterprise/copilot-pull-request-summaries/about-copilot-pull-request-summaries.md @github/legal-product
|
||||
data/reusables/rai/** @github/legal-product
|
||||
|
||||
73
.github/workflows/codeowners-legal.yml
vendored
Normal file
73
.github/workflows/codeowners-legal.yml
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
name: Codeowners - Legal
|
||||
|
||||
# **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.
|
||||
# **Why we have it**: RAI content must be reviewed by the GitHub legal team.
|
||||
# **Who does it impact**: Content writers and the GitHub legal team.
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- edited
|
||||
- opened
|
||||
- ready_for_review
|
||||
- reopened
|
||||
- synchronize
|
||||
paths:
|
||||
- 'content/**'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
codeowners-legal:
|
||||
if: >-
|
||||
${{ github.repository == 'github/docs-internal' &&
|
||||
!github.event.pull_request.draft &&
|
||||
github.event.pull_request.head.ref != 'repo-sync' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Get files changed
|
||||
uses: dorny/paths-filter@4512585405083f25c027a35db413c2b3b9006d50
|
||||
id: filter
|
||||
with:
|
||||
# Base branch used to get changed files
|
||||
base: 'main'
|
||||
|
||||
# Enables setting an output in the format in `${FILTER_NAME}_files
|
||||
# with the names of the matching files formatted as JSON array
|
||||
list-files: json
|
||||
|
||||
# Returns list of changed files matching each filter
|
||||
filters: |
|
||||
rai:
|
||||
- 'content/**'
|
||||
|
||||
- name: Check out repo
|
||||
if: ${{ steps.filter.outputs.rai}}
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
|
||||
- name: Check content type
|
||||
id: checkContentType
|
||||
if: ${{ steps.filter.outputs.rai}}
|
||||
run: npm run check-content-type
|
||||
env:
|
||||
FILE_PATHS_CONTENT_TYPES: ${{ steps.filter.outputs.rai_files }}
|
||||
CONTENT_TYPE: 'rai'
|
||||
|
||||
- name: Add Legal team as a reviewer
|
||||
if: ${{ steps.checkContentType.outputs.contentType }}
|
||||
env:
|
||||
# The GH CLI uses a slightly different env name for
|
||||
# the token than the GITHUB_TOKEN used by actions
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
PR: ${{ github.event.pull_request.html_url }}
|
||||
run: |
|
||||
has_reviewer=$(
|
||||
gh pr view $PR --json reviews |
|
||||
jq 'any(.reviews[]; select(length > 0))'
|
||||
)
|
||||
if ! $has_reviewer
|
||||
then
|
||||
gh pr edit $PR --add-reviewer github/legal-product
|
||||
fi
|
||||
@@ -7,19 +7,6 @@ name: Check unallowed file changes
|
||||
on:
|
||||
# Needed in lieu of `pull_request` so that PRs from a fork can be notified of unallowed changes.
|
||||
pull_request_target:
|
||||
paths:
|
||||
- '.devcontainer/**'
|
||||
- '.github/workflows/**'
|
||||
- '.github/CODEOWNERS'
|
||||
- 'assets/fonts/**'
|
||||
- 'data/graphql/**'
|
||||
- 'Dockerfile*'
|
||||
- 'src/*/scripts/**'
|
||||
- 'src/**.json'
|
||||
- 'src/workflows/**'
|
||||
- 'lib/redirects/**'
|
||||
- 'package*.json'
|
||||
- 'content/actions/deployment/security-hardening-your-deployments/**'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -35,6 +22,9 @@ jobs:
|
||||
}}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out repo
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
|
||||
- name: Get files changed
|
||||
uses: dorny/paths-filter@4512585405083f25c027a35db413c2b3b9006d50
|
||||
id: filter
|
||||
@@ -47,61 +37,16 @@ jobs:
|
||||
list-files: json
|
||||
|
||||
# Returns list of changed files matching each filter
|
||||
filters: |
|
||||
openapi:
|
||||
- 'src/rest/data/**'
|
||||
notAllowed:
|
||||
- '.devcontainer/**'
|
||||
- '.github/workflows/**'
|
||||
- '.github/CODEOWNERS'
|
||||
- 'assets/fonts/**'
|
||||
- 'data/graphql/**'
|
||||
- 'Dockerfile*'
|
||||
- 'src/*/scripts/**'
|
||||
- 'src/**.json'
|
||||
- 'src/workflows/**'
|
||||
- 'lib/redirects/**'
|
||||
- 'package*.json'
|
||||
- 'content/actions/deployment/security-hardening-your-deployments/**'
|
||||
filters: 'src/workflows/unallowed-contribution-filters.yml'
|
||||
|
||||
# When there are changes to files we can't accept, leave a comment
|
||||
# explaining this to the PR author
|
||||
- name: "Comment about changes we can't accept"
|
||||
if: ${{ steps.filter.outputs.notAllowed }}
|
||||
uses: actions/github-script@e69ef5462fd455e02edcaf4dd7708eda96b9eda0
|
||||
with:
|
||||
script: |
|
||||
const badFilesArr = [
|
||||
'.devcontainer/**',
|
||||
'.github/workflows/**',
|
||||
'.github/CODEOWNERS',
|
||||
'assets/fonts/**',
|
||||
'data/graphql/**',
|
||||
'Dockerfile*',
|
||||
'src/*/scripts/**',
|
||||
'src/**.json',
|
||||
'src/workflows/**',
|
||||
'lib/redirects/**',
|
||||
'package*.json',
|
||||
'content/actions/deployment/security-hardening-your-deployments/**',
|
||||
]
|
||||
|
||||
const badFiles = badFilesArr.join('\n')
|
||||
|
||||
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:`
|
||||
let workflowFailMessage = "It looks like you've modified some files that we can't accept as contributions."
|
||||
|
||||
try {
|
||||
createdComment = await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.payload.number,
|
||||
body: reviewMessage,
|
||||
})
|
||||
|
||||
workflowFailMessage = `${workflowFailMessage} Please see ${createdComment.data.html_url} for details.`
|
||||
} catch(err) {
|
||||
console.log("Error creating comment.", err)
|
||||
}
|
||||
|
||||
core.setFailed(workflowFailMessage)
|
||||
if: ${{ steps.filter.outputs.notAllowed || steps.filter.outputs.contentTypes}}
|
||||
run: npm run unallowed-contributions
|
||||
env:
|
||||
REPO_OWNER_AND_NAME: ${{ github.repository }}
|
||||
PR_NUMBER: ${{ github.event.number }}
|
||||
FILE_PATHS_NOT_ALLOWED: ${{ steps.filter.outputs.notAllowed_files }}
|
||||
FILE_PATHS_CONTENT_TYPES: ${{ steps.filter.outputs.contentTypes_files }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
"analyze-text": "node src/search/scripts/analyze-text.js",
|
||||
"archive-version": "node src/ghes-releases/scripts/archive-version.js",
|
||||
"build": "next build",
|
||||
"check-content-type": "node src/workflows/check-content-type.js",
|
||||
"check-github-github-links": "node src/links/scripts/check-github-github-links.js",
|
||||
"copy-fixture-data": "node src/tests/scripts/copy-fixture-data.js",
|
||||
"create-translation-health-report": "node src/languages/scripts/create-translation-health-report.js",
|
||||
@@ -63,6 +64,7 @@
|
||||
"test-watch": "cross-env NODE_OPTIONS=--experimental-vm-modules jest --watch --notify --notifyMode=change --coverage",
|
||||
"toggle-ghae-feature-flags": "node src/versions/scripts/toggle-ghae-feature-flags.js",
|
||||
"tsc": "tsc --noEmit",
|
||||
"unallowed-contributions": "node src/workflows/unallowed-contributions.js",
|
||||
"update-data-and-image-paths": "node src/early-access/scripts/update-data-and-image-paths.js",
|
||||
"update-internal-links": "node src/links/scripts/update-internal-links.js",
|
||||
"validate-asset-images": "node src/assets/scripts/validate-asset-images.js",
|
||||
|
||||
@@ -15,6 +15,11 @@ const secureFiles = [
|
||||
path: 'content/actions/deployment/security-hardening-your-deployments/**',
|
||||
requiredCodeOwner: 'github/oidc',
|
||||
},
|
||||
{
|
||||
name: 'RAI transparency note reusable directory',
|
||||
path: 'data/reusables/rai',
|
||||
requiredCodeOwner: 'github/legal-product',
|
||||
},
|
||||
]
|
||||
|
||||
const codeOwnersFile = await fs.readFile('.github/CODEOWNERS', 'utf8')
|
||||
|
||||
19
src/workflows/check-content-type.js
Executable file
19
src/workflows/check-content-type.js
Executable file
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import coreLib from '@actions/core'
|
||||
|
||||
import { checkContentType } from '#src/workflows/fm-utils.js'
|
||||
|
||||
const { FILE_PATHS_CONTENT_TYPES, CONTENT_TYPE } = process.env
|
||||
|
||||
main()
|
||||
|
||||
async function main() {
|
||||
const filePaths = JSON.parse(FILE_PATHS_CONTENT_TYPES)
|
||||
const containsRai = checkContentType(filePaths, CONTENT_TYPE)
|
||||
if (containsRai.length === 0) {
|
||||
coreLib.setOutput('contentType', false)
|
||||
} else {
|
||||
coreLib.setOutput('contentType', true)
|
||||
}
|
||||
}
|
||||
16
src/workflows/fm-utils.js
Normal file
16
src/workflows/fm-utils.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import { readFileSync } from 'fs'
|
||||
import matter from 'gray-matter'
|
||||
|
||||
// Filters out files from a list of filePaths
|
||||
// that have a type property in their frontmatter
|
||||
// where the type value matches the type argument
|
||||
export function checkContentType(filePaths, type) {
|
||||
const unallowedChangedFiles = []
|
||||
for (const filePath of filePaths) {
|
||||
const { data } = matter(readFileSync(filePath, 'utf8'))
|
||||
if (data.type === type) {
|
||||
unallowedChangedFiles.push(filePath)
|
||||
}
|
||||
}
|
||||
return unallowedChangedFiles
|
||||
}
|
||||
15
src/workflows/unallowed-contribution-filters.yml
Normal file
15
src/workflows/unallowed-contribution-filters.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
notAllowed:
|
||||
- '.devcontainer/**'
|
||||
- '.github/workflows/**'
|
||||
- '.github/CODEOWNERS'
|
||||
- 'assets/fonts/**'
|
||||
- 'data/graphql/**'
|
||||
- 'Dockerfile*'
|
||||
- 'src/*/scripts/**'
|
||||
- 'src/**.json'
|
||||
- 'src/workflows/**'
|
||||
- 'lib/redirects/**'
|
||||
- 'package*.json'
|
||||
- 'content/actions/deployment/security-hardening-your-deployments/**'
|
||||
contentTypes:
|
||||
- 'content/**'
|
||||
56
src/workflows/unallowed-contributions.js
Executable file
56
src/workflows/unallowed-contributions.js
Executable file
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import coreLib from '@actions/core'
|
||||
import { readFileSync } from 'fs'
|
||||
import yaml from 'js-yaml'
|
||||
|
||||
import { checkContentType } from '#src/workflows/fm-utils.js'
|
||||
import github from '#src/workflows/github.js'
|
||||
|
||||
const core = coreLib
|
||||
const octokit = github()
|
||||
|
||||
const { PR_NUMBER, REPO_OWNER_AND_NAME, FILE_PATHS_NOT_ALLOWED, FILE_PATHS_CONTENT_TYPES } =
|
||||
process.env
|
||||
const [owner, repo] = REPO_OWNER_AND_NAME.split('/')
|
||||
const filters = yaml.load(readFileSync('src/workflows/unallowed-contribution-filters.yml', 'utf8'))
|
||||
|
||||
main()
|
||||
|
||||
async function main() {
|
||||
// Files in the diff that match specific paths we don't allow
|
||||
const unallowedChangedFiles = [...JSON.parse(FILE_PATHS_NOT_ALLOWED)]
|
||||
// Any changes to a file in the content directory could potentially
|
||||
// have `type: rai` so each changed content file's frontmatter needs
|
||||
// to be checked.
|
||||
unallowedChangedFiles.push(
|
||||
...(await checkContentType(JSON.parse(FILE_PATHS_CONTENT_TYPES), 'rai')),
|
||||
)
|
||||
|
||||
if (unallowedChangedFiles.length === 0) return
|
||||
|
||||
// Format into Markdown bulleted list to use in the PR comment
|
||||
const listUnallowedChangedFiles = unallowedChangedFiles.map((file) => `\n - ${file}`).join('')
|
||||
const listUnallowedFiles = filters.notAllowed.map((file) => `\n - ${file}`).join('')
|
||||
|
||||
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\`.`
|
||||
|
||||
let workflowFailMessage =
|
||||
"It looks like you've modified some files that we can't accept as contributions."
|
||||
let createdComment
|
||||
|
||||
try {
|
||||
createdComment = await octokit.rest.issues.createComment({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: PR_NUMBER,
|
||||
body: reviewMessage,
|
||||
})
|
||||
|
||||
workflowFailMessage = `${workflowFailMessage} Please see ${createdComment.data.html_url} for details.`
|
||||
} catch (err) {
|
||||
console.log('Error creating comment.', err)
|
||||
}
|
||||
|
||||
core.setFailed(workflowFailMessage)
|
||||
}
|
||||
Reference in New Issue
Block a user