History

feat(pull-requests): link multiple pull requests to threads (#10839)

Julius Marminge committed 2 months ago

Commit afb84891 parent 383cc40172 files · +12057 −908Browse files

Showing the changes to packages/shared/src/sourceControl.ts (1 of 172 files). Show all

Unified · Split

packages/shared/src/sourceControl.ts+45−1
@@ -1,4 +1,8 @@
1−import type { SourceControlProviderInfo, SourceControlProviderKind } from "@t3tools/contracts";
1+import type {
2+ RepositoryIdentity,
3+ SourceControlProviderInfo,
4+ SourceControlProviderKind,
5+} from "@t3tools/contracts";
26
37 export interface ChangeRequestPresentation {
48 readonly icon: "github" | "gitlab" | "azure-devops" | "bitbucket" | "change-request";
@@ -234,3 +238,43 @@ export function detectSourceControlProviderFromRemoteUrl( · 229 unmodified lines
234238 baseUrl: toBaseUrl(host),
235239 };
236240 }
241+
242+/**
243+ * The provider-native repository selector. `displayName` is the full path below the host, which
244+ * is what nested GitLab groups need; owner/name is the two-segment fallback for identities
245+ * recorded before that field existed.
246+ *
247+ * Azure DevOps is the exception: `az repos pr list --repository` takes a repository name, and
248+ * takes the organisation and project from the checkout it detects — so the recorded
249+ * `org/project/_git/repo` path is refused outright and the whole repository reads as
250+ * unavailable. Its name is the last segment, which is what this hands over.
251+ *
252+ * One function because everything downstream is keyed by what it answers: the rows' own
253+ * `repository`, the per-repository cursors, and the detail and diff reads a row leads to.
254+ */
255+export function sourceControlRepositorySelector(
256+ identity:
257+ | Pick<RepositoryIdentity, "provider" | "displayName" | "owner" | "name">
258+ | null
259+ | undefined,
260+): string | null {
261+ if (!identity) return null;
262+ if (identity.provider === "azure-devops") {
263+ const segments = (identity.displayName ?? "").split("/").filter((part) => part !== "_git");
264+ return identity.name || segments.at(-1) || null;
265+ }
266+ if (identity.displayName) return identity.displayName;
267+ return identity.owner && identity.name ? `${identity.owner}/${identity.name}` : null;
268+}
269+
270+export function canonicalRepositoryKey(key: string): string {
271+ return key
272+ .replace(
273+ /^(?:ssh\.dev\.azure\.com|vs-ssh\.visualstudio\.com)\/v3\/([^/]+)\/([^/]+)\/([^/]+)$/u,
274+ "dev.azure.com/$1/$2/_git/$3",
275+ )
276+ .replace(
277+ /^([^.]+)\.visualstudio\.com\/(?:defaultcollection\/)?([^/]+)\/_git\/([^/]+)$/u,
278+ "dev.azure.com/$1/$2/_git/$3",
279+ );
280+}