-
Notifications
You must be signed in to change notification settings - Fork 416
211 lines (177 loc) · 7.13 KB
/
release-notify.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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
name: Notify Slack channel about upcoming release
concurrency:
group: ${{ github.workflow }}-${{ github.event.number }}
cancel-in-progress: true
on:
pull_request:
# TODO: uncomment
# branches:
# - release
types:
# Default types that triggers a workflow:
# - https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request
- opened
- synchronize
- reopened
# Additional types that we want to handle:
- closed
jobs:
notify:
runs-on: [ ubuntu-latest ]
steps:
- name: Generate Slack Message
id: generate-slack-message
uses: actions/github-script@v6
with:
retries: 5
result-encoding: string
github-token: ${{ secrets.CI_ACCESS_TOKEN }}
script: |
// Helper function to create a URL for a PR
const urlForPr = function (prNumber) {
return `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/pull/${prNumber}`
}
// Helper function to replace #XXX with a Slack mrkdwn link to the PR XXX
const linkify = function (text) {
return text.replace(/#(\d+)/g, (match, prNumber) => { return `<${urlForPr(prNumber)}|#${prNumber}>` })
}
// Helper function to return an emoji for a PR status
const statusEmoji = function (pullRequest) {
const prState = pullRequest.state
const isMerged = pullRequest.merged
console.log(prState)
console.log(isMerged)
let emoji = ":new:"
if (prState == "synchronize") {
emoji = ":hammer_and_wrench:" //
} else if (prState == "closed" && isMerged) {
emoji = ":pr-merged:"
} else if (prState == "closed" && !isMerged) {
emoji = ":no_entry_sign:"
}
return emoji
}
console.log(context.eventName)
const pullRequest = context.payload.pull_request
const ownerRepoPrParams = {
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number,
}
// Prepare a header for the Slack message
const repoToProjectName = {
"neondatabase/neon": "Storage",
"neondatabase/cloud": "Console & Control Plane",
}
const project = repoToProjectName[pullRequest.base.repo.full_name] || pullRequest.base.repo.name.toUpperCase()
const header = `${project} release is coming: "${pullRequest.title}" :tada:`
// Fetch commits for the PR
const listCommitsOpts = github.rest.pulls.listCommits.endpoint.merge(ownerRepoPrParams)
const commits = await github.paginate(listCommitsOpts)
const blocks = []
blocks.push({
type: "header",
text: {
type: "plain_text",
text: header,
},
}, {
type: "context",
elements: [
{
type: "mrkdwn",
text: `Release PR: ${urlForPr(context.issue.number)}`,
},
],
}, {
"type": "divider"
})
// The length of each section is limited to 3000 characters,
// split them into several sections if needed
const messages = []
let currentMessage = ""
for (const commit of commits) {
const commitMessage = commit.commit.message.replace(/\r\n/g, "\n")
let firstLine = commitMessage.split("\n\n", 1)[0].trim()
// if the first line doesn't end with PR like "(#1234)", add a direct link to the commit
if (!/\(#\d+\)$/.test(firstLine)) {
const sha = commit.sha
const htmlUrl = commit.html_url
firstLine += ` (<${htmlUrl}|${sha.slice(0, 7)}>)`
}
let title = `- ${linkify(firstLine)}\n`
if (title.length > 3000) {
title = title.slice(0, 3000)
}
if (title.length + currentMessage.length > 3000) {
messages.push(currentMessage)
currentMessage = ""
}
currentMessage += title
}
messages.push(currentMessage)
for (const message of messages) {
blocks.push({
type: "section",
text: {
type: "mrkdwn",
text: message,
},
})
}
const updatedAt = Math.floor(new Date(pullRequest.updated_at) / 1000)
blocks.push({
"type": "divider"
}, {
type: "context",
elements: [{
type: "mrkdwn",
text: `${statusEmoji(pullRequest)} PR updated at <!date^${updatedAt}^{date_num} {time_secs} (local time)|${new Date().toISOString()}>`,
}],
})
const slackMessage = {
text: header,
blocks,
}
return JSON.stringify(slackMessage)
- name: Get a file with Slack message ID from GitHub Actions cache
uses: actions/cache/restore@v3
id: posted-message
with:
path: release-notify.json
key: release-notify-${{ github.event.number }}.json
- name: Get Slack message ID from the file from GitHub Actions cache
id: message-id
run: |
UPDATE_TS=$(cat release-notify.json | jq --raw-output '.ts' || true)
echo "update-ts=${UPDATE_TS}" >> $GITHUB_OUTPUT
- name: Send Slack message
uses: slackapi/slack-github-action@v1
id: slack
env:
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
with:
channel-id: ${{ vars.SLACK_UPCOMING_RELEASE_CHANNEL_ID || 'C05QQ9J1BRC' }} # if not set, then `#test-release-notifications`
update-ts: ${{ steps.message-id.outputs.update-ts }}
payload: ${{ steps.generate-slack-message.outputs.result }}
- name: Prepare a file with Slack message ID for GitHub Actions cache
if: steps.posted-message.outputs.cache-hit != 'true'
run: |
echo '{"ts": "${{ steps.slack.outputs.ts }}"}' > release-notify.json
- name: Save a file with Slack message ID to GitHub Actions cache
uses: actions/cache/save@v3
if: steps.posted-message.outputs.cache-hit != 'true'
with:
path: release-notify.json
key: release-notify-${{ github.event.number }}.json
- name: Delete a file with Slack message ID from GitHub Actions cache
uses: actions/github-script@v6
if: always() && github.event.action == 'closed'
with:
retries: 5
script: |
github.rest.actions.deleteActionsCacheByKey({
owner: context.repo.owner,
repo: context.repo.repo,
key: "release-notify-${{ github.event.number }}.json"
});