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
238 linesCodeBlameRaw
1import type { User, Viewer } from "./identity";
2import type { RepoPath } from "./repos";
3import type { Result } from "./result";
4
5/**
6 * Projects: the thing a workspace builds and runs. A project has exactly
7 * one source, where its code lives; everything about running it
8 * (deployments, environments, domains, secrets and variables) belongs to
9 * the project, while branches, pull requests and review stay with the
10 * repository. One repository may carry several projects, each from its own
11 * root directory. Every repository on g1t gets a project of its own name.
12 */
13
14/** Where a project's code lives: a repository hosted on g1t. */
15export type ProjectSource = { kind: "hosted"; repoId: string; repo: RepoPath; rootDir: string; defaultBranch: string };
16
17export type Project = {
18 id: string;
19 /** The workspace's slug. */
20 workspace: string;
21 /** Unique in its workspace; the project's address is `g1t.sh/<workspace>/<slug>`. */
22 slug: string;
23 name: string;
24 /** What it is, in a line. Only projects have descriptions; repositories do not. */
25 description: string | null;
26 source: ProjectSource;
27 /** Whether its repository is private: only people with a role on it can see it. */
28 private: boolean;
29 /** Whether its repository is archived: read-only, kept for reference. */
30 archived: boolean;
31 /** Whether it is the project its repository's workflows read secrets from. */
32 primary: boolean;
33 /** What a person set it to be; each part null while it is left to detection. */
34 setting: ProjectSetting;
35 /** What it is: from `setting`, or for what is left out, from its deployments, packages and files. */
36 kind: ProjectKind;
37 /** Why it is that kind. */
38 kindReason: KindReason;
39 /**
40 * Where it runs: `g1t` when it is hosted on g1t.page (Deployments),
41 * `elsewhere` when it deploys itself (a workflow on g1t Actions, or
42 * another pipeline) to hosting of its own, at `productionUrl`. Null for
43 * what is not deployed (a library, a tool) and for an app nobody has
44 * placed that has neither Deployments on nor deployed production itself.
45 */
46 runs: ProjectRuns | null;
47 /** Where production is when it deploys itself; null when nobody has given one. */
48 productionUrl: string | null;
49 /**
50 * An image of production someone uploaded, shown on its overview when
51 * there is no screenshot, or instead of one when `preferred`. Null when
52 * none was uploaded.
53 */
54 productionImage: ProductionImage | null;
55 /** What detection decides, whatever the setting is, to show beside it. */
56 detected: { kind: ProjectKind; reason: KindReason };
57 /** The ecosystem its files say it publishes to, for how to publish; null when none says. */
58 ecosystem: ProjectEcosystem | null;
59 /** Its homepage, docs and other links, shown wherever the project is. */
60 links: ProjectLinks;
61 createdBy: string;
62 /** RFC 3339. */
63 createdAt: string;
64 updatedAt: string;
65 /** RFC 3339: when its repository was last pushed to; null until it is, after projects began keeping it. */
66 pushedAt: string | null;
67 /**
68 * How active it is lately: each push, issue or pull request opened or
69 * closed, review, comment and deployment counts one, halving every week.
70 * 0 for none.
71 */
72 activity: number;
73};
74
75/**
76 * What a project is. An app (or a site) runs somewhere and gets a
77 * production card; a library is published and installed; a tool, such as
78 * a CLI, ships as releases people install; docs are documentation or site
79 * content; other is anything else, such as configuration or research.
80 */
81export type ProjectKind = "app" | "library" | "tool" | "docs" | "other";
82
83/** Every kind, in the order pages offer them. */
84export const PROJECT_KINDS: readonly ProjectKind[] = ["app", "library", "tool", "docs", "other"];
85
86/** Where an app or a site runs: hosted on g1t.page, or deploying itself to hosting of its own. */
87export type ProjectRuns = "g1t" | "elsewhere";
88
89/** What a person set; null for each part left to detection. */
90export type ProjectSetting = { kind: ProjectKind | null; runs: ProjectRuns | null };
91
92/** One of a project's own links: a label and an http(s) address. */
93export type ProjectLink = { label: string; url: string };
94
95export type ProjectLinks = {
96 /** Its homepage: its own, or its repository's website while it has none. */
97 homepage: string | null;
98 /** Whether `homepage` is its repository's website, following it as it changes. */
99 homepageInherited: boolean;
100 /** Where its documentation is read. */
101 docs: string | null;
102 /** Any others, in the order given, at most `MAX_PROJECT_LINKS`. */
103 custom: ProjectLink[];
104};
105
106/**
107 * An uploaded image of a project's production. `file` is the SHA-256 of
108 * its bytes: it is served from the usercontent origin at
109 * `/production-images/<file>`.
110 */
111export type ProductionImage = { file: string; contentType: string; preferred: boolean };
112
113/** The largest production image taken, in bytes. */
114export const MAX_PRODUCTION_IMAGE_BYTES = 5 * 1024 * 1024;
115
116/** The types a production image may be, read from its bytes. */
117export const PRODUCTION_IMAGE_TYPES: readonly string[] = ["image/png", "image/jpeg", "image/webp"];
118
119/**
120 * A change to a project's production image. `image` is the new one's bytes
121 * in base64, or null to remove it (and with it the preference); left out,
122 * it stays. `preferred` shows it instead of a screenshot.
123 */
124export type ProductionImageChange = { image?: string | null; preferred?: boolean };
125
126/** How many links of its own a project keeps besides its homepage and docs. */
127export const MAX_PROJECT_LINKS = 10;
128/** The longest link label. */
129export const MAX_LINK_LABEL = 40;
130/** The longest link address. */
131export const MAX_LINK_URL = 255;
132
133/** A change to a project; only what is given changes. */
134export type ProjectChanges = {
135 name?: string;
136 /** Null or blank clears it. */
137 description?: string | null;
138 rootDir?: string;
139 /** What it is; `auto` leaves it to detection. Anything but an app or docs stops it running anywhere. */
140 kind?: ProjectKind | "auto";
141 /** Where it runs; `auto` leaves it to Deployments. Setting it makes it an app unless it is docs. */
142 runs?: ProjectRuns | "auto";
143 /** Production's address when it deploys itself; null or blank clears it. */
144 productionUrl?: string | null;
145 /** Null or blank goes back to the repository's website. */
146 homepage?: string | null;
147 /** Null or blank clears it. */
148 docsUrl?: string | null;
149 /** Replaces its other links. */
150 links?: ProjectLink[];
151};
152
153/**
154 * What decided the kind: the setting, Deployments being on, a package its
155 * repository publishes, its files, or nothing (an app, by default).
156 * `detail` says it in a sentence.
157 */
158export type KindReason = { by: "set" | "deployments" | "packages" | "files" | "default"; detail: string };
159
160/** Where a library's files say it is published. */
161export type ProjectEcosystem = "composer" | "npm" | "cargo" | "go" | "python";
162
163export type NewProject = {
164 name: string;
165 description?: string | null;
166 /** The hosted repository it builds from. */
167 repo: RepoPath;
168 /** Where in the repository it lives; empty for the whole repository. */
169 rootDir?: string;
170};
171
172/**
173 * What a person keeps at hand in a workspace: the projects they pinned, in
174 * their order, then the ones they opened last that they have not pinned.
175 * Only projects they can still see.
176 */
177export type ProjectShortcuts = { pinned: Project[]; recent: Project[] };
178
179export interface ProjectsApi {
180 /** A workspace's projects, by name. Members, or anyone for public repositories. */
181 list(workspace: string, viewer: Viewer): Promise<Result<Project[]>>;
182 get(workspace: string, slug: string, viewer: Viewer): Promise<Result<Project>>;
183 /** The projects built from a repository, its primary one first. For services. */
184 byRepo(repoId: string): Promise<Project[]>;
185 /**
186 * The descriptions of repositories' own projects, by repository id, for
187 * lists of repositories; one without is left out. Up to 500 ids.
188 */
189 descriptions(repoIds: string[]): Promise<Record<string, string>>;
190 /**
191 * Sets the description of a repository's own project, making the project
192 * first if need be: for what makes a repository with a description.
193 */
194 describe(actor: User, repoId: string, description: string | null): Promise<Result<Project>>;
195 /** Members only. */
196 create(actor: User, workspace: string, input: NewProject): Promise<Result<Project>>;
197 /**
198 * Members with a role that may change its settings. Making it something
199 * that does not run (a library, a tool, other) while Deployments are on
200 * is refused: they are turned off first.
201 */
202 update(actor: User, workspace: string, slug: string, changes: ProjectChanges): Promise<Result<Project>>;
203 /**
204 * Uploads, replaces or removes its production image, or says whether it
205 * is shown instead of a screenshot. Whoever may change the project's
206 * settings, as for its production address.
207 */
208 setProductionImage(actor: User, workspace: string, slug: string, change: ProductionImageChange): Promise<Result<Project>>;
209 /**
210 * For the screenshot service's daily refresh: every production address
211 * of a project deployed elsewhere that shows a screenshot (not an
212 * uploaded image instead), once each.
213 */
214 externalProductions(): Promise<string[]>;
215 /** For deployments: Deployments were turned on or off for the project. */
216 deploymentsChanged(projectId: string, enabled: boolean): Promise<void>;
217 /** A person's pinned and recent projects in a workspace. Empty for anyone else. */
218 shortcuts(workspace: string, viewer: Viewer): Promise<ProjectShortcuts>;
219 /**
220 * Pins a project the person can see, at `position` (0 first) or at the
221 * end; pinning one already pinned moves it. A person's own, at most 8 a
222 * workspace. Returns their pins, in order.
223 */
224 pin(actor: User, workspace: string, slug: string, position?: number | null): Promise<Result<Project[]>>;
225 /** Unpins it. Returns their pins, in order. */
226 unpin(actor: User, workspace: string, slug: string): Promise<Result<Project[]>>;
227 /** Puts their pins in this order: every pinned project's slug, once. */
228 reorderPins(actor: User, workspace: string, slugs: string[]): Promise<Result<Project[]>>;
229 /** The person opened the project: it leads their recent ones. */
230 visited(actor: User, projectId: string): Promise<void>;
231 /**
232 * For lists of public repositories, such as Explore: each one's own
233 * project's address to show, by `namespace/name` in lower case:
234 * production when it is deployed elsewhere, else its homepage, else its
235 * docs. Repositories without one are left out. At most 100 asked at once.
236 */
237 publicLinks(repos: RepoPath[]): Promise<Record<string, string>>;
238}