1import type * as Effect from "effect/Effect";
2import type * as Stream from "effect/Stream";
3import {
4 consumeBusEvents,
5 type EventRecord,
6 type EventRouteProps,
7} from "../EventBridge/EventSource.ts";
8
9/**
10 * The `detail` payload CodeDeploy delivers to EventBridge when a deployment
11 * or one of its instances changes state. Fields not shared by every event
12 * kind are optional (the schema grows over time).
13 */
14export interface DeploymentEventDetail {
15 /** The deployment's id (`d-…`). */
16 deploymentId?: string;
17 /** Name of the CodeDeploy application. */
18 application?: string;
19 /** Name of the deployment group. */
20 deploymentGroup?: string;
21 /**
22 * The new state — `START`, `READY`, `SUCCESS`, `FAILURE`, or `STOP`.
23 */
24 state?: string;
25 /** Region the deployment runs in. */
26 region?: string;
27 /** Instance state-change events: the affected instance's id. */
28 instanceId?: string;
29 /** Instance state-change events: the deployment's instance group id. */
30 instanceGroupId?: string;
31 /** Additional event fields (the schema grows over time). */
32 [key: string]: unknown;
33}
34
35/** A CodeDeploy EventBridge event delivered to the handler. */
36export type DeploymentEvent = EventRecord<DeploymentEventDetail>;
37
38/** Which CodeDeploy notifications to subscribe to. */
39export type DeploymentEventKind = "deployment" | "instance";
40
41const DETAIL_TYPES: Record<DeploymentEventKind, string> = {
42 deployment: "CodeDeploy Deployment State-change Notification",
43 instance: "CodeDeploy Instance State-change Notification",
44};
45
46export interface DeploymentEventSourceProps extends EventRouteProps {
47 /**
48 * Logical id for the backing EventBridge rule.
49 * @default "CodeDeployEvents"
50 */
51 id?: string;
52 /**
53 * Which notifications to subscribe to: deployment state changes,
54 * per-instance state changes, or both.
55 * @default ["deployment"]
56 */
57 kinds?: readonly DeploymentEventKind[];
58 /**
59 * Restrict to events about specific applications (matched against the
60 * event's `application`).
61 */
62 applications?: readonly string[];
63 /**
64 * Restrict to events about specific deployment groups (matched against
65 * the event's `deploymentGroup`).
66 */
67 deploymentGroups?: readonly string[];
68}
69
70/**
71 * Event source connecting CodeDeploy deployment notifications to the
72 * hosting compute. CodeDeploy publishes every deployment state change (and
73 * per-instance state change) to the account's default EventBridge bus
74 * (source `aws.codedeploy`); this subscribes the host Function to those
75 * events so it can alert on failed deployments or chain post-deploy
76 * automation.
77 *
78 * CodeDeploy publishes to EventBridge automatically — no additional
79 * resource is created besides the EventBridge rule targeting the host.
80 * Provide the host-specific implementation layer (e.g.
81 * `AWS.Lambda.EventSource`) on the Function effect.
82 *
83 * ### Consuming Deployment Events
84 * **Example:** Alert On Failed Deployments
85 * ```typescript
86 * import * as AWS from "alchemy/AWS";
87 *
88 * export default AlertFunction.make(
89 * { main: import.meta.url },
90 * Effect.gen(function* () {
91 * yield* AWS.CodeDeploy.consumeDeploymentEvents(
92 * { kinds: ["deployment"] },
93 * (events) =>
94 * Stream.runForEach(events, (event) =>
95 * event.detail.state === "FAILURE"
96 * ? Effect.log(`deployment ${event.detail.deploymentId} failed`)
97 * : Effect.void,
98 * ),
99 * );
100 * return {};
101 * }).pipe(Effect.provide(AWS.Lambda.EventSource)),
102 * );
103 * ```
104 */
105export const consumeDeploymentEvents = <StreamReq = never, Req = never>(
106 props: DeploymentEventSourceProps,
107 process: (
108 events: Stream.Stream<DeploymentEvent, never, StreamReq>,
109 ) => Effect.Effect<void, never, Req>,
110) =>
111 consumeBusEvents(
112 props.id ?? "CodeDeployEvents",
113 {
114 source: ["aws.codedeploy"],
115 "detail-type": (props.kinds ?? (["deployment"] as const)).map(
116 (kind) => DETAIL_TYPES[kind],
117 ),
118 ...(props.applications !== undefined ||
119 props.deploymentGroups !== undefined
120 ? {
121 detail: {
122 ...(props.applications !== undefined
123 ? { application: [...props.applications] }
124 : {}),
125 ...(props.deploymentGroups !== undefined
126 ? { deploymentGroup: [...props.deploymentGroups] }
127 : {}),
128 },
129 }
130 : {}),
131 },
132 { description: props.description, state: props.state },
133 process,
134 );