@pnpm/resolving.git-resolver 1100.1.10 → 1100.1.12

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # @pnpm/git-resolver
2
2
 
3
+ ## 1100.1.12
4
+
5
+ ### Patch Changes
6
+
7
+ - Added a new setting, `update.githubActionsServer`, for specifying the base URL of the GitHub server that hosts the repositories of the GitHub Actions referenced by the workflow files (for example, a GitHub Enterprise Server). When the setting is not defined, the URL is read from the `GITHUB_SERVER_URL` environment variable, falling back to `https://github.com`. The URL must use the `https://` or `http://` protocol [#13220](https://github.com/pnpm/pnpm/issues/13220).
8
+
9
+ `pnpm outdated` and `pnpm update` no longer fail when the refs of a GitHub Action's repository cannot be read (for example, when the action's repository is private or hosted on a different GitHub server). Such actions are now skipped with a warning.
10
+
11
+ Setting `update.githubActions` to `false` now makes `pnpm outdated` and the interactive `pnpm update` skip GitHub Actions dependencies.
12
+
13
+ - Republished every package: the tarballs published by the v11.13.1 through v11.16.0 releases were missing most of their compiled files due to a packing bug [#13164](https://github.com/pnpm/pnpm/issues/13164).
14
+
15
+ - Updated dependencies:
16
+ - @pnpm/error@1100.1.0
17
+ - @pnpm/network.fetch@1100.1.8
18
+ - @pnpm/resolving.resolver-base@1100.5.4
19
+
20
+ ## 1100.1.11
21
+
22
+ ### Patch Changes
23
+
24
+ - Added GitHub Actions dependencies to `pnpm outdated` and interactive `pnpm update`. Non-interactive updates can include them with `--include-github-actions` or by setting `update.githubActions` to `true` in `pnpm-workspace.yaml`. Updated actions are pinned to exact commit hashes with their release tags preserved in comments.
25
+
26
+ - Updated dependencies:
27
+ - @pnpm/network.fetch@1100.1.7
28
+ - @pnpm/resolving.resolver-base@1100.5.3
29
+
3
30
  ## 1100.1.10
4
31
 
5
32
  ### Patch Changes
@@ -0,0 +1,6 @@
1
+ import type { PkgResolutionId } from '@pnpm/resolving.resolver-base';
2
+ export declare function createGitHostedPkgId({ repo, commit, path }: {
3
+ repo: string;
4
+ commit: string;
5
+ path?: string;
6
+ }): PkgResolutionId;
@@ -0,0 +1,21 @@
1
+ export function createGitHostedPkgId({ repo, commit, path }) {
2
+ const normalizedRepo = normalizeGitRepoForPkgResolutionId(repo);
3
+ let id = `${normalizedRepo.includes('://') ? '' : 'https://'}${normalizedRepo}#${commit}`;
4
+ if (!id.startsWith('git+'))
5
+ id = `git+${id}`;
6
+ if (path) {
7
+ id += `&path:${path}`;
8
+ }
9
+ return id;
10
+ }
11
+ function normalizeGitRepoForPkgResolutionId(repo) {
12
+ // Only scp-style shorthand (`user@host:path`) needs rewriting. A repo that
13
+ // already carries a URL scheme (e.g. `ssh://user@host:2222/path`) is left
14
+ // alone — its `@host:port` would otherwise match the scp pattern and get
15
+ // mangled into `ssh://ssh://…`.
16
+ if (repo.includes('://'))
17
+ return repo;
18
+ const scp = /^([^@\s]+@[^:\s]+):(.+)$/.exec(repo);
19
+ return scp == null ? repo : `ssh://${scp[1]}/${scp[2]}`;
20
+ }
21
+ //# sourceMappingURL=createGitHostedPkgId.js.map
package/lib/index.d.ts ADDED
@@ -0,0 +1,17 @@
1
+ import type { DispatcherOptions } from '@pnpm/network.fetch';
2
+ import type { GitResolution, LatestInfo, LatestQuery, ResolveOptions, ResolveResult, TarballResolution } from '@pnpm/resolving.resolver-base';
3
+ import { createGitHostedPkgId } from './createGitHostedPkgId.js';
4
+ import { type HostedPackageSpec } from './parseBareSpecifier.js';
5
+ export { createGitHostedPkgId };
6
+ export type { HostedPackageSpec };
7
+ export interface GitResolveResult extends ResolveResult {
8
+ normalizedBareSpecifier?: string;
9
+ resolution: GitResolution | TarballResolution;
10
+ resolvedVia: 'git-repository';
11
+ }
12
+ export type GitResolver = (wantedDependency: {
13
+ bareSpecifier: string;
14
+ }, opts?: Pick<ResolveOptions, 'currentPkg' | 'update'>) => Promise<GitResolveResult | null>;
15
+ export declare function createGitResolver(opts: DispatcherOptions): GitResolver;
16
+ export declare function resolveLatestFromGit(query: LatestQuery): Promise<LatestInfo | undefined>;
17
+ export declare function getRepoRefs(repo: string, ref: string | null): Promise<Record<string, string>>;
package/lib/index.js CHANGED
@@ -89,8 +89,10 @@ export async function resolveLatestFromGit(query) {
89
89
  function resolveVTags(vTags, range) {
90
90
  return semver.maxSatisfying(vTags, range, true);
91
91
  }
92
- async function getRepoRefs(repo, ref) {
93
- const gitArgs = [repo];
92
+ export async function getRepoRefs(repo, ref) {
93
+ // `--` keeps a repo URL that starts with a dash (e.g. from a malicious
94
+ // config value) from being parsed as a git flag, matching the Rust runner.
95
+ const gitArgs = ['--', repo];
94
96
  if (ref) {
95
97
  gitArgs.push(ref);
96
98
  // Also request the peeled ref for annotated tags (e.g., refs/tags/v1.0.0^{})
@@ -102,7 +104,8 @@ async function getRepoRefs(repo, ref) {
102
104
  const refs = {};
103
105
  for (const line of result.stdout.split('\n')) {
104
106
  const [commit, refName] = line.split('\t');
105
- refs[refName] = commit;
107
+ if (commit && refName)
108
+ refs[refName] = commit;
106
109
  }
107
110
  return refs;
108
111
  }
@@ -0,0 +1,16 @@
1
+ import { type DispatcherOptions } from '@pnpm/network.fetch';
2
+ export interface HostedPackageSpec {
3
+ fetchSpec: string;
4
+ hosted?: {
5
+ type: string;
6
+ user: string;
7
+ project: string;
8
+ committish: string;
9
+ tarball: () => string | undefined;
10
+ };
11
+ normalizedBareSpecifier: string;
12
+ gitCommittish: string | null;
13
+ gitRange?: string;
14
+ path?: string;
15
+ }
16
+ export declare function parseBareSpecifier(bareSpecifier: string, opts: DispatcherOptions): null | (() => Promise<HostedPackageSpec>);
@@ -0,0 +1,177 @@
1
+ // cspell:ignore sshurl
2
+ import urlLib, { URL } from 'node:url';
3
+ import { fetchWithDispatcher } from '@pnpm/network.fetch';
4
+ import { gracefulGit as git } from 'graceful-git';
5
+ import HostedGit from 'hosted-git-info';
6
+ const gitProtocols = new Set([
7
+ 'git',
8
+ 'git+http',
9
+ 'git+https',
10
+ 'git+rsync',
11
+ 'git+ftp',
12
+ 'git+file',
13
+ 'git+ssh',
14
+ 'ssh',
15
+ ]);
16
+ export function parseBareSpecifier(bareSpecifier, opts) {
17
+ const hosted = HostedGit.fromUrl(bareSpecifier);
18
+ if (hosted != null) {
19
+ return () => fromHostedGit(hosted, opts);
20
+ }
21
+ const colonsPos = bareSpecifier.indexOf(':');
22
+ if (colonsPos === -1)
23
+ return null;
24
+ const protocol = bareSpecifier.slice(0, colonsPos);
25
+ // Also detect http/https URLs ending in .git as git repositories
26
+ const isGitUrl = gitProtocols.has(protocol.toLocaleLowerCase()) ||
27
+ ((protocol === 'http' || protocol === 'https') && /\.git(?:#|$)/.test(bareSpecifier));
28
+ if (protocol && isGitUrl) {
29
+ const correctBareSpecifier = correctUrl(bareSpecifier);
30
+ const url = new URL(correctBareSpecifier);
31
+ if (!url?.protocol)
32
+ return null;
33
+ const hash = (url.hash?.length > 1) ? decodeURIComponent(url.hash.slice(1)) : null;
34
+ return async () => ({
35
+ fetchSpec: urlToFetchSpec(url),
36
+ normalizedBareSpecifier: bareSpecifier,
37
+ ...parseGitParams(hash),
38
+ });
39
+ }
40
+ return null;
41
+ }
42
+ function urlToFetchSpec(url) {
43
+ url.hash = '';
44
+ const fetchSpec = urlLib.format(url);
45
+ if (fetchSpec.startsWith('git+')) {
46
+ return fetchSpec.slice(4);
47
+ }
48
+ return fetchSpec;
49
+ }
50
+ async function fromHostedGit(hosted, dispatcherOptions) {
51
+ let fetchSpec = null;
52
+ // try git/https url before fallback to ssh url
53
+ const gitHttpsUrl = hosted.https({ noCommittish: true, noGitPlus: true });
54
+ if (gitHttpsUrl && await isRepoPublic(gitHttpsUrl, dispatcherOptions) && await accessRepository(gitHttpsUrl)) {
55
+ fetchSpec = gitHttpsUrl;
56
+ }
57
+ else {
58
+ const gitSshUrl = hosted.ssh({ noCommittish: true });
59
+ if (gitSshUrl && await accessRepository(gitSshUrl)) {
60
+ fetchSpec = gitSshUrl;
61
+ }
62
+ }
63
+ if (!fetchSpec) {
64
+ const httpsUrl = hosted.https({ noGitPlus: true, noCommittish: true });
65
+ if (httpsUrl) {
66
+ if ((hosted.auth || !await isRepoPublic(httpsUrl, dispatcherOptions)) && await accessRepository(httpsUrl)) {
67
+ return {
68
+ fetchSpec: httpsUrl,
69
+ hosted: {
70
+ ...hosted,
71
+ _fill: hosted._fill,
72
+ tarball: undefined,
73
+ },
74
+ normalizedBareSpecifier: `git+${httpsUrl}`,
75
+ ...parseGitParams(hosted.committish),
76
+ };
77
+ }
78
+ else {
79
+ try {
80
+ // when git ls-remote private repo, it asks for login credentials.
81
+ // use HTTP HEAD request to test whether this is a private repo, to avoid login prompt.
82
+ // this is very similar to yarn classic's behavior.
83
+ // npm instead tries git ls-remote directly which prompts user for login credentials.
84
+ // HTTP HEAD on https://domain/user/repo, strip out ".git"
85
+ const response = await fetchWithDispatcher(httpsUrl.replace(/\.git$/, ''), { method: 'HEAD', redirect: 'manual', retry: { retries: 0 }, dispatcherOptions });
86
+ if (response.ok) {
87
+ fetchSpec = httpsUrl;
88
+ }
89
+ }
90
+ catch {
91
+ // ignore
92
+ }
93
+ }
94
+ }
95
+ }
96
+ if (!fetchSpec) {
97
+ // use ssh url for likely private repo
98
+ fetchSpec = hosted.sshurl({ noCommittish: true });
99
+ }
100
+ return {
101
+ fetchSpec: fetchSpec,
102
+ hosted: {
103
+ ...hosted,
104
+ tarballtemplate: hosted.type === 'gitlab' ? gitlabTarballTemplate : hosted.tarballtemplate,
105
+ _fill: hosted._fill,
106
+ tarball: hosted.tarball,
107
+ },
108
+ normalizedBareSpecifier: hosted.shortcut(),
109
+ ...parseGitParams(hosted.committish),
110
+ };
111
+ }
112
+ // hosted-git-info's default GitLab tarball URL contains an encoded slash
113
+ // (`%2F`) which survives into the virtual store directory name and makes
114
+ // Node refuse to import the package (ERR_INVALID_MODULE_SPECIFIER).
115
+ function gitlabTarballTemplate({ domain, user, project, committish }) {
116
+ const ref = committish ? encodeURIComponent(committish) : 'HEAD';
117
+ return `https://${domain}/${user}/${project}/-/archive/${ref}/${project}-${ref}.tar.gz`;
118
+ }
119
+ async function isRepoPublic(httpsUrl, dispatcherOptions) {
120
+ try {
121
+ const response = await fetchWithDispatcher(httpsUrl.replace(/\.git$/, ''), { method: 'HEAD', redirect: 'manual', retry: { retries: 0 }, dispatcherOptions });
122
+ return response.ok;
123
+ }
124
+ catch {
125
+ return false;
126
+ }
127
+ }
128
+ async function accessRepository(repository) {
129
+ try {
130
+ await git(['ls-remote', '--exit-code', repository, 'HEAD'], { retries: 0 });
131
+ return true;
132
+ }
133
+ catch {
134
+ return false;
135
+ }
136
+ }
137
+ function parseGitParams(committish) {
138
+ const result = { gitCommittish: null };
139
+ if (!committish) {
140
+ return result;
141
+ }
142
+ const params = committish.split('&');
143
+ for (const param of params) {
144
+ if (param.length >= 7 && param.slice(0, 7) === 'semver:') {
145
+ result.gitRange = param.slice(7);
146
+ }
147
+ else if (param.slice(0, 5) === 'path:') {
148
+ result.path = param.slice(5);
149
+ }
150
+ else {
151
+ result.gitCommittish = param;
152
+ }
153
+ }
154
+ return result;
155
+ }
156
+ // handle SCP-like URLs
157
+ // see https://github.com/yarnpkg/yarn/blob/5682d55/src/util/git.js#L103
158
+ function correctUrl(gitUrl) {
159
+ let _gitUrl = gitUrl.replace(/^git\+/, '');
160
+ if (_gitUrl.startsWith('ssh://')) {
161
+ const hashIndex = _gitUrl.indexOf('#');
162
+ let hash = '';
163
+ if (hashIndex !== -1) {
164
+ hash = _gitUrl.slice(hashIndex);
165
+ _gitUrl = _gitUrl.slice(0, hashIndex);
166
+ }
167
+ const [auth, ...pathname] = _gitUrl.slice(6).split('/');
168
+ const [, host] = auth.split('@');
169
+ if (host.includes(':') && !/:\d+$/.test(host)) {
170
+ const authArr = auth.split(':');
171
+ const protocol = gitUrl.split('://')[0];
172
+ gitUrl = `${protocol}://${authArr.slice(0, -1).join(':') + '/' + authArr[authArr.length - 1]}${pathname.length ? '/' + pathname.join('/') : ''}${hash}`;
173
+ }
174
+ }
175
+ return gitUrl;
176
+ }
177
+ //# sourceMappingURL=parseBareSpecifier.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pnpm/resolving.git-resolver",
3
- "version": "1100.1.10",
3
+ "version": "1100.1.12",
4
4
  "description": "Resolver for git-hosted packages",
5
5
  "keywords": [
6
6
  "pnpm",
@@ -29,16 +29,16 @@
29
29
  "!*.map"
30
30
  ],
31
31
  "dependencies": {
32
- "@pnpm/error": "1100.0.1",
33
- "@pnpm/network.fetch": "1100.1.6",
34
- "@pnpm/resolving.resolver-base": "1100.5.2",
32
+ "@pnpm/error": "1100.1.0",
33
+ "@pnpm/network.fetch": "1100.1.8",
34
+ "@pnpm/resolving.resolver-base": "1100.5.4",
35
35
  "graceful-git": "^5.0.0",
36
36
  "hosted-git-info": "npm:@pnpm/hosted-git-info@1.0.0",
37
37
  "semver": "^7.8.4"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@jest/globals": "30.4.1",
41
- "@pnpm/resolving.git-resolver": "1100.1.10",
41
+ "@pnpm/resolving.git-resolver": "1100.1.12",
42
42
  "@types/hosted-git-info": "^3.0.5",
43
43
  "@types/is-windows": "^1.0.2",
44
44
  "@types/semver": "7.7.1",