Skip to content
288 linesCodeBlameRaw

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

Webhooks: every event, to your own addresses, signed and retried1//! The webhooks service: events, delivered to the addresses a repository or
2//! a workspace registers.
3//!
4//! Every event g1t publishes can be delivered: an HTTPS `POST` of JSON,
5//! signed with the webhook's secret in `X-G1t-Signature-256`, and retried
6//! with growing waits when the receiver does not answer with a 2xx. Each
7//! delivery is kept, with what was sent and what came back, and can be sent
8//! again.
9//!
10//! Mirrors `packages/contracts/src/webhooks.ts`.
11
12use serde::{Deserialize, Serialize};
13
14use crate::repos::RepoPath;
15use crate::{User, Viewer};
16
17/// Every event a webhook can be sent, in the order people are shown them.
Merge Actions: cross-repo workflows and actions, release and deployment triggers, step timeouts18pub const EVENT_TYPES: [&str; 101] = [
Webhooks: every event, to your own addresses, signed and retried19 "git.push",
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look20 "branch.renamed",
Webhooks: every event, to your own addresses, signed and retried21 "repo.created",
22 "repo.forked",
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look23 "repo.updated",
24 "repo.visibility_changed",
25 "repo.renamed",
26 "repo.transferred",
27 "repo.default_branch_changed",
28 "repo.archived",
29 "repo.unarchived",
30 "repo.deleted",
31 "repo.restored",
32 "repo.purged",
33 "repo.collaborator_added",
34 "repo.collaborator_removed",
35 "repo.collaborator_role_changed",
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar36 "team.created",
37 "team.edited",
38 "team.deleted",
39 "team.member_added",
40 "team.member_role_changed",
41 "team.member_removed",
42 "team.repo_added",
43 "team.repo_role_changed",
44 "team.repo_removed",
Merge rulesets: branch and tag rules, agent-first, enforced on push and merge45 "ruleset.created",
46 "ruleset.updated",
47 "ruleset.deleted",
Webhooks: every event, to your own addresses, signed and retried48 "issue.opened",
49 "issue.updated",
50 "issue.assigned",
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar51 "issue.labeled",
52 "issue.unlabeled",
53 "issue.milestoned",
54 "issue.demilestoned",
Webhooks: every event, to your own addresses, signed and retried55 "issue.closed",
56 "issue.reopened",
57 "comment.created",
Merge Actions: cross-repo workflows and actions, release and deployment triggers, step timeouts58 "comment.edited",
59 "comment.deleted",
Webhooks: every event, to your own addresses, signed and retried60 "pull.opened",
61 "pull.ready",
62 "pull.updated",
63 "pull.merge_requested",
64 "pull.merged",
65 "pull.closed",
Merge Actions: cross-repo workflows and actions, release and deployment triggers, step timeouts66 "pull.reopened",
67 "pull.converted_to_draft",
Events: review requests, assignments, stops and deployments are published68 "pull.assigned",
69 "pull.review_requested",
70 "pull.review_request_removed",
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar71 "pull.labeled",
72 "pull.unlabeled",
73 "pull.milestoned",
74 "pull.demilestoned",
75 "pull.base_changed",
Events: review requests, assignments, stops and deployments are published76 "pull.stalled",
77 "pull.resumed",
Merge Actions: cross-repo workflows and actions, release and deployment triggers, step timeouts78 "release.created",
79 "release.published",
80 "release.released",
81 "release.prereleased",
82 "release.edited",
83 "release.unpublished",
84 "release.deleted",
Agents asked while not at work are woken to answer85 "agent.asked",
Webhooks: every event, to your own addresses, signed and retried86 "checks.completed",
Merge checks: statuses and check runs on every commit87 "status.created",
88 "check_run.created",
89 "check_run.completed",
90 "check_run.rerequested",
91 "check_run.requested_action",
92 "check_suite.completed",
93 "check_suite.rerequested",
Webhooks: every event, to your own addresses, signed and retried94 "review.completed",
Actions: workflow_run, workflow.completed, artifacts on the run page, Node 2495 "workflow.completed",
Events: review requests, assignments, stops and deployments are published96 "deployment.succeeded",
97 "deployment.failed",
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb9798 "deployment.created",
99 "deployment_status.created",
Webhooks: every event, to your own addresses, signed and retried100 "queue.changed",
101 "session.appended",
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member102 "package.published",
103 "package.version_deleted",
104 "package.deleted",
105 "package.visibility_changed",
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar106 "secret_scanning_alert.created",
107 "secret_scanning_alert.fixed",
108 "secret_scanning_alert.dismissed",
109 "secret_scanning_alert.reopened",
110 "secret_scanning.bypass_requested",
111 "secret_scanning.bypass_reviewed",
112 "code_scanning_alert.created",
113 "code_scanning_alert.fixed",
114 "code_scanning_alert.dismissed",
115 "code_scanning_alert.reopened",
116 "vulnerability_alert.created",
117 "vulnerability_alert.fixed",
118 "vulnerability_alert.dismissed",
119 "vulnerability_alert.reopened",
Webhooks: every event, to your own addresses, signed and retried120];
121
122/// What a webhook belongs to.
123#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
124#[serde(rename_all = "snake_case")]
125pub enum HookScope {
126 /// One repository's events.
127 Repo,
128 /// The events of every repository in a workspace.
129 Workspace,
130}
131
132#[derive(Clone, Debug, Serialize, Deserialize)]
133#[serde(rename_all = "camelCase")]
134pub struct Hook {
135 pub id: String,
136 pub scope: HookScope,
137 pub workspace: String,
138 /// For a repository's webhook: `owner/name`.
139 pub repo: Option<String>,
140 pub url: String,
141 /// The event types it is sent, or `["*"]` for all.
142 pub events: Vec<String>,
143 pub active: bool,
144 /// The last four characters of its secret.
145 pub secret_hint: String,
146 pub created_by: String,
147 /// RFC 3339.
148 pub created_at: String,
149 /// How its latest delivery went: `delivered`, `pending` or `failed`.
150 pub last_status: Option<String>,
151 pub last_delivered_at: Option<String>,
152}
153
154/// One event sent, or being sent, to a webhook.
155#[derive(Clone, Debug, Serialize, Deserialize)]
156#[serde(rename_all = "camelCase")]
157pub struct HookDelivery {
158 pub id: String,
159 pub hook_id: String,
160 /// The event's id, or empty for a ping.
161 pub event_id: String,
162 pub event: String,
163 /// `pending` while it will be tried again, `delivered`, or `failed` once
164 /// it has been tried as often as it will be.
165 pub status: String,
166 pub attempts: u32,
167 /// The receiver's HTTP status, the last time it answered.
168 pub response_status: Option<u16>,
169 /// The start of what it answered.
170 pub response_body: Option<String>,
171 /// Why the last attempt failed, when the receiver could not be reached.
172 pub error: Option<String>,
173 pub duration_ms: Option<u32>,
174 /// The JSON that was sent.
175 pub payload: String,
176 /// RFC 3339.
177 pub created_at: String,
178 pub delivered_at: Option<String>,
179 pub next_attempt_at: Option<String>,
180}
181
182/// Which webhooks a call is about: a repository's, or with `repo` left out,
183/// the workspace's own.
184#[derive(Clone, Debug, Serialize, Deserialize)]
185pub struct HookOwner {
186 pub workspace: String,
187 #[serde(default)]
188 pub repo: Option<RepoPath>,
189}
190
191/// `list`. Returns `Outcome<Vec<Hook>>`. Members of the workspace only.
192#[derive(Debug, Serialize, Deserialize)]
193pub struct ListArgs {
194 pub viewer: Viewer,
195 #[serde(flatten)]
196 pub owner: HookOwner,
197}
198
199/// `create`. Returns `Outcome<CreatedHook>`. Members, for a repository's
200/// webhooks; owners, for the workspace's.
201#[derive(Debug, Serialize, Deserialize)]
202pub struct CreateArgs {
203 pub actor: User,
204 #[serde(flatten)]
205 pub owner: HookOwner,
206 pub url: String,
207 /// Event types, or `["*"]` for all. All when empty.
208 #[serde(default)]
209 pub events: Vec<String>,
210 /// Made by g1t when left out.
211 #[serde(default)]
212 pub secret: Option<String>,
213}
214
215#[derive(Clone, Debug, Serialize, Deserialize)]
216pub struct CreatedHook {
217 pub hook: Hook,
218 /// The secret, when g1t made it: shown this once.
219 pub secret: Option<String>,
220}
221
222/// `update`: only the fields given change. Returns `Outcome<Hook>`.
223#[derive(Debug, Serialize, Deserialize)]
224pub struct UpdateArgs {
225 pub actor: User,
226 #[serde(flatten)]
227 pub owner: HookOwner,
228 pub id: String,
229 #[serde(default)]
230 pub url: Option<String>,
231 #[serde(default)]
232 pub events: Option<Vec<String>>,
233 #[serde(default)]
234 pub active: Option<bool>,
235}
236
237/// `delete` (returns `Outcome<bool>`) and `ping` (sends a `ping` event and
238/// returns `Outcome<HookDelivery>`).
239#[derive(Debug, Serialize, Deserialize)]
240pub struct HookArgs {
241 pub actor: User,
242 #[serde(flatten)]
243 pub owner: HookOwner,
244 pub id: String,
245}
246
247/// `deliveries`: a webhook's latest deliveries, newest first. Returns
248/// `Outcome<Vec<HookDelivery>>`.
249#[derive(Debug, Serialize, Deserialize)]
250pub struct DeliveriesArgs {
251 pub viewer: Viewer,
252 #[serde(flatten)]
253 pub owner: HookOwner,
254 pub id: String,
255}
256
257/// `redeliver`: sends a delivery's payload again, as a new delivery.
258/// Returns `Outcome<HookDelivery>`.
259#[derive(Debug, Serialize, Deserialize)]
260#[serde(rename_all = "camelCase")]
261pub struct RedeliverArgs {
262 pub actor: User,
263 #[serde(flatten)]
264 pub owner: HookOwner,
265 pub delivery_id: String,
266}
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look267
268#[cfg(test)]
269mod tests {
270 use super::EVENT_TYPES;
271
272 /// The TypeScript mirror lists the same events, in the same order.
273 #[test]
274 fn the_typescript_mirror_lists_the_same_events() {
275 let ts = include_str!("../../../packages/contracts/src/webhooks.ts");
276 let list = ts
277 .split_once("export const EVENT_TYPES = [")
278 .and_then(|(_, rest)| rest.split_once("] as const"))
279 .map(|(list, _)| list)
280 .expect("EVENT_TYPES in webhooks.ts");
281 let mirrored: Vec<&str> = list
282 .split(',')
283 .map(|item| item.trim().trim_matches('"'))
284 .filter(|item| !item.is_empty())
285 .collect();
286 assert_eq!(mirrored, EVENT_TYPES);
287 }
288}

This file's history is long; its oldest lines are credited to the oldest commit read.