Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

225 linesCodeBlameRaw
1//! Work items (`g1t_contracts::work::items`): any tracked work of a
2//! workspace, linked to the repositories it concerns
3//! (migrations/0049_work_items.sql).
4//!
5//! A work item is numbered in its workspace's sequence (`work_counters`,
6//! numbering.rs), which new pull requests share, and links repositories in
7//! `work_item_repos` (links.rs): its origin, its targets and those it
8//! touched. Who may see one is `access::item_role`: the workspace's
9//! members, and anyone who can read its origin. What `#N` names is
10//! refs.rs; what landing one does, landing.rs. Labels and milestones are
11//! the workspace's, and pull requests carry them too (labels.rs,
12//! milestones.rs). Moving repositories' issues in is migration.rs.
13//!
14//! This module is the boundary. Its RPC methods (`open_work_item` and the
15//! other `*_work_item` methods, labels, milestones, `resolve_ref`,
16//! `closed_by_commit`, `legacy_item_map`, `migrate_work_items`) are the
17//! service's; the rest of the crate (pull requests, merging, pushes,
18//! coordination) reaches work items only through the functions below, and
19//! reads them in its own queries only through the fragments in [`sql`]. A
20//! later split of work items into a service of their own turns each
21//! function into an RPC method and each fragment into a copy the caller
22//! keeps from `work_item.*` events.
23//!
24//! Work items call out the other way at a few points, which a split would
25//! turn into events: what a close means for claims and blocked work
26//! (`Work::item_resolved`), the session a close was made in
27//! (`Work::caller_session`, `Work::trailer_session`), and repositories and
28//! their settings. `repo_workspaces`, where each repository is, is kept
29//! here and read by anyone.
30
31use g1t_contracts::access::Capability;
32use g1t_contracts::repos::Repo;
33use g1t_contracts::work::refs::Ref;
34use g1t_contracts::{Outcome, User, Viewer};
35use worker::Result;
36
37use crate::Work;
38
39/// Unwraps an `Outcome`, returning its failure from the enclosing method.
40macro_rules! check {
41 ($outcome:expr) => {
42 match $outcome {
43 Outcome::Ok(value) => value,
44 Outcome::Fail(failure) => return Ok(Outcome::Fail(failure)),
45 }
46 };
47}
48
49mod changes;
50mod dependencies;
51mod labels;
52mod landing;
53mod links;
54mod migration;
55mod milestones;
56mod numbering;
57mod reading;
58mod refs;
59mod rows;
60pub(crate) mod sql;
61#[cfg(test)]
62mod tests;
63
64pub(crate) use dependencies::Brief;
65pub(crate) use labels::Labeled;
66pub(crate) use landing::{Landing, PastLanding};
67pub(crate) use migration::{DELETED, RENAMED, TRANSFERRED};
68pub(crate) use reading::{held_rows, select};
69pub(crate) use refs::{MAX_CLOSES, Named, NamedItems};
70pub(crate) use rows::{Held, path_of, workspace_of};
71
72// --- Reading ---------------------------------------------------------------
73
74/// The work item numbered `number` in `workspace`, whoever is asking.
75pub(crate) async fn by_number(work: &Work, workspace: &str, number: u32) -> Result<Option<Held>> {
76 work.item(workspace, number).await
77}
78
79/// A work item by id, whoever is asking.
80pub(crate) async fn by_id(work: &Work, id: &str) -> Result<Option<Held>> {
81 work.item_by_id(id).await
82}
83
84/// The work items of these ids, in no particular order.
85pub(crate) async fn by_ids(work: &Work, ids: &[String]) -> Result<Vec<Held>> {
86 work.items_by_ids(ids).await
87}
88
89/// A work item, if `viewer` may do `capability` on it; not found when they
90/// may not see it.
91pub(crate) async fn for_viewer(work: &Work, viewer: &Viewer, workspace: &str, number: u32, capability: Capability) -> Result<Outcome<Held>> {
92 work.item_for(viewer, workspace, number, capability).await
93}
94
95/// A pull request's own work item, if it has one.
96pub(crate) async fn for_pull(work: &Work, pull_id: &str) -> Result<Option<Held>> {
97 match work.pull_item_id(pull_id).await? {
98 Some(id) => work.item_by_id(&id).await,
99 None => Ok(None),
100 }
101}
102
103// --- Numbers and repositories ----------------------------------------------
104
105/// The next number of `workspace`'s sequence, for a pull request of
106/// `repo_id` or (without one) a work item.
107pub(crate) async fn next_number(work: &Work, workspace: &str, repo_id: Option<&str>) -> Result<u32> {
108 work.next_number(workspace, repo_id).await
109}
110
111/// Records which workspace `repo` is in, its path and whether it is
112/// private, as repos just said.
113pub(crate) async fn remember_repo(work: &Work, repo: &Repo) -> Result<()> {
114 work.remember_repo(repo).await
115}
116
117/// The workspace repository `repo_id` is in.
118pub(crate) async fn repo_workspace(work: &Work, repo_id: &str) -> Result<Option<String>> {
119 work.repo_workspace(repo_id).await
120}
121
122/// `repo.created`: where the repository is, and for a workspace's first,
123/// the default labels.
124pub(crate) async fn repo_created(work: &Work, data: &serde_json::Value) -> Result<()> {
125 work.repo_created(data).await
126}
127
128/// `repo.visibility_changed`: whether its work items are public.
129pub(crate) async fn repo_visibility(work: &Work, data: &serde_json::Value) -> Result<()> {
130 work.repo_visibility(data).await
131}
132
133// --- References and landing ------------------------------------------------
134
135/// What `#number` names, read in repository `repo_id` of `workspace`.
136pub(crate) async fn resolve(work: &Work, repo_id: &str, workspace: &str, number: u32) -> Result<Option<Named>> {
137 Ok(work.lookup_ref(Some(repo_id), workspace, number).await?.named(true))
138}
139
140/// The work item `reference` names, read in `repo`, by id: one of its
141/// workspace's, or none.
142pub(crate) async fn referenced(work: &Work, repo: &Repo, reference: &Ref) -> Result<Option<String>> {
143 work.ref_item(repo, reference).await
144}
145
146/// The work items `text`, written in `repo`, closes and is part of, with
147/// `own` (a pull request's own work item) closed first.
148pub(crate) async fn named(work: &Work, repo: &Repo, own: Option<&str>, text: &str) -> Result<NamedItems> {
149 work.named_items(repo, own, text).await
150}
151
152/// Lands `closes` (work items, by id) in `repo` by `landing` and links
153/// `parts` (landing.rs). Returns the work items it closed.
154#[allow(clippy::too_many_arguments)]
155pub(crate) async fn land(
156 work: &Work,
157 repo: &Repo,
158 landing: &Landing,
159 actor: &User,
160 closes: &[String],
161 parts: &[String],
162 session: Option<&str>,
163 agent: bool,
164) -> Result<Vec<Held>> {
165 work.land(repo, landing, actor, closes, parts, session, agent).await
166}
167
168/// Links `repo` to `held` as touched (it has a pull request for it),
169/// unless it is linked already.
170pub(crate) async fn touch(work: &Work, held: &Held, repo: &Repo, actor: &User) -> Result<()> {
171 work.touch_item(held, repo, actor).await
172}
173
174/// Where the other work items of `held`'s workspace landed, newest first.
175pub(crate) async fn landed_before(work: &Work, held: &Held, limit: u32) -> Result<Vec<PastLanding>> {
176 work.landed_before(held, limit).await
177}
178
179// --- Its conversation ------------------------------------------------------
180
181/// Notes something that happened to a work item, in its conversation.
182pub(crate) async fn note<'a>(work: &Work, item_id: &str, author: impl Into<crate::rows::By<'a>>, text: &str) -> Result<()> {
183 work.note_item(item_id, author, text).await
184}
185
186/// A statement that marks a work item updated at `at`, for a caller's
187/// batch (a comment on it).
188pub(crate) fn stamp(work: &Work, item_id: &str, at: &str) -> Result<worker::D1PreparedStatement> {
189 work.db.prepare("UPDATE work_items SET updated_at = ? WHERE id = ?").bind(&[at.into(), item_id.into()])
190}
191
192// --- Labels and milestones -------------------------------------------------
193
194/// Sets the labels of a work item or pull request to `wanted`.
195pub(crate) async fn relabel(work: &Work, actor: &User, labeled: &Labeled, wanted: &[String]) -> Result<Outcome<Vec<String>>> {
196 work.relabel(actor, labeled, wanted).await
197}
198
199/// Puts a work item or pull request in milestone `number` (0: none).
200pub(crate) async fn set_milestone(work: &Work, actor: &User, labeled: &Labeled, number: u32) -> Result<Outcome<()>> {
201 work.set_milestone(actor, labeled, number).await
202}
203
204// --- What blocks what ------------------------------------------------------
205
206/// The open work items `held` blocks.
207pub(crate) async fn dependents(work: &Work, held: &Held) -> Result<Vec<Held>> {
208 work.dependents(held).await
209}
210
211/// Those of `numbers` whose work item in `workspace` is still open,
212/// leaving out `cleared`.
213pub(crate) async fn still_open(work: &Work, workspace: &str, numbers: &[u32], cleared: Option<u32>) -> Result<Vec<u32>> {
214 work.still_open(workspace, numbers, cleared).await
215}
216
217/// The work items of `workspace` that `numbers` names, in brief.
218pub(crate) async fn briefs(work: &Work, workspace: &str, numbers: &[u32]) -> Result<Vec<Brief>> {
219 work.briefs(workspace, numbers).await
220}
221
222/// The work items that name `held` in their `blocked_by`.
223pub(crate) async fn blocked_items(work: &Work, held: &Held) -> Result<Vec<g1t_contracts::work::ItemBrief>> {
224 work.blocked_items(held).await
225}