Skip to content

Commit edf82db

Browse files
authored
Fixes registry used to check for deprecation settings (#5534)
**What's the problem this PR addresses?** The new `yarn npm audit` implementation checks the packages' metadata on the audit server rather than the server configured to serve their metadata. Closes #5533 **How did you fix it?** Updated the code to use the new `getPackageMetadata` function, which should use the proper server while also leveraging the cache if possible. **Checklist** <!--- Don't worry if you miss something, chores are automatically tested. --> <!--- This checklist exists to help you remember doing the chores when you submit a PR. --> <!--- Put an `x` in all the boxes that apply. --> - [x] I have read the [Contributing Guide](https://yarnpkg.com/advanced/contributing). <!-- See https://yarnpkg.com/advanced/contributing#preparing-your-pr-to-be-released for more details. --> <!-- Check with `yarn version check` and fix with `yarn version check -i` --> - [x] I have set the packages that need to be released for my changes to be effective. <!-- The "Testing chores" workflow validates that your PR follows our guidelines. --> <!-- If it doesn't pass, click on it to see details as to what your PR might be missing. --> - [x] I will check that all automated PR checks pass before the PR gets reviewed.
1 parent 6a01c21 commit edf82db

File tree

6 files changed

+133
-65
lines changed

6 files changed

+133
-65
lines changed

.yarn/versions/3da18857.yml

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
releases:
2+
"@yarnpkg/cli": patch
3+
"@yarnpkg/plugin-npm": patch
4+
"@yarnpkg/plugin-npm-cli": patch
5+
6+
declined:
7+
- "@yarnpkg/plugin-compat"
8+
- "@yarnpkg/plugin-constraints"
9+
- "@yarnpkg/plugin-dlx"
10+
- "@yarnpkg/plugin-essentials"
11+
- "@yarnpkg/plugin-init"
12+
- "@yarnpkg/plugin-interactive-tools"
13+
- "@yarnpkg/plugin-nm"
14+
- "@yarnpkg/plugin-pack"
15+
- "@yarnpkg/plugin-patch"
16+
- "@yarnpkg/plugin-pnp"
17+
- "@yarnpkg/plugin-pnpm"
18+
- "@yarnpkg/plugin-stage"
19+
- "@yarnpkg/plugin-typescript"
20+
- "@yarnpkg/plugin-version"
21+
- "@yarnpkg/plugin-workspace-tools"
22+
- "@yarnpkg/builder"
23+
- "@yarnpkg/core"
24+
- "@yarnpkg/doctor"

packages/acceptance-tests/pkg-tests-core/sources/utils/tests.ts

+70-44
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {semverUtils} from '@yarnpkg/core';
1+
import {miscUtils, semverUtils} from '@yarnpkg/core';
22
import {PortablePath, npath, toFilename, xfs, ppath, Filename} from '@yarnpkg/fslib';
33
import {npmAuditTypes} from '@yarnpkg/plugin-npm-cli';
44
import assert from 'assert';
@@ -60,27 +60,33 @@ export enum RequestType {
6060
}
6161

6262
export type Request = {
63+
registry?: string;
6364
type: RequestType.Login;
6465
username: string;
6566
} | {
67+
registry?: string;
6668
type: RequestType.PackageInfo;
6769
scope?: string;
6870
localName: string;
6971
} | {
72+
registry?: string;
7073
type: RequestType.PackageTarball;
7174
scope?: string;
7275
localName: string;
7376
version?: string;
7477
} | {
78+
registry?: string;
7579
type: RequestType.Whoami;
7680
login: Login;
7781
} | {
7882
type: RequestType.Repository;
7983
} | {
84+
registry?: string;
8085
type: RequestType.Publish;
8186
scope?: string;
8287
localName: string;
8388
} | {
89+
registry?: string;
8490
type: RequestType.BulkAdvisories;
8591
};
8692

@@ -160,6 +166,12 @@ export const validLogins = {
160166
let whitelist = new Map();
161167
let recording: Array<Request> | null = null;
162168

169+
export function sortJson<T>(data: Iterable<T>): Array<T> {
170+
return miscUtils.sortMap(data, request => {
171+
return JSON.stringify(request);
172+
});
173+
}
174+
163175
export const startRegistryRecording = async (
164176
fn: () => Promise<void>,
165177
) => {
@@ -573,51 +585,65 @@ export const startPackageServer = ({type}: { type: keyof typeof packageServerUrl
573585
return {
574586
type: RequestType.Repository,
575587
};
576-
} else if ((match = url.match(/^\/-\/user\/org\.couchdb\.user:(.+)/))) {
577-
const [, username] = match;
578-
579-
return {
580-
type: RequestType.Login,
581-
username,
582-
};
583-
} else if (url === `/-/whoami`) {
584-
return {
585-
type: RequestType.Whoami,
586-
// Set later when login is parsed
587-
login: null as any,
588-
};
589-
} else if (url === `/-/npm/v1/security/advisories/bulk`) {
590-
return {
591-
type: RequestType.BulkAdvisories,
592-
};
593-
} else if ((match = url.match(/^\/(?:(@[^/]+)\/)?([^@/][^/]*)$/)) && method == `PUT`) {
594-
const [, scope, localName] = match;
595-
596-
return {
597-
type: RequestType.Publish,
598-
scope,
599-
localName,
600-
};
601-
} else if ((match = url.match(/^\/(?:(@[^/]+)\/)?([^@/][^/]*)$/))) {
602-
const [, scope, localName] = match;
603-
604-
return {
605-
type: RequestType.PackageInfo,
606-
scope,
607-
localName,
608-
};
609-
} else if ((match = url.match(/^\/(?:(@[^/]+)\/)?([^@/][^/]*)\/(-|tralala)\/\2-(.*)\.tgz$/))) {
610-
const [, scope, localName, split, version] = match;
588+
} else {
589+
let registry: {registry: string} | undefined;
590+
if ((match = url.match(/^\/registry\/([a-z]+)\//))) {
591+
url = url.slice(match[0].length - 1);
592+
registry = {registry: match[1]};
593+
}
611594

612-
if ((localName === `unconventional-tarball` || localName === `private-unconventional-tarball`) && split === `-`)
613-
return null;
595+
if ((match = url.match(/^\/-\/user\/org\.couchdb\.user:(.+)/))) {
596+
const [, username] = match;
614597

615-
return {
616-
type: RequestType.PackageTarball,
617-
scope,
618-
localName,
619-
version,
620-
};
598+
return {
599+
...registry,
600+
type: RequestType.Login,
601+
username,
602+
};
603+
} else if (url === `/-/whoami`) {
604+
return {
605+
...registry,
606+
type: RequestType.Whoami,
607+
// Set later when login is parsed
608+
login: null as any,
609+
};
610+
} else if (url === `/-/npm/v1/security/advisories/bulk`) {
611+
return {
612+
...registry,
613+
type: RequestType.BulkAdvisories,
614+
};
615+
} else if ((match = url.match(/^\/(?:(@[^/]+)\/)?([^@/][^/]*)$/)) && method == `PUT`) {
616+
const [, scope, localName] = match;
617+
618+
return {
619+
...registry,
620+
type: RequestType.Publish,
621+
scope,
622+
localName,
623+
};
624+
} else if ((match = url.match(/^\/(?:(@[^/]+)\/)?([^@/][^/]*)$/))) {
625+
const [, scope, localName] = match;
626+
627+
return {
628+
...registry,
629+
type: RequestType.PackageInfo,
630+
scope,
631+
localName,
632+
};
633+
} else if ((match = url.match(/^\/(?:(@[^/]+)\/)?([^@/][^/]*)\/(-|tralala)\/\2-(.*)\.tgz$/))) {
634+
const [, scope, localName, split, version] = match;
635+
636+
if ((localName === `unconventional-tarball` || localName === `private-unconventional-tarball`) && split === `-`)
637+
return null;
638+
639+
return {
640+
...registry,
641+
type: RequestType.PackageTarball,
642+
scope,
643+
localName,
644+
version,
645+
};
646+
}
621647
}
622648

623649
return null;

packages/acceptance-tests/pkg-tests-specs/sources/commands/npm/audit.test.ts

+19-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {Filename, ppath, xfs} from '@yarnpkg/fslib';
2+
import {tests} from 'pkg-tests-core';
23

34
describe(`Commands`, () => {
45
describe(`npm audit`, () => {
@@ -181,15 +182,31 @@ describe(`Commands`, () => {
181182
);
182183

183184
test(
184-
`it should report deprecations as audit issues`,
185+
`it should perform the deprecation checks against the package registry, not the audit registry`,
185186
makeTemporaryEnv({
186187
dependencies: {
187188
[`no-deps-deprecated`]: `1.0.0`,
188189
},
189190
}, async ({path, run, source}) => {
191+
await xfs.writeJsonPromise(ppath.join(path, Filename.rc), {
192+
npmAuditRegistry: `${await tests.startPackageServer()}/registry/audit`,
193+
});
194+
190195
await run(`install`);
191196

192-
await expect(run(`npm`, `audit`)).rejects.toThrow(/no-deps-deprecated \(deprecation\)/);
197+
const requests = await tests.startRegistryRecording(async () => {
198+
await expect(run(`npm`, `audit`)).rejects.toThrow(/no-deps-deprecated \(deprecation\)/);
199+
});
200+
201+
expect(tests.sortJson(requests)).toEqual([{
202+
registry: `audit`,
203+
type: `bulkAdvisories`,
204+
}, {
205+
registry: undefined,
206+
scope: undefined,
207+
localName: `no-deps-deprecated`,
208+
type: `packageInfo`,
209+
}]);
193210
}),
194211
);
195212

packages/acceptance-tests/pkg-tests-specs/sources/commands/stage.test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ describe(`Commands`, () => {
4848
await expect(run(`stage`, `-n`, {cwd: path})).resolves.toMatchObject({
4949
stdout: [
5050
`${npath.fromPortablePath(`${path}/.pnp.cjs`)}\n`,
51-
`${npath.fromPortablePath(`${path}/.yarn/global/metadata/npm/b98544/localhost/no-deps.json`)}\n`,
51+
`${npath.fromPortablePath(`${path}/.yarn/global/metadata/npm/3fb1ad/localhost/no-deps.json`)}\n`,
5252
`${npath.fromPortablePath(`${path}/.yarn/global/cache/no-deps-npm-1.0.0-cf533b267a-0.zip`)}\n`,
5353
`${npath.fromPortablePath(`${path}/.yarn/cache/.gitignore`)}\n`,
5454
`${npath.fromPortablePath(`${path}/.yarn/cache/no-deps-npm-1.0.0-cf533b267a-af041f19ff.zip`)}\n`,

packages/plugin-npm-cli/sources/commands/npm/audit.ts

+4-11
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {BaseCommand, WorkspaceRequiredError} from '@yarnpkg/cli';
22
import {Configuration, Project, MessageName, treeUtils, LightReport, StreamReport, semverUtils, LocatorHash, Locator, miscUtils} from '@yarnpkg/core';
3+
import {structUtils} from '@yarnpkg/core';
34
import {npmConfigUtils, npmHttpUtils} from '@yarnpkg/plugin-npm';
45
import {Command, Option, Usage} from 'clipanion';
56
import micromatch from 'micromatch';
@@ -131,17 +132,9 @@ export default class NpmAuditCommand extends BaseCommand {
131132
}) as unknown as Promise<npmAuditTypes.AuditResponse>;
132133

133134
const deprecations = await Promise.all(this.noDeprecations ? [] : Array.from(packages, async ([packageName, versions]) => {
134-
const registryData = await npmHttpUtils.get(`/${packageName}`, {
135-
configuration,
136-
jsonResponse: true,
137-
registry,
138-
headers: {
139-
[`Accept`]: `application/vnd.npm.install-v1+json`,
140-
},
141-
}) as any;
142-
143-
if (!Object.prototype.hasOwnProperty.call(registryData, `versions`))
144-
return [];
135+
const registryData = await npmHttpUtils.getPackageMetadata(structUtils.parseIdent(packageName), {
136+
project,
137+
});
145138

146139
return miscUtils.mapAndFilter(versions.keys(), version => {
147140
const {deprecated} = registryData.versions[version];

packages/plugin-npm/sources/npmHttpUtils.ts

+15-7
Original file line numberDiff line numberDiff line change
@@ -170,11 +170,6 @@ type CachedMetadata = {
170170
lastModified?: string;
171171
};
172172

173-
export type PackageMetadata = {
174-
'dist-tags': Record<string, string>;
175-
versions: Record<string, any>;
176-
};
177-
178173
const CACHED_FIELDS = [
179174
`name`,
180175

@@ -193,14 +188,27 @@ const CACHED_FIELDS = [
193188

194189
`peerDependencies`,
195190
`peerDependenciesMeta`,
196-
];
191+
192+
`deprecated`,
193+
] as const;
194+
195+
export type PackageMetadata = {
196+
'dist-tags': Record<string, string>;
197+
versions: Record<string, {
198+
[key in typeof CACHED_FIELDS[number]]: any;
199+
} & {
200+
dist: {
201+
tarball: string;
202+
};
203+
}>;
204+
};
197205

198206
function pickPackageMetadata(metadata: PackageMetadata): PackageMetadata {
199207
return {
200208
'dist-tags': metadata[`dist-tags`],
201209
versions: Object.fromEntries(Object.entries(metadata.versions).map(([key, value]) => [
202210
key,
203-
pick(value, CACHED_FIELDS),
211+
pick(value, CACHED_FIELDS) as any,
204212
])),
205213
};
206214
}

0 commit comments

Comments
 (0)