1/**
2 * GitCafe's client definition. Browser- and React Native-safe.
3 *
4 * @module source-control-gitcafe/client/definition
5 */
6import {
7 makeProviderSettingsSchema,
8 secretSetting,
9 SourceControlProviderKind,
10 TrimmedString,
11} from "@t3tools/contracts";
12import * as Effect from "effect/Effect";
13import * as Schema from "effect/Schema";
14import {
15 defineSourceControlClient,
16 isChangeRequestInProjectRepository,
17 isChangeRequestOnProjectHost,
18 isChangeRequestPath,
19} from "@t3tools/source-control-core/client/definition";
20
21/** GitCafe has no self-hosted installs: production and staging are its only hosts. */
22const GITCAFE_HOSTS = new Set(["git.cafe", "staging.git.cafe"]);
23
24const KIND = SourceControlProviderKind.make("gitcafe");
25
26/** A git.cafe token, used before `CAFE_TOKEN` and the `cafe` login. */
27export const settings = makeProviderSettingsSchema({
28 token: TrimmedString.pipe(
29 secretSetting,
30 Schema.withDecodingDefault(Effect.succeed("")),
31 Schema.annotateKey({
32 title: "Token",
33 description:
34 "A git.cafe token, used before CAFE_TOKEN and the cafe login, so GitCafe works without the CLI signed in.",
35 providerSettingsForm: { control: "password" },
36 }),
37 ),
38});
39export type GitCafeSettings = typeof settings.Type;
40
41export const definition = defineSourceControlClient({
42 kind: KIND,
43 label: "GitCafe",
44 pickerLabel: "GitCafe",
45 icon: "gitcafe",
46 changeRequest: { shortLabel: "PR", singular: "pull request" },
47 repositoryPathHint: "owner/repo",
48 // Left unattributed so a reference with no repository identity behind it gets no checkout
49 // command guessed from its hostname alone.
50 publicHost: null,
51 publishDescription: "git.cafe",
52 publishHost: () => "git.cafe",
53 newRepositoryOwner: (account) => (account ? { owner: account } : null),
54 defaultCloneTransport: "ssh",
55 changeRequestUrl: ({ host, repository, number }) =>
56 `https://${host}/${repository}/pulls/${number}`,
57 changeRequestActions: new Set([
58 "ready",
59 "draft",
60 "close",
61 "reopen",
62 "merge",
63 "update-branch",
64 ] as const),
65 checkoutCommand: ({ number }) => `cafe pr checkout ${number}`,
66 authorProfileUrl: () => null,
67 referenceAutolinkRepositoryUrl: () => null,
68 reviewSummaryRequired: () => false,
69 // `/pulls/` is Forgejo's shape too, so only GitCafe's own hosts claim it.
70 // `cafe pr checkout` and git.cafe URLs were never accepted in reference fields.
71 checkoutCommandArgument: () => null,
72 isChangeRequestReference: () => false,
73 changeRequestUrlHost: (url) => url.hostname,
74 checkoutChangeRequestHost: () => null,
75 isChangeRequestInRepository: (identity, link) =>
76 isChangeRequestInProjectRepository(KIND, identity, link),
77 canReadChangeRequestOnHost: (identity, link) =>
78 isChangeRequestOnProjectHost(KIND, identity, link),
79 isChangeRequestUrl: (url) => {
80 try {
81 return (
82 GITCAFE_HOSTS.has(new URL(url).hostname.toLowerCase()) &&
83 isChangeRequestPath(url, "/pulls/")
84 );
85 } catch {
86 return false;
87 }
88 },
89 settings,
90});