-
Notifications
You must be signed in to change notification settings - Fork 65
131 lines (127 loc) · 5.62 KB
/
pr-watcher.yml
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
# Workflow that runs periodically to check whether PRs have been imported into
# Gerrit and if they have been merged. The workflow adds a PR comment after
# import and after merge. PRs that are merged upstream are then closed.
name: PR Watcher
on:
workflow_dispatch:
schedule:
- cron: "*/10 * * * *" # Every 10 minutes
jobs:
check:
name: Check Copybara Import Status
runs-on: ubuntu-latest
permissions:
actions: write
steps:
- name: Check Pull Requests
uses: actions/github-script@v6
with:
script: |
// Get all open PRs.
const pulls = await github.rest.pulls.list({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
base: 'main',
});
for (const pull of pulls.data) {
const query = `query($owner:String!, $name:String!, $pullNumber:Int!, $statusContext:String!) {
repository(owner:$owner, name:$name) {
pullRequest(number:$pullNumber) {
commits(last:1) {
nodes {
commit {
status {
context(name: $statusContext) {
state
targetUrl
}
}
}
}
}
}
}
}`;
// Query the import/copybara status check.
const result = await github.graphql(query, {
owner: context.repo.owner,
name: context.repo.repo,
pullNumber: pull.number,
statusContext: 'import/copybara',
});
const nodes = result.repository.pullRequest.commits.nodes;
const commit = nodes[0].commit;
if (commit && commit.status && commit.status.context) {
// Forward the status check information to the pr-manager workflow.
await github.rest.actions.createWorkflowDispatch({
owner: context.repo.owner,
repo: context.repo.repo,
ref: 'main',
workflow_id: 'pr-manager.yml',
inputs: {
pullNumber: pull.number.toString(),
state: commit.status.context.state,
targetUrl: commit.status.context.targetUrl,
},
});
}
// Look through all timeline events on the PR.
// When the PR is merged upstream, it will include a backreference back to
// the PR in the commit message.
const timeline = github.paginate.iterator(github.rest.issues.listEventsForTimeline, {
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pull.number,
});
for await (const { data: timelineData } of timeline) {
for (const timelineItem of timelineData) {
if (timelineItem.event === 'referenced' && timelineItem.commit_id) {
try {
// Compare commit ids against refs/heads/main to see if they are
// included in the main branch.
const compare = await github.rest.repos.compareCommitsWithBasehead({
basehead: `${timelineItem.commit_id}...refs/heads/main`,
owner: context.repo.owner,
repo: context.repo.repo,
});
if (compare.data.status == "ahead" || compare.data.status == "identical") {
// This is a commit that has already been merged into main.
const commit = await github.rest.repos.getCommit({
owner: context.repo.owner,
repo: context.repo.repo,
ref: timelineItem.commit_id,
});
const message = commit.data.commit.message;
const lines = message.split('\n');
for (const line of lines) {
// Check if the commit has a footer referencing a PR.
const tag = 'GITHUB_PR_HEAD_SHA=';
if (line.startsWith(tag)) {
const sha = line.slice(tag.length);
// The merged commit footer matches the PR sha.
if (pull.head.sha === sha) {
// Close the PR.
await github.rest.actions.createWorkflowDispatch({
owner: context.repo.owner,
repo: context.repo.repo,
ref: 'main',
workflow_id: 'pr-manager.yml',
inputs: {
pullNumber: pull.number.toString(),
state: 'merged',
targetUrl: commit.data.html_url,
},
});
}
}
}
}
} catch (err) {
// This is OK because not all commits will be comparable.
console.warn(err);
}
}
}
}
}