generated from actions/javascript-action
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathmain.js
170 lines (135 loc) Β· 4.98 KB
/
main.js
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
const core = require('@actions/core')
const http = require('@actions/http-client')
/** @typedef {import('./typings/dependencies').Response} ListDependencies */
/** @typedef {import('@actions/http-client/lib/interfaces').TypedResponse<ListDependencies>} ListDependenciesResponse */
/** @typedef {import('./typings/delete-dependencies').Response} DeleteDependencies */
const apiUrl = 'https://api.cloudflare.com/client/v4'
const httpClient = new http.HttpClient('Cloudflare Pages Deployments Delete Action')
/**
* Return a promise that resolves after the delay
*
* @param {number} delay
*/
const wait = delay => new Promise(resolve => setTimeout(resolve, delay))
/**
* Fetch list of Cloudflare deployments
*
* @param {string} project
* @param {string} account
* @param {Date} since
* @param {string} token
*/
const getDeployments = async (project, account, since, token) => {
core.startGroup('Fetching deployments')
/** @type {import('./typings/dependencies').Response['result']} */
const deployments = []
let page = 1
let lastResult
let resultInfo
let hasNextPage
let dateSinceNotReached
do {
core.info(
`Fetching page ${page} / ${
resultInfo ? Math.ceil(resultInfo.total_count / resultInfo.per_page) : '?'
} of deployments`
)
/** @type {ListDependenciesResponse} */
const res = await httpClient.getJson(
`${apiUrl}/accounts/${account}/pages/projects/${project}/deployments?page=${page}&sort_by=created_on&sort_order=desc`,
{
Authorization: `Bearer ${token}`,
}
)
if (!res.result) {
throw new Error('Could not fetch deployments')
}
if (!res.result.success) {
throw new Error(res.result.errors.map(e => e.message).join('\n'))
}
resultInfo = res.result.result_info
const nextResults = res.result.result
lastResult = nextResults[nextResults.length - 1]
deployments.push(...nextResults)
hasNextPage = page++ < Math.ceil(resultInfo.total_count / resultInfo.per_page)
dateSinceNotReached = new Date(lastResult.created_on).getTime() >= since.getTime()
await wait(250) // Api rate limit is 1200req/5min <--> 4req/s
} while (hasNextPage && dateSinceNotReached)
core.endGroup()
return deployments
}
/**
* @param {import('./typings/dependencies').Deployment} deployment
*/
const deleteDeployment = async (project, account, token, force, deployment) => {
const res = await httpClient.del(
`https://api.cloudflare.com/client/v4/accounts/${account}/pages/projects/${project}/deployments/${deployment.id}${
force ? '?force=true' : ''
}`,
{
authorization: `Bearer ${token}`,
}
)
/** @type {DeleteDependencies} */
const body = JSON.parse(await res.readBody())
await wait(250) // Api rate limit is 1200req/5min <--> 4req/s
if (!body.success) {
throw new Error(body.errors.map(e => e.message).join('\n'))
}
// Returns null, but value is not used anyway
return null
}
/**
* Transform a date string to a Date object
*
* @param {string} input
*/
const sinceDate = input => {
if (input === '') return new Date(0)
const date = new Date(input)
if (isNaN(date.getTime())) {
throw new Error(`Invalid since date: ${input}`)
}
return date
}
const parseNumber = input => {
core.info('Keep value: ' + input)
const number = Number.parseInt(input, 10)
if (isNaN(number)) {
throw new Error(`Invalid keep value: ${input}`)
}
return number
}
const main = async ({ project, account, branch, since, token, deploymentTriggerType, keep }) => {
core.info('πββοΈ Running Cloudflare Deployments Delete Action')
const sinceSafe = sinceDate(since)
const keepNumber = parseNumber(keep)
core.info(`Fetching deployments for project ${project} since ${sinceSafe.toISOString()}`)
/** @type {import('./typings/dependencies').Response['result']} */
const deployments = await getDeployments(project, account, sinceSafe, token)
core.info(`Found ${deployments.length} deployments in total`)
// Filter deployments by branch name
const branchDeployments = deployments
.filter(d => new Date(d.created_on).getTime() >= sinceSafe.getTime())
.filter(d => deploymentTriggerType === '' || d.deployment_trigger.type === deploymentTriggerType)
.filter(d => d.deployment_trigger.metadata.branch === branch)
.slice(keepNumber)
core.info(`πͺ Deleting ${branchDeployments.length} deployments matching branch ${branch}`)
core.startGroup('Deleted Deployments')
// Delete all deployments for the branch
let deleted = 0
for (let i = 0; i < branchDeployments.length; i++) {
try {
await deleteDeployment(project, account, token, keepNumber === 0, branchDeployments[i])
deleted = deleted + 1
core.info(`π’ Deleted deployment ${branchDeployments[i].id}`)
} catch (e) {
core.error(`π΄ Failed to delete deployment ${branchDeployments[i].id}`)
}
}
core.endGroup()
core.info('π Finished Cloudflare Deployments Delete Action')
// Used mainly for testing purposes
return deleted
}
exports.main = main