-
-
Notifications
You must be signed in to change notification settings - Fork 837
148 lines (131 loc) · 5.22 KB
/
Copy pathbase_ref_check_comment.yml
File metadata and controls
148 lines (131 loc) · 5.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
name: Fork PR target guidance
on:
pull_request_target:
types: [opened, reopened, synchronize, edited, ready_for_review]
permissions: {}
jobs:
comment:
name: Sync PR target guidance comment
if: ${{ github.event.pull_request.head.repo.fork == true }}
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- name: Create, update, delete, or skip policy comment
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3
with:
script: |
const prNumber = context.payload.pull_request.number;
const marker = '<!-- fork-pr-target-policy -->';
const allowedPatterns = [
/^README\.md$/,
/^CONTRIBUTING\.md$/,
/^SECURITY\.md$/,
/^LICENSE$/,
/^THIRD_PARTY_LICENSES$/,
/^\.gitignore$/,
/^crowdin\.yml$/,
/^\.github\/.+$/
];
const isAllowedPath = (path) =>
allowedPatterns.some((pattern) => pattern.test(path));
const toSafeLiteral = (value) =>
JSON.stringify(String(value))
.replace(/[\u200E\u200F\u202A-\u202E\u2066-\u2069]/g, (c) =>
`\\u${c.codePointAt(0).toString(16).padStart(4, '0')}`
)
.replace(/</g, '\\u003c')
.replace(/>/g, '\\u003e')
.replace(/&/g, '\\u0026')
.replace(/@/g, '\\u0040')
.replace(/#/g, '\\u0023')
.replace(/`/g, '\\u0060');
const pr = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: prNumber
});
if (pr.data.head.repo.fork !== true) {
core.info('PR does not come from a fork; skipping comment sync.');
return;
}
const base = pr.data.base.ref;
const comments = await github.paginate(github.rest.issues.listComments, {
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
per_page: 100
});
const existing = comments.find((comment) =>
comment.user?.login === 'github-actions[bot]' &&
comment.body?.includes(marker)
);
let body = null;
if (base === 'main') {
const files = await github.paginate(github.rest.pulls.listFiles, {
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: prNumber,
per_page: 100
});
const disallowed = files.filter((file) => {
const pathsToCheck = [file.filename, file.previous_filename].filter(Boolean);
return pathsToCheck.some((path) => !isAllowedPath(path));
});
if (disallowed.length > 0) {
body = [
marker,
'⚠️ This PR targets `main`.',
'',
'Fork PRs should normally target `dev`.',
'Targeting `main` is only allowed for repository metadata changes such as docs, licensing, and `.github/**` updates.',
'This PR changes one or more files outside that allowlist, so please retarget it to `dev`.'
].join('\n');
}
} else if (base === 'dev') {
if (existing) {
body = [
marker,
'✅ This PR now targets `dev`.',
'',
'`dev` is the expected target branch for fork PRs, so no further action is needed.'
].join('\n');
}
} else {
body = [
marker,
`❌ This PR targets ${toSafeLiteral(base)}.`,
'',
'Fork PRs may only target `dev` or `main`.',
'Please retarget this PR to one of those branches.'
].join('\n');
}
if (body && existing) {
if (existing.body !== body) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existing.id,
body
});
} else {
core.info('Existing policy comment already matches desired body.');
}
} else if (body && !existing) {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
body
});
} else if (!body && existing) {
await github.rest.issues.deleteComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existing.id
});
core.info('Deleted stale policy comment because no comment is needed now.');
} else {
core.info(`No comment action needed for base branch ${toSafeLiteral(base)}.`);
}