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.

g1t.sh
583 linesCodeBlameRaw
1import type { Contributors, Languages, License, NewRelease, Release, ReleaseChange, RepoAbout, Stargazer, StarredRepo, Stars } from "./about";
2import type { User, Viewer } from "./identity";
3import type { Result } from "./result";
4
5export type Repo = {
6 id: string;
7 /** The slug of the workspace that owns it: the first URL segment. */
8 namespace: string;
9 name: string;
10 description: string | null;
11 isPrivate: boolean;
12 ownerId: string;
13 defaultBranch: string;
14 /**
15 * Set when this repo is a pull request's working copy of another repo
16 * (namespace `pulls`, hidden from listings). A fork someone made into a
17 * workspace is an ordinary repository with `parentId` instead.
18 */
19 forkOf: string | null;
20 /**
21 * The repository this one was forked from, when someone forked it into
22 * their workspace. Null once it is detached, or when its parent changed
23 * who can see it.
24 */
25 parentId?: string | null;
26 /**
27 * What it was made a fork of, kept when it is detached: Actions and
28 * issues stay off by default, and its pull requests into the parent
29 * still merge.
30 */
31 forkedFrom?: string | null;
32 /** Where `parentId` is, for a viewer who may read it (from `get`, `getById` and `forks`). */
33 parent?: RepoPath | null;
34 /** A private repository's admins let others fork it. Public ones can always be forked. */
35 allowForking?: boolean;
36 /** The most branches and tags one push may change; pushes over it are refused whole. Null for no limit. */
37 maxRefUpdates?: number | null;
38 /**
39 * Whether the default branch is protected: it changes only by merging a
40 * pull request, and pushes to it are refused.
41 */
42 protected: boolean;
43 /** RFC 3339. */
44 createdAt: string;
45 /**
46 * Words that say what it is about, for search and Explore: lowercase
47 * letters, digits and hyphens, at most 20.
48 */
49 topics: string[];
50 /** Its home page, an http(s) address. */
51 website?: string | null;
52 /**
53 * RFC 3339: when it was archived. While archived it is read-only:
54 * pushes, merges, agents and workflows are refused, and issues and pull
55 * requests are locked. Null when it is not archived.
56 */
57 archivedAt?: string | null;
58};
59
60export type ForkRepoInput = {
61 /** The workspace to fork it into. */
62 namespace: string;
63 /** The fork's name; the source's when absent. */
64 name?: string;
65 /** The source's when absent; an empty string clears it. */
66 description?: string;
67 /** Copy only the default branch (the default), or every branch and tag. */
68 defaultBranchOnly?: boolean;
69};
70
71/** How a pull request's branch is brought up to date: a merge commit, or its commits made again on the base. */
72export type UpdateMethod = "merge" | "rebase";
73
74/** What `syncFork` did. */
75export type SyncStatus = "up_to_date" | "fast_forwarded" | "merged" | "conflicted";
76
77export type SyncedFork = {
78 status: SyncStatus;
79 branch: string;
80 /** The branch's head after syncing. */
81 head: string;
82 /** Commits the parent's branch had that the fork's did not. */
83 behind: number;
84 /** Commits the fork's branch had that the parent's did not. */
85 ahead: number;
86 /** For `conflicted`: the files both sides changed (at most 20). */
87 conflicts: string[];
88};
89
90/** How long a deleted repository can be restored before it is purged. */
91export const RESTORE_DAYS = 30;
92
93/** A deleted repository, as its workspace's Recently deleted list shows it. */
94export type DeletedRepo = {
95 id: string;
96 namespace: string;
97 name: string;
98 description: string | null;
99 isPrivate: boolean;
100 /** RFC 3339. */
101 deletedAt: string;
102 /** The username of who deleted it. */
103 deletedBy: string;
104 /** RFC 3339: when it is purged, unless restored first. */
105 purgeAfter: string;
106};
107
108/** Whether a repository is archived or deleted, for services deciding whether to act on it. */
109export type RepoStatus = { archived: boolean; deleted: boolean };
110
111/** The most topics a repository has, and the longest topic. */
112export const MAX_TOPICS = 20;
113export const MAX_TOPIC_CHARS = 35;
114
115export type RepoPath = { namespace: string; name: string };
116
117export type Commit = {
118 hash: string;
119 treeHash: string;
120 message: string;
121 author: { name: string; email: string };
122 parents: string[];
123 /** RFC 3339. */
124 authoredAt: string;
125};
126
127export type TreeEntry = {
128 name: string;
129 hash: string;
130 kind: "tree" | "blob" | "symlink" | "gitlink" | "exec";
131};
132
133export type TreeView = {
134 repo: Repo;
135 ref: string;
136 path: string;
137 /** Null when the repo has no commits yet. */
138 head: Commit | null;
139 entries: TreeEntry[];
140 readme: { name: string; text: string | null } | null;
141};
142
143export type BlobView = {
144 repo: Repo;
145 ref: string;
146 path: string;
147 size: number;
148 /** Null when the file is binary or too large to show. */
149 text: string | null;
150};
151
152/** A git remote and a short-lived credential for it. */
153export type GitAccess = { remote: string; token: string };
154
155export type GitService = "git-upload-pack" | "git-receive-pack";
156
157export type CreateRepoInput = {
158 /** The workspace to create it in; the creator must be a member. */
159 namespace: string;
160 name: string;
161 description?: string | null;
162 isPrivate?: boolean;
163 /**
164 * The https address of a public git repository to copy the default branch
165 * of, such as `https://github.com/owner/repo`.
166 */
167 importUrl?: string;
168};
169
170/**
171 * Where repositories may be kept. `euAvailable`: an EU namespace takes new
172 * repositories, so a workspace may keep its data in the EU.
173 */
174export type StorageOptions = { euAvailable: boolean };
175
176/** Repositories: metadata, contents and git access. */
177export interface ReposApi {
178 get(path: RepoPath, viewer: Viewer): Promise<Result<Repo>>;
179 getById(id: string, viewer: Viewer): Promise<Result<Repo>>;
180 /**
181 * Of these ids (at most 500), the repositories the viewer can read, in
182 * one call. Forks and deleted repositories are left out.
183 */
184 readable(ids: string[], viewer: Viewer): Promise<Repo[]>;
185 /**
186 * The workspaces in which this account made a public repository, and so
187 * a public project anyone can see. By account id.
188 */
189 publicNamespaces(ownerId: string): Promise<string[]>;
190 /** Repos the viewer may see, newest first, optionally matching `query`. */
191 list(
192 viewer: Viewer,
193 options?: {
194 query?: string;
195 /** Only repos in this workspace. */
196 namespace?: string;
197 /** Only repos in workspaces the viewer belongs to. */
198 memberOnly?: boolean;
199 },
200 ): Promise<Repo[]>;
201 create(owner: User, input: CreateRepoInput): Promise<Result<Repo>>;
202 /**
203 * Changes whichever details are given. Members of the repository's
204 * workspace only. An empty description clears it.
205 */
206 update(
207 actor: User,
208 path: RepoPath,
209 changes: {
210 description?: string;
211 isPrivate?: boolean;
212 protected?: boolean;
213 topics?: string[];
214 /** An empty string clears it. */
215 website?: string;
216 /** Whether a private repository may be forked (Admin). */
217 allowForking?: boolean;
218 /** The most branches and tags one push may change (Admin); 0 for no limit. */
219 maxRefUpdates?: number;
220 },
221 ): Promise<Result<Repo>>;
222 /**
223 * Deletes the repository. Owners only, who type its full name as
224 * `confirm`. It is hidden at once and can be restored for
225 * `RESTORE_DAYS` days, then purged. Publishes `repo.deleted`.
226 */
227 delete(actor: User, path: RepoPath, confirm: string): Promise<Result<DeletedRepo>>;
228 /** A workspace's recently deleted repositories, newest first. Owners only; empty otherwise. */
229 deleted(viewer: Viewer, namespace: string): Promise<DeletedRepo[]>;
230 /** Brings a deleted repository back as it was. Owners only. Publishes `repo.restored`. */
231 restore(actor: User, path: RepoPath): Promise<Result<Repo>>;
232 /**
233 * Removes a deleted repository for good now, its git data with it, and
234 * frees its name. Owners only, who type its full name. Publishes `repo.purged`.
235 */
236 purge(actor: User, path: RepoPath, confirm: string): Promise<Result<boolean>>;
237 /**
238 * Renames the repository in its workspace. Owners only. The old path
239 * redirects until a repository is made there. Publishes `repo.renamed`.
240 */
241 rename(actor: User, path: RepoPath, name: string): Promise<Result<Repo>>;
242 /**
243 * Archives the repository (read-only) or unarchives it. Owners only.
244 * Publishes `repo.archived` or `repo.unarchived`.
245 */
246 archive(actor: User, path: RepoPath, archived: boolean): Promise<Result<Repo>>;
247 /**
248 * Makes the repository public or private. Owners only, who type its full
249 * name as `confirm`. Publishes `repo.updated` and `repo.visibility_changed`.
250 */
251 setVisibility(actor: User, path: RepoPath, isPrivate: boolean, confirm: string): Promise<Result<Repo>>;
252 /** Makes another existing branch the default. Members. */
253 setDefaultBranch(actor: User, path: RepoPath, branch: string): Promise<Result<Repo>>;
254 /**
255 * Renames a branch; pull requests from it follow, and addresses naming the
256 * old one redirect. Members; only owners rename the default branch.
257 */
258 renameBranch(actor: User, path: RepoPath, from: string, to: string): Promise<Result<Repo>>;
259 /**
260 * Makes a branch named `name` at `from`: a branch, a tag or a commit (the
261 * default branch when empty). Needs push; the rules for creating it hold.
262 * Restoring a deleted branch is making it again at the commit
263 * `removeBranch` answered with.
264 */
265 createBranch(actor: User, path: RepoPath, name: string, from?: string): Promise<Result<Branch>>;
266 /**
267 * Deletes a branch, never the default. Needs push; the rules for deleting
268 * it hold. Given `head`, a branch that has moved from it is left alone.
269 * Answers with the branch as it was, to restore it with `createBranch`.
270 */
271 removeBranch(actor: User, path: RepoPath, branch: string, head?: string): Promise<Result<Branch>>;
272 /** What a branch renamed away from `branch` is called now, or null. */
273 resolveBranch(repoId: string, branch: string): Promise<string | null>;
274 /** Whether a repository is archived or deleted; an unknown id answers as deleted. */
275 statusById(id: string): Promise<RepoStatus>;
276 /** What a workspace may choose about where its repositories are kept. */
277 storageOptions(): Promise<StorageOptions>;
278 /**
279 * Moves the repository to the workspace `to`, keeping its name, id and
280 * everything under it. The actor must own both workspaces. The old path
281 * redirects until a repository is made there. Publishes `repo.transferred`.
282 */
283 transfer(actor: User, path: RepoPath, to: string): Promise<Result<Repo>>;
284 /**
285 * Where a repository transferred away from `path` is now; null when
286 * `path` is a repository or never was one that moved. Callers check the
287 * viewer may see it there.
288 */
289 resolvePath(path: RepoPath): Promise<RepoPath | null>;
290
291 tree(path: RepoPath, viewer: Viewer, ref: string | null, treePath: string): Promise<Result<TreeView>>;
292 blob(path: RepoPath, viewer: Viewer, ref: string, filePath: string): Promise<Result<BlobView>>;
293 log(path: RepoPath, viewer: Viewer, ref: string | null, limit: number): Promise<Result<Commit[]>>;
294 /**
295 * Who last changed each line of a file as of `ref` (the default branch if
296 * null). Not found when the file is missing or is not text.
297 */
298 blame(path: RepoPath, viewer: Viewer, ref: string | null, filePath: string): Promise<Result<Blame>>;
299
300 /**
301 * A copy-on-write copy of `source`, hidden from listings, for one pull request
302 * to work in.
303 */
304 forkForPull(sourceId: string, pullId: string, actor: User): Promise<Result<Repo>>;
305 /**
306 * Forks `path` into the workspace `input.namespace`: a new repository
307 * with the source's code (its default branch unless
308 * `defaultBranchOnly` is false) and description, nothing else. Public
309 * repositories can always be forked, private ones when their admins
310 * allow it; a fork is as visible as its source. Publishes `repo.created`
311 * and `repo.forked`.
312 */
313 fork(actor: User, path: RepoPath, input: ForkRepoInput): Promise<Result<Repo>>;
314 /** The forks of `path` the viewer may read, newest first. */
315 forks(path: RepoPath, viewer: Viewer, limit?: number): Promise<Result<Repo[]>>;
316 /**
317 * Brings a fork's branch (its default branch when absent) up to date
318 * with its parent's: fast-forwarded when only behind, merged when the
319 * two changed different files, left alone (`conflicted`) otherwise.
320 */
321 syncFork(actor: User, path: RepoPath, branch?: string): Promise<Result<SyncedFork>>;
322 /** Makes a fork an ordinary repository with no parent (Admin). */
323 detachFork(actor: User, path: RepoPath): Promise<Result<Repo>>;
324
325 /**
326 * Authorizes a git operation and returns where to send it. Pushing to a
327 * repo that does not exist creates it in the pusher's own namespace.
328 */
329 gitAccess(path: RepoPath, viewer: Viewer, service: GitService): Promise<Result<GitAccess>>;
330
331 /** The repository's branches, default branch first. */
332 branches(path: RepoPath, viewer: Viewer): Promise<Result<Branch[]>>;
333
334 /** Where a repository is now, by id; null when it is gone. For services: it checks nobody's access. */
335 pathById(id: string): Promise<RepoPath | null>;
336
337 /** Which commit last changed each entry of a directory at `ref` (the default branch when null). */
338 lastCommits(path: RepoPath, viewer: Viewer, ref: string | null, treePath: string): Promise<Result<LastCommits>>;
339
340 /**
341 * How far each branch head has moved from `base` (the default branch's
342 * head commit), with each head's commit and `base`'s own, in one call.
343 * Kept by the pair of hashes in the repos service.
344 */
345 branchDrift(path: RepoPath, viewer: Viewer, base: string, heads: string[]): Promise<Result<BranchDrifts>>;
346
347 /** The repository's tags, newest commit first, at most 100. */
348 tags(path: RepoPath, viewer: Viewer): Promise<Result<Tag[]>>;
349
350 /**
351 * The Files page's About in one answer: license, security policy,
352 * languages, contributors, stars and releases. What comes from files and
353 * history is kept by commit and worked out in the background when it is
354 * behind the head (see `about.ts`).
355 */
356 about(path: RepoPath, viewer: Viewer): Promise<Result<RepoAbout>>;
357 languages(path: RepoPath, viewer: Viewer): Promise<Result<Languages>>;
358 /** Everyone whose commits are on the default branch, with their weeks. */
359 contributors(path: RepoPath, viewer: Viewer): Promise<Result<Contributors>>;
360 license(path: RepoPath, viewer: Viewer): Promise<Result<License | null>>;
361 /** How many starred it, and whether the viewer did. */
362 stars(path: RepoPath, viewer: Viewer): Promise<Result<Stars>>;
363 /** Stars it for the actor, or takes the star back. */
364 star(actor: User, path: RepoPath, starred: boolean): Promise<Result<Stars>>;
365 /** Who starred it, newest first, 100 a page from 1. */
366 stargazers(path: RepoPath, viewer: Viewer, page?: number): Promise<Result<Stargazer[]>>;
367 /** What a person starred that the viewer can see, newest first, at most 100. */
368 starred(username: string, viewer: Viewer): Promise<StarredRepo[]>;
369 /** Releases, newest first, at most 100: drafts only for those who can push. */
370 releases(path: RepoPath, viewer: Viewer): Promise<Result<Release[]>>;
371 /** One release by id or tag, or the latest. */
372 release(path: RepoPath, viewer: Viewer, which: { id?: string; tag?: string; latest?: boolean }): Promise<Result<Release>>;
373 /** Needs Write. A tag that does not exist yet is made at `target`. */
374 createRelease(actor: User, path: RepoPath, release: NewRelease): Promise<Result<Release>>;
375 updateRelease(actor: User, path: RepoPath, id: string, change: ReleaseChange): Promise<Result<Release>>;
376 /** The tag stays. */
377 deleteRelease(actor: User, path: RepoPath, id: string): Promise<Result<boolean>>;
378
379 /**
380 * Every file at `ref` (the default branch when null), at most `limit`
381 * (10,000 at most); symlinks too with `links`, for an archive. No viewer:
382 * check access first.
383 */
384 listFiles(repoId: string, ref: string | null, limit: number, options?: { links?: boolean }): Promise<FileList>;
385
386 /**
387 * What an archive link's `token` (made by the API's tarball and zipball
388 * routes) lets anyone holding it download from the repository at
389 * `path`: one commit, for ten minutes. Null when it is not for this
390 * repository, has ended or does not open. No viewer: the token is the
391 * permission.
392 */
393 openArchiveToken(path: RepoPath, token: string): Promise<ArchiveGrant | null>;
394
395 /** Blobs' bytes as standard base64, at most 100; `data` is null for one missing or over `maxBytes`. No viewer. */
396 rawBlobs(repoId: string, hashes: string[], maxBytes: number): Promise<RawBlob[]>;
397
398 /** One file's bytes at a branch, tag or commit; null when missing or over `maxBytes`. No viewer: check access first. */
399 rawFile(repoId: string, ref: string, path: string, maxBytes: number): Promise<RawFile | null>;
400
401 /**
402 * Writes one file on a new branch made from the default branch's head, as
403 * one commit by `actor`, for a change g1t proposes on their behalf (a
404 * starter workflow). Refused unless they may push, when the branch exists,
405 * or when the file is already there.
406 */
407 commitFile(
408 repo: RepoPath,
409 actor: User,
410 file: { branch: string; path: string; content: string; message: string },
411 ): Promise<Result<{ branch: string; commit: string }>>;
412
413 /**
414 * Moves a repository's default branch to the head of a pull request's
415 * source: a fork (`sourceId` is the fork) or one of the repository's own
416 * branches (`sourceId` is the repository, and `branch` is required).
417 * Refused with "conflict" when the source is behind, since that would
418 * discard commits.
419 */
420 land(sourceId: string, actor: User, branch?: string | null): Promise<Result<{ commit: string; previous: string | null }>>;
421
422 /**
423 * What `head` changes relative to `base`. `head` is a branch or a commit
424 * and defaults to the default branch. With no `base`, a fork is compared
425 * with the last commit it shares with the repository it came from, and a
426 * branch with the point where it left the default branch.
427 */
428 /**
429 * With no `base`, a branch is compared from where it left `baseBranch`
430 * (the default branch when absent). With `paths`, only those files (at
431 * most 50), with their lines: how a file a large comparison lists as
432 * `omitted` is read. With `targetId` (no `base`), `repoId` is a fork of
433 * that repository, directly or through other forks, and the head is
434 * compared from where it left `baseBranch` there: a pull request from a
435 * fork. Both must be readable.
436 */
437 compare(
438 repoId: string,
439 viewer: Viewer,
440 base?: string | null,
441 head?: string | null,
442 baseBranch?: string | null,
443 paths?: string[] | null,
444 targetId?: string | null,
445 ): Promise<Result<Comparison>>;
446
447 /**
448 * Services only, for the runner's sweep: up to `limit` queued nightly
449 * backups, each now running with a token of its own, so long as no more
450 * than `maxRunning` are then running. Empty when backups are off.
451 */
452 claimBackups(limit: number, maxRunning: number): Promise<BackupClaim[]>;
453
454 /**
455 * Services only: a backup's sandbox stopped before it reported, so the
456 * job is tried again later. Refused harmlessly once it has reported.
457 */
458 failBackup(jobId: string, token: string, error: string): Promise<Result<boolean>>;
459}
460
461/**
462 * A nightly backup to start (`g1t_contracts::backups`): the sandbox is
463 * given the job's id and token, and nothing else.
464 */
465export type BackupClaim = {
466 jobId: string;
467 token: string;
468 repoId: string;
469 path: RepoPath;
470};
471
472/** Lines `start` to `end` (inclusive, from 1) last changed by `commit`. */
473export type BlameRange = { start: number; end: number; commit: string };
474
475/** Who last changed each line of a file. */
476export type Blame = {
477 /** The commit the file was read at. */
478 head: string;
479 /** Every line, in order, in runs that share a commit. */
480 ranges: BlameRange[];
481 /** The commits the ranges name, each once. */
482 commits: Commit[];
483 /** True when the history was too long to read in full (too many commits, or too long to read). */
484 partial: boolean;
485};
486
487/** A branch and the commit it points to. */
488export type Branch = { name: string; hash: string };
489
490/** Each entry's last commit; `complete` is false when some were not reached. */
491export type LastCommits = { entries: { name: string; commit: Commit }[]; complete: boolean };
492
493/** Commits a branch has that the default branch does not, and the other way round. */
494export type BranchDriftCount = { ahead: number; behind: number };
495
496/**
497 * `base`'s commit, and each head's commit and drift in the order asked;
498 * `drift` is null when the two histories do not meet within what is read.
499 */
500export type BranchDrifts = {
501 base: Commit | null;
502 branches: { head: string; commit: Commit | null; drift: BranchDriftCount | null }[];
503};
504
505/** A tag, and its commit when it could be read. */
506export type Tag = { name: string; commit: Commit | null };
507
508/** Files at a commit, and whether there were more than were listed. */
509export type FileList = {
510 commit: string | null;
511 /** When the commit was made (RFC 3339). */
512 date?: string;
513 /** `kind`: `exec` for an executable file, `symlink` for a link (its blob is where it points). */
514 files: { path: string; hash: string | null; kind?: "blob" | "exec" | "symlink" }[];
515 truncated: boolean;
516};
517
518/** One commit of a repository an archive link lets its holder download. */
519export type ArchiveGrant = { repoId: string; namespace: string; name: string; commit: string };
520
521/** One blob's bytes, standard base64; null when missing or too large. */
522export type RawBlob = { hash: string; size: number; data: string | null };
523
524/** One file's bytes, standard base64. */
525export type RawFile = { size: number; data: string };
526
527/**
528 * What came of bringing a pull request up to date with the default branch
529 * without a sandbox. `needs_agent` pushed nothing: the runner's `update`
530 * merges it in a sandbox, with an agent if it conflicts.
531 */
532export type PullBranchUpdate =
533 | { outcome: "updated"; commit: string; previous: string }
534 | { outcome: "up_to_date"; commit: string }
535 | {
536 outcome: "needs_agent";
537 /**
538 * `overlap`: both sides changed some of the same files. `conflicting`:
539 * merging is known to conflict. `unsupported`: it could not be worked
540 * out without git, such as for a very large change.
541 */
542 reason: "overlap" | "conflicting" | "unsupported";
543 detail: string;
544 /** The files both changed, or that conflict, when known. */
545 paths: string[];
546 };
547
548export type DiffLine = {
549 kind: "context" | "add" | "delete";
550 /** Line number in the old file; null for added lines. */
551 old: number | null;
552 /** Line number in the new file; null for deleted lines. */
553 new: number | null;
554 text: string;
555};
556
557/** A run of changed lines with their surrounding context. */
558export type Hunk = { lines: DiffLine[] };
559
560export type FileDiff = {
561 path: string;
562 status: "added" | "modified" | "deleted";
563 additions: number;
564 deletions: number;
565 /** True when the file is binary or too large, so no lines are shown. */
566 binary: boolean;
567 hunks: Hunk[];
568 /**
569 * True when the comparison reached its limit on lines before this file,
570 * so it is listed without its lines or counts: `compare` with its path
571 * in `paths` has them.
572 */
573 omitted?: boolean;
574};
575
576/** What changed between two commits. */
577export type Comparison = {
578 base: string | null;
579 head: string;
580 files: FileDiff[];
581 /** True when more files changed than are listed. */
582 truncated: boolean;
583};