Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@fengmk2
Copy link
Member

@fengmk2 fengmk2 commented Dec 13, 2025

Summary by CodeRabbit

  • New Features

    • Added configurable block lists to prevent specific scopes and packages from syncing.
  • Bug Fixes

    • Enhanced validation to detect blocked and invalid packages early, with improved error messages and task finalization.
    • Improved error logging for sync operations involving blocked packages or unsupported versions.

✏️ Tip: You can customize this high-level summary in your review settings.

@fengmk2 fengmk2 requested review from Copilot and elrrrrrrr December 13, 2025 14:52
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 13, 2025

Walkthrough

The pull request introduces package blocking controls to prevent syncing of specific scopes and packages via new whitelist fields in PackageVersionFileService. It renames isAllowLargePackageVersion() to isLargePackageVersionAllowed(), adds a new isPackageBlockedToSync() method, and implements fail-fast error handling in PackageSyncerService to exit early when packages are blocked or disallowed.

Changes

Cohort / File(s) Summary
Service implementation
app/core/service/PackageVersionFileService.ts
Added private block-sync whitelist controls (blockSyncScopes, blockSyncPackages); renamed isAllowLargePackageVersion() to isLargePackageVersionAllowed(); added new public method isPackageBlockedToSync() to check if a package is blocked by scope or full name; extended logging and manifest update to include block sync counts.
Sync logic updates
app/core/service/PackageSyncerService.ts
Added pre-check for blocked packages with early fail return; renamed call site for large-package-version check; replaced large-package-version rejection behavior with fail-fast task error, logging, and early return.
Test coverage
test/core/service/PackageSyncerService/executeTask.test.ts
Added test case for block-list-based stopping when unpkg whitelist is enabled; updated method call from isAllowLargePackageVersion() to isLargePackageVersionAllowed().
Test mocks
test/core/service/PackageSyncerService/executeTaskWithPackument.test.ts
Updated mock references from isAllowLargePackageVersion to isLargePackageVersionAllowed on PackageVersionFileService prototype.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Verify all call sites of the renamed method (isAllowLargePackageVersionisLargePackageVersionAllowed) are correctly updated across service and test files
  • Review the block-sync whitelist initialization and manifest update logic for correctness
  • Validate that fail-fast behavior in PackageSyncerService does not skip necessary cleanup or logging
  • Confirm test coverage adequately exercises the new block-list stopping scenario and method renames

Possibly related PRs

Suggested labels

enhancement

Suggested reviewers

  • elrrrrrrr
  • Copilot

Poem

🐰 A hop, a block, a sync denied,
With whitelists enforcing pride!
Early returns, no wasted care,
The packages flow oh-so-fair! ✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: support block sync packages' accurately describes the main feature addition: introducing blocking capability for package synchronization. This aligns with the changes across multiple files that add blockSyncScopes/blockSyncPackages controls and the new isPackageBlockedToSync method.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch support-block-sync-packages

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @fengmk2, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a critical feature for managing package synchronization by allowing the explicit blocking of packages or scopes. This provides greater control over the content being mirrored, preventing unwanted or problematic packages from being synced. Additionally, it refines the existing large package version handling to be more robust, ensuring that oversized packages that are not whitelisted immediately fail their sync tasks, improving the reliability and predictability of the synchronization process.

Highlights

  • Package Sync Blocking: Implemented a mechanism to block the synchronization of specific packages or entire scopes based on a whitelist configuration, preventing unwanted packages from being synced.
  • Enhanced Large Package Version Handling: Improved the handling of large package versions by immediately failing the sync task if a package exceeds the size limit and is not explicitly allowed, rather than merely logging a warning and continuing.
  • Code Refactoring and Clarity: Renamed the method isAllowLargePackageVersion to isLargePackageVersionAllowed in PackageVersionFileService for improved clarity and consistency across the codebase.
  • New Service Method for Blocking: Introduced a new asynchronous method isPackageBlockedToSync within PackageVersionFileService to centralize the logic for checking if a package or its scope is on the block list.
  • Comprehensive Test Coverage: Added a new test case to validate the functionality of blocking package synchronization, ensuring the new feature works as expected.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces support for blocking package synchronization based on a configurable blocklist. The changes include adding a check for blocked packages at the start of the sync task, and modifying the behavior for large packages to fail the entire sync task instead of just skipping a version. The implementation is logical and includes corresponding test updates. My main feedback is regarding code duplication for handling task failures, which could be refactored to improve maintainability.

Comment on lines +524 to +535
if (await this.packageVersionFileService.isPackageBlockedToSync(scope, name)) {
task.error = `stop sync by block list, see ${UNPKG_WHITE_LIST_URL}`;
logs.push(`[${isoNow()}] ❌ ${task.error}, log: ${logUrl}`);
logs.push(`[${isoNow()}] ❌❌❌❌❌ ${fullname} ❌❌❌❌❌`);
await this.taskService.finishTask(task, TaskState.Fail, logs.join('\n'));
this.logger.info(
'[PackageSyncerService.executeTask:fail-package-blocked-to-sync] taskId: %s, targetName: %s, %s',
task.taskId,
task.targetName,
task.error,
);
return;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This block of code for failing a task is very similar to the logic in other parts of this file (e.g., lines 511-521, 892-902, and 1396-1406). To improve maintainability and reduce redundancy, consider extracting this logic into a private helper method. This would make the code cleaner and easier to manage in the future.

Comment on lines 891 to +902
if (!isAllowLargePackageVersion) {
lastErrorMessage = `large package version size: ${size}, allow size: ${this.config.cnpmcore.largePackageVersionSize}, see ${UNPKG_WHITE_LIST_URL}`;
logs.push(`[${isoNow()}] ❌ [${syncIndex}] Synced version ${version} fail, ${lastErrorMessage}`);
await this.taskService.appendTaskLog(task, logs.join('\n'));
logs = [];
continue;
task.error = `Synced version ${version} fail, large package version size: ${size}, allow size: ${this.config.cnpmcore.largePackageVersionSize}, see ${UNPKG_WHITE_LIST_URL}`;
logs.push(`[${isoNow()}] ❌ ${task.error}, log: ${logUrl}`);
logs.push(`[${isoNow()}] ❌❌❌❌❌ ${fullname} ❌❌❌❌❌`);
await this.taskService.finishTask(task, TaskState.Fail, logs.join('\n'));
this.logger.info(
'[PackageSyncerService.executeTask:fail-large-package-version-size] taskId: %s, targetName: %s, %s',
task.taskId,
task.targetName,
task.error,
);
return;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This logic for failing a task due to a large package size is also duplicated. As mentioned in another comment, this is similar to the logic for handling blocked packages. Extracting this repeated logic into a shared helper function would improve code quality.

Comment on lines 1395 to +1406
if (!isAllowLargePackageVersion) {
lastErrorMessage = `large package version size: ${size}, allow size: ${this.config.cnpmcore.largePackageVersionSize}, see ${UNPKG_WHITE_LIST_URL}`;
logs.push(`[${isoNow()}] ❌ [${syncIndex}] Synced version ${version} fail, ${lastErrorMessage}`);
await this.taskService.appendTaskLog(task, logs.join('\n'));
logs = [];
continue;
task.error = `Synced version ${version} fail, large package version size: ${size}, allow size: ${this.config.cnpmcore.largePackageVersionSize}, see ${UNPKG_WHITE_LIST_URL}`;
logs.push(`[${isoNow()}] ❌ ${task.error}, log: ${logUrl}`);
logs.push(`[${isoNow()}] ❌❌❌❌❌ ${fullname} ❌❌❌❌❌`);
await this.taskService.finishTask(task, TaskState.Fail, logs.join('\n'));
this.logger.info(
'[PackageSyncerService.executeTask:fail-large-package-version-size] taskId: %s, targetName: %s, %s',
task.taskId,
task.targetName,
task.error,
);
return;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This is another instance of duplicated task-failing logic. Consolidating these blocks into a single helper method would be beneficial for the long-term health of the codebase.

@codecov
Copy link

codecov bot commented Dec 13, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.27%. Comparing base (30e99ab) to head (0d7cf68).
⚠️ Report is 3 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #929      +/-   ##
==========================================
- Coverage   95.38%   95.27%   -0.11%     
==========================================
  Files         197      197              
  Lines       22211    22263      +52     
  Branches     2614     2072     -542     
==========================================
+ Hits        21185    21212      +27     
- Misses       1026     1051      +25     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
app/core/service/PackageSyncerService.ts (2)

524-536: Consider gating isPackageBlockedToSync() behind the feature flag to avoid unnecessary work.
If isPackageBlockedToSync() isn’t already a cheap no-op when enableSyncUnpkgFilesWhiteList=false, this adds avoidable overhead on every sync task.

Suggested diff:

-    if (await this.packageVersionFileService.isPackageBlockedToSync(scope, name)) {
+    if (
+      this.config.cnpmcore.enableSyncUnpkgFilesWhiteList &&
+      (await this.packageVersionFileService.isPackageBlockedToSync(scope, name))
+    ) {
       task.error = `stop sync by block list, see ${UNPKG_WHITE_LIST_URL}`;
       logs.push(`[${isoNow()}] ❌ ${task.error}, log: ${logUrl}`);
       logs.push(`[${isoNow()}] ❌❌❌❌❌ ${fullname} ❌❌❌❌❌`);
       await this.taskService.finishTask(task, TaskState.Fail, logs.join('\n'));
       this.logger.info(
         '[PackageSyncerService.executeTask:fail-package-blocked-to-sync] taskId: %s, targetName: %s, %s',
         task.taskId,
         task.targetName,
         task.error,
       );
       return;
     }

885-907: Rename local isAllowLargePackageVersion to match new API + keep fail-fast consistent.
Small clarity improvement; the new fail-fast behavior aligns with the added tests.

Suggested diff:

-        const isAllowLargePackageVersion = await this.packageVersionFileService.isLargePackageVersionAllowed(
+        const isLargePackageVersionAllowed = await this.packageVersionFileService.isLargePackageVersionAllowed(
           scope,
           name,
           version,
         );
-        if (!isAllowLargePackageVersion) {
+        if (!isLargePackageVersionAllowed) {
           task.error = `Synced version ${version} fail, large package version size: ${size}, allow size: ${this.config.cnpmcore.largePackageVersionSize}, see ${UNPKG_WHITE_LIST_URL}`;
           logs.push(`[${isoNow()}] ❌ ${task.error}, log: ${logUrl}`);
           logs.push(`[${isoNow()}] ❌❌❌❌❌ ${fullname} ❌❌❌❌❌`);
           await this.taskService.finishTask(task, TaskState.Fail, logs.join('\n'));
           this.logger.info(
             '[PackageSyncerService.executeTask:fail-large-package-version-size] taskId: %s, targetName: %s, %s',
             task.taskId,
             task.targetName,
             task.error,
           );
           return;
         }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bb580b4 and 0d7cf68.

📒 Files selected for processing (4)
  • app/core/service/PackageSyncerService.ts (3 hunks)
  • app/core/service/PackageVersionFileService.ts (3 hunks)
  • test/core/service/PackageSyncerService/executeTask.test.ts (2 hunks)
  • test/core/service/PackageSyncerService/executeTaskWithPackument.test.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{js,ts,tsx,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{js,ts,tsx,jsx}: Use single quotes in JavaScript/TypeScript code (from Prettier configuration)
Use 2-space indentation (from Prettier configuration)
Maintain 120 character line width maximum (from Prettier configuration)
Use trailing commas in ES5 syntax (from Prettier configuration)
Avoid parentheses in arrow functions when possible (from Prettier configuration)
Maximum of 6 function parameters (from Oxlint configuration)
Warn on console usage (from Oxlint configuration)
Disallow anonymous default exports (from Oxlint configuration)
Use ES modules (import/export) syntax throughout

Files:

  • app/core/service/PackageVersionFileService.ts
  • app/core/service/PackageSyncerService.ts
  • test/core/service/PackageSyncerService/executeTaskWithPackument.test.ts
  • test/core/service/PackageSyncerService/executeTask.test.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Use strict TypeScript with comprehensive type definitions - avoid any types, use proper typing or unknown
Export types and interfaces for reusability in TypeScript

Files:

  • app/core/service/PackageVersionFileService.ts
  • app/core/service/PackageSyncerService.ts
  • test/core/service/PackageSyncerService/executeTaskWithPackument.test.ts
  • test/core/service/PackageSyncerService/executeTask.test.ts
app/core/service/**/*.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

app/core/service/**/*.ts: Services must implement core business logic without HTTP concerns or direct database access
Services must use @SingletonProto() for service lifecycle management
Services must orchestrate multiple repositories and entities, managing transactions and events

Files:

  • app/core/service/PackageVersionFileService.ts
  • app/core/service/PackageSyncerService.ts
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.ts: Use strict TypeScript with proper typing - avoid any types, use proper typing or unknown instead
Use ES modules with import/export syntax throughout the codebase
Use single quotes (') for strings
Use 2-space indentation
Enforce 120 character line width
Use ES5 trailing commas
Limit functions to a maximum of 6 parameters
Do not use console statements - use logger instead

Files:

  • app/core/service/PackageVersionFileService.ts
  • app/core/service/PackageSyncerService.ts
  • test/core/service/PackageSyncerService/executeTaskWithPackument.test.ts
  • test/core/service/PackageSyncerService/executeTask.test.ts
test/**/*.test.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

test/**/*.test.ts: Test files must use .test.ts suffix
Tests must use @eggjs/mock for mocking and testing
Tests must use assert from node:assert/strict for assertions
Test files should be organized in test/ directory mirroring source structure
Mock external dependencies using mock() from @eggjs/mock in tests
Use realistic test data created through TestUtil helper methods
Clean up after tests - database is reset between test files
Test both success and failure cases - error paths are equally important
Test files should follow naming pattern: describe('[HTTP_METHOD /api/path] functionName()', ...)

test/**/*.test.ts: Test files must use .test.ts suffix
Use @eggjs/mock for mocking in test files
Use assert from node:assert/strict in test files
Test files must mirror source structure in test/ directory and test both success and error cases

Files:

  • test/core/service/PackageSyncerService/executeTaskWithPackument.test.ts
  • test/core/service/PackageSyncerService/executeTask.test.ts
🧠 Learnings (1)
📚 Learning: 2025-11-29T15:42:37.586Z
Learnt from: CR
Repo: cnpm/cnpmcore PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-29T15:42:37.586Z
Learning: Applies to test/**/*.test.ts : Mock external dependencies using `mock()` from `eggjs/mock` in tests

Applied to files:

  • test/core/service/PackageSyncerService/executeTaskWithPackument.test.ts
  • test/core/service/PackageSyncerService/executeTask.test.ts
🧬 Code graph analysis (2)
app/core/service/PackageVersionFileService.ts (2)
app/core/entity/Package.ts (1)
  • fullname (63-65)
app/common/PackageUtil.ts (1)
  • getFullname (25-27)
app/core/service/PackageSyncerService.ts (3)
app/core/service/PackageVersionFileService.ts (1)
  • UNPKG_WHITE_LIST_URL (24-24)
app/common/LogUtil.ts (1)
  • isoNow (1-3)
app/core/entity/Package.ts (1)
  • fullname (63-65)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (19)
  • GitHub Check: test on mysql (node@20, shard@1/3, enableJSONBuilder@false)
  • GitHub Check: test on mysql (node@22, shard@0/3, enableJSONBuilder@true)
  • GitHub Check: test on mysql (node@22, shard@2/3, enableJSONBuilder@true)
  • GitHub Check: test on mysql (node@20, shard@2/3, enableJSONBuilder@true)
  • GitHub Check: test on mysql (node@20, shard@1/3, enableJSONBuilder@true)
  • GitHub Check: test on mysql (node@24, shard@0/3, enableJSONBuilder@true)
  • GitHub Check: test on mysql (node@20, shard@2/3, enableJSONBuilder@false)
  • GitHub Check: test on mysql (node@24, shard@2/3, enableJSONBuilder@false)
  • GitHub Check: test on postgresql (node@20, shard@0/3)
  • GitHub Check: test on postgresql (node@24, shard@0/3)
  • GitHub Check: test on postgresql (node@20, shard@1/3)
  • GitHub Check: test on postgresql (node@22, shard@0/3)
  • GitHub Check: test on postgresql (node@20, shard@2/3)
  • GitHub Check: test on postgresql (node@22, shard@1/3)
  • GitHub Check: test on postgresql (node@24, shard@1/3)
  • GitHub Check: test on postgresql (node@22, shard@2/3)
  • GitHub Check: test on postgresql (node@24, shard@2/3)
  • GitHub Check: test-deployment
  • GitHub Check: build-and-push-image
🔇 Additional comments (7)
app/core/service/PackageVersionFileService.ts (4)

63-66: LGTM! Clean addition of block sync fields.

The new private fields for blocking scopes and packages follow the established naming conventions and are properly typed as string arrays, consistent with the existing whitelist fields.


126-136: LGTM! Whitelist update correctly populates block sync data.

The new block sync fields are populated from the manifest using the same pattern as existing whitelist fields, with appropriate fallback to empty arrays. The log output enhancement provides useful visibility into the block list counts.


139-152: LGTM! Method rename improves API naming.

The rename from isAllowLargePackageVersion to isLargePackageVersionAllowed is more grammatically correct and follows better naming conventions (adjective form). The implementation remains unchanged, preserving existing behavior.


154-170: LGTM! New block check method is well-implemented.

The isPackageBlockedToSync method follows the same pattern as isLargePackageVersionAllowed, with proper config checks, whitelist updates, and scope/package-level blocking logic. The use of getFullname helper is correct for constructing the full package identifier.

test/core/service/PackageSyncerService/executeTaskWithPackument.test.ts (1)

2251-2251: LGTM! Test mock correctly updated.

The mock is properly updated to reflect the API rename from isAllowLargePackageVersion to isLargePackageVersionAllowed, ensuring test compatibility with the service changes.

test/core/service/PackageSyncerService/executeTask.test.ts (1)

2292-2292: Good update for renamed API (isLargePackageVersionAllowed).
Keeps the test aligned with the new PackageVersionFileService method name.

app/core/service/PackageSyncerService.ts (1)

1388-1411: Packument path matches the same large-version fail-fast policy (good).
Nice to see the same policy mirrored in syncPackageWithPackument() so behavior doesn’t diverge by code path.

Comment on lines +2046 to +2061
it('should stop sync by block list, see https://github.com/cnpm/unpkg-white-list', async () => {
const name = 'cnpmcore-test-sync-blocklist';
mock(app.config.cnpmcore, 'enableSyncUnpkgFilesWhiteList', true);
mock(PackageVersionFileService.prototype, 'isPackageBlockedToSync', async () => true);
await packageSyncerService.createTask(name);
const task = await packageSyncerService.findExecuteTask();
assert.ok(task);
assert.equal(task.targetName, name);
await packageSyncerService.executeTask(task);
const stream = await packageSyncerService.findTaskLog(task);
assert.ok(stream);
const log = await TestUtil.readStreamToLog(stream);
// console.log(log);
assert.ok(log.includes(`❌❌❌❌❌ ${name} ❌❌❌❌❌`));
assert.ok(log.includes('❌ stop sync by block list, see https://github.com/cnpm/unpkg-white-list'));
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Check the package.json to understand which mock library is used
cat package.json | grep -A2 -B2 mock

Repository: cnpm/cnpmcore

Length of output: 332


🏁 Script executed:

# Look for mock restore patterns in the test file
cd test/core/service/PackageSyncerService && wc -l executeTask.test.ts

Repository: cnpm/cnpmcore

Length of output: 81


🏁 Script executed:

# Search for existing mock restore patterns in the test suite
rg "mock\(.+\)" test/core/service/PackageSyncerService/executeTask.test.ts -A 3 | head -50

Repository: cnpm/cnpmcore

Length of output: 8291


🏁 Script executed:

# Check if there are afterEach hooks that restore mocks
rg "afterEach|restore" test/core/service/PackageSyncerService/executeTask.test.ts

Repository: cnpm/cnpmcore

Length of output: 197


🏁 Script executed:

# Look for the mock library import and how it's used
head -50 test/core/service/PackageSyncerService/executeTask.test.ts

Repository: cnpm/cnpmcore

Length of output: 2992


🏁 Script executed:

# Get the test at lines 2046-2061 and surrounding context to check for cleanup
sed -n '2046,2070p' test/core/service/PackageSyncerService/executeTask.test.ts

Repository: cnpm/cnpmcore

Length of output: 4687


🏁 Script executed:

# Search for afterEach hooks in the entire test file
rg "afterEach|afterAll" test/core/service/PackageSyncerService/executeTask.test.ts -A 5

Repository: cnpm/cnpmcore

Length of output: 39


🏁 Script executed:

# Check for other prototype mocking patterns in the file
rg "mock\(\w+\.prototype" test/core/service/PackageSyncerService/executeTask.test.ts

Repository: cnpm/cnpmcore

Length of output: 311


🏁 Script executed:

# Verify @eggjs/mock documentation by checking if there's a README or guide
find . -name "*.md" -type f | xargs grep -l "@eggjs/mock" 2>/dev/null | head -5

Repository: cnpm/cnpmcore

Length of output: 102


🏁 Script executed:

# Check one more test that uses prototype mocking to see if it has restore
rg -B5 -A10 "NPMRegistry\.prototype.*getSyncTask" test/core/service/PackageSyncerService/executeTask.test.ts | head -30

Repository: cnpm/cnpmcore

Length of output: 773


🏁 Script executed:

# Check if there's an afterEach or test cleanup at the describe block level
sed -n '1,100p' test/core/service/PackageSyncerService/executeTask.test.ts | grep -A 10 "describe"

Repository: cnpm/cnpmcore

Length of output: 1039


🏁 Script executed:

# Look for any test runner configuration or setup files
find . -maxdepth 2 -name "*.config.*" -o -name "test.setup.*" -o -name "mocha.opts" | head -10

Repository: cnpm/cnpmcore

Length of output: 39


🏁 Script executed:

# Check if @eggjs/mock has auto-restore behavior by looking at package docs or comments
cat package.json | grep -A 5 "@eggjs/mock"

Repository: cnpm/cnpmcore

Length of output: 283


Add explicit mock restoration to prevent prototype-mock leakage across tests.

The mock(PackageVersionFileService.prototype, 'isPackageBlockedToSync', ...) call should be restored after the test completes. @eggjs/mock does not automatically restore prototype mocks between test cases, so subsequent tests in the same describe block may inherit this mock and trigger unexpected behavior. Add await mock.restore(); before the test closes.

    it('should stop sync by block list, see https://github.com/cnpm/unpkg-white-list', async () => {
      const name = 'cnpmcore-test-sync-blocklist';
      mock(app.config.cnpmcore, 'enableSyncUnpkgFilesWhiteList', true);
      mock(PackageVersionFileService.prototype, 'isPackageBlockedToSync', async () => true);
      await packageSyncerService.createTask(name);
      const task = await packageSyncerService.findExecuteTask();
      assert.ok(task);
      assert.equal(task.targetName, name);
      await packageSyncerService.executeTask(task);
      const stream = await packageSyncerService.findTaskLog(task);
      assert.ok(stream);
      const log = await TestUtil.readStreamToLog(stream);
      // console.log(log);
      assert.ok(log.includes(`❌❌❌❌❌ ${name} ❌❌❌❌❌`));
      assert.ok(log.includes('❌ stop sync by block list, see https://github.com/cnpm/unpkg-white-list'));
+     await mock.restore();
    });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it('should stop sync by block list, see https://github.com/cnpm/unpkg-white-list', async () => {
const name = 'cnpmcore-test-sync-blocklist';
mock(app.config.cnpmcore, 'enableSyncUnpkgFilesWhiteList', true);
mock(PackageVersionFileService.prototype, 'isPackageBlockedToSync', async () => true);
await packageSyncerService.createTask(name);
const task = await packageSyncerService.findExecuteTask();
assert.ok(task);
assert.equal(task.targetName, name);
await packageSyncerService.executeTask(task);
const stream = await packageSyncerService.findTaskLog(task);
assert.ok(stream);
const log = await TestUtil.readStreamToLog(stream);
// console.log(log);
assert.ok(log.includes(`❌❌❌❌❌ ${name} ❌❌❌❌❌`));
assert.ok(log.includes('❌ stop sync by block list, see https://github.com/cnpm/unpkg-white-list'));
});
it('should stop sync by block list, see https://github.com/cnpm/unpkg-white-list', async () => {
const name = 'cnpmcore-test-sync-blocklist';
mock(app.config.cnpmcore, 'enableSyncUnpkgFilesWhiteList', true);
mock(PackageVersionFileService.prototype, 'isPackageBlockedToSync', async () => true);
await packageSyncerService.createTask(name);
const task = await packageSyncerService.findExecuteTask();
assert.ok(task);
assert.equal(task.targetName, name);
await packageSyncerService.executeTask(task);
const stream = await packageSyncerService.findTaskLog(task);
assert.ok(stream);
const log = await TestUtil.readStreamToLog(stream);
// console.log(log);
assert.ok(log.includes(`❌❌❌❌❌ ${name} ❌❌❌❌❌`));
assert.ok(log.includes('❌ stop sync by block list, see https://github.com/cnpm/unpkg-white-list'));
await mock.restore();
});
🤖 Prompt for AI Agents
In test/core/service/PackageSyncerService/executeTask.test.ts around lines 2046
to 2061, the prototype mock for PackageVersionFileService.isPackageBlockedToSync
is not restored, which can leak into subsequent tests; add a call to await
mock.restore() at the end of this test (after the assertions) or wrap the test
body in try/finally and call await mock.restore() in finally to ensure the
prototype mock is always restored.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds support for blocking package synchronization through the unpkg-white-list manifest. It introduces a new mechanism to prevent specific packages or scopes from being synced, complementing the existing allow-list and large package version controls.

Key changes:

  • Added isPackageBlockedToSync method to check if packages are in block lists
  • Renamed isAllowLargePackageVersion to isLargePackageVersionAllowed for naming consistency
  • Changed large package version handling behavior from skipping to failing the entire sync task

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
app/core/service/PackageVersionFileService.ts Added block sync support with new private fields for blocked scopes/packages, implemented isPackageBlockedToSync method, and renamed isAllowLargePackageVersion to isLargePackageVersionAllowed
app/core/service/PackageSyncerService.ts Added early check for blocked packages, changed large package version handling from continuing to failing the task, and updated method calls to use renamed method
test/core/service/PackageSyncerService/executeTask.test.ts Added integration test for block list functionality and updated mock calls to use renamed method
test/core/service/PackageSyncerService/executeTaskWithPackument.test.ts Updated mock calls to use renamed method isLargePackageVersionAllowed

Comment on lines +892 to +902
task.error = `Synced version ${version} fail, large package version size: ${size}, allow size: ${this.config.cnpmcore.largePackageVersionSize}, see ${UNPKG_WHITE_LIST_URL}`;
logs.push(`[${isoNow()}] ❌ ${task.error}, log: ${logUrl}`);
logs.push(`[${isoNow()}] ❌❌❌❌❌ ${fullname} ❌❌❌❌❌`);
await this.taskService.finishTask(task, TaskState.Fail, logs.join('\n'));
this.logger.info(
'[PackageSyncerService.executeTask:fail-large-package-version-size] taskId: %s, targetName: %s, %s',
task.taskId,
task.targetName,
task.error,
);
return;
Copy link

Copilot AI Dec 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This changes the behavior from skipping large versions and continuing with other versions to failing the entire sync task when a large version is encountered that is not allowed. This is a breaking behavior change that could impact users who were previously able to partially sync packages with mixed version sizes. Consider documenting this breaking change in the PR description or changelog.

Suggested change
task.error = `Synced version ${version} fail, large package version size: ${size}, allow size: ${this.config.cnpmcore.largePackageVersionSize}, see ${UNPKG_WHITE_LIST_URL}`;
logs.push(`[${isoNow()}] ❌ ${task.error}, log: ${logUrl}`);
logs.push(`[${isoNow()}] ❌❌❌❌❌ ${fullname} ❌❌❌❌❌`);
await this.taskService.finishTask(task, TaskState.Fail, logs.join('\n'));
this.logger.info(
'[PackageSyncerService.executeTask:fail-large-package-version-size] taskId: %s, targetName: %s, %s',
task.taskId,
task.targetName,
task.error,
);
return;
const errorMsg = `Synced version ${version} fail, large package version size: ${size}, allow size: ${this.config.cnpmcore.largePackageVersionSize}, see ${UNPKG_WHITE_LIST_URL}`;
logs.push(`[${isoNow()}] ❌ ${errorMsg}, log: ${logUrl}`);
logs.push(`[${isoNow()}] ❌❌❌❌❌ ${fullname} ❌❌❌❌❌`);
await this.taskService.appendTaskLog(task, logs.join('\n'));
logs = [];
this.logger.info(
'[PackageSyncerService.executeTask:skip-large-package-version-size] taskId: %s, targetName: %s, %s',
task.taskId,
task.targetName,
errorMsg,
);
continue;

Copilot uses AI. Check for mistakes.
Comment on lines +160 to +170
async isPackageBlockedToSync(pkgScope: string, pkgName: string) {
if (!this.config.cnpmcore.enableSyncUnpkgFilesWhiteList) return false;
await this.#updateUnpkgWhiteList();

// check block scopes
if (this.#unpkgWhiteListBlockSyncScopes.includes(pkgScope)) return true;

// check block packages
const fullname = getFullname(pkgScope, pkgName);
return this.#unpkgWhiteListBlockSyncPackages.includes(fullname);
}
Copy link

Copilot AI Dec 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new isPackageBlockedToSync method lacks direct unit tests. While there is an integration test that mocks this method, there are no tests that verify the actual logic of checking block sync scopes and packages against the unpkg whitelist manifest. Consider adding unit tests to verify the behavior when packages/scopes are in blockSyncScopes and blockSyncPackages arrays, similar to how isLargePackageVersionAllowed is tested.

Copilot uses AI. Check for mistakes.
@elrrrrrrr elrrrrrrr merged commit 4cc30fe into master Dec 13, 2025
43 of 44 checks passed
@elrrrrrrr elrrrrrrr deleted the support-block-sync-packages branch December 13, 2025 14:59
fengmk2 pushed a commit that referenced this pull request Dec 13, 2025
[skip ci]

## 4.17.0 (2025-12-13)

* feat: support block sync packages (#929) ([4cc30fe](4cc30fe)), closes [#929](#929) [hi#level](https://github.com/hi/issues/level)
@github-actions
Copy link

🎉 This PR is included in version 4.17.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants