From d81950b8bda06bf58fdc674774cbfd7864c30660 Mon Sep 17 00:00:00 2001 From: Henry <30578846+hjball@users.noreply.github.com> Date: Fri, 27 Feb 2026 22:38:01 +0000 Subject: [PATCH] fix: publish migrate image (#423) --- .github/workflows/docker-publish.yml | 37 ++++++++++++++++++++++++++++ packages/api/src/utils/webhook.ts | 19 +++++++------- 2 files changed, 47 insertions(+), 9 deletions(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index bba5295b..6ec5e759 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -26,6 +26,7 @@ env: REGISTRY: ghcr.io # github.repository as / IMAGE_NAME: ${{ github.repository }} + MIGRATE_IMAGE_NAME: ${{ github.repository }}-migrate jobs: build: @@ -85,6 +86,19 @@ jobs: type=semver,pattern={{major}} type=raw,value=latest,enable={{is_default_branch}} + - name: Extract Docker metadata (migrate) + id: meta-migrate + uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5.0.0 + with: + images: ${{ env.REGISTRY }}/${{ env.MIGRATE_IMAGE_NAME }} + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=raw,value=latest,enable={{is_default_branch}} + # Extract version from git tag or ref # Uses git describe to get latest tag + commit hash in SemVer format: 1.2.3+abc1234 - name: Extract version @@ -138,6 +152,22 @@ jobs: cache-from: type=gha cache-to: type=gha,mode=max + - name: Build and push migrate Docker image + id: build-and-push-migrate + uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0 + with: + context: . + file: apps/web/Dockerfile + target: migrate + push: ${{ github.event_name != 'pull_request' }} + platforms: linux/amd64,linux/arm64 + tags: ${{ steps.meta-migrate.outputs.tags }} + labels: ${{ steps.meta-migrate.outputs.labels }} + build-args: | + APP_VERSION=${{ steps.version.outputs.version }} + cache-from: type=gha + cache-to: type=gha,mode=max + # Sign the resulting Docker image digest except on PRs. # This will only write to the public Rekor transparency log when the Docker # repository is public to avoid leaking data. If you would like to publish @@ -152,3 +182,10 @@ jobs: # This step uses the identity token to provision an ephemeral certificate # against the sigstore community Fulcio instance. run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST} + + - name: Sign the published migrate Docker image + if: ${{ github.event_name != 'pull_request' }} + env: + TAGS: ${{ steps.meta-migrate.outputs.tags }} + DIGEST: ${{ steps.build-and-push-migrate.outputs.digest }} + run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST} diff --git a/packages/api/src/utils/webhook.ts b/packages/api/src/utils/webhook.ts index c13d928b..f1fb2f37 100644 --- a/packages/api/src/utils/webhook.ts +++ b/packages/api/src/utils/webhook.ts @@ -2,8 +2,8 @@ import crypto from "crypto"; import { z } from "zod"; import type { dbClient } from "@kan/db/client"; -import * as webhookRepo from "@kan/db/repository/webhook.repo"; import type { WebhookEvent } from "@kan/db/schema"; +import * as webhookRepo from "@kan/db/repository/webhook.repo"; export type WebhookEventType = WebhookEvent; @@ -92,7 +92,7 @@ export const webhookUrlSchema = z (url) => { try { const hostname = new URL(url).hostname.toLowerCase(); - const ipv4Match = hostname.match(/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/); + const ipv4Match = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/.exec(hostname); if (ipv4Match) { const [, a, b] = ipv4Match.map(Number); if ( @@ -185,15 +185,16 @@ export async function sendWebhooksForWorkspace( payload: WebhookPayload, ): Promise { try { - // Get active webhooks for this workspace, pre-filtered by event at the DB level - const webhooks = await webhookRepo.getActiveByWorkspaceId( - db, - workspaceId, - payload.event, + // Get active webhooks for this workspace + const webhooks = await webhookRepo.getActiveByWorkspaceId(db, workspaceId); + + // Filter webhooks that are subscribed to this specific event + const webhooksForEvent = webhooks.filter((webhook) => + webhook.events.includes(payload.event), ); - // Send to all webhooks in parallel (fire and forget) - const promises = webhooks.map((webhook) => + // Send to all subscribed webhooks in parallel (fire and forget) + const promises = webhooksForEvent.map((webhook) => sendWebhookToUrl(webhook.url, webhook.secret ?? undefined, payload).then( (result) => { if (!result.success) {