185 lines
4.1 KiB
TypeScript
185 lines
4.1 KiB
TypeScript
|
import * as github from '@actions/github'
|
||
|
import { readFileSync } from 'fs'
|
||
|
import { appendFile } from 'fs/promises'
|
||
|
import { join } from 'path'
|
||
|
import { env } from 'process'
|
||
|
import toml from 'toml'
|
||
|
import { exec } from './exec'
|
||
|
|
||
|
export function getDeployInfo() {
|
||
|
const githubPrNumber = process.env.GITHUB_REF?.match(/refs\/pull\/(\d+)\/merge/)?.[1]
|
||
|
|
||
|
let previewId = process.env.TLDRAW_PREVIEW_ID
|
||
|
if (!previewId && env.TLDRAW_ENV === 'preview') {
|
||
|
if (githubPrNumber) {
|
||
|
previewId = `pr-${githubPrNumber}`
|
||
|
} else {
|
||
|
throw new Error(
|
||
|
'If running preview deploys from outside of a PR action, TLDRAW_PREVIEW_ID env var must be set'
|
||
|
)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const sha: string | undefined =
|
||
|
// if the event is 'pull_request', github.context.sha is an ephemeral merge commit
|
||
|
// while the actual commit we want to create the deployment for is the 'head' of the PR.
|
||
|
github.context.eventName === 'pull_request'
|
||
|
? github.context.payload.pull_request?.head.sha
|
||
|
: github.context.sha
|
||
|
|
||
|
if (!sha) {
|
||
|
throw new Error('Could not determine the SHA of the commit to deploy')
|
||
|
}
|
||
|
|
||
|
return {
|
||
|
githubPrNumber,
|
||
|
previewId,
|
||
|
sha,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Creates a github 'deployment', which creates a 'View Deployment' button in the PR timeline.
|
||
|
export async function createGithubDeployment(
|
||
|
env: { GH_TOKEN: string; TLDRAW_ENV: string },
|
||
|
{
|
||
|
app,
|
||
|
deploymentUrl,
|
||
|
inspectUrl,
|
||
|
sha,
|
||
|
}: { app: string; deploymentUrl: string; inspectUrl?: string; sha: string }
|
||
|
) {
|
||
|
const client = github.getOctokit(env.GH_TOKEN)
|
||
|
|
||
|
const deployment = await client.rest.repos.createDeployment({
|
||
|
owner: 'tldraw',
|
||
|
repo: 'tldraw',
|
||
|
ref: sha,
|
||
|
payload: { web_url: deploymentUrl },
|
||
|
environment: `${app}-${env.TLDRAW_ENV}`,
|
||
|
transient_environment: env.TLDRAW_ENV === 'preview',
|
||
|
production_environment: env.TLDRAW_ENV === 'production',
|
||
|
required_contexts: [],
|
||
|
auto_merge: false,
|
||
|
task: 'deploy',
|
||
|
})
|
||
|
|
||
|
await client.rest.repos.createDeploymentStatus({
|
||
|
owner: 'tldraw',
|
||
|
repo: 'tldraw',
|
||
|
deployment_id: (deployment.data as any).id,
|
||
|
state: 'success',
|
||
|
environment_url: deploymentUrl,
|
||
|
log_url: inspectUrl,
|
||
|
})
|
||
|
}
|
||
|
|
||
|
/** Deploy a worker to wrangler, returning the deploy ID */
|
||
|
export async function wranglerDeploy({
|
||
|
location,
|
||
|
dryRun,
|
||
|
env,
|
||
|
vars,
|
||
|
sentry,
|
||
|
}: {
|
||
|
location: string
|
||
|
dryRun: boolean
|
||
|
env: string
|
||
|
vars: Record<string, string>
|
||
|
sentry?: {
|
||
|
authToken: string
|
||
|
project: string
|
||
|
release?: string
|
||
|
}
|
||
|
}) {
|
||
|
const varsArray = []
|
||
|
for (const [key, value] of Object.entries(vars)) {
|
||
|
varsArray.push('--var', `${key}:${value}`)
|
||
|
}
|
||
|
|
||
|
const out = await exec(
|
||
|
'yarn',
|
||
|
[
|
||
|
'wrangler',
|
||
|
'deploy',
|
||
|
dryRun ? '--dry-run' : null,
|
||
|
'--env',
|
||
|
env,
|
||
|
'--outdir',
|
||
|
'.wrangler/dist',
|
||
|
...varsArray,
|
||
|
],
|
||
|
{
|
||
|
pwd: location,
|
||
|
env: {
|
||
|
NODE_ENV: 'production',
|
||
|
// wrangler needs CI=1 set to prevent it from trying to do interactive prompts
|
||
|
CI: '1',
|
||
|
},
|
||
|
}
|
||
|
)
|
||
|
|
||
|
if (dryRun) return
|
||
|
|
||
|
const versionMatch = out.match(/Current Version ID: (.+)/)
|
||
|
if (!versionMatch) {
|
||
|
throw new Error('Could not find the deploy ID in wrangler output')
|
||
|
}
|
||
|
|
||
|
const workerName = toml.parse(readFileSync(join(location, 'wrangler.toml')).toString())?.env?.[
|
||
|
env
|
||
|
]?.name
|
||
|
|
||
|
if (!workerName) {
|
||
|
throw new Error('Could not find the worker name in wrangler output')
|
||
|
}
|
||
|
|
||
|
if (sentry) {
|
||
|
const release = sentry.release ?? `${workerName}.${versionMatch[1]}`
|
||
|
|
||
|
const sentryEnv = {
|
||
|
SENTRY_AUTH_TOKEN: sentry.authToken,
|
||
|
SENTRY_ORG: 'tldraw',
|
||
|
SENTRY_PROJECT: sentry.project,
|
||
|
}
|
||
|
|
||
|
// create a sentry release:
|
||
|
exec('yarn', ['run', '-T', 'sentry-cli', 'releases', 'new', release], {
|
||
|
pwd: location,
|
||
|
env: sentryEnv,
|
||
|
})
|
||
|
|
||
|
// upload sourcemaps to the release:
|
||
|
exec(
|
||
|
'yarn',
|
||
|
[
|
||
|
'run',
|
||
|
'-T',
|
||
|
'sentry-cli',
|
||
|
'releases',
|
||
|
'files',
|
||
|
release,
|
||
|
'upload-sourcemaps',
|
||
|
'.wrangler/dist',
|
||
|
],
|
||
|
{
|
||
|
pwd: location,
|
||
|
env: sentryEnv,
|
||
|
}
|
||
|
)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
export async function setWranglerPreviewConfig(
|
||
|
location: string,
|
||
|
{ name, customDomain }: { name: string; customDomain?: string }
|
||
|
) {
|
||
|
await appendFile(
|
||
|
join(location, 'wrangler.toml'),
|
||
|
`
|
||
|
[env.preview]
|
||
|
name = "${name}"
|
||
|
${customDomain ? `routes = [ { pattern = "${customDomain}", custom_domain = true} ]` : ''}
|
||
|
`
|
||
|
)
|
||
|
}
|