/** * Reads a repository's `origin` remote. Injected by tests so no test depends * on the checkout it happens to run in. */ export type GitRemoteFn = (root: string) => string | undefined; /** * Ask git for `origin`, and treat every failure as "no remote". * * git missing, not a repository, no `origin`, a stalled filesystem - all of * them mean the same thing to a caller, and none of them is worth an error or * a log line. `shell: false` unlike the npm probe in `migration-target.ts`: * git is a real executable, so routing it through a shell would only add * quoting hazards around the user's path. */ export declare const readGitRemote: GitRemoteFn; /** * One repository, one identity, whichever URL form was cloned. * * `git@host:org/repo.git` and `https://host/org/repo` are the same repository * and must produce the same string, or a binding made from one clone would be * invisible from another. * * Two rules are load-bearing rather than cosmetic: * * - **Any userinfo is dropped.** A remote can carry `user:token@`, and a token * must never reach `~/.jam/projects.yaml`, a log line or telemetry. * - **An explicit non-default port is kept.** `git.example.com:8443/org/repo` * and `git.example.com/org/repo` may be different services - self-hosted * GitLab makes that ordinary - so only a port that is the scheme's own * default is dropped. */ export declare function canonicalRemote(url: string): string | undefined; export type WorkspaceIdentityOptions = { /** The repository root, when `root` is inside one. */ gitRoot?: string; /** Injected by tests. */ git?: GitRemoteFn; }; /** * A stable id for "this workspace", used to bind it to a Jira project. * * Prefers the canonical remote plus the project's offset inside the * repository, so two clones of one repo share a binding and two packages in a * monorepo do not. Falls back to the normalised absolute path when there is no * remote to ask about - which is honest about its ceiling: a folder that moves * loses its binding. * * No realpath: the preset matcher already compares non-realpathed absolute * paths, and resolving symlinks only here would create a case where a preset * matches and a binding does not. */ export declare function workspaceIdentity(root: string, options?: WorkspaceIdentityOptions): string;