-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathget-webhook-event-payload.js
More file actions
executable file
·150 lines (128 loc) · 6.52 KB
/
get-webhook-event-payload.js
File metadata and controls
executable file
·150 lines (128 loc) · 6.52 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
149
150
#!/usr/bin/env node
(async () => {
const fs = require('fs')
let eventType = undefined
let aroundDate = undefined
let repository = undefined
// parse arguments, e.g. --event-type=check_run --date='Tue, 21 Nov 2023 11:13:12 GMT'
const args = process.argv.slice(2)
while (args.length) {
let option = args.shift()
const issueCommentMatch = option.match(/^https:\/\/github\.com\/([^/]+)\/([^/]+)\/(?:pull|issues)\/\d+#issuecomment-(\d+)$/)
if (issueCommentMatch) {
eventType = 'issue_comment'
const githubRequest = require('./GitForWindowsHelper/github-api-request')
const [owner, repo, comment_id] = issueCommentMatch.slice(1)
const comment = await githubRequest(console, null, 'GET', `/repos/${owner}/${repo}/issues/comments/${comment_id}`)
aroundDate = new Date(comment.updated_at)
continue
}
const optionWithArgument = option.match(/^(--[^=]+)=(.*)$/)
if (optionWithArgument) {
option = optionWithArgument[1]
args.unshift(optionWithArgument[2])
}
const getArg = () => {
if (!args.length) throw new Error(`'${option} requires an argument!`)
return args.shift()
}
if (option === '--event-type') eventType = getArg()
else if (option === '--date') {
const arg = getArg()
if (isNaN(Date.parse(arg))) throw new Error(`--date requires a valid date (got '${arg}')`)
aroundDate = new Date(arg)
} else if (option === '--repository') repository = getArg()
else
throw new Error(`Unhandled option: '${option}`)
}
const since = aroundDate ? aroundDate.getTime() - 5 * 60 * 1000 : undefined // 5 minutes before
const until = aroundDate ? aroundDate.getTime() + 5 * 60 * 1000 : undefined // 5 minutes after
const localSettings = JSON.parse(fs.readFileSync('local.settings.json'))
process.env.GITHUB_APP_ID = localSettings.Values.GITHUB_APP_ID
process.env.GITHUB_APP_PRIVATE_KEY = localSettings.Values.GITHUB_APP_PRIVATE_KEY
const repositoryID = !repository ? false : await (async () => {
const [owner, repo] = repository.split('/')
const getInstallationIdForRepo = require('./GitForWindowsHelper/get-installation-id-for-repo')
const installationId = await getInstallationIdForRepo(console, owner, repo)
const getInstallationAccessToken = require('./GitForWindowsHelper/get-installation-access-token')
const token = await getInstallationAccessToken(console, installationId)
const gitHubRequest = require('./GitForWindowsHelper/github-api-request')
return (await gitHubRequest(console, token, 'GET', `/repos/${repository}`)).id
})()
const gitHubRequestAsApp = require('./GitForWindowsHelper/github-api-request-as-app')
const getAtCursor = async cursor => {
const answer = await gitHubRequestAsApp(console, 'GET', `/app/hook/deliveries?per_page=30${cursor ? `&cursor=${cursor}` : ''}`)
answer.forEach(e => {
e.epoch = (new Date(e.delivered_at)).getTime()
})
// sort newest to oldest
answer.sort((a, b) => b.epoch - a.epoch)
const events = answer.filter(e => {
if (eventType && e.event !== eventType) return false
if (since && e.epoch < since) return false
if (until && e.epoch > until) return false
if (repositoryID && e.repository_id !== repositoryID) return false
return true
})
const newest = answer.shift()
const oldest = answer.pop() || newest
return {
events, newest, oldest
}
}
const getMatchingEvents = async () => {
let answer = await getAtCursor()
if (!since || !until || answer.newest === answer.oldest) return answer.events
if (answer.oldest.epoch < since) return answer.events
if (answer.oldest.epoch > until) {
let tooNew = answer.oldest
// first find a good starting cursor
while (answer.oldest?.epoch && answer.oldest.epoch > until) {
tooNew = answer.oldest
const previousAnswer = answer
let rate = (answer.newest.id - answer.oldest.id) / (answer.newest.epoch - answer.oldest.epoch)
let cursor = Math.floor(answer.oldest.id - rate * (answer.oldest.epoch - until))
for (;;) {
answer = await getAtCursor(cursor)
console.error(`got answer ${JSON.stringify(answer)} for cursor ${cursor}`)
if (answer.newest) break
rate /= 2
const newCursor = Math.floor(previousAnswer.oldest.id - rate * (previousAnswer.oldest.epoch - until))
if (newCursor === cursor) break
cursor = newCursor
}
console.error(`got final answer ${JSON.stringify(answer)} for cursor ${cursor}`)
}
while (answer.newest?.epoch && answer.newest.epoch < until) {
const tooOldID = answer.newest.id
// we overshot, now the time window does not include `until`, backtrack via bisecting
const rate = (tooNew.id - answer.newest.id) / (tooNew.epoch - answer.newest.epoch)
let cursor = Math.floor(tooNew.id - rate * (tooNew.epoch - until))
answer = await getAtCursor(cursor)
// if we received events from the same time window, shift back by the same amount
while (tooOldID === answer.newest.id) {
cursor += (cursor - tooOldID)
answer = await getAtCursor(cursor)
}
}
while (answer.oldest?.epoch && answer.oldest.epoch > until) {
// we overshot, maybe again, now even the oldest is too new
answer = await getAtCursor(answer.oldest.id - 1)
}
}
const events = [...answer.events]
while (answer.oldest?.epoch && answer.oldest.epoch > since) {
answer = await getAtCursor(answer.oldest.id - 1)
events.push(...answer.events)
}
return events
}
const events = await getMatchingEvents()
for (const e of events) {
const fullEvent = await gitHubRequestAsApp(console, 'GET', `/app/hook/deliveries/${e.id}`)
console.log(`id: ${e.id}\naction: ${e.action}\nevent: ${JSON.stringify(fullEvent, null, 2)}`)
}
})().catch(e => {
console.error(e)
process.exitCode = 1
})