@gitkraken/core-gitlens 0.20.1 → 0.20.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,4 @@
1
+ import { CancellationError } from "../../utils/cancellation.js";
1
2
  import { getScopedLogger } from "../../utils/logger.scoped.js";
2
3
  import { debug } from "../../utils/decorators/log.js";
3
4
  import __decorate from "../../_virtual/@oxc-project+runtime/helpers/esm/decorate.js";
@@ -7,6 +8,40 @@ import { RemotesGitProviderBase } from "../../git/providers/shared/remotes.js";
7
8
  import { createRemoteProviderMatcher } from "../../git/remotes/matcher.js";
8
9
  import { parseGitRemotes } from "../parsers/remoteParser.js";
9
10
  //#region ../git-cli/src/providers/remotes.ts
11
+ /** Long enough for a slow host to answer, short enough that a blackholed one doesn't stall the wizard */
12
+ const probeRemoteTimeoutMs = 5e3;
13
+ /**
14
+ * Keeps the probe from asking anyone for credentials: an empty `GIT_ASKPASS` stops git's askpass lookup
15
+ * outright (it doesn't fall through to `core.askPass` or `SSH_ASKPASS`), so the editor's askpass can't
16
+ * open a credential prompt over the wizard, and `GIT_TERMINAL_PROMPT=0` refuses the tty fallback.
17
+ * Non-interactive credential helpers still run, so a stored credential still authenticates.
18
+ */
19
+ const probeRemoteEnv = {
20
+ GIT_ASKPASS: "",
21
+ GIT_TERMINAL_PROMPT: "0",
22
+ GCM_INTERACTIVE: "NEVER",
23
+ SSH_ASKPASS: ""
24
+ };
25
+ function getProbeFailureReason(stderr) {
26
+ if (/Could not resolve host|nodename nor servname|Name or service not known|No such host/i.test(stderr)) return "hostNotFound";
27
+ if (/Host key verification failed/i.test(stderr)) return "hostKeyUnverified";
28
+ if (/could not read (?:Username|Password)|terminal prompts disabled|Authentication failed|Permission denied|returned error: 40[13]\b/i.test(stderr)) return "authRequired";
29
+ if (/Repository not found|repository '.*' not found|does not appear to be a git repository|returned error: 404\b/i.test(stderr)) return "notFound";
30
+ return "other";
31
+ }
32
+ /** SSH's generic tail, which follows (and buries) the host's own explanation of the failure */
33
+ const genericProbeFailureLine = /^(?:fatal:\s*)?(?:Could not read from remote repository|Please make sure you have the correct access rights|and the repository exists)/;
34
+ /**
35
+ * The line of stderr that names what went wrong most plainly: SSH's generic trailer is dropped first so the host's
36
+ * message before it (e.g. `ERROR: Repository not found.`) can win; then the last `fatal:` line (prefix stripped),
37
+ * else the last line. If only the generic trailer remains, it is used rather than returning nothing.
38
+ */
39
+ function getProbeFailureMessage(stderr) {
40
+ const lines = stderr.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length);
41
+ const specific = lines.filter((l) => !genericProbeFailureLine.test(l));
42
+ const candidates = specific.length ? specific : lines;
43
+ return (candidates.findLast((l) => l.startsWith("fatal:")) ?? candidates.at(-1))?.replace(/^fatal:\s*/, "") || void 0;
44
+ }
10
45
  var RemotesGitSubProvider = class extends RemotesGitProviderBase {
11
46
  git;
12
47
  constructor(context, git, cache, provider) {
@@ -42,11 +77,16 @@ var RemotesGitSubProvider = class extends RemotesGitProviderBase {
42
77
  return remotes;
43
78
  }
44
79
  async addRemote(repoPath, name, url, options) {
80
+ if (!options?.fetch) {
81
+ await this.git.run({ cwd: repoPath }, "remote", "add", name, url);
82
+ this.context.hooks?.cache?.onReset?.(repoPath, "remotes");
83
+ this.context.hooks?.repository?.onChanged?.(repoPath, ["config", "remotes"]);
84
+ return;
85
+ }
45
86
  try {
46
- await this.git.run({ cwd: repoPath }, "remote", "add", options?.fetch ? "-f" : void 0, name, url);
87
+ await this.git.run({ cwd: repoPath }, "remote", "add", "-f", name, url);
47
88
  } finally {
48
- if (options?.fetch) this.context.hooks?.cache?.onReset?.(repoPath, "remotes", "branches");
49
- else this.context.hooks?.cache?.onReset?.(repoPath, "remotes");
89
+ this.context.hooks?.cache?.onReset?.(repoPath, "remotes", "branches");
50
90
  this.context.hooks?.repository?.onChanged?.(repoPath, ["config", "remotes"]);
51
91
  }
52
92
  }
@@ -55,6 +95,40 @@ var RemotesGitSubProvider = class extends RemotesGitProviderBase {
55
95
  const [remote] = await this.getRemotes(repoPath, { filter: (r) => r.url === url });
56
96
  return remote;
57
97
  }
98
+ async probeRemote(repoPath, url, cancellation) {
99
+ if (url.startsWith("-")) return {
100
+ reachable: false,
101
+ reason: "other"
102
+ };
103
+ const timeoutSignal = AbortSignal.timeout(probeRemoteTimeoutMs);
104
+ const signal = cancellation != null ? AbortSignal.any([cancellation, timeoutSignal]) : timeoutSignal;
105
+ const result = await this.git.run({
106
+ cwd: repoPath,
107
+ cancellation: signal,
108
+ errors: "ignore",
109
+ env: probeRemoteEnv,
110
+ priority: "interactive"
111
+ }, "ls-remote", url, "HEAD");
112
+ const { completion } = result;
113
+ if (completion.status === "exited" && completion.code === 0) return { reachable: true };
114
+ if (completion.status === "cancelled") {
115
+ if (cancellation?.aborted) throw new CancellationError(completion.error, "aborted");
116
+ return {
117
+ reachable: false,
118
+ reason: "timeout"
119
+ };
120
+ }
121
+ if (completion.status === "failed") {
122
+ if (cancellation?.aborted) throw new CancellationError(completion.error, "aborted");
123
+ throw completion.error;
124
+ }
125
+ const stderr = typeof result.stderr === "string" ? result.stderr : "";
126
+ return {
127
+ reachable: false,
128
+ reason: getProbeFailureReason(stderr),
129
+ message: getProbeFailureMessage(stderr)
130
+ };
131
+ }
58
132
  async pruneRemote(repoPath, name) {
59
133
  try {
60
134
  await this.git.run({ cwd: repoPath }, "remote", "prune", name);
@@ -78,9 +152,10 @@ __decorate([debug({
78
152
  })], RemotesGitSubProvider.prototype, "getRemotes", null);
79
153
  __decorate([gate(), debug()], RemotesGitSubProvider.prototype, "addRemote", null);
80
154
  __decorate([gate(), debug()], RemotesGitSubProvider.prototype, "addRemoteWithResult", null);
155
+ __decorate([debug({ args: (repoPath) => ({ repoPath }) })], RemotesGitSubProvider.prototype, "probeRemote", null);
81
156
  __decorate([gate(), debug()], RemotesGitSubProvider.prototype, "pruneRemote", null);
82
157
  __decorate([gate(), debug()], RemotesGitSubProvider.prototype, "removeRemote", null);
83
158
  //#endregion
84
- export { RemotesGitSubProvider };
159
+ export { RemotesGitSubProvider, getProbeFailureMessage, getProbeFailureReason };
85
160
 
86
161
  //# sourceMappingURL=remotes.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"remotes.js","names":[],"sources":["../../../../git-cli/src/providers/remotes.ts"],"sourcesContent":["import type { Cache } from '@gitlens/git/cache.js';\nimport type { GitServiceContext } from '@gitlens/git/context.js';\nimport type { GitRemote } from '@gitlens/git/models/remote.js';\nimport type { GitRemotesSubProvider } from '@gitlens/git/providers/remotes.js';\nimport { RemotesGitProviderBase } from '@gitlens/git/providers/shared/remotes.js';\nimport { createRemoteProviderMatcher } from '@gitlens/git/remotes/matcher.js';\nimport { sortRemotes } from '@gitlens/git/utils/sorting.js';\nimport { gate } from '@gitlens/utils/decorators/gate.js';\nimport { debug } from '@gitlens/utils/decorators/log.js';\nimport { getScopedLogger } from '@gitlens/utils/logger.scoped.js';\nimport type { CliGitProviderInternal } from '../cliGitProvider.js';\nimport type { Git } from '../exec/git.js';\nimport { parseGitRemotes } from '../parsers/remoteParser.js';\n\nexport class RemotesGitSubProvider extends RemotesGitProviderBase implements GitRemotesSubProvider {\n\tconstructor(\n\t\tcontext: GitServiceContext,\n\t\tprivate readonly git: Git,\n\t\tcache: Cache,\n\t\tprovider: CliGitProviderInternal,\n\t) {\n\t\tsuper(context, cache, provider);\n\t}\n\n\t@debug({ quiet: true, args: repoPath => ({ repoPath: repoPath }) })\n\tasync getRemotes(\n\t\trepoPath: string | undefined,\n\t\toptions?: { filter?: (remote: GitRemote) => boolean; sort?: boolean },\n\t\tcancellation?: AbortSignal,\n\t): Promise<GitRemote[]> {\n\t\tif (repoPath == null) return [];\n\n\t\tconst scope = getScopedLogger();\n\n\t\tlet remotes = await this.cache.getRemotes(\n\t\t\trepoPath,\n\t\t\tasync (commonPath, cacheable, signal) => {\n\t\t\t\t// Prefer the aggregate signal from the cache; fall back to the caller's cancellation.\n\t\t\t\tsignal ??= cancellation;\n\t\t\t\tconst configs = await this.context.remotes?.getCustomProviders?.(repoPath);\n\t\t\t\tconst remoteProviderMatcher = createRemoteProviderMatcher(configs, this.context.remotes);\n\n\t\t\t\ttry {\n\t\t\t\t\tconst result = await this.git.run(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcwd: repoPath,\n\t\t\t\t\t\t\tcancellation: signal,\n\t\t\t\t\t\t\tcaching: { cache: this.cache.gitResults, commonPath: commonPath },\n\t\t\t\t\t\t},\n\t\t\t\t\t\t'remote',\n\t\t\t\t\t\t'-v',\n\t\t\t\t\t);\n\n\t\t\t\t\t// Fetch default remote name from GK config before parsing so it's set at construction time\n\t\t\t\t\tconst defaultName = await this.provider.config.getGkConfig?.(repoPath, 'gk.defaultRemote');\n\t\t\t\t\tconst remotes = parseGitRemotes(result.stdout, commonPath, remoteProviderMatcher, defaultName);\n\n\t\t\t\t\treturn remotes;\n\t\t\t\t} catch (ex) {\n\t\t\t\t\tcacheable.invalidate();\n\t\t\t\t\tscope?.error(ex);\n\t\t\t\t\treturn [];\n\t\t\t\t}\n\t\t\t},\n\t\t\tcancellation,\n\t\t);\n\n\t\tif (options?.filter != null) {\n\t\t\tremotes = remotes.filter(options.filter);\n\t\t}\n\n\t\tif (options?.sort) {\n\t\t\tsortRemotes(remotes);\n\t\t}\n\n\t\treturn remotes;\n\t}\n\n\t@gate()\n\t@debug()\n\tasync addRemote(repoPath: string, name: string, url: string, options?: { fetch?: boolean }): Promise<void> {\n\t\ttry {\n\t\t\tawait this.git.run({ cwd: repoPath }, 'remote', 'add', options?.fetch ? '-f' : undefined, name, url);\n\t\t} finally {\n\t\t\t// A fetch creates the remote's tracking branches, which the branch, ref and history caches hold. The\n\t\t\t// remote is added before the fetch runs, so a failed `-f` still leaves it behind.\n\t\t\tif (options?.fetch) {\n\t\t\t\tthis.context.hooks?.cache?.onReset?.(repoPath, 'remotes', 'branches');\n\t\t\t} else {\n\t\t\t\tthis.context.hooks?.cache?.onReset?.(repoPath, 'remotes');\n\t\t\t}\n\t\t\tthis.context.hooks?.repository?.onChanged?.(repoPath, ['config', 'remotes']);\n\t\t}\n\t}\n\n\t@gate()\n\t@debug()\n\tasync addRemoteWithResult(\n\t\trepoPath: string,\n\t\tname: string,\n\t\turl: string,\n\t\toptions?: { fetch?: boolean },\n\t): Promise<GitRemote | undefined> {\n\t\tawait this.addRemote(repoPath, name, url, options);\n\t\tconst [remote] = await this.getRemotes(repoPath, { filter: r => r.url === url });\n\t\treturn remote;\n\t}\n\n\t@gate()\n\t@debug()\n\tasync pruneRemote(repoPath: string, name: string): Promise<void> {\n\t\ttry {\n\t\t\tawait this.git.run({ cwd: repoPath }, 'remote', 'prune', name);\n\t\t} finally {\n\t\t\tthis.context.hooks?.cache?.onReset?.(repoPath, 'remotes', 'branches');\n\t\t\tthis.context.hooks?.repository?.onChanged?.(repoPath, ['remotes']);\n\t\t}\n\t}\n\n\t@gate()\n\t@debug()\n\tasync removeRemote(repoPath: string, name: string): Promise<void> {\n\t\ttry {\n\t\t\tawait this.git.run({ cwd: repoPath }, 'remote', 'remove', name);\n\t\t} finally {\n\t\t\t// Removing a remote deletes its tracking branches too\n\t\t\tthis.context.hooks?.cache?.onReset?.(repoPath, 'remotes', 'branches');\n\t\t\tthis.context.hooks?.repository?.onChanged?.(repoPath, ['config', 'remotes']);\n\t\t}\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"remotes.js","names":[],"sources":["../../../../git-cli/src/providers/remotes.ts"],"sourcesContent":["import type { Cache } from '@gitlens/git/cache.js';\nimport type { GitServiceContext } from '@gitlens/git/context.js';\nimport type { GitRemote } from '@gitlens/git/models/remote.js';\nimport type {\n\tGitRemoteProbeFailureReason,\n\tGitRemoteProbeResult,\n\tGitRemotesSubProvider,\n} from '@gitlens/git/providers/remotes.js';\nimport { RemotesGitProviderBase } from '@gitlens/git/providers/shared/remotes.js';\nimport { createRemoteProviderMatcher } from '@gitlens/git/remotes/matcher.js';\nimport { sortRemotes } from '@gitlens/git/utils/sorting.js';\nimport { CancellationError } from '@gitlens/utils/cancellation.js';\nimport { gate } from '@gitlens/utils/decorators/gate.js';\nimport { debug } from '@gitlens/utils/decorators/log.js';\nimport { getScopedLogger } from '@gitlens/utils/logger.scoped.js';\nimport type { CliGitProviderInternal } from '../cliGitProvider.js';\nimport type { Git } from '../exec/git.js';\nimport { parseGitRemotes } from '../parsers/remoteParser.js';\n\n/** Long enough for a slow host to answer, short enough that a blackholed one doesn't stall the wizard */\nconst probeRemoteTimeoutMs = 5000;\n\n/**\n * Keeps the probe from asking anyone for credentials: an empty `GIT_ASKPASS` stops git's askpass lookup\n * outright (it doesn't fall through to `core.askPass` or `SSH_ASKPASS`), so the editor's askpass can't\n * open a credential prompt over the wizard, and `GIT_TERMINAL_PROMPT=0` refuses the tty fallback.\n * Non-interactive credential helpers still run, so a stored credential still authenticates.\n */\nconst probeRemoteEnv: Record<string, string> = {\n\tGIT_ASKPASS: '',\n\tGIT_TERMINAL_PROMPT: '0',\n\tGCM_INTERACTIVE: 'NEVER',\n\tSSH_ASKPASS: '',\n};\n\nexport function getProbeFailureReason(stderr: string): GitRemoteProbeFailureReason {\n\tif (/Could not resolve host|nodename nor servname|Name or service not known|No such host/i.test(stderr)) {\n\t\treturn 'hostNotFound';\n\t}\n\t// Checked before auth: the probe forbids ssh's prompt to trust an unknown host, so a correct URL on a host\n\t// not yet in known_hosts fails here, and that isn't an authentication problem\n\tif (/Host key verification failed/i.test(stderr)) {\n\t\treturn 'hostKeyUnverified';\n\t}\n\t// HTTP statuses are matched on git's own wording so digits elsewhere (e.g. a `/403/` URL path) don't count\n\tif (\n\t\t/could not read (?:Username|Password)|terminal prompts disabled|Authentication failed|Permission denied|returned error: 40[13]\\b/i.test(\n\t\t\tstderr,\n\t\t)\n\t) {\n\t\treturn 'authRequired';\n\t}\n\tif (\n\t\t/Repository not found|repository '.*' not found|does not appear to be a git repository|returned error: 404\\b/i.test(\n\t\t\tstderr,\n\t\t)\n\t) {\n\t\treturn 'notFound';\n\t}\n\treturn 'other';\n}\n\n/** SSH's generic tail, which follows (and buries) the host's own explanation of the failure */\nconst genericProbeFailureLine =\n\t/^(?:fatal:\\s*)?(?:Could not read from remote repository|Please make sure you have the correct access rights|and the repository exists)/;\n\n/**\n * The line of stderr that names what went wrong most plainly: SSH's generic trailer is dropped first so the host's\n * message before it (e.g. `ERROR: Repository not found.`) can win; then the last `fatal:` line (prefix stripped),\n * else the last line. If only the generic trailer remains, it is used rather than returning nothing.\n */\nexport function getProbeFailureMessage(stderr: string): string | undefined {\n\tconst lines = stderr\n\t\t.split(/\\r?\\n/)\n\t\t.map(l => l.trim())\n\t\t.filter(l => l.length);\n\tconst specific = lines.filter(l => !genericProbeFailureLine.test(l));\n\tconst candidates = specific.length ? specific : lines;\n\tconst fatal = candidates.findLast(l => l.startsWith('fatal:'));\n\treturn (fatal ?? candidates.at(-1))?.replace(/^fatal:\\s*/, '') || undefined;\n}\n\nexport class RemotesGitSubProvider extends RemotesGitProviderBase implements GitRemotesSubProvider {\n\tconstructor(\n\t\tcontext: GitServiceContext,\n\t\tprivate readonly git: Git,\n\t\tcache: Cache,\n\t\tprovider: CliGitProviderInternal,\n\t) {\n\t\tsuper(context, cache, provider);\n\t}\n\n\t@debug({ quiet: true, args: repoPath => ({ repoPath: repoPath }) })\n\tasync getRemotes(\n\t\trepoPath: string | undefined,\n\t\toptions?: { filter?: (remote: GitRemote) => boolean; sort?: boolean },\n\t\tcancellation?: AbortSignal,\n\t): Promise<GitRemote[]> {\n\t\tif (repoPath == null) return [];\n\n\t\tconst scope = getScopedLogger();\n\n\t\tlet remotes = await this.cache.getRemotes(\n\t\t\trepoPath,\n\t\t\tasync (commonPath, cacheable, signal) => {\n\t\t\t\t// Prefer the aggregate signal from the cache; fall back to the caller's cancellation.\n\t\t\t\tsignal ??= cancellation;\n\t\t\t\tconst configs = await this.context.remotes?.getCustomProviders?.(repoPath);\n\t\t\t\tconst remoteProviderMatcher = createRemoteProviderMatcher(configs, this.context.remotes);\n\n\t\t\t\ttry {\n\t\t\t\t\tconst result = await this.git.run(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcwd: repoPath,\n\t\t\t\t\t\t\tcancellation: signal,\n\t\t\t\t\t\t\tcaching: { cache: this.cache.gitResults, commonPath: commonPath },\n\t\t\t\t\t\t},\n\t\t\t\t\t\t'remote',\n\t\t\t\t\t\t'-v',\n\t\t\t\t\t);\n\n\t\t\t\t\t// Fetch default remote name from GK config before parsing so it's set at construction time\n\t\t\t\t\tconst defaultName = await this.provider.config.getGkConfig?.(repoPath, 'gk.defaultRemote');\n\t\t\t\t\tconst remotes = parseGitRemotes(result.stdout, commonPath, remoteProviderMatcher, defaultName);\n\n\t\t\t\t\treturn remotes;\n\t\t\t\t} catch (ex) {\n\t\t\t\t\tcacheable.invalidate();\n\t\t\t\t\tscope?.error(ex);\n\t\t\t\t\treturn [];\n\t\t\t\t}\n\t\t\t},\n\t\t\tcancellation,\n\t\t);\n\n\t\tif (options?.filter != null) {\n\t\t\tremotes = remotes.filter(options.filter);\n\t\t}\n\n\t\tif (options?.sort) {\n\t\t\tsortRemotes(remotes);\n\t\t}\n\n\t\treturn remotes;\n\t}\n\n\t@gate()\n\t@debug()\n\tasync addRemote(repoPath: string, name: string, url: string, options?: { fetch?: boolean }): Promise<void> {\n\t\tif (!options?.fetch) {\n\t\t\tawait this.git.run({ cwd: repoPath }, 'remote', 'add', name, url);\n\t\t\tthis.context.hooks?.cache?.onReset?.(repoPath, 'remotes');\n\t\t\tthis.context.hooks?.repository?.onChanged?.(repoPath, ['config', 'remotes']);\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tawait this.git.run({ cwd: repoPath }, 'remote', 'add', '-f', name, url);\n\t\t} finally {\n\t\t\t// Reset even on failure: `remote add -f` keeps the remote when only its fetch fails, and a\n\t\t\t// caller checking whether the remote now exists must not read the pre-add cache.\n\t\t\t// A fetch creates the remote's tracking branches, which the branch, ref and history caches hold\n\t\t\tthis.context.hooks?.cache?.onReset?.(repoPath, 'remotes', 'branches');\n\t\t\tthis.context.hooks?.repository?.onChanged?.(repoPath, ['config', 'remotes']);\n\t\t}\n\t}\n\n\t@gate()\n\t@debug()\n\tasync addRemoteWithResult(\n\t\trepoPath: string,\n\t\tname: string,\n\t\turl: string,\n\t\toptions?: { fetch?: boolean },\n\t): Promise<GitRemote | undefined> {\n\t\tawait this.addRemote(repoPath, name, url, options);\n\t\tconst [remote] = await this.getRemotes(repoPath, { filter: r => r.url === url });\n\t\treturn remote;\n\t}\n\n\t// The URL is left out of the log — it can carry credentials (`https://<token>@host/...`)\n\t@debug({ args: repoPath => ({ repoPath: repoPath }) })\n\tasync probeRemote(repoPath: string, url: string, cancellation?: AbortSignal): Promise<GitRemoteProbeResult> {\n\t\t// A leading `-` would be parsed as an option rather than the repository to query\n\t\tif (url.startsWith('-')) return { reachable: false, reason: 'other' };\n\n\t\tconst timeoutSignal = AbortSignal.timeout(probeRemoteTimeoutMs);\n\t\tconst signal = cancellation != null ? AbortSignal.any([cancellation, timeoutSignal]) : timeoutSignal;\n\n\t\tconst result = await this.git.run(\n\t\t\t{ cwd: repoPath, cancellation: signal, errors: 'ignore', env: probeRemoteEnv, priority: 'interactive' },\n\t\t\t// No `--exit-code`: a freshly created, empty repository advertises no HEAD but is a valid remote\n\t\t\t'ls-remote',\n\t\t\turl,\n\t\t\t'HEAD',\n\t\t);\n\n\t\tconst { completion } = result;\n\t\tif (completion.status === 'exited' && completion.code === 0) return { reachable: true };\n\n\t\tif (completion.status === 'cancelled') {\n\t\t\tif (cancellation?.aborted) throw new CancellationError(completion.error, 'aborted');\n\n\t\t\treturn { reachable: false, reason: 'timeout' };\n\t\t}\n\n\t\t// A run that never exited (spawn failure, missing binary, queue rejection) says nothing about the remote;\n\t\t// throw so callers treat it as unprobed rather than unreachable\n\t\tif (completion.status === 'failed') {\n\t\t\tif (cancellation?.aborted) throw new CancellationError(completion.error, 'aborted');\n\n\t\t\tthrow completion.error;\n\t\t}\n\n\t\t// Under `errors: 'ignore'` a non-zero exit resolves with its stderr; a run that never exited has none\n\t\tconst stderr = typeof result.stderr === 'string' ? result.stderr : '';\n\t\treturn {\n\t\t\treachable: false,\n\t\t\treason: getProbeFailureReason(stderr),\n\t\t\tmessage: getProbeFailureMessage(stderr),\n\t\t};\n\t}\n\n\t@gate()\n\t@debug()\n\tasync pruneRemote(repoPath: string, name: string): Promise<void> {\n\t\ttry {\n\t\t\tawait this.git.run({ cwd: repoPath }, 'remote', 'prune', name);\n\t\t} finally {\n\t\t\tthis.context.hooks?.cache?.onReset?.(repoPath, 'remotes', 'branches');\n\t\t\tthis.context.hooks?.repository?.onChanged?.(repoPath, ['remotes']);\n\t\t}\n\t}\n\n\t@gate()\n\t@debug()\n\tasync removeRemote(repoPath: string, name: string): Promise<void> {\n\t\ttry {\n\t\t\tawait this.git.run({ cwd: repoPath }, 'remote', 'remove', name);\n\t\t} finally {\n\t\t\t// Removing a remote deletes its tracking branches too\n\t\t\tthis.context.hooks?.cache?.onReset?.(repoPath, 'remotes', 'branches');\n\t\t\tthis.context.hooks?.repository?.onChanged?.(repoPath, ['config', 'remotes']);\n\t\t}\n\t}\n}\n"],"mappings":"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"}
@@ -625,12 +625,7 @@ function toIssueShape(issue, provider, options) {
625
625
  avatarUrl: assignee.avatarUrl ?? void 0,
626
626
  url: assignee.url ?? void 0
627
627
  })) ?? [],
628
- project: issue.project?.id && issue.project.resourceId && issue.project.namespace ? {
629
- id: issue.project.id,
630
- name: issue.project.name,
631
- resourceId: issue.project.resourceId,
632
- resourceName: issue.project.namespace
633
- } : void 0,
628
+ project: toIssueProject(issue.project, provider),
634
629
  repository: issue.repository?.owner?.login != null ? {
635
630
  owner: issue.repository.owner.login,
636
631
  repo: issue.repository.name,
@@ -651,6 +646,15 @@ function toIssueShape(issue, provider, options) {
651
646
  ...toIssueViewer(issue, options?.viewer)
652
647
  };
653
648
  }
649
+ function toIssueProject(project, provider) {
650
+ if (!project?.id || !project.resourceId || !project.namespace && !isJiraIntegrationId(provider.id)) return;
651
+ return {
652
+ id: project.id,
653
+ name: project.name,
654
+ resourceId: project.resourceId,
655
+ resourceName: project.namespace ?? ""
656
+ };
657
+ }
654
658
  function toIssueBodyField(field) {
655
659
  return {
656
660
  id: field.referenceName,
@@ -1027,12 +1031,7 @@ function fromProviderIssue(issue, integration, options) {
1027
1031
  name: options.project.name,
1028
1032
  resourceId: options.project.resourceId,
1029
1033
  resourceName: options.project.resourceName
1030
- } : issue.project?.id && issue.project?.resourceId && issue.project?.namespace ? {
1031
- id: issue.project.id,
1032
- name: issue.project.name,
1033
- resourceId: issue.project.resourceId,
1034
- resourceName: issue.project.namespace
1035
- } : void 0, identifier, issue.type ?? void 0, toIssueProviderState(issue.state), integration.id === "jira" ? "jira-wiki" : void 0, toIssueIterations(issue), options?.projection, issue.descriptionField != null ? toIssueBodyField(issue.descriptionField) : void 0, viewer?.viewer, viewer?.viewerUnresolved);
1034
+ } : toIssueProject(issue.project, integration), identifier, issue.type ?? void 0, toIssueProviderState(issue.state), integration.id === "jira" ? "jira-wiki" : void 0, toIssueIterations(issue), options?.projection, issue.descriptionField != null ? toIssueBodyField(issue.descriptionField) : void 0, viewer?.viewer, viewer?.viewerUnresolved);
1036
1035
  }
1037
1036
  function toProviderPullRequestWithUniqueId(pr) {
1038
1037
  const { reviews, mergeableState, ...providerPr } = toProviderPullRequest(pr);