Deploy approvals

CI creates one notification when a release is ready. The webhook resumes the pipeline after a person taps Deploy or Hold.

send approval
const deployWorkflow = roundtrip.webhooks.workflow({
  name: "deploy",
  actions: {
    decision: ["deploy", "hold"] as const,
  },
});

const notification = await roundtrip.notify({
  channel: "ops",
  push: {
    title: "Deploy api@1.4.2 to production?",
    body: "12 commits since the last release.",
    priority: "high",
  },
  content: {
    title: "Deploy api@1.4.2 to production?",
    description: "12 commits since the last release.",
    status: "warning",
    actions: deployWorkflow.actions("decision", {
      deploy: { label: "Deploy", style: "primary" },
      hold: { label: "Hold", style: "destructive" },
    }),
  },
  detail: {
    title: "Release notes",
    layout: [
      {
        type: "markdown",
        value:
          "## api@1.4.2\n- Fix checkout retries\n- Add billing audit trail",
      },
    ],
  },
  response: {
    mode: "required",
    behavior: "resolve",
    webhook: "https://ci.example.com/roundtrip/webhooks",
  },
  metadata: { deploymentId: "deploy_8f21", sha: "abc123" },
  idempotency: ["deploy", "deploy_8f21"],
});
webhook
const deployWorkflow = roundtrip.webhooks.workflow({
  name: "deploy",
  actions: {
    decision: ["deploy", "hold"] as const,
  },
});

deployWorkflow
  .onAction("decision", "deploy", async ({ data }) => {
    await continuePipeline(data.metadata.deploymentId);
  })
  .onAction("decision", "hold", async ({ data }) => {
    await stopPipeline(data.metadata.deploymentId);
  });

Follow-up updates

Append progress while the deployment runs and replace the main content when it finishes.

updates
await notification.content.append({
  content: {
    title: "Rollout started",
    description: "Canary receiving 5% of traffic.",
    status: "info",
  },
});

await notification.content.replace({
  title: "api@1.4.2 deployed",
  description: "Rollout completed successfully.",
  status: "success",
});