From a56649ff3582de7b6e64cd0e2ee56862f8249b10 Mon Sep 17 00:00:00 2001 From: Dante Danelian Date: Fri, 26 Jun 2026 14:40:25 -0400 Subject: [PATCH] CD-1243: Update Case.dev Linc Edge Config after publish --- .github/workflows/npm-publish.yml | 20 ++++ scripts/update-casedev-linc-edge-config.mjs | 121 ++++++++++++++++++++ 2 files changed, 141 insertions(+) create mode 100644 scripts/update-casedev-linc-edge-config.mjs diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index c2910245..740ae569 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -14,6 +14,16 @@ on: description: 'Source ref to publish (defaults to tag; use only for release recovery)' required: false type: string + edge_config_channel: + description: 'Case.dev Linc Edge Config channel to update after publish' + required: false + default: preview + type: choice + options: + - preview + - production + - both + - skip permissions: {} @@ -27,6 +37,7 @@ jobs: env: RELEASE_TAG: ${{ github.event.inputs.tag || github.ref_name }} SOURCE_REF: ${{ github.event.inputs.source_ref || github.event.inputs.tag || github.ref_name }} + CASEDEV_LINC_EDGE_CONFIG_CHANNEL: ${{ inputs.edge_config_channel || 'preview' }} steps: - name: Checkout uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -69,3 +80,12 @@ jobs: - name: Publish npm packages run: node scripts/publish.mjs + + - name: Update Case.dev Linc Edge Config + if: env.CASEDEV_LINC_EDGE_CONFIG_CHANNEL != 'skip' + env: + CASEDEV_LINC_EDGE_CONFIG_ID: ${{ secrets.CASEDEV_LINC_EDGE_CONFIG_ID }} + CASEDEV_LINC_EDGE_CONFIG_VERCEL_TOKEN: ${{ secrets.CASEDEV_LINC_EDGE_CONFIG_VERCEL_TOKEN }} + CASEDEV_LINC_EDGE_CONFIG_TEAM_ID: ${{ vars.CASEDEV_LINC_EDGE_CONFIG_TEAM_ID }} + CASEDEV_LINC_EDGE_CONFIG_TEAM_SLUG: ${{ vars.CASEDEV_LINC_EDGE_CONFIG_TEAM_SLUG }} + run: node scripts/update-casedev-linc-edge-config.mjs --channel "$CASEDEV_LINC_EDGE_CONFIG_CHANNEL" diff --git a/scripts/update-casedev-linc-edge-config.mjs b/scripts/update-casedev-linc-edge-config.mjs new file mode 100644 index 00000000..fa24c7e6 --- /dev/null +++ b/scripts/update-casedev-linc-edge-config.mjs @@ -0,0 +1,121 @@ +#!/usr/bin/env node + +import { readFileSync } from "node:fs"; + +const EDGE_CONFIG_KEYS = { + preview: "linc-preview-version", + production: "linc-production-version", +}; + +function getArgValue(flag) { + const index = process.argv.indexOf(flag); + if (index === -1) return undefined; + return process.argv[index + 1]; +} + +function hasFlag(flag) { + return process.argv.includes(flag); +} + +function usage() { + return [ + "Usage: node scripts/update-casedev-linc-edge-config.mjs [--channel preview|production|both] [--version x.y.z] [--dry-run]", + "", + "Required env when not using --dry-run:", + " CASEDEV_LINC_EDGE_CONFIG_ID", + " CASEDEV_LINC_EDGE_CONFIG_VERCEL_TOKEN", + "", + "Optional env:", + " CASEDEV_LINC_EDGE_CONFIG_TEAM_ID", + " CASEDEV_LINC_EDGE_CONFIG_TEAM_SLUG", + ].join("\n"); +} + +function readPackageVersion() { + const packageJson = JSON.parse(readFileSync("packages/coding-agent/package.json", "utf8")); + if (packageJson.name !== "@casemark/linc") { + throw new Error(`Unexpected package name: ${packageJson.name}`); + } + return packageJson.version; +} + +function normalizeVersion(version) { + if (typeof version !== "string") return null; + const trimmed = version.trim(); + return /^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/.test(trimmed) ? trimmed : null; +} + +function resolveChannels(channel) { + if (!channel || channel === "preview") return ["preview"]; + if (channel === "production") return ["production"]; + if (channel === "both") return ["preview", "production"]; + throw new Error(`Invalid channel: ${channel}`); +} + +function buildEndpoint(edgeConfigId) { + const url = new URL(`https://api.vercel.com/v1/edge-config/${edgeConfigId}/items`); + if (process.env.CASEDEV_LINC_EDGE_CONFIG_TEAM_ID) { + url.searchParams.set("teamId", process.env.CASEDEV_LINC_EDGE_CONFIG_TEAM_ID); + } + if (process.env.CASEDEV_LINC_EDGE_CONFIG_TEAM_SLUG) { + url.searchParams.set("slug", process.env.CASEDEV_LINC_EDGE_CONFIG_TEAM_SLUG); + } + return url.toString(); +} + +async function main() { + if (hasFlag("--help")) { + console.log(usage()); + return; + } + + const version = normalizeVersion(getArgValue("--version") ?? readPackageVersion()); + if (!version) { + throw new Error(`Invalid Linc version: ${getArgValue("--version")}`); + } + + const channels = resolveChannels(getArgValue("--channel") ?? process.env.CASEDEV_LINC_EDGE_CONFIG_CHANNEL); + const items = channels.map((channel) => ({ + operation: "upsert", + key: EDGE_CONFIG_KEYS[channel], + value: version, + })); + + if (hasFlag("--dry-run")) { + for (const item of items) { + console.log(`[dry-run] ${item.key} = ${item.value}`); + } + return; + } + + const edgeConfigId = process.env.CASEDEV_LINC_EDGE_CONFIG_ID; + const vercelToken = process.env.CASEDEV_LINC_EDGE_CONFIG_VERCEL_TOKEN; + if (!edgeConfigId || !vercelToken) { + throw new Error( + "CASEDEV_LINC_EDGE_CONFIG_ID and CASEDEV_LINC_EDGE_CONFIG_VERCEL_TOKEN are required" + ); + } + + const response = await fetch(buildEndpoint(edgeConfigId), { + method: "PATCH", + headers: { + Authorization: `Bearer ${vercelToken}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ items }), + }); + + if (!response.ok) { + const errorText = await response.text(); + throw new Error(`Edge Config update failed (${response.status}): ${errorText}`); + } + + for (const item of items) { + console.log(`Updated Case.dev Edge Config: ${item.key} = ${item.value}`); + } +} + +main().catch((error) => { + console.error(error instanceof Error ? error.message : error); + process.exitCode = 1; +});