f9ed1bf2c9
Typescript's type aliases (`type X = thing`) can refer to basically anything, which makes it hard to write an automatic document formatter for them. Interfaces on the other hand are only object, so they play much nicer with docs. Currently, object-flavoured type aliases don't really get expanded at all on our docs site, which means we have a bunch of docs content that's not shown on the site. This diff introduces a lint rule that forces `interface X {foo: bar}`s instead of `type X = {foo: bar}` where possible, as it results in a much better documentation experience: Before: <img width="437" alt="Screenshot 2024-05-22 at 15 24 13" src="https://github.com/tldraw/tldraw/assets/1489520/32606fd1-6832-4a1e-aa5f-f0534d160c92"> After: <img width="431" alt="Screenshot 2024-05-22 at 15 33 01" src="https://github.com/tldraw/tldraw/assets/1489520/4e0d59ee-c38e-4056-b9fd-6a7f15d28f0f"> ### Change Type - [x] `sdk` — Changes the tldraw SDK - [x] `docs` — Changes to the documentation, examples, or templates. - [x] `improvement` — Improving existing features
96 lines
2.6 KiB
TypeScript
96 lines
2.6 KiB
TypeScript
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'])
|
|
|
|
interface ListWorkersResult {
|
|
success: boolean
|
|
result: { id: string }[]
|
|
}
|
|
|
|
const _isPrClosedCache = new Map<number, boolean>()
|
|
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
|