Skip to content
Source
Sign inSign up

previewStages.ts

74 lines2.6 KB

1import {
2 MANAGED_ENDPOINT_KEY_PATTERN,
3 managedEndpointTunnelNamePrefix,
4} from "./deploymentConfig.ts";
5​
6// Only pull-request stages may be torn down by the cleanup job. Anchoring the
7// pattern keeps `prod`, personal `dev_<user>` stages, and `pr-0` out of reach.
8const PREVIEW_STAGE_PATTERN = /^pr-[1-9][0-9]*$/u;
9​
10export function isPreviewStage(stage: string): boolean {
11 return PREVIEW_STAGE_PATTERN.test(stage);
13​
14// The prefix ends with a dash, so stage `pr-4` never matches tunnels of `pr-42`.
15export function isPreviewTunnelName(name: string, stage: string): boolean {
16 if (!isPreviewStage(stage)) return false;
17 const prefix = managedEndpointTunnelNamePrefix(stage);
18 return name.startsWith(prefix) && MANAGED_ENDPOINT_KEY_PATTERN.test(name.slice(prefix.length));
20​
21// Managed endpoint hostnames are `<stage>-<endpoint key>.<tunnel zone>`.
22export function isPreviewTunnelRecordName(
23 recordName: string,
24 stage: string,
25 tunnelZoneName: string,
26): boolean {
27 if (!isPreviewStage(stage)) return false;
28 const name = recordName.toLowerCase();
29 const suffix = `.${tunnelZoneName.trim().toLowerCase()}`;
30 if (!name.endsWith(suffix)) return false;
31 const label = name.slice(0, -suffix.length);
32 const labelPrefix = `${stage}-`;
33 return (
34 label.startsWith(labelPrefix) &&
35 MANAGED_ENDPOINT_KEY_PATTERN.test(label.slice(labelPrefix.length))
36 );
38​
39export function tunnelCnameContent(tunnelId: string): string {
40 return `${tunnelId}.cfargotunnel.com`;
42​
43export interface SweepTunnel {
44 readonly id: string;
45 readonly name?: string | null | undefined;
47​
48export interface SweepRecord {
49 readonly id: string;
50 readonly name: string;
51 readonly content?: string | null | undefined;
53​
54// Records are matched through the tunnel they point at, not just by name, so a
55// record that happens to share the label is never deleted unless it targets one
56// of this stage's tunnels.
57export function previewSweepTargets(input: {
58 readonly stage: string;
59 readonly tunnelZoneName: string;
60 readonly tunnels: ReadonlyArray<SweepTunnel>;
61 readonly records: ReadonlyArray<SweepRecord>;
62}): { readonly tunnels: ReadonlyArray<SweepTunnel>; readonly records: ReadonlyArray<SweepRecord> } {
63 const tunnels = input.tunnels.filter(
64 (tunnel) => tunnel.name != null && isPreviewTunnelName(tunnel.name, input.stage),
65 );
66 const tunnelContents = new Set(tunnels.map((tunnel) => tunnelCnameContent(tunnel.id)));
67 const records = input.records.filter(
68 (record) =>
69 isPreviewTunnelRecordName(record.name, input.stage, input.tunnelZoneName) &&
70 record.content != null &&
71 tunnelContents.has(record.content),
72 );
73 return { tunnels, records };