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 @@
11 import type { GitStatusRemoteResult, GitStatusResult } from "@t3tools/contracts";
22 import { describe, expect, it } from "vitest";
33
4−import { applyGitStatusStreamEvent } from "./git";
4+import {
5+ applyGitStatusStreamEvent,
6+ normalizeGitRemoteUrl,
7+ parseGitHubRepositoryNameWithOwnerFromRemoteUrl,
8+} from "./git";
9+
10+describe("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+
42+describe("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+});
552
653 describe("applyGitStatusStreamEvent", () => {
754 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
8080 return branchName.slice(firstSeparatorIndex + 1);
8181 }
8282
83+/**
84+ * Normalize a git remote URL into a stable comparison key.
85+ */
86+export 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+ */
119+export 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+
83133 function deriveLocalBranchNameCandidatesFromRemoteRef(
84134 branchName: string,
85135 remoteName?: string,