Skip to content
Source
Sign inSign up

projectClone.ts

111 lines4.1 KB

1import * as Schema from "effect/Schema";
2​
3import { IsoDateTime, NonNegativeInt, ProjectId, TrimmedNonEmptyString } from "./baseSchemas.ts";
4import {
5 SourceControlCloneProtocol,
6 SourceControlProviderKind,
7 SourceControlRepositoryInfo,
8} from "./sourceControl.ts";
9​
10export const PROJECT_CLONE_DETAIL_MAX_LENGTH = 200;
11export const PROJECT_CLONE_ERROR_MAX_LENGTH = 1000;
12​
13export const ProjectCloneStage = Schema.Literals([
14 "connecting",
15 "counting",
16 "receiving",
17 "resolving",
18 "checkout",
19]);
20export type ProjectCloneStage = typeof ProjectCloneStage.Type;
21​
22export const ProjectClonePhase = Schema.Literals(["running", "done", "failed", "cancelled"]);
23export type ProjectClonePhase = typeof ProjectClonePhase.Type;
24​
25export const ProjectCloneSnapshot = Schema.Struct({
26 projectId: ProjectId,
27 remoteUrl: TrimmedNonEmptyString,
28 destinationPath: TrimmedNonEmptyString,
29 repository: Schema.NullOr(SourceControlRepositoryInfo),
30 phase: ProjectClonePhase,
31 stage: ProjectCloneStage,
32 percent: Schema.NullOr(Schema.Int.check(Schema.isBetween({ minimum: 0, maximum: 100 }))),
33 detail: Schema.NullOr(Schema.String.check(Schema.isMaxLength(PROJECT_CLONE_DETAIL_MAX_LENGTH))),
34 /** Git has printed nothing for a while; `detail` is no longer current. Older servers omit it. */
35 stalled: Schema.optionalKey(Schema.Boolean),
36 error: Schema.NullOr(Schema.String.check(Schema.isMaxLength(PROJECT_CLONE_ERROR_MAX_LENGTH))),
37 startedAt: IsoDateTime,
38 endedAt: Schema.NullOr(IsoDateTime),
39 sequence: NonNegativeInt,
40});
41export type ProjectCloneSnapshot = typeof ProjectCloneSnapshot.Type;
42​
43export const ProjectCloneSubscribeInput = Schema.Struct({});
44export type ProjectCloneSubscribeInput = typeof ProjectCloneSubscribeInput.Type;
45​
46export const ProjectCloneListEvent = Schema.Array(ProjectCloneSnapshot);
47export type ProjectCloneListEvent = typeof ProjectCloneListEvent.Type;
48​
49export const ProjectCloneStartInput = Schema.Struct({
50 projectId: ProjectId,
51 title: TrimmedNonEmptyString,
52 createdAt: IsoDateTime,
53 provider: Schema.optional(SourceControlProviderKind),
54 repository: Schema.optional(TrimmedNonEmptyString),
55 remoteUrl: Schema.optional(TrimmedNonEmptyString),
56 destinationPath: TrimmedNonEmptyString,
57 protocol: Schema.optional(SourceControlCloneProtocol),
58});
59export type ProjectCloneStartInput = typeof ProjectCloneStartInput.Type;
60​
61export const ProjectCloneStartResult = Schema.Struct({
62 projectId: ProjectId,
63 cwd: TrimmedNonEmptyString,
64 remoteUrl: TrimmedNonEmptyString,
65 repository: Schema.NullOr(SourceControlRepositoryInfo),
66});
67export type ProjectCloneStartResult = typeof ProjectCloneStartResult.Type;
68​
69export const ProjectCloneActionInput = Schema.Struct({
70 projectId: ProjectId,
71});
72export type ProjectCloneActionInput = typeof ProjectCloneActionInput.Type;
73​
74export const ProjectCloneActionResult = Schema.Struct({
75 applied: Schema.Boolean,
76});
77export type ProjectCloneActionResult = typeof ProjectCloneActionResult.Type;
78​
79function projectCloneStageLabel(stage: ProjectCloneStage): string {
80 switch (stage) {
81 case "connecting":
82 return "Connecting";
83 case "counting":
84 return "Counting objects";
85 case "receiving":
86 return "Receiving objects";
87 case "resolving":
88 return "Resolving deltas";
89 case "checkout":
90 return "Checking out files";
91 }
93​
94export function projectCloneDisplayName(
95 snapshot: Pick<ProjectCloneSnapshot, "repository" | "destinationPath">,
96): string {
97 if (snapshot.repository) return snapshot.repository.nameWithOwner;
98 const segments = snapshot.destinationPath.split(/[/\\]/).filter((segment) => segment.length > 0);
99 return segments[segments.length - 1] ?? snapshot.destinationPath;
101​
102export function projectCloneProgressSummary(
103 snapshot: Pick<ProjectCloneSnapshot, "stage" | "percent" | "detail" | "stalled">,
104): string {
105 const parts = [projectCloneStageLabel(snapshot.stage)];
106 if (snapshot.percent !== null) parts.push(`${snapshot.percent}%`);
107 // The detail carries git's last transfer speed, which a stall makes a lie.
108 if (snapshot.stalled) parts.push("Stalled");
109 else if (snapshot.detail) parts.push(snapshot.detail);
110 return parts.join(" · ");