A work item's activity counts for every repository it links
Projects grow more active on work_item.opened, .closed and .reopened, and on comments, in each repository the work item is linked to.
- G1T-Session:
- ses_01m566357hejh9yeqjbetx23f3
3 files+28−90/3 viewed
| 1 | 1 | import { test } from "node:test"; | |
| 2 | 2 | import assert from "node:assert/strict"; | |
| 3 | 3 | ||
| 4 | − | import { ACTIVE, decayed, raised } from "./activity.ts"; | |
| 4 | + | import { ACTIVE, activeRepos, decayed, raised } from "./activity.ts"; | |
| 5 | 5 | ||
| 6 | 6 | const DAY = 24 * 60 * 60 * 1000; | |
| 7 | 7 | const at = (days: number) => new Date(Date.UTC(2026, 9, 1) + days * DAY).toISOString(); | |
| ⋯ | |||
| 39 | 39 | assert.ok(ACTIVE.has("git.push")); | |
| 40 | 40 | assert.ok(ACTIVE.has("pull.merged")); | |
| 41 | 41 | assert.ok(!ACTIVE.has("repo.created")); | |
| 42 | + | assert.ok(ACTIVE.has("work_item.opened")); | |
| 43 | + | }); | |
| 44 | + | ||
| 45 | + | test("a work item's activity counts for every repository it is linked to", () => { | |
| 46 | + | assert.deepEqual(activeRepos("rep_1", { itemId: "wki_1", repoIds: ["rep_1", "rep_2"] }), ["rep_1", "rep_2"]); | |
| 47 | + | // Filed in the workspace and linked nowhere: no project's. | |
| 48 | + | assert.deepEqual(activeRepos(null, { itemId: "wki_2", repoIds: [] }), []); | |
| 49 | + | assert.deepEqual(activeRepos(null, { repoId: "rep_3", number: 4 }), ["rep_3"]); | |
| 50 | + | assert.deepEqual(activeRepos("rep_4", {}), ["rep_4"]); | |
| 42 | 51 | }); | |
| 1 | 1 | /** | |
| 2 | 2 | * How active a project is lately, for sorting a workspace's projects: each | |
| 3 | − | * push, issue or pull request opened or closed, review, comment and | |
| 4 | − | * deployment adds one, and what came before counts for half as much every | |
| 3 | + | * push, work item or pull request opened or closed, review, comment and | |
| 4 | + | * deployment adds one (a work item's, to every repository it is linked to), and what came before counts for half as much every | |
| 5 | 5 | * week. Kept as a score and the time it was last raised, so raising it is | |
| 6 | 6 | * one row's update. Pure, so it is tested apart from the service. | |
| 7 | 7 | */ | |
| ⋯ | |||
| 11 | 11 | /** What happens to a project's code and work that counts as activity. */ | |
| 12 | 12 | export const ACTIVE: ReadonlySet<EventType> = new Set<EventType>([ | |
| 13 | 13 | "git.push", | |
| 14 | − | "issue.opened", | |
| 15 | − | "issue.closed", | |
| 16 | − | "issue.reopened", | |
| 14 | + | "work_item.opened", | |
| 15 | + | "work_item.closed", | |
| 16 | + | "work_item.reopened", | |
| 17 | 17 | "pull.opened", | |
| 18 | 18 | "pull.updated", | |
| 19 | 19 | "pull.merged", | |
| ⋯ | |||
| 24 | 24 | "deployment.failed", | |
| 25 | 25 | ]); | |
| 26 | 26 | ||
| 27 | + | /** | |
| 28 | + | * The repositories an event's activity counts for: every repository a work | |
| 29 | + | * item (or a comment on one) is linked to, else the one it names. | |
| 30 | + | */ | |
| 31 | + | export function activeRepos(repoId: string | null | undefined, data: unknown): string[] { | |
| 32 | + | const linked = (data as { repoIds?: unknown } | null)?.repoIds; | |
| 33 | + | if (Array.isArray(linked) && linked.length > 0) return linked.filter((id): id is string => typeof id === "string" && id !== ""); | |
| 34 | + | const named = repoId ?? (data as { repoId?: unknown } | null)?.repoId; | |
| 35 | + | return typeof named === "string" && named !== "" ? [named] : []; | |
| 36 | + | } | |
| 37 | + | ||
| 27 | 38 | /** How long until activity counts for half. */ | |
| 28 | 39 | export const HALF_LIFE_MS = 7 * 24 * 60 * 60 * 1000; | |
| 29 | 40 | ||
| 29 | 29 | runsSetting, | |
| 30 | 30 | } from "./kind"; | |
| 31 | 31 | import { cleanLinks, cleanUrl, projectLinks } from "./links"; | |
| 32 | − | import { ACTIVE, decayed, raised } from "./activity"; | |
| 32 | + | import { ACTIVE, activeRepos, decayed, raised } from "./activity"; | |
| 33 | 33 | import { MAX_PINS, MAX_RECENT, MAX_VISITS, placePin, recentAfterPins, reorderPins } from "./pins"; | |
| 34 | 34 | import { renameStatements } from "./rename"; | |
| 35 | 35 | import { moveStatements, slugOf, strandedQuery } from "./transfer"; | |
| ⋯ | |||
| 984 | 984 | ||
| 985 | 985 | async onEvent(event: G1tEvent): Promise<void> { | |
| 986 | 986 | if (ACTIVE.has(event.type)) { | |
| 987 | − | const repoId = event.repoId ?? ("repoId" in event.data ? (event.data as { repoId?: string }).repoId : undefined); | |
| 988 | − | if (repoId) await this.active(repoId, event.time, event.type === "git.push"); | |
| 987 | + | for (const repoId of activeRepos(event.repoId, event.data)) await this.active(repoId, event.time, event.type === "git.push"); | |
| 989 | 988 | } | |
| 990 | 989 | if (event.type === "deployment_status.created") { | |
| 991 | 990 | await this.productionReported(event.data); | |
Comments
Sign in to comment.