mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-08-21 06:35:07 +00:00
chore(deps): bump sharp to ^0.35.0 to resolve GHSA-f88m-g3jw-g9cj (#8952)
* chore(deps): bump sharp to ^0.35.0 to resolve GHSA-f88m-g3jw-g9cj * chore(vscode): regenerate NOTICES.txt for sharp 0.35 bump * fix(scripts): read sharp pin from core package.json to prevent drift The published CLI's sharp version was hardcoded in prepare-package.js, which drifted from the workspace dependency on every bump. Read it from packages/core/package.json so the publish pin always matches the declared dependency. Add a test assertion to catch future drift in CI. * fix(scripts): read sharp pin from package-lock.json instead of core package.json range The previous approach read the sharp version from packages/core/package.json (which has ^0.35.0) and stripped the caret, producing 0.35.0. This is the range floor, not the lockfile-resolved version (0.35.3). Read from package-lock.json so the published CLI ships the same version CI tests. * fix(scripts): pin published sharp to core resolution Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(scripts): validate sharp pin against core manifest and add hoisted fallback test - Align the lockfile reader with the sibling pattern in build-standalone-release.js: validate the resolved version against packages/core's declared sharp range - Wrap the lockfile read in try/catch so a missing or malformed file surfaces a clear error instead of an opaque ENOENT/SyntaxError - Add a test for the hoisted fallback path (node_modules/sharp) that actually executes in the current production release - Add a comment explaining why sharp is exact-pinned like all other native optional deps in the published manifest * fix(scripts): accept compatible sharp lock versions --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
parent
a8bcaefea7
commit
f159100c8e
7 changed files with 810 additions and 155 deletions
818
package-lock.json
generated
818
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -155,7 +155,7 @@
|
|||
"prettier": "^3.5.3",
|
||||
"react-devtools-core": "^6.1.5",
|
||||
"semver": "^7.7.2",
|
||||
"sharp": "^0.34.5",
|
||||
"sharp": "^0.35.0",
|
||||
"strip-ansi": "^7.1.2",
|
||||
"tsx": "^4.20.3",
|
||||
"typescript-eslint": "^8.30.1",
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@
|
|||
"picomatch": "^4.0.1",
|
||||
"prompts": "^2.4.2",
|
||||
"proper-lockfile": "^4.1.2",
|
||||
"sharp": "^0.34.5",
|
||||
"sharp": "^0.35.0",
|
||||
"shell-quote": "^1.9.0",
|
||||
"simple-git": "^3.36.0",
|
||||
"strip-ansi": "^7.1.0",
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import fs from 'node:fs/promises';
|
||||
import type { Metadata } from 'sharp';
|
||||
import type { Metadata, SharpConstructor } from 'sharp';
|
||||
|
||||
const IMAGE_VIEW_MAX_EDGE = 1568;
|
||||
const IMAGE_VIEW_MAX_PATCHES = 1568;
|
||||
|
|
@ -42,7 +42,7 @@ interface ImageSize {
|
|||
interface PreparedImage {
|
||||
bytes: Buffer;
|
||||
metadata: Metadata;
|
||||
sharp: typeof import('sharp');
|
||||
sharp: SharpConstructor;
|
||||
}
|
||||
|
||||
export type ImageViewErrorCode =
|
||||
|
|
@ -116,15 +116,9 @@ async function prepareImage(
|
|||
signal: AbortSignal,
|
||||
): Promise<PreparedImage> {
|
||||
signal.throwIfAborted();
|
||||
let sharp: typeof import('sharp');
|
||||
let sharp: SharpConstructor;
|
||||
try {
|
||||
// sharp is a CJS `export =` module, so the callable is on `.default`
|
||||
// at runtime even though NodeNext types collapse that namespace away.
|
||||
sharp = (
|
||||
(await import('sharp')) as unknown as {
|
||||
default: typeof import('sharp');
|
||||
}
|
||||
).default;
|
||||
sharp = (await import('sharp')).default;
|
||||
} catch {
|
||||
throw new ImageViewError(
|
||||
'renderer_unavailable',
|
||||
|
|
|
|||
|
|
@ -14575,8 +14575,8 @@ ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
|
||||
|
||||
============================================================
|
||||
sharp@0.34.5
|
||||
(git://github.com/lovell/sharp.git)
|
||||
sharp@0.35.3
|
||||
(git+https://github.com/lovell/sharp.git)
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
|
|
@ -15067,7 +15067,7 @@ detect-libc@2.1.2
|
|||
|
||||
|
||||
============================================================
|
||||
semver@7.7.3
|
||||
semver@7.8.5
|
||||
(git+https://github.com/npm/node-semver.git)
|
||||
|
||||
The ISC License
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import fs from 'node:fs';
|
|||
import { createRequire } from 'node:module';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import semver from 'semver';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
|
@ -271,6 +272,31 @@ function writeDistPackageJson(rootDir, distDir) {
|
|||
const rootPackageJson = JSON.parse(
|
||||
fs.readFileSync(path.join(rootDir, 'package.json'), 'utf-8'),
|
||||
);
|
||||
let lockfile;
|
||||
try {
|
||||
lockfile = JSON.parse(
|
||||
fs.readFileSync(path.join(rootDir, 'package-lock.json'), 'utf-8'),
|
||||
);
|
||||
} catch (error) {
|
||||
throw new Error(`Cannot read package-lock.json: ${error.message}`);
|
||||
}
|
||||
const coreManifest = JSON.parse(
|
||||
fs.readFileSync(
|
||||
path.join(rootDir, 'packages', 'core', 'package.json'),
|
||||
'utf-8',
|
||||
),
|
||||
);
|
||||
const sharpVersion =
|
||||
lockfile.packages?.['packages/core/node_modules/sharp']?.version ??
|
||||
lockfile.packages?.['node_modules/sharp']?.version;
|
||||
const declared = coreManifest.dependencies?.sharp;
|
||||
if (!sharpVersion || !declared || !semver.satisfies(sharpVersion, declared)) {
|
||||
throw new Error(
|
||||
`sharp version is not locked in package-lock.json ` +
|
||||
`(resolved ${sharpVersion ?? 'none'}, ` +
|
||||
`packages/core declares ${declared ?? 'none'})`,
|
||||
);
|
||||
}
|
||||
|
||||
const distPackageJson = {
|
||||
name: rootPackageJson.name,
|
||||
|
|
@ -322,7 +348,12 @@ function writeDistPackageJson(rootDir, distDir) {
|
|||
// is sufficient: its own optionalDependencies pull in the matching @img
|
||||
// platform binary for every OS/arch npm installs onto, so the platform
|
||||
// packages are not pinned here (pinning them drifts on a sharp bump).
|
||||
sharp: '0.34.5',
|
||||
// The version is exact-pinned like all other native optional deps in this
|
||||
// manifest — a project-wide convention that keeps the published tarball
|
||||
// reproducible. Sharp's recurring CVE stream means users must wait for a
|
||||
// CLI release to pick up libvips fixes; nightly releases keep the
|
||||
// turnaround short.
|
||||
sharp: sharpVersion,
|
||||
},
|
||||
engines: rootPackageJson.engines,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -722,6 +722,8 @@ describe('package asset scripts', () => {
|
|||
expect(distPackageJson.optionalDependencies).toMatchObject({
|
||||
'@qwen-code/audio-capture': rootPackageJson.version,
|
||||
});
|
||||
|
||||
expect(distPackageJson.optionalDependencies.sharp).toBe('0.35.4');
|
||||
expect(
|
||||
existsSync(
|
||||
path.join(
|
||||
|
|
@ -740,6 +742,60 @@ describe('package asset scripts', () => {
|
|||
).toBe(true);
|
||||
});
|
||||
|
||||
it('falls back to the hoisted lockfile entry when core has no nested sharp', () => {
|
||||
const rootDir = createFixtureRoot();
|
||||
writeFile(
|
||||
rootDir,
|
||||
'package-lock.json',
|
||||
JSON.stringify({
|
||||
packages: {
|
||||
'node_modules/sharp': {
|
||||
version: '0.35.3',
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
writeFile(
|
||||
rootDir,
|
||||
'packages/core/package.json',
|
||||
JSON.stringify(
|
||||
{
|
||||
name: '@qwen-code/qwen-code-core',
|
||||
version: '0.17.0',
|
||||
dependencies: {
|
||||
sharp: '^0.35.0',
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
);
|
||||
createBundleArtifacts(rootDir);
|
||||
stubConsole();
|
||||
|
||||
preparePackage({ rootDir, requireNativeAudioCapture: false });
|
||||
|
||||
const distPackageJson = JSON.parse(
|
||||
readFileSync(path.join(rootDir, 'dist', 'package.json'), 'utf8'),
|
||||
);
|
||||
expect(distPackageJson.optionalDependencies.sharp).toBe('0.35.3');
|
||||
});
|
||||
|
||||
it('rejects a locked sharp version outside the core declaration', () => {
|
||||
const rootDir = createFixtureRoot();
|
||||
writeFile(
|
||||
rootDir,
|
||||
'packages/core/package.json',
|
||||
JSON.stringify({ dependencies: { sharp: '^0.34.0' } }),
|
||||
);
|
||||
createBundleArtifacts(rootDir);
|
||||
stubConsole();
|
||||
|
||||
expect(() =>
|
||||
preparePackage({ rootDir, requireNativeAudioCapture: false }),
|
||||
).toThrow(/resolved 0\.35\.4, packages\/core declares \^0\.34\.0/);
|
||||
});
|
||||
|
||||
it('omits browser MCP install hooks and deps from the prepared dist package', () => {
|
||||
const rootDir = createFixtureRoot();
|
||||
createBundleArtifacts(rootDir);
|
||||
|
|
@ -1076,11 +1132,45 @@ describe('package asset scripts', () => {
|
|||
),
|
||||
);
|
||||
|
||||
writeFile(
|
||||
rootDir,
|
||||
'package-lock.json',
|
||||
JSON.stringify(
|
||||
{
|
||||
packages: {
|
||||
'node_modules/sharp': {
|
||||
version: '0.35.3',
|
||||
},
|
||||
'packages/core/node_modules/sharp': {
|
||||
version: '0.35.4',
|
||||
},
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
);
|
||||
|
||||
writeFile(
|
||||
rootDir,
|
||||
'packages/cli/src/i18n/locales/en.json',
|
||||
'{"hello":"world"}\n',
|
||||
);
|
||||
writeFile(
|
||||
rootDir,
|
||||
'packages/core/package.json',
|
||||
JSON.stringify(
|
||||
{
|
||||
name: '@qwen-code/qwen-code-core',
|
||||
version: '0.17.0',
|
||||
dependencies: {
|
||||
sharp: '^0.35.0',
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
);
|
||||
writeFile(
|
||||
rootDir,
|
||||
'packages/audio-capture/package.json',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue