import * as github from '@actions/github' import { makeEnv } from './lib/makeEnv' import { nicelog } from './lib/nicelog' // Do not use `process.env` directly in this script. Add your variable to `makeEnv` and use it via // `env` instead. This makes sure that all required env vars are present. const env = makeEnv(['CLOUDFLARE_ACCOUNT_ID', 'CLOUDFLARE_API_TOKEN', 'GH_TOKEN']) type ListWorkersResult = { success: boolean result: { id: string }[] } const _isPrClosedCache = new Map() async function isPrClosedForAWhile(prNumber: number) { if (_isPrClosedCache.has(prNumber)) { return _isPrClosedCache.get(prNumber)! } let prResult try { prResult = await github.getOctokit(env.GH_TOKEN).rest.pulls.get({ owner: 'tldraw', repo: 'tldraw', pull_number: prNumber, }) } catch (err: any) { if (err.status === 404) { _isPrClosedCache.set(prNumber, true) return true } throw err } const twoDays = 1000 * 60 * 60 * 24 * 2 const result = prResult.data.state === 'closed' && Date.now() - new Date(prResult.data.closed_at!).getTime() > twoDays _isPrClosedCache.set(prNumber, result) return result } async function ListPreviewWorkerDeployments() { const res = await fetch( `https://api.cloudflare.com/client/v4/accounts/${env.CLOUDFLARE_ACCOUNT_ID}/workers/scripts`, { headers: { Authorization: `Bearer ${env.CLOUDFLARE_API_TOKEN}`, 'Content-Type': 'application/json', }, } ) const data = (await res.json()) as ListWorkersResult if (!data.success) { throw new Error('Failed to list workers ' + JSON.stringify(data)) } return data.result.map((r) => r.id).filter((id) => id.match(/^pr-(\d+)-/)) } async function deletePreviewWorkerDeployment(id: string) { const res = await fetch( `https://api.cloudflare.com/client/v4/accounts/${env.CLOUDFLARE_ACCOUNT_ID}/workers/scripts/${id}`, { method: 'DELETE', headers: { Authorization: `Bearer ${env.CLOUDFLARE_API_TOKEN}`, 'Content-Type': 'application/json', }, } ) if (!res.ok) { throw new Error('Failed to delete worker ' + JSON.stringify(await res.json())) } } async function main() { const previewDeployments = await ListPreviewWorkerDeployments() for (const deployment of previewDeployments) { const prNumber = Number(deployment.match(/^pr-(\d+)-/)![1]) if (await isPrClosedForAWhile(prNumber)) { nicelog(`Deleting ${deployment} because PR is closed`) await deletePreviewWorkerDeployment(deployment) } else { nicelog(`Skipping ${deployment} because PR is still open`) } } nicelog('Done') } main() // clean up cloudflare preview deploys