ci/github-script/commits: various fixes and improvements (#425789)
This commit is contained in:
@@ -55,7 +55,6 @@ jobs:
|
||||
|
||||
- name: Check cherry-picks
|
||||
id: check
|
||||
continue-on-error: true
|
||||
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||
with:
|
||||
script: |
|
||||
@@ -63,84 +62,9 @@ jobs:
|
||||
github,
|
||||
context,
|
||||
core,
|
||||
dry: context.eventName == 'pull_request',
|
||||
})
|
||||
|
||||
- name: Request changes
|
||||
if: ${{ github.event_name == 'pull_request_target' && steps.check.outcome == 'failure' }}
|
||||
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||
with:
|
||||
script: |
|
||||
const { readFile } = require('node:fs/promises')
|
||||
const body = await readFile('review.md', 'utf-8')
|
||||
|
||||
const pendingReview = (await github.paginate(github.rest.pulls.listReviews, {
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: context.payload.pull_request.number
|
||||
})).find(review =>
|
||||
review.user.login == 'github-actions[bot]' && (
|
||||
// If a review is still pending, we can just update this instead
|
||||
// of posting a new one.
|
||||
review.state == 'CHANGES_REQUESTED' ||
|
||||
// No need to post a new review, if an older one with the exact
|
||||
// same content had already been dismissed.
|
||||
review.body == body
|
||||
)
|
||||
)
|
||||
|
||||
// Either of those two requests could fail for very long comments. This can only happen
|
||||
// with multiple commits all hitting the truncation limit for the diff. If you ever hit
|
||||
// this case, consider just splitting up those commits into multiple PRs.
|
||||
if (pendingReview) {
|
||||
await github.rest.pulls.updateReview({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: context.payload.pull_request.number,
|
||||
review_id: pendingReview.id,
|
||||
body
|
||||
})
|
||||
} else {
|
||||
await github.rest.pulls.createReview({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: context.payload.pull_request.number,
|
||||
event: 'REQUEST_CHANGES',
|
||||
body
|
||||
})
|
||||
}
|
||||
|
||||
- name: Dismiss old reviews
|
||||
if: ${{ github.event_name == 'pull_request_target' && steps.check.outcome == 'success' }}
|
||||
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||
with:
|
||||
script: |
|
||||
await Promise.all(
|
||||
(await github.paginate(github.rest.pulls.listReviews, {
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: context.payload.pull_request.number
|
||||
})).filter(review =>
|
||||
review.user.login == 'github-actions[bot]'
|
||||
).map(async (review) => {
|
||||
if (review.state == 'CHANGES_REQUESTED') {
|
||||
await github.rest.pulls.dismissReview({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: context.payload.pull_request.number,
|
||||
review_id: review.id,
|
||||
message: 'All cherry-picks are good now, thank you!'
|
||||
})
|
||||
}
|
||||
await github.graphql(`mutation($node_id:ID!) {
|
||||
minimizeComment(input: {
|
||||
classifier: RESOLVED,
|
||||
subjectId: $node_id
|
||||
})
|
||||
{ clientMutationId }
|
||||
}`, { node_id: review.node_id })
|
||||
})
|
||||
)
|
||||
|
||||
- name: Log current API rate limits
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
This report is automatically generated by the `check-cherry-picks` CI workflow.
|
||||
This report is automatically generated by the `PR / Check / cherry-pick` CI workflow.
|
||||
|
||||
Some of the commits in this PR have not been cherry-picked exactly and require the author's and reviewer's attention.
|
||||
Some of the commits in this PR require the author's and reviewer's attention.
|
||||
|
||||
Please make sure to follow the [backporting guidelines](https://github.com/NixOS/nixpkgs/blob/master/CONTRIBUTING.md#how-to-backport-pull-requests) and cherry-pick with the `-x` flag. This requires changes to go to the unstable branches (`master` / `staging`) first, before backporting them.
|
||||
Please follow the [backporting guidelines](https://github.com/NixOS/nixpkgs/blob/master/CONTRIBUTING.md#how-to-backport-pull-requests) and cherry-pick with the `-x` flag.
|
||||
This requires changes to the unstable `master` and `staging` branches first, before backporting them.
|
||||
|
||||
Occasionally, it is not possible to cherry-pick exactly the same patch. This most frequently happens when resolving merge conflicts while cherry-picking or when updating minor versions of packages which have already advanced to the next major on unstable. If you need to merge this PR despite the warnings, please [dismiss](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/dismissing-a-pull-request-review) this review.
|
||||
Occasionally, it is not possible to cherry-pick exactly the same patch.
|
||||
This most frequently happens when resolving merge conflicts or when updating minor versions of packages which have already advanced to the next major on unstable.
|
||||
If you need to merge this PR despite the warnings, please [dismiss](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/dismissing-a-pull-request-review) this review shortly before merging.
|
||||
|
||||
+102
-15
@@ -1,6 +1,6 @@
|
||||
module.exports = async function ({ github, context, core }) {
|
||||
module.exports = async function ({ github, context, core, dry }) {
|
||||
const { execFileSync } = require('node:child_process')
|
||||
const { readFile, writeFile } = require('node:fs/promises')
|
||||
const { readFile } = require('node:fs/promises')
|
||||
const { join } = require('node:path')
|
||||
const { classify } = require('../supportedBranches.js')
|
||||
const withRateLimit = require('./withRateLimit.js')
|
||||
@@ -8,16 +8,19 @@ module.exports = async function ({ github, context, core }) {
|
||||
await withRateLimit({ github, core }, async (stats) => {
|
||||
stats.prs = 1
|
||||
|
||||
const pull_number = context.payload.pull_request.number
|
||||
|
||||
const job_url =
|
||||
context.runId &&
|
||||
(
|
||||
await github.rest.actions.listJobsForWorkflowRun({
|
||||
await github.paginate(github.rest.actions.listJobsForWorkflowRun, {
|
||||
...context.repo,
|
||||
run_id: context.runId,
|
||||
per_page: 100,
|
||||
})
|
||||
).data.jobs[0].html_url +
|
||||
).find(({ name }) => name == 'Check / cherry-pick').html_url +
|
||||
'?pr=' +
|
||||
context.payload.pull_request.number
|
||||
pull_number
|
||||
|
||||
async function handle({ sha, commit }) {
|
||||
// Using the last line with "cherry" + hash, because a chained backport
|
||||
@@ -70,7 +73,9 @@ module.exports = async function ({ github, context, core }) {
|
||||
__dirname,
|
||||
'range-diff',
|
||||
'--no-color',
|
||||
'--ignore-all-space',
|
||||
'--no-notes',
|
||||
// 100 means "any change will be reported"; 0 means "no change will be reported"
|
||||
'--creation-factor=100',
|
||||
`${original_sha}~..${original_sha}`,
|
||||
`${sha}~..${sha}`,
|
||||
@@ -113,12 +118,12 @@ module.exports = async function ({ github, context, core }) {
|
||||
|
||||
const commits = await github.paginate(github.rest.pulls.listCommits, {
|
||||
...context.repo,
|
||||
pull_number: context.payload.pull_request.number,
|
||||
pull_number,
|
||||
})
|
||||
|
||||
const results = await Promise.all(commits.map(handle))
|
||||
|
||||
// Log all results without truncation and with better highlighting to the job log.
|
||||
// Log all results without truncation, with better highlighting and all whitespace changes to the job log.
|
||||
results.forEach(({ sha, commit, severity, message, colored_diff }) => {
|
||||
core.startGroup(`Commit ${sha}`)
|
||||
core.info(`Author: ${commit.author.name} ${commit.author.email}`)
|
||||
@@ -129,8 +134,46 @@ module.exports = async function ({ github, context, core }) {
|
||||
})
|
||||
|
||||
// Only create step summary below in case of warnings or errors.
|
||||
if (results.every(({ severity }) => severity == 'info')) return
|
||||
else process.exitCode = 1
|
||||
// Also clean up older reviews, when all checks are good now.
|
||||
if (results.every(({ severity }) => severity == 'info')) {
|
||||
if (!dry) {
|
||||
await Promise.all(
|
||||
(
|
||||
await github.paginate(github.rest.pulls.listReviews, {
|
||||
...context.repo,
|
||||
pull_number,
|
||||
})
|
||||
)
|
||||
.filter((review) => review.user.login == 'github-actions[bot]')
|
||||
.map(async (review) => {
|
||||
if (review.state == 'CHANGES_REQUESTED') {
|
||||
await github.rest.pulls.dismissReview({
|
||||
...context.repo,
|
||||
pull_number,
|
||||
review_id: review.id,
|
||||
message: 'All cherry-picks are good now, thank you!',
|
||||
})
|
||||
}
|
||||
await github.graphql(
|
||||
`mutation($node_id:ID!) {
|
||||
minimizeComment(input: {
|
||||
classifier: RESOLVED,
|
||||
subjectId: $node_id
|
||||
})
|
||||
{ clientMutationId }
|
||||
}`,
|
||||
{ node_id: review.node_id },
|
||||
)
|
||||
}),
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// In the case of "error" severity, we also fail the job.
|
||||
// Those should be considered blocking and not be dismissable via review.
|
||||
if (results.some(({ severity }) => severity == 'error'))
|
||||
process.exitCode = 1
|
||||
|
||||
core.summary.addRaw(
|
||||
await readFile(join(__dirname, 'check-cherry-picks.md'), 'utf-8'),
|
||||
@@ -177,9 +220,14 @@ module.exports = async function ({ github, context, core }) {
|
||||
}
|
||||
|
||||
core.summary.addRaw('<details><summary>Show diff</summary>')
|
||||
core.summary.addRaw('\n\n```diff', true)
|
||||
core.summary.addRaw(truncated.join('\n'), true)
|
||||
core.summary.addRaw('```', true)
|
||||
core.summary.addCodeBlock(
|
||||
truncated
|
||||
.join('\n')
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>'),
|
||||
'diff',
|
||||
)
|
||||
core.summary.addRaw('</details>')
|
||||
}
|
||||
|
||||
@@ -191,9 +239,48 @@ module.exports = async function ({ github, context, core }) {
|
||||
`\n\n_Hint: The full diffs are also available in the [runner logs](${job_url}) with slightly better highlighting._`,
|
||||
)
|
||||
|
||||
// Write to disk temporarily for next step in GHA.
|
||||
await writeFile('review.md', core.summary.stringify())
|
||||
|
||||
const body = core.summary.stringify()
|
||||
core.summary.write()
|
||||
|
||||
const pendingReview = (
|
||||
await github.paginate(github.rest.pulls.listReviews, {
|
||||
...context.repo,
|
||||
pull_number,
|
||||
})
|
||||
).find(
|
||||
(review) =>
|
||||
review.user.login == 'github-actions[bot]' &&
|
||||
// If a review is still pending, we can just update this instead
|
||||
// of posting a new one.
|
||||
(review.state == 'CHANGES_REQUESTED' ||
|
||||
// No need to post a new review, if an older one with the exact
|
||||
// same content had already been dismissed.
|
||||
review.body == body),
|
||||
)
|
||||
|
||||
if (dry) {
|
||||
if (pendingReview)
|
||||
core.info('pending review found: ' + pendingReview.html_url)
|
||||
else core.info('no pending review found')
|
||||
} else {
|
||||
// Either of those two requests could fail for very long comments. This can only happen
|
||||
// with multiple commits all hitting the truncation limit for the diff. If you ever hit
|
||||
// this case, consider just splitting up those commits into multiple PRs.
|
||||
if (pendingReview) {
|
||||
await github.rest.pulls.updateReview({
|
||||
...context.repo,
|
||||
pull_number,
|
||||
review_id: pendingReview.id,
|
||||
body,
|
||||
})
|
||||
} else {
|
||||
await github.rest.pulls.createReview({
|
||||
...context.repo,
|
||||
pull_number,
|
||||
event: 'REQUEST_CHANGES',
|
||||
body,
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user