Skip to content
292 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 branch 'mirroring' into artifacts-mode18pub const EVENT_TYPES: [&str; 105] = [
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",
Merge branch 'mirroring' into artifacts-mode120 "mirror.unreachable",
121 "mirror.reachable",
122 "mirror.state_changed",
123 "mirror.moved_in",
Webhooks: every event, to your own addresses, signed and retried124];
125
126/// What a webhook belongs to.
127#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
128#[serde(rename_all = "snake_case")]
129pub enum HookScope {
130 /// One repository's events.
131 Repo,
132 /// The events of every repository in a workspace.
133 Workspace,
134}
135
136#[derive(Clone, Debug, Serialize, Deserialize)]
137#[serde(rename_all = "camelCase")]
138pub struct Hook {
139 pub id: String,
140 pub scope: HookScope,
141 pub workspace: String,
142 /// For a repository's webhook: `owner/name`.
143 pub repo: Option<String>,
144 pub url: String,
145 /// The event types it is sent, or `["*"]` for all.
146 pub events: Vec<String>,
147 pub active: bool,
148 /// The last four characters of its secret.
149 pub secret_hint: String,
150 pub created_by: String,
151 /// RFC 3339.
152 pub created_at: String,
153 /// How its latest delivery went: `delivered`, `pending` or `failed`.
154 pub last_status: Option<String>,
155 pub last_delivered_at: Option<String>,
156}
157
158/// One event sent, or being sent, to a webhook.
159#[derive(Clone, Debug, Serialize, Deserialize)]
160#[serde(rename_all = "camelCase")]
161pub struct HookDelivery {
162 pub id: String,
163 pub hook_id: String,
164 /// The event's id, or empty for a ping.
165 pub event_id: String,
166 pub event: String,
167 /// `pending` while it will be tried again, `delivered`, or `failed` once
168 /// it has been tried as often as it will be.
169 pub status: String,
170 pub attempts: u32,
171 /// The receiver's HTTP status, the last time it answered.
172 pub response_status: Option<u16>,
173 /// The start of what it answered.
174 pub response_body: Option<String>,
175 /// Why the last attempt failed, when the receiver could not be reached.
176 pub error: Option<String>,
177 pub duration_ms: Option<u32>,
178 /// The JSON that was sent.
179 pub payload: String,
180 /// RFC 3339.
181 pub created_at: String,
182 pub delivered_at: Option<String>,
183 pub next_attempt_at: Option<String>,
184}
185
186/// Which webhooks a call is about: a repository's, or with `repo` left out,
187/// the workspace's own.
188#[derive(Clone, Debug, Serialize, Deserialize)]
189pub struct HookOwner {
190 pub workspace: String,
191 #[serde(default)]
192 pub repo: Option<RepoPath>,
193}
194
195/// `list`. Returns `Outcome<Vec<Hook>>`. Members of the workspace only.
196#[derive(Debug, Serialize, Deserialize)]
197pub struct ListArgs {
198 pub viewer: Viewer,
199 #[serde(flatten)]
200 pub owner: HookOwner,
201}
202
203/// `create`. Returns `Outcome<CreatedHook>`. Members, for a repository's
204/// webhooks; owners, for the workspace's.
205#[derive(Debug, Serialize, Deserialize)]
206pub struct CreateArgs {
207 pub actor: User,
208 #[serde(flatten)]
209 pub owner: HookOwner,
210 pub url: String,
211 /// Event types, or `["*"]` for all. All when empty.
212 #[serde(default)]
213 pub events: Vec<String>,
214 /// Made by g1t when left out.
215 #[serde(default)]
216 pub secret: Option<String>,
217}
218
219#[derive(Clone, Debug, Serialize, Deserialize)]
220pub struct CreatedHook {
221 pub hook: Hook,
222 /// The secret, when g1t made it: shown this once.
223 pub secret: Option<String>,
224}
225
226/// `update`: only the fields given change. Returns `Outcome<Hook>`.
227#[derive(Debug, Serialize, Deserialize)]
228pub struct UpdateArgs {
229 pub actor: User,
230 #[serde(flatten)]
231 pub owner: HookOwner,
232 pub id: String,
233 #[serde(default)]
234 pub url: Option<String>,
235 #[serde(default)]
236 pub events: Option<Vec<String>>,
237 #[serde(default)]
238 pub active: Option<bool>,
239}
240
241/// `delete` (returns `Outcome<bool>`) and `ping` (sends a `ping` event and
242/// returns `Outcome<HookDelivery>`).
243#[derive(Debug, Serialize, Deserialize)]
244pub struct HookArgs {
245 pub actor: User,
246 #[serde(flatten)]
247 pub owner: HookOwner,
248 pub id: String,
249}
250
251/// `deliveries`: a webhook's latest deliveries, newest first. Returns
252/// `Outcome<Vec<HookDelivery>>`.
253#[derive(Debug, Serialize, Deserialize)]
254pub struct DeliveriesArgs {
255 pub viewer: Viewer,
256 #[serde(flatten)]
257 pub owner: HookOwner,
258 pub id: String,
259}
260
261/// `redeliver`: sends a delivery's payload again, as a new delivery.
262/// Returns `Outcome<HookDelivery>`.
263#[derive(Debug, Serialize, Deserialize)]
264#[serde(rename_all = "camelCase")]
265pub struct RedeliverArgs {
266 pub actor: User,
267 #[serde(flatten)]
268 pub owner: HookOwner,
269 pub delivery_id: String,
270}
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look271
272#[cfg(test)]
273mod tests {
274 use super::EVENT_TYPES;
275
276 /// The TypeScript mirror lists the same events, in the same order.
277 #[test]
278 fn the_typescript_mirror_lists_the_same_events() {
279 let ts = include_str!("../../../packages/contracts/src/webhooks.ts");
280 let list = ts
281 .split_once("export const EVENT_TYPES = [")
282 .and_then(|(_, rest)| rest.split_once("] as const"))
283 .map(|(list, _)| list)
284 .expect("EVENT_TYPES in webhooks.ts");
285 let mirrored: Vec<&str> = list
286 .split(',')
287 .map(|item| item.trim().trim_matches('"'))
288 .filter(|item| !item.is_empty())
289 .collect();
290 assert_eq!(mirrored, EVENT_TYPES);
291 }
292}

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