import { createHmac } from "node:crypto"; import { pathToFileURL } from "node:url"; export async function notifyDockhand({ url, secret }, send = fetch) { if (!url && !secret) return false; if (!url || !secret) throw new Error( "Configure both Dockhand webhook URL and secret for this channel.", ); const target = new URL(url); if (!["http:", "https:"].includes(target.protocol)) throw new Error("Dockhand webhook requires an HTTP(S) URL."); const body = "{}"; const signature = createHmac("sha256", secret).update(body).digest("hex"); const response = await send(target, { method: "POST", headers: { "Content-Type": "application/json", "X-Hub-Signature-256": `sha256=${signature}`, }, body, redirect: "error", signal: AbortSignal.timeout(60_000), }); if (!response.ok) throw new Error( `Dockhand rejected the deployment request (HTTP ${response.status}).`, ); return true; } if ( process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href ) { try { const sent = await notifyDockhand({ url: process.env.DOCKHAND_WEBHOOK_URL, secret: process.env.DOCKHAND_WEBHOOK_SECRET, }); console.log( sent ? "Dockhand deployment requested. Check the stack's deployment status in Dockhand." : "Dockhand webhook not configured for this channel; image publication is complete.", ); } catch { // Do not print the URL, secret or server response into the job log. console.error( "Dockhand deployment request failed. The image was already published; check this channel's webhook configuration and connectivity.", ); process.exitCode = 1; } }