History

Prepare datamodel for multi-environment (#1765)

Julius Marminge committed 7 months ago

Commit b96308f1 parent 72b7f90121 files · +10206 −4969Browse files

Showing the changes to packages/shared/src (2 of 121 files). Show all

Unified · Split

2 files
  1. packages/shared/src/git.test.ts +48 −1
  2. packages/shared/src/git.ts +50 −0
packages/shared/src/git.test.ts+48−1
@@ -1,7 +1,54 @@
1import type { GitStatusRemoteResult, GitStatusResult } from "@t3tools/contracts";1import type { GitStatusRemoteResult, GitStatusResult } from "@t3tools/contracts";
2import { describe, expect, it } from "vitest";2import { describe, expect, it } from "vitest";
3 3
4import { applyGitStatusStreamEvent } from "./git";4import {
5 applyGitStatusStreamEvent,
6 normalizeGitRemoteUrl,
7 parseGitHubRepositoryNameWithOwnerFromRemoteUrl,
8} from "./git";
9
10describe("normalizeGitRemoteUrl", () => {
11 it("canonicalizes equivalent GitHub remotes across protocol variants", () => {
12 expect(normalizeGitRemoteUrl("git@github.com:T3Tools/T3Code.git")).toBe(
13 "github.com/t3tools/t3code",
14 );
15 expect(normalizeGitRemoteUrl("https://github.com/T3Tools/T3Code.git")).toBe(
16 "github.com/t3tools/t3code",
17 );
18 expect(normalizeGitRemoteUrl("ssh://git@github.com/T3Tools/T3Code")).toBe(
19 "github.com/t3tools/t3code",
20 );
21 });
22
23 it("preserves nested group paths for providers like GitLab", () => {
24 expect(normalizeGitRemoteUrl("git@gitlab.com:T3Tools/platform/T3Code.git")).toBe(
25 "gitlab.com/t3tools/platform/t3code",
26 );
27 expect(normalizeGitRemoteUrl("https://gitlab.com/T3Tools/platform/T3Code.git")).toBe(
28 "gitlab.com/t3tools/platform/t3code",
29 );
30 });
31
32 it("drops explicit ports from URL-shaped remotes", () => {
33 expect(normalizeGitRemoteUrl("https://gitlab.company.com:8443/team/project.git")).toBe(
34 "gitlab.company.com/team/project",
35 );
36 expect(normalizeGitRemoteUrl("ssh://git@gitlab.company.com:2222/team/project.git")).toBe(
37 "gitlab.company.com/team/project",
38 );
39 });
40});
41
42describe("parseGitHubRepositoryNameWithOwnerFromRemoteUrl", () => {
43 it("extracts the owner and repository from common GitHub remote shapes", () => {
44 expect(
45 parseGitHubRepositoryNameWithOwnerFromRemoteUrl("git@github.com:T3Tools/T3Code.git"),
46 ).toBe("T3Tools/T3Code");
47 expect(
48 parseGitHubRepositoryNameWithOwnerFromRemoteUrl("https://github.com/T3Tools/T3Code.git"),
49 ).toBe("T3Tools/T3Code");
50 });
51});
5 52
6describe("applyGitStatusStreamEvent", () => {53describe("applyGitStatusStreamEvent", () => {
7 it("treats a remote-only update as a repository when local state is missing", () => {54 it("treats a remote-only update as a repository when local state is missing", () => {
packages/shared/src/git.ts+50−0
@@ -80,6 +80,56 @@ export function deriveLocalBranchNameFromRemoteRef(branchName: string): string { · 79 unmodified lines
80 return branchName.slice(firstSeparatorIndex + 1);80 return branchName.slice(firstSeparatorIndex + 1);
81}81}
82 82
83/**
84 * Normalize a git remote URL into a stable comparison key.
85 */
86export function normalizeGitRemoteUrl(value: string): string {
87 const normalized = value
88 .trim()
89 .replace(/\/+$/g, "")
90 .replace(/\.git$/i, "")
91 .toLowerCase();
92
93 if (/^(?:ssh|https?|git):\/\//i.test(normalized)) {
94 try {
95 const url = new URL(normalized);
96 const repositoryPath = url.pathname
97 .split("/")
98 .filter((segment) => segment.length > 0)
99 .join("/");
100 if (url.hostname && repositoryPath.includes("/")) {
101 return `${url.hostname}/${repositoryPath}`;
102 }
103 } catch {
104 return normalized;
105 }
106 }
107
108 const scpStyleHostAndPath = /^git@([^:/\s]+)[:/]([^/\s]+(?:\/[^/\s]+)+)$/i.exec(normalized);
109 if (scpStyleHostAndPath?.[1] && scpStyleHostAndPath[2]) {
110 return `${scpStyleHostAndPath[1]}/${scpStyleHostAndPath[2]}`;
111 }
112
113 return normalized;
114}
115
116/**
117 * Best-effort parse of a GitHub `owner/repo` identifier from common remote URL shapes.
118 */
119export function parseGitHubRepositoryNameWithOwnerFromRemoteUrl(url: string | null): string | null {
120 const trimmed = url?.trim() ?? "";
121 if (trimmed.length === 0) {
122 return null;
123 }
124
125 const match =
126 /^(?:git@github\.com:|ssh:\/\/git@github\.com\/|https:\/\/github\.com\/|git:\/\/github\.com\/)([^/\s]+\/[^/\s]+?)(?:\.git)?\/?$/i.exec(
127 trimmed,
128 );
129 const repositoryNameWithOwner = match?.[1]?.trim() ?? "";
130 return repositoryNameWithOwner.length > 0 ? repositoryNameWithOwner : null;
131}
132
83function deriveLocalBranchNameCandidatesFromRemoteRef(133function deriveLocalBranchNameCandidatesFromRemoteRef(
84 branchName: string,134 branchName: string,
85 remoteName?: string,135 remoteName?: string,