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

Commit

Search shows every repository's own project's description, also where the project had one of its own before #45. Search kept repositories' descriptions until then, so such a project still showed the repository's old text there and on Explore. Projects now tells search every one once: the first listing or lookup after this deploys publishes project.updated for each repository's own project with a description, after its answer is sent, claimed by a row in the new sweeps table so it runs once for all of g1t. The fork form also says what a fork's project starts with, not that a description is copied. Checked: projects' type-check and tests (47), its migrations on local D1, and node --test scripts/deploy (92). Looked at signed-in: the repository header and About, Repository and General settings, the New project and fork forms, the workspace's Repositories, a repository's forks, and the AI page's Anthropic form.

G1t-Session:
ses_01m4ybyg80e6qrrzww8qzs19yw
syntaqxcommitted Parent46d665fBrowse files
3 files+63−170/3 viewed
+1−1
161161 />
162162
163163 <ul className="list-disc space-y-1.5 pl-5 text-sm text-muted">
164− <li>Its code and description are copied. Its settings, secrets, variables, rules, webhooks, deployments, issues and pull requests are not.</li>
164+ <li>Its code is copied, and its project starts with this one's description. Its settings, secrets, variables, rules, webhooks, deployments, issues and pull requests are not.</li>
165165 <li>Workflows do not run in the fork until you turn Actions on, and Issues start off.</li>
166166 <li>Its size counts toward the storage of the workspace it goes in.{source.isPrivate ? " A free workspace may not have room for it." : ""}</li>
167167 </ul>
+7−0
1+-- One-off jobs the service runs once for all of g1t, each claimed by its
2+-- row. 'descriptions': search is told every repository's own project's
3+-- description (#45), since it had kept repositories' own.
4+CREATE TABLE sweeps (
5+ key TEXT PRIMARY KEY,
6+ at TEXT NOT NULL
7+);
+55−16
156156
157157 const now = () => new Date().toISOString();
158158
159+/**
160+ * Whether this isolate has seen the descriptions sweep done (see
161+ * `sweepDescriptions`), so a listing reads its row once, not every time.
162+ */
163+let descriptionsSwept = false;
164+
159165 function toProject(row: Row): Project {
160166 const setting = { kind: kindSetting(row.kind_setting), runs: runsSetting(row.runs_setting) };
161167 const facts: Omit<KindFacts, "setting"> = {
323329 async list(a: { workspace: string; viewer: Viewer }): Promise<Result<Project[]>> {
324330 const workspace = a.workspace.toLowerCase();
325331 await this.backfill(workspace);
332+ if (!descriptionsSwept) this.defer(this.sweepDescriptions());
326333 const rows = await this.db
327334 .prepare("SELECT * FROM projects WHERE workspace = ? AND repo_deleted_at IS NULL ORDER BY name COLLATE NOCASE")
328335 .bind(workspace)
337344 async get(a: { workspace: string; slug: string; viewer: Viewer }): Promise<Result<Project>> {
338345 const workspace = a.workspace.toLowerCase();
339346 await this.backfill(workspace);
347+ if (!descriptionsSwept) this.defer(this.sweepDescriptions());
340348 const row = await this.db
341349 .prepare("SELECT * FROM projects WHERE workspace = ? AND slug = ? AND repo_deleted_at IS NULL")
342350 .bind(workspace, a.slug.toLowerCase())
401409 private async updated(row: Row, actor: User): Promise<void> {
402410 if (!this.env.EVENTS) return;
403411 await eventsClient(this.env.EVENTS)
404− .publish([
405− {
406− type: "project.updated",
407− source: "projects",
408− repoId: row.repo_id,
409− actor: actor.username,
410− data: {
411− projectId: row.id,
412− workspace: row.workspace,
413− slug: row.slug,
414− repoId: row.repo_id,
415− primary: row.is_primary === 1,
416− description: row.description,
417− },
418− },
419− ])
412+ .publish([projectUpdated(row, actor.username)])
420413 .catch((error: unknown) => console.error("project.updated not published", row.id, String(error)));
421414 }
422415
423416 /**
417+ * Once for all of g1t: tells search every repository's own project's
418+ * description. Search kept repositories' descriptions until #45, so a
419+ * project that already had its own showed the repository's old one there.
420+ * Claimed by its row in `sweeps` (migrations/0016), so it runs once.
421+ */
422+ private async sweepDescriptions(): Promise<void> {
423+ if (!this.env.EVENTS) return;
424+ const done = await this.db.prepare("SELECT 1 FROM sweeps WHERE key = 'descriptions'").first();
425+ if (done) {
426+ descriptionsSwept = true;
427+ return;
428+ }
429+ const claimed = await this.db.prepare("INSERT OR IGNORE INTO sweeps (key, at) VALUES ('descriptions', ?)").bind(now()).run();
430+ descriptionsSwept = true;
431+ if (!claimed.meta.changes) return;
432+ const rows = await this.db
433+ .prepare("SELECT * FROM projects WHERE is_primary = 1 AND repo_deleted_at IS NULL AND description IS NOT NULL")
434+ .all<Row>();
435+ const events = rows.results.map((row) => projectUpdated(row, null));
436+ for (let at = 0; at < events.length; at += 50) {
437+ await eventsClient(this.env.EVENTS)
438+ .publish(events.slice(at, at + 50))
439+ .catch((error: unknown) => console.error("descriptions sweep: some not published", String(error)));
440+ }
441+ console.log("descriptions sweep: published", events.length);
442+ }
443+
444+ /**
424445 * Sets the description of a repository's own project, making the project
425446 * first if it is not there yet: for whatever makes a repository with a
426447 * description, such as creating one through the API or importing one.
11651186 }
11661187 }
11671188
1189+/** `project.updated` for a project as it is now. */
1190+function projectUpdated(row: Row, actor: string | null) {
1191+ return {
1192+ type: "project.updated" as const,
1193+ source: "projects",
1194+ repoId: row.repo_id,
1195+ actor,
1196+ data: {
1197+ projectId: row.id,
1198+ workspace: row.workspace,
1199+ slug: row.slug,
1200+ repoId: row.repo_id,
1201+ primary: row.is_primary === 1,
1202+ description: row.description,
1203+ },
1204+ };
1205+}
1206+
11681207 export default {
11691208 async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
11701209 const match = new URL(request.url).pathname.match(/^\/rpc\/([a-z_]+)$/);

Comments

Sign in to comment.