Skip to content

Conversation

@syi0808
Copy link
Owner

@syi0808 syi0808 commented Oct 23, 2024

Summary by CodeRabbit

  • New Features

    • Added landing page with features showcase, quickstart guide, comparison table, and interactive terminal preview.
    • CLI now supports --help/-h and --version/-v flags.
  • Bug Fixes

    • Improved error resilience when querying registries.
    • Fixed registry detection logic for npm-only scenarios.
  • Tests

    • Expanded test coverage with CLI, Git, package management, and registry tests.
  • Chores

    • Updated dev dependencies (vitest, coverage tools).
    • Added test fixtures and refined coverage configuration.

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

@syi0808 syi0808 self-assigned this Oct 23, 2024
@github-actions
Copy link

github-actions bot commented Oct 23, 2024

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 96.72% 1124 / 1162
🔵 Statements 96.72% 1124 / 1162
🔵 Functions 100% 110 / 110
🔵 Branches 94.68% 303 / 320
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
src/options.ts 100% 100% 100% 100%
Generated in workflow #150 for commit 2b5ab7a by the Vitest Coverage Report Action

@syi0808 syi0808 requested a review from Copilot April 7, 2025 04:36
Copy link

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.

Copilot reviewed 6 out of 10 changed files in this pull request and generated 1 comment.

Files not reviewed (4)
  • package.json: Language not supported
  • pnpm-lock.yaml: Language not supported
  • tests/fixtures/build-script/package.json: Language not supported
  • tests/fixtures/test-script/package.json: Language not supported
Comments suppressed due to low confidence (2)

tests/utils/cli.ts:120

  • Converting runPubmCli from an async to a synchronous function may introduce timing issues if callers rely on awaiting subprocess completion; ensure that the subprocess exit is awaited or that tests are updated accordingly.
export function runPubmCli(

src/tasks/runner.ts:38

  • The revised logic for determining npmOnly is stricter than before and may not account for mixed registry configurations; please confirm that this behavior is intentional.
npmOnly: options.registries.every((registry) => registry === 'npm'),

@syi0808 syi0808 force-pushed the main branch 4 times, most recently from 263a7fd to 435d406 Compare April 28, 2025 13:33
@syi0808 syi0808 force-pushed the test/integrated-test branch from d1c1a6a to a57fb87 Compare April 28, 2025 14:08
@coderabbitai
Copy link

coderabbitai bot commented Jan 3, 2026

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

This PR introduces a comprehensive landing page (docs/landing.html) with interactive features, adds extensive unit and integration test coverage (15+ test files), updates testing infrastructure (vitest/coverage v3.1.2, new UI), refactors CLI and options handling to canonicalize paths and resolve options consistently, improves error handling in registry operations, and adds test fixtures for various package configurations.

Changes

Cohort / File(s) Summary
Documentation & Landing Page
docs/landing.html
New static landing page with hero section, features grid, comparison table, quickstart code blocks with copy-to-clipboard, fixed navigation, scroll-reveal animations, and terminal typing effect. Embedded CSS for dark theme and responsive design.
Project Configuration
.gitignore, package.json, vitest.config.mts
Added "coverage" to gitignore; bumped vitest and @vitest/coverage-v8 from v2.1.1 to v3.1.2; added @vitest/ui ^3.1.2; changed test/coverage scripts to remove --run flag; refined coverage includes to src/**/*.ts and excludes src/types/**, src/cli.ts, src/tasks/**.
CLI & Options Refactoring
src/cli.ts, src/options.ts, src/tasks/runner.ts
Resolves CLI options to absolute path when contents provided; moved directory change to occur after option resolution; reversed options/defaults merge precedence so user options take priority; narrowed npmOnly detection to exactly 'npm' registries only; removed redundant directory change from runner.
Error Handling & Resilience
src/tasks/required-missing-information.ts
Wrapped distTags computation in try/catch to gracefully handle registry errors without throwing.
Test Infrastructure & Utilities
tests/utils/cli.ts
Refactored runPubmCli from async command-based to synchronous path-based runner; added UP and ENTER key constants; returns direct controller object with exitCode, stdout, stderr, and waitForClose.
Integration Tests
tests/cli.test.ts
New CLI integration test suite verifying --help, --version, -h, -v flags with stdout assertions.
Unit Tests - Core
tests/unit/index.test.ts, tests/unit/options.test.ts, tests/unit/listr.test.ts
Tests for pubm entry point, options resolution with merge precedence, and Listr utility.
Unit Tests - Utilities
tests/unit/db.test.ts, tests/unit/error.test.ts, tests/unit/engine-version.test.ts, tests/unit/git.test.ts, tests/unit/notify-new-version.test.ts, tests/unit/package.test.ts, tests/unit/package-manager.test.ts, tests/unit/package-name.test.ts, tests/unit/rollback.test.ts, tests/unit/utils-cli.test.ts
Comprehensive unit test coverage for database operations, error handling, engine validation, git operations, version notifications, package utilities, package manager detection, package name validation, rollback mechanics, and CLI utilities.
Unit Tests - Registries
tests/unit/registry/index.test.ts, tests/unit/registry/npm.test.ts, tests/unit/registry/jsr.test.ts, tests/unit/registry/custom-registry.test.ts, tests/unit/registry/registry.test.ts
Unit tests for registry abstraction, npm registry operations, jsr registry and client, custom registry with URL override, and registry factory.
Test Fixtures
tests/fixtures/both-json/*, tests/fixtures/build-script/*, tests/fixtures/jsr-json/*, tests/fixtures/package-json/*, tests/fixtures/test-script/*
New fixture manifests for testing multi-manifest packages (npm+jsr), build scripts, jsr-only packages, standard npm packages, and test scripts.
Removed Test
tests/test.test.ts
Deleted trivial placeholder test.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

The PR spans heterogeneous changes: a full landing page with embedded interactivity, 15+ unit test files with mocked dependencies, test fixture additions, CLI/options refactoring affecting control flow, dependency version bumps, and coverage configuration. Each test file and core logic change requires separate reasoning; however, test files follow consistent patterns, reducing overall review density.

Poem

🐰 A landing page emerges, gleaming bright,
Tests bloom in clusters through the night,
Options now resolve with paths so true,
And registries handle errors anew—
Pubm hops forward, shiny and new!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'test: add integrated test for cli' accurately describes the main changes in the PR. The changeset includes a new CLI test file (tests/cli.test.ts) and numerous supporting test infrastructure additions, making this a test-focused PR.
✨ Finishing touches
  • 📝 Generate docstrings

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.

Copy link

@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: 19

🧹 Nitpick comments (9)
docs/landing.html (2)

13-778: Consider extracting CSS to an external file.

The embedded CSS is well-structured, but at 765+ lines, it affects maintainability and prevents browser caching. For production use, consider moving this to an external stylesheet.


1049-1107: Consider extracting JavaScript to an external file.

Similar to the CSS, the inline JavaScript could be moved to an external file for better maintainability and browser caching. This is particularly beneficial if the landing page is frequently updated.

src/tasks/required-missing-information.ts (1)

75-81: Consider adding warning logging for distTags retrieval failures.

In a previous review, you mentioned you would "make a warning" when distTags retrieval fails. However, the current implementation silently catches errors without logging. This could make debugging registry issues difficult.

🔎 Suggested implementation with warning
 try {
   distTags = [
     ...new Set(
       (await Promise.all([npm.distTags(), jsr.distTags()])).flat(),
     ),
   ].filter((tag) => tag !== defaultOptions.tag);
-} catch {}
+} catch (error) {
+  console.warn('Failed to retrieve dist-tags from registry:', error);
+}

Based on past review comments where you indicated you would add warning logging for this error case.

tests/unit/listr.test.ts (1)

1-49: Consider adding test coverage for externalSignalHandler.

The implementation in src/utils/listr.ts (line 11) also sets the externalSignalHandler property. Consider adding a test to verify this property is correctly assigned.

🔎 Suggested test addition
+	test('sets externalSignalHandler', () => {
+		const listr = createListr([]);
+
+		// @ts-ignore - accessing patched property
+		expect(listr.externalSignalHandler).toBeDefined();
+	});
tests/unit/registry/npm.test.ts (1)

1-17: Import sorting issue flagged by CI.

The pipeline indicates imports should be sorted. Consider reordering to satisfy the linter.

Proposed fix
-import { describe, expect, test, vi, beforeEach, afterEach } from 'vitest';
-import { NpmRegistry, npmRegistry } from '../../../src/registry/npm';
-
-vi.mock('tinyexec', () => ({
-	exec: vi.fn(),
-}));
-
-vi.mock('../../../src/utils/package', () => ({
-	getPackageJson: vi.fn().mockResolvedValue({
-		name: 'test-package',
-		version: '1.0.0',
-	}),
-}));
-
-import { exec } from 'tinyexec';
+import { exec } from 'tinyexec';
+import { beforeEach, afterEach, describe, expect, test, vi } from 'vitest';
+
+import { NpmRegistry, npmRegistry } from '../../../src/registry/npm';
+
+vi.mock('tinyexec', () => ({
+	exec: vi.fn(),
+}));
+
+vi.mock('../../../src/utils/package', () => ({
+	getPackageJson: vi.fn().mockResolvedValue({
+		name: 'test-package',
+		version: '1.0.0',
+	}),
+}));

Note: The exact ordering depends on your project's lint rules. The vi.mock calls must remain hoisted, but the static imports can be reordered.

src/cli.ts (1)

1-12: Import sorting issue flagged by CI.

The pipeline indicates imports should be sorted. The path import on line 12 should be grouped with other node imports if a sorting convention is enforced.

Proposed fix
+import path from 'node:path';
 import cac from 'cac';
 import type { OptionConfig } from 'cac/deno/Option.js';
 import semver from 'semver';
 import { isCI } from 'std-env';
 import { consoleError } from './error.js';
 import { Git } from './git.js';
 import { pubm } from './index.js';
 import { requiredMissingInformationTasks } from './tasks/required-missing-information.js';
 import type { Options } from './types/options.js';
 import { notifyNewVersion } from './utils/notify-new-version.js';
 import { version } from './utils/package.js';
-import path from 'node:path';
tests/unit/index.test.ts (1)

1-21: Import sorting issue flagged by CI.

The pipeline indicates imports should be sorted.

Proposed fix
-import { describe, expect, test, vi, beforeEach } from 'vitest';
-
-vi.mock('../../src/options', () => ({
+import { beforeEach, describe, expect, test, vi } from 'vitest';
+
+import { pubm } from '../../src/index';
+import { resolveOptions } from '../../src/options';
+import { run } from '../../src/tasks/runner';
+
+vi.mock('../../src/options', () => ({
 	resolveOptions: vi.fn((options) => ({
 		...options,
 		testScript: options.testScript ?? 'test',
 		buildScript: options.buildScript ?? 'build',
 		branch: options.branch ?? 'main',
 		tag: options.tag ?? 'latest',
 		registries: options.registries ?? ['npm', 'jsr'],
 	})),
 }));
 
 vi.mock('../../src/tasks/runner', () => ({
 	run: vi.fn().mockResolvedValue(undefined),
 }));
-
-import { pubm } from '../../src/index';
-import { run } from '../../src/tasks/runner';
-import { resolveOptions } from '../../src/options';

Note: The exact ordering depends on your project's lint configuration.

tests/unit/registry/jsr.test.ts (1)

1-24: Import sorting issue flagged by CI.

The pipeline indicates imports should be sorted.

Proposed fix
-import { describe, expect, test, vi, beforeEach, afterEach } from 'vitest';
-import { JsrRegisry, JsrClient, jsrRegistry } from '../../../src/registry/jsr';
-
-vi.mock('tinyexec', () => ({
-	exec: vi.fn(),
-}));
+import { exec } from 'tinyexec';
+import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
+
+import { JsrClient, JsrRegisry, jsrRegistry } from '../../../src/registry/jsr';
 
+vi.mock('tinyexec', () => ({
+	exec: vi.fn(),
+}));
 vi.mock('../../../src/utils/package', () => ({
 ...
 }));
-
-import { exec } from 'tinyexec';
tests/utils/cli.ts (1)

1-5: Import sorting issue flagged by CI.

The pipeline indicates imports should be sorted. The node:path import should be grouped with other node imports.

Proposed fix
+import path from 'node:path';
 import type { Readable, Writable } from 'node:stream';
 import { stripVTControlCharacters } from 'node:util';
 import { isCI } from 'std-env';
 import { type Options, exec } from 'tinyexec';
-import path from 'node:path';
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 435d406 and 2b5ab7a.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (35)
  • .gitignore
  • docs/landing.html
  • package.json
  • src/cli.ts
  • src/options.ts
  • src/tasks/required-missing-information.ts
  • src/tasks/runner.ts
  • tests/cli.test.ts
  • tests/fixtures/both-json/jsr.json
  • tests/fixtures/both-json/package.json
  • tests/fixtures/build-script/package.json
  • tests/fixtures/jsr-json/jsr.json
  • tests/fixtures/package-json/package.json
  • tests/fixtures/test-script/package.json
  • tests/test.test.ts
  • tests/unit/db.test.ts
  • tests/unit/engine-version.test.ts
  • tests/unit/error.test.ts
  • tests/unit/git.test.ts
  • tests/unit/index.test.ts
  • tests/unit/listr.test.ts
  • tests/unit/notify-new-version.test.ts
  • tests/unit/options.test.ts
  • tests/unit/package-manager.test.ts
  • tests/unit/package-name.test.ts
  • tests/unit/package.test.ts
  • tests/unit/registry/custom-registry.test.ts
  • tests/unit/registry/index.test.ts
  • tests/unit/registry/jsr.test.ts
  • tests/unit/registry/npm.test.ts
  • tests/unit/registry/registry.test.ts
  • tests/unit/rollback.test.ts
  • tests/unit/utils-cli.test.ts
  • tests/utils/cli.ts
  • vitest.config.mts
💤 Files with no reviewable changes (1)
  • tests/test.test.ts
🧰 Additional context used
🧬 Code graph analysis (19)
tests/unit/engine-version.test.ts (2)
src/utils/package.ts (1)
  • getPackageJson (49-90)
src/utils/engine-version.ts (1)
  • validateEngineVersion (5-16)
src/tasks/required-missing-information.ts (3)
src/registry/jsr.ts (2)
  • distTags (53-55)
  • jsr (35-41)
src/registry/npm.ts (2)
  • distTags (106-119)
  • npm (14-20)
src/options.ts (1)
  • defaultOptions (3-9)
tests/unit/utils-cli.test.ts (1)
src/utils/cli.ts (2)
  • warningBadge (3-3)
  • link (5-7)
tests/unit/db.test.ts (1)
src/utils/db.ts (1)
  • Db (20-58)
tests/unit/listr.test.ts (1)
src/utils/listr.ts (1)
  • createListr (4-17)
tests/unit/package-name.test.ts (1)
src/utils/package-name.ts (4)
  • isScopedPackage (3-5)
  • getScope (7-9)
  • getScopeAndName (11-17)
  • isValidPackageName (24-60)
tests/unit/package.test.ts (1)
src/utils/package.ts (7)
  • jsrJsonToPackageJson (178-204)
  • packageJsonToJsrJson (130-176)
  • findOutFile (24-47)
  • patchCachedJsrJson (17-22)
  • getPackageJson (49-90)
  • getJsrJson (92-128)
  • replaceVersion (223-256)
tests/unit/notify-new-version.test.ts (3)
src/utils/package.ts (3)
  • findOutFile (24-47)
  • getPackageJson (49-90)
  • getJsrJson (92-128)
src/utils/package-name.ts (1)
  • getScopeAndName (11-17)
src/utils/notify-new-version.ts (1)
  • notifyNewVersion (6-61)
tests/unit/index.test.ts (3)
src/index.ts (1)
  • pubm (15-19)
src/options.ts (1)
  • resolveOptions (11-15)
src/tasks/runner.ts (1)
  • run (34-246)
tests/unit/git.test.ts (1)
src/git.ts (7)
  • git (10-16)
  • Git (9-343)
  • branch (153-161)
  • status (107-115)
  • tags (36-48)
  • commit (286-296)
  • commits (117-141)
tests/utils/cli.ts (1)
src/index.ts (1)
  • Options (21-21)
tests/unit/registry/index.test.ts (1)
src/registry/index.ts (1)
  • getRegistry (12-22)
tests/unit/registry/custom-registry.test.ts (1)
src/registry/custom-registry.ts (2)
  • CustomRegistry (5-16)
  • customRegistry (18-22)
tests/unit/package-manager.test.ts (2)
src/utils/package.ts (1)
  • findOutFile (24-47)
src/utils/package-manager.ts (1)
  • getPackageManager (11-19)
tests/unit/error.test.ts (1)
src/error.ts (2)
  • AbstractError (3-12)
  • consoleError (42-54)
tests/unit/rollback.test.ts (1)
src/utils/rollback.ts (2)
  • addRollback (6-11)
  • rollback (15-27)
tests/unit/registry/npm.test.ts (2)
src/registry/npm.ts (2)
  • NpmRegistry (11-183)
  • npmRegistry (185-189)
src/utils/package.ts (1)
  • getPackageJson (49-90)
src/cli.ts (2)
src/utils/notify-new-version.ts (1)
  • notifyNewVersion (6-61)
src/index.ts (1)
  • pubm (15-19)
tests/unit/registry/jsr.test.ts (2)
src/registry/jsr.ts (3)
  • JsrRegisry (25-123)
  • JsrClient (125-300)
  • jsrRegistry (302-306)
src/utils/package.ts (1)
  • getJsrJson (92-128)
🪛 GitHub Actions: CI
tests/unit/engine-version.test.ts

Organize imports: Import statements could be sorted.

tests/unit/utils-cli.test.ts

[error] 1-1: Import statements could be sorted: current order differs from recommended.

tests/unit/db.test.ts

Organize imports: Import statements could be sorted.

tests/cli.test.ts

[error] 5-5: CLI help command shows usage timed out after 5000ms.


[error] 13-13: CLI version command shows version timed out after 5000ms.


[error] 19-19: CLI -h alias shows help timed out after 5000ms.


[error] 26-26: CLI -v alias shows version timed out after 5000ms.

tests/fixtures/both-json/package.json

[error] 1-1: Formatter would have printed the following content: - name: "@test/both-package" - version: "3.0.0"

tests/unit/index.test.ts

Organize imports: Import statements could be sorted.

tests/fixtures/package-json/package.json

[error] 1-1: Formatter would have printed the following content: - name: "@test/package" - version: "1.0.0" - description: "Test package for unit tests" - exports: { ": { import: "./dist/index.js", require: "./dist/index.cjs" }, "./utils": { import: "./dist/utils.js" } } - files: ["dist", "!dist/test", "src"] - engines: { node: ">=18" }

tests/fixtures/test-script/package.json

[error] 1-1: Formatter would have printed the following content: - name: "test-script-fixtures" - version: "0.0.1" - scripts: { test: "test" }

tests/fixtures/build-script/package.json

[error] 1-1: Formatter would have printed the following content: - name: "build-script-fixtures" - version: "0.0.1" - scripts: { build: "build" }

tests/utils/cli.ts

[error] 1-1: Import statements could be sorted: current order differs from recommended.

tests/unit/registry/index.test.ts

Organize imports: Import statements could be sorted.

tests/unit/registry/custom-registry.test.ts

[error] 43-43: lint/suspicious/noExplicitAny - Unexpected any. Specify a different type.


[error] 60-60: lint/suspicious/noExplicitAny - Unexpected any. Specify a different type.

tests/unit/error.test.ts

Organize imports: Import statements could be sorted.

tests/unit/rollback.test.ts

[error] 1-1: Import statements could be sorted: current order differs from recommended.

package.json

[error] 1-1: Formatter would have printed the following content: - name: "@test/both-package" - version: "3.0.0"


[error] 1-1: Formatter would have printed the following content: - name: "build-script-fixtures" - version: "0.0.1" - scripts: { build: "build" }


[error] 1-1: Formatter would have printed the following content: - name: "test-script-fixtures" - version: "0.0.1" - scripts: { test: "test" }


[error] 1-1: Formatter would have printed the following content: - name: "@test/package" - version: "1.0.0" - description: "Test package for unit tests" - exports: { ": { import: "./dist/index.js", require: "./dist/index.cjs" }, "./utils": { import: "./dist/utils.js" } } - files: ["dist", "!dist/test", "src"] - engines: { node: ">=18" }

tests/fixtures/both-json/jsr.json

[error] 1-1: Formatter would have printed the following content: - name: "@test/both-jsr" - version: "3.0.0" - exports: "./src/index.ts"

tests/unit/registry/npm.test.ts

[error] 1-1: Import statements could be sorted: current order differs from recommended.

src/cli.ts

[error] 1-1: Import statements could be sorted: current order differs from recommended. Suggested reordering.


[error] 1-1: Import statements could be sorted: current order differs from recommended.

tests/fixtures/jsr-json/jsr.json

[error] 1-1: Formatter would have printed the following content: - name: "@test/jsr-package" - version: "2.0.0" - exports: { ".": "./src/index.ts", "./utils": "./src/utils.ts" } - publish: { include: ["src"], exclude: ["src/test"] }

tests/unit/registry/jsr.test.ts

Organize imports: Import statements could be sorted.

🪛 GitHub Check: Coverage: node-20, ubuntu-latest
tests/cli.test.ts

[failure] 26-26: tests/cli.test.ts > CLI Basic Commands > CLI -v alias shows version
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:26:2


[failure] 19-19: tests/cli.test.ts > CLI Basic Commands > CLI -h alias shows help
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:19:2


[failure] 13-13: tests/cli.test.ts > CLI Basic Commands > CLI version command shows version
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:13:2


[failure] 5-5: tests/cli.test.ts > CLI Basic Commands > CLI help command shows usage
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:5:2

🪛 GitHub Check: Test: node-18, ubuntu-latest
tests/cli.test.ts

[failure] 26-26: tests/cli.test.ts > CLI Basic Commands > CLI -v alias shows version
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:26:2


[failure] 19-19: tests/cli.test.ts > CLI Basic Commands > CLI -h alias shows help
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:19:2


[failure] 13-13: tests/cli.test.ts > CLI Basic Commands > CLI version command shows version
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:13:2


[failure] 5-5: tests/cli.test.ts > CLI Basic Commands > CLI help command shows usage
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:5:2

🪛 GitHub Check: Test: node-20, macos-latest
tests/cli.test.ts

[failure] 26-26: tests/cli.test.ts > CLI Basic Commands > CLI -v alias shows version
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:26:2


[failure] 19-19: tests/cli.test.ts > CLI Basic Commands > CLI -h alias shows help
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:19:2


[failure] 13-13: tests/cli.test.ts > CLI Basic Commands > CLI version command shows version
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:13:2


[failure] 5-5: tests/cli.test.ts > CLI Basic Commands > CLI help command shows usage
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:5:2

🪛 GitHub Check: Test: node-20, ubuntu-latest
tests/cli.test.ts

[failure] 26-26: tests/cli.test.ts > CLI Basic Commands > CLI -v alias shows version
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:26:2


[failure] 19-19: tests/cli.test.ts > CLI Basic Commands > CLI -h alias shows help
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:19:2


[failure] 13-13: tests/cli.test.ts > CLI Basic Commands > CLI version command shows version
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:13:2


[failure] 5-5: tests/cli.test.ts > CLI Basic Commands > CLI help command shows usage
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:5:2

🪛 GitHub Check: Test: node-20, windows-latest
tests/cli.test.ts

[failure] 26-26: tests/cli.test.ts > CLI Basic Commands > CLI -v alias shows version
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:26:2


[failure] 19-19: tests/cli.test.ts > CLI Basic Commands > CLI -h alias shows help
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:19:2


[failure] 13-13: tests/cli.test.ts > CLI Basic Commands > CLI version command shows version
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:13:2


[failure] 5-5: tests/cli.test.ts > CLI Basic Commands > CLI help command shows usage
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:5:2

🪛 GitHub Check: Test: node-22, ubuntu-latest
tests/cli.test.ts

[failure] 26-26: tests/cli.test.ts > CLI Basic Commands > CLI -v alias shows version
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:26:2


[failure] 19-19: tests/cli.test.ts > CLI Basic Commands > CLI -h alias shows help
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:19:2


[failure] 13-13: tests/cli.test.ts > CLI Basic Commands > CLI version command shows version
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:13:2


[failure] 5-5: tests/cli.test.ts > CLI Basic Commands > CLI help command shows usage
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:5:2

🔇 Additional comments (48)
tests/unit/db.test.ts (1)

6-75: Excellent test coverage for the Db utility.

The test suite comprehensively covers the Db class functionality including:

  • Basic set/get operations
  • Numeric value handling (correctly expecting string conversion)
  • Null returns for non-existent keys
  • Value overwrites
  • Multiple key operations
  • Special characters and Unicode support

The setup and teardown hooks ensure proper test isolation.

docs/landing.html (4)

1-12: LGTM! Solid HTML foundation.

The document structure, metadata, and font preloading setup are correct. The preconnect hints will improve font loading performance.


797-835: LGTM! Well-structured hero section.

The hero section effectively showcases the product with a simulated terminal and clear value proposition. The markup is semantic and accessible.


837-941: LGTM! Accessible and semantic markup.

The features grid and comparison table use proper semantic HTML. The emoji icons are supplemented with descriptive text, maintaining accessibility.


943-1018: LGTM! Accessible quickstart section.

The step-by-step guide includes proper ARIA labels on copy buttons and uses data attributes for clean JavaScript integration. Well done.

.gitignore (1)

23-24: LGTM!

The additions appropriately ignore coverage reports and the .pubm directory, aligning with the test infrastructure enhancements.

src/tasks/runner.ts (1)

38-38: Current npmOnly logic correctly identifies npm-only registries using exact matching.

The logic at line 38 (registries.every((registry) => registry === 'npm')) correctly checks for exact matches. This is more precise than negation-based approaches, properly excluding any mixed or unknown registry configurations.

tests/unit/package-manager.test.ts (1)

1-81: LGTM! Comprehensive test coverage for package manager detection.

The tests properly cover all lock file scenarios (npm, pnpm, yarn) including the prioritization logic and default fallback. The mock pattern with vi.mock before import and vi.mocked is correct for Vitest.

tests/unit/registry/index.test.ts (1)

25-47: LGTM! Registry resolution tests are well-structured.

The tests correctly verify that getRegistry returns the appropriate registry instance for 'npm', 'jsr', and unknown keys (falling back to custom registry).

tests/unit/rollback.test.ts (1)

4-72: LGTM! Excellent use of module isolation for stateful module testing.

The pattern of using vi.resetModules() with dynamic imports correctly isolates module state between tests, which is essential for testing the rollback module's called flag and rollbacks array. The test cases thoroughly cover:

  • Registration without immediate execution
  • Execution with context passing
  • Idempotency (single execution guarantee)
  • No-op behavior when empty
tests/unit/notify-new-version.test.ts (1)

1-144: LGTM! Thorough test coverage for version notification.

The tests comprehensively cover:

  • No-op when config files are missing
  • npm update notification with version comparison
  • No notification when versions match
  • jsr update notification with API fetch
  • Graceful error handling for both npm and jsr paths

The mock setup correctly isolates external dependencies (tinyexec, fetch, package utilities).

src/options.ts (1)

11-14: LGTM! Correct fix for option merging precedence.

The updated spread order { ...defaultOptions, ...options } correctly ensures user-provided options override defaults. The previous order would have allowed defaults to overwrite explicitly provided options, which is counterintuitive behavior.

tests/unit/registry/custom-registry.test.ts (1)

22-27: Remove the manual registry assignment—the constructor properly sets it from the parameter.

The Registry base class constructor accepts registry as an optional parameter and assigns it directly: public registry?: string. When you instantiate new CustomRegistry('test-package', 'https://custom.registry.com'), the constructor sets this.registry to the passed value, making the subsequent manual assignment line redundant.

package.json (1)

52-58: Vitest 3.x is a major version upgrade with breaking changes.

Upgrading from Vitest ^2.1.1 to ^3.1.2 is a major version bump with several breaking changes including: modified test hook signatures, altered spy.mockReset behavior, test files excluded from coverage by default, and browser mode configuration changes. Ensure all tests pass locally and review the Vitest 3.0 migration guide for any changes that might affect your test suite.

tests/unit/listr.test.ts (4)

5-10: LGTM!

The test correctly verifies basic Listr instance creation and the presence of the run method.


12-16: LGTM!

The test correctly verifies that createListr overrides isRoot to always return false, matching the implementation in src/utils/listr.ts.


18-31: LGTM!

The test correctly verifies that createListr accepts task definitions with titles and task functions.


33-48: LGTM!

The test correctly verifies that createListr accepts both tasks and Listr options, matching the implementation's use of spread arguments.

vitest.config.mts (1)

10-16: LGTM!

The coverage configuration updates appropriately focus coverage collection on source TypeScript files while excluding type definitions, the CLI entry point, task implementations, and node_modules. This aligns well with the PR's goal of adding integrated test coverage.

tests/unit/registry/registry.test.ts (2)

32-48: LGTM!

The constructor tests correctly verify both single-argument (package name only) and two-argument (package name with registry URL) initialization patterns.


50-61: LGTM!

The test comprehensively verifies that all abstract methods are implemented and callable, providing good coverage of the Registry interface contract.

tests/unit/utils-cli.test.ts (1)

4-33: LGTM!

The test suite provides good coverage of the CLI utility functions. The tests correctly verify the warningBadge format and the link function's OSC 8 escape sequence generation, including edge cases for empty inputs.

tests/unit/options.test.ts (1)

1-135: LGTM!

Excellent test coverage for the options module. The test suite thoroughly validates default values, option merging behavior, overrides, partial options, feature flags, and various registry configurations. The tests are well-structured and cover both common use cases and edge cases.

tests/unit/engine-version.test.ts (1)

12-84: LGTM!

The test suite provides comprehensive coverage of the validateEngineVersion function, including satisfied/unsatisfied requirements, prerelease version handling, multiple engine types, and missing engine definitions. The mock setup and cleanup are properly implemented.

tests/unit/error.test.ts (2)

4-33: LGTM!

The AbstractError test suite effectively validates error construction, cause handling, inheritance chain, and stack trace generation.


35-115: LGTM!

The consoleError test suite is comprehensive and well-structured. It covers string and Error instance logging, formatting behavior, nested cause chains, and edge cases with proper use of console.error spies. The @ts-ignore directives are appropriately used to test runtime edge cases.

tests/unit/package-name.test.ts (1)

1-134: LGTM!

Exceptional test coverage for package name utilities. The test suite is well-organized and thoroughly validates:

  • Scoped package detection and parsing
  • Comprehensive package name validation including npm naming rules
  • Boundary conditions (214 character limit)
  • Blacklisted and builtin module names
  • Special characters and formatting rules

The nested describe blocks provide excellent test organization and readability.

tests/unit/registry/npm.test.ts (5)

19-34: LGTM!

Test setup is well-structured with proper mock clearing in beforeEach and restoration in afterEach. The registry instantiation and global fetch mocking pattern is appropriate.


36-122: LGTM!

Tests for isInstalled, isPublished, userName, and isLoggedIn provide good coverage of success, failure, and edge case paths (like ENEEDAUTH handling).


124-218: LGTM!

Tests for collaborators, hasPermission, distTags, version, and ping are well-structured. The chained mockResolvedValueOnce calls for hasPermission correctly simulate the sequential userName() then collaborators() calls.


220-311: LGTM!

Good coverage for publish, publishProvenance, isPackageNameAvaliable, and installGlobally. The OTP parameter verification (Lines 249-253) and the invalid package name test with capital letters correctly exercise the implementation.


313-369: LGTM!

Error handling tests comprehensively cover failure paths for multiple methods. The npmRegistry factory test correctly verifies instance creation from mocked package metadata.

src/cli.ts (2)

129-143: LGTM!

The contents path resolution using path.resolve(process.cwd(), options.contents) correctly canonicalizes the path to an absolute path. This ensures consistent behavior regardless of whether a relative or absolute path is provided.


159-206: LGTM!

The refactored flow is cleaner:

  1. Options resolved once upfront
  2. chdir to contents directory if specified
  3. notifyNewVersion called after directory change (correct context)
  4. Single resolvedOptions object used consistently throughout

This eliminates redundant option resolution and ensures all subsequent operations use the canonicalized paths.

tests/unit/index.test.ts (1)

22-86: LGTM!

Unit tests appropriately verify pubm's delegation behavior - calling resolveOptions with provided options and invoking run. The test cases cover various option combinations (registries, skip flags, preview mode).

tests/unit/git.test.ts (4)

1-33: LGTM!

Test setup and version test are well-structured. The mock pattern for tinyexec is consistent with other test files.


35-193: LGTM!

Tests for branch, status, userName, latestTag, tags, latestCommit, firstCommit, and repository provide comprehensive coverage. The latestTag null case and tags version prefix stripping are correctly tested.


195-406: LGTM!

Tests for staging, commit, tag, and branch operations are thorough. The chained mock pattern for commit (Lines 259-269) correctly simulates the two-step process of committing and retrieving the hash.


533-867: LGTM!

Excellent coverage of revisionDiffsCount, dryFetch, push (including GH006 protected branch handling), and comprehensive error handling tests. The push tests correctly verify that GH006 errors return false rather than throwing, which aligns with protected branch workflow handling.

tests/unit/registry/jsr.test.ts (3)

26-165: LGTM!

JsrRegisry tests are comprehensive, covering initialization, CLI availability checks, publishing, and permission verification. The distTags test correctly expects an empty array since JSR doesn't support this feature.


167-381: LGTM!

JsrClient tests thoroughly cover API interactions including success paths, 401 unauthorized handling (returning null/empty), and network failure error propagation.


384-397: LGTM!

Factory test correctly verifies jsrRegistry creates a JsrRegisry instance using the package name from getJsrJson.

tests/unit/package.test.ts (4)

1-9: LGTM!

Imports are properly structured with node:path followed by vitest imports and then local imports.


11-178: LGTM!

Conversion tests for jsrJsonToPackageJson and packageJsonToJsrJson comprehensively cover:

  • Simple string exports
  • Object/nested exports transformation
  • Publish include/exclude mapping to files array (and vice versa)
  • Edge cases (missing exports, missing files/publish)

180-221: LGTM!

Tests for findOutFile, patchCachedJsrJson, and version provide basic coverage. The findOutFile tests exercise real filesystem traversal behavior.


223-382: LGTM!

Mocked tests for getPackageJson, getJsrJson, and replaceVersion are well-structured:

  • Uses vi.doMock with vi.resetModules for fresh module state
  • Unique cwd paths prevent cache interference between tests
  • Covers success, invalid JSON, fallback disabled, and no-files scenarios
tests/utils/cli.ts (2)

120-163: Note: Return values are captured at spawn time.

The exitCode, stdout, and stderr in the return object (Lines 158-160) capture values at subprocess spawn time. Since the process runs asynchronously:

  • exitCode will typically be undefined initially
  • stdout/stderr will be empty strings initially

Callers should use waitForClose() or controller.waitForStdout()/controller.waitForStderr() for final values. The controller.stdout and controller.stderr properties update in real-time via listeners.

This design is appropriate for interactive CLI testing where you need to write input and observe output incrementally.


165-167: LGTM!

The exported ANSI escape sequence constants (DOWN, UP, ENTER) are useful for simulating keyboard input in interactive CLI tests.

<a href="#features">Features</a>
<a href="#comparison">Compare</a>
<a href="#quickstart">Quick Start</a>
<a href="https://github.com/syi0808/pubm" target="_blank" rel="noopener" class="nav-cta">GitHub</a>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add noreferrer to external links for security.

External links use rel="noopener" but are missing rel="noreferrer". Adding noreferrer prevents referrer information leakage when users navigate to external sites.

🔎 Suggested fix

Update all external links to include both noopener and noreferrer:

-        <a href="https://github.com/syi0808/pubm" target="_blank" rel="noopener" class="nav-cta">GitHub</a>
+        <a href="https://github.com/syi0808/pubm" target="_blank" rel="noopener noreferrer" class="nav-cta">GitHub</a>

Apply the same pattern to lines 1024, 1027, and 1040-1043.

Also applies to: 1024-1027, 1040-1043

🤖 Prompt for AI Agents
In docs/landing.html around line 792 (and also apply to lines 1024-1027 and
1040-1043), external anchor tags currently use rel="noopener" only; update those
anchor tags to include rel="noopener noreferrer" (preserving target and other
attributes) so the links retain the noopener protection while also preventing
referrer header leakage when users follow external links.

Comment on lines +1067 to +1081
// Typing effect for terminal
const terminalCmd = document.querySelector('.terminal-cmd');
const originalText = terminalCmd.textContent;
terminalCmd.textContent = '';

let i = 0;
const typeWriter = () => {
if (i < originalText.length) {
terminalCmd.textContent += originalText.charAt(i);
i++;
setTimeout(typeWriter, 50);
}
};

setTimeout(typeWriter, 500);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add null check for terminal typing effect.

If the .terminal-cmd element is not found, lines 1069-1070 will throw a runtime error when trying to access textContent on null.

🔎 Suggested fix
 // Typing effect for terminal
 const terminalCmd = document.querySelector('.terminal-cmd');
+if (terminalCmd) {
   const originalText = terminalCmd.textContent;
   terminalCmd.textContent = '';
 
   let i = 0;
   const typeWriter = () => {
     if (i < originalText.length) {
       terminalCmd.textContent += originalText.charAt(i);
       i++;
       setTimeout(typeWriter, 50);
     }
   };
 
   setTimeout(typeWriter, 500);
+}
📝 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
// Typing effect for terminal
const terminalCmd = document.querySelector('.terminal-cmd');
const originalText = terminalCmd.textContent;
terminalCmd.textContent = '';
let i = 0;
const typeWriter = () => {
if (i < originalText.length) {
terminalCmd.textContent += originalText.charAt(i);
i++;
setTimeout(typeWriter, 50);
}
};
setTimeout(typeWriter, 500);
// Typing effect for terminal
const terminalCmd = document.querySelector('.terminal-cmd');
if (terminalCmd) {
const originalText = terminalCmd.textContent;
terminalCmd.textContent = '';
let i = 0;
const typeWriter = () => {
if (i < originalText.length) {
terminalCmd.textContent += originalText.charAt(i);
i++;
setTimeout(typeWriter, 50);
}
};
setTimeout(typeWriter, 500);
}
🤖 Prompt for AI Agents
In docs/landing.html around lines 1067 to 1081, the code assumes
document.querySelector('.terminal-cmd') always returns an element and
immediately reads .textContent, which will throw if the element is missing;
update the code to first check that terminalCmd is not null and only proceed to
read originalText, clear textContent, and start the typeWriter; if terminalCmd
is null, simply skip initializing the typing effect (or return early) to avoid
runtime errors.

Comment on lines +1088 to +1106
copyButtons.forEach(btn => {
btn.addEventListener('click', async () => {
const codeBlock = btn.parentElement.querySelector('.step-code');
const textToCopy = codeBlock.getAttribute('data-copy');

try {
await navigator.clipboard.writeText(textToCopy);
btn.innerHTML = checkIcon;
btn.classList.add('copied');

setTimeout(() => {
btn.innerHTML = copyIcon;
btn.classList.remove('copied');
}, 2000);
} catch (err) {
console.error('Failed to copy:', err);
}
});
});
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add null check in copy functionality.

The code assumes .step-code exists, but if the DOM structure changes or elements are missing, line 1091 will throw an error when accessing getAttribute on null.

🔎 Suggested fix
     copyButtons.forEach(btn => {
       btn.addEventListener('click', async () => {
         const codeBlock = btn.parentElement.querySelector('.step-code');
+        if (!codeBlock) return;
+        
         const textToCopy = codeBlock.getAttribute('data-copy');
 
         try {
           await navigator.clipboard.writeText(textToCopy);
           btn.innerHTML = checkIcon;
           btn.classList.add('copied');
 
           setTimeout(() => {
             btn.innerHTML = copyIcon;
             btn.classList.remove('copied');
           }, 2000);
         } catch (err) {
           console.error('Failed to copy:', err);
         }
       });
     });
📝 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
copyButtons.forEach(btn => {
btn.addEventListener('click', async () => {
const codeBlock = btn.parentElement.querySelector('.step-code');
const textToCopy = codeBlock.getAttribute('data-copy');
try {
await navigator.clipboard.writeText(textToCopy);
btn.innerHTML = checkIcon;
btn.classList.add('copied');
setTimeout(() => {
btn.innerHTML = copyIcon;
btn.classList.remove('copied');
}, 2000);
} catch (err) {
console.error('Failed to copy:', err);
}
});
});
copyButtons.forEach(btn => {
btn.addEventListener('click', async () => {
const codeBlock = btn.parentElement.querySelector('.step-code');
if (!codeBlock) return;
const textToCopy = codeBlock.getAttribute('data-copy');
try {
await navigator.clipboard.writeText(textToCopy);
btn.innerHTML = checkIcon;
btn.classList.add('copied');
setTimeout(() => {
btn.innerHTML = copyIcon;
btn.classList.remove('copied');
}, 2000);
} catch (err) {
console.error('Failed to copy:', err);
}
});
});
🤖 Prompt for AI Agents
In docs/landing.html around lines 1088 to 1106, the copy button handler assumes
btn.parentElement.querySelector('.step-code') always returns an element and
calls getAttribute on it; add a null check for codeBlock before using it and
abort/return early if missing (optionally disable the button or show a console
warning), and if you want a safe fallback use codeBlock.textContent or innerText
when data-copy attribute is absent; ensure no further operations run when
codeBlock is null so the click handler doesn't throw.

Comment on lines +31 to +32
"test": "vitest",
"coverage": "vitest --coverage",
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check for CI workflow files and how they invoke tests
find .github/workflows -type f \( -name "*.yml" -o -name "*.yaml" \) 2>/dev/null | while read file; do
  echo "=== File: $file ==="
  cat "$file"
  echo ""
done

Repository: syi0808/pubm

Length of output: 3679


Update CI configuration: test scripts now require --run flag to prevent watch mode in CI.

Removing --run from the test scripts changes the default behavior to watch mode. The CI pipeline invokes pnpm run test and pnpm run coverage without the --run flag, which will cause tests to hang waiting for interactive input. Either update the CI workflow to pass --run explicitly:

- name: 'Test'
  run: pnpm run test -- --run

- name: 'Test'
  run: pnpm run coverage -- --run

Or revert the scripts in package.json to include --run by default.

🤖 Prompt for AI Agents
In package.json around lines 31-32, the "test" and "coverage" scripts currently
call vitest without the "--run" flag which causes watch mode in CI; fix by
either updating these scripts to include "--run" (e.g., change "test": "vitest"
to "test": "vitest --run" and "coverage": "vitest --coverage --run") or, if you
prefer to keep scripts generic, update the CI workflow to invoke the scripts
with the extra flag (e.g., use "pnpm run test -- --run" and "pnpm run coverage
-- --run"); pick one approach and apply it consistently so CI runs
non-interactively.

Comment on lines +5 to +30
test('CLI help command shows usage', async () => {
const { controller } = runPubmCli('--help');

await controller.waitForStdout('pubm');
await controller.waitForStdout('Usage:');
await controller.waitForStdout('Options:');
});

test('CLI version command shows version', async () => {
const { controller } = runPubmCli('--version');

await controller.waitForStdout('pubm');
});

test('CLI -h alias shows help', async () => {
const { controller } = runPubmCli('-h');

await controller.waitForStdout('pubm');
await controller.waitForStdout('Usage:');
});

test('CLI -v alias shows version', async () => {
const { controller } = runPubmCli('-v');

await controller.waitForStdout('pubm');
});
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Examine the test file and utility structure
find tests -name "*.ts" -o -name "*.js" | head -20

Repository: syi0808/pubm

Length of output: 641


🏁 Script executed:

# Check the test utilities directory
ls -la tests/utils/ 2>/dev/null || echo "utils directory not found, checking alternative paths"

Repository: syi0808/pubm

Length of output: 222


🏁 Script executed:

# Search for the runPubmCli function definition
rg -n "runPubmCli|waitForStdout" tests/ --type ts --type js -B 2 -A 5

Repository: syi0808/pubm

Length of output: 2332


🏁 Script executed:

# Check test configuration and timeout settings
fd -e "config" -e "json" tests/ 
cat package.json | grep -A 10 -B 2 "jest\|vitest\|test"

Repository: syi0808/pubm

Length of output: 1228


🏁 Script executed:

cat -n tests/utils/cli.ts

Repository: syi0808/pubm

Length of output: 5322


🏁 Script executed:

# Also check if there's a vitest config file that might set timeout
fd . tests -name "*.config.*" -o -name "vitest.config.*" -o -name "vitest.json"

Repository: syi0808/pubm

Length of output: 286


🏁 Script executed:

# Check the actual CLI entry point to understand what gets executed
fd . src -type f -name "*.ts" | head -10

Repository: syi0808/pubm

Length of output: 226


🏁 Script executed:

# Find the CLI entry point
find . -path "./bin/*" -type f -name "*.js" -o -name "cli.js" | grep -E "bin|cli"

Repository: syi0808/pubm

Length of output: 38


🏁 Script executed:

# Check vitest configuration in package.json or config files
cat package.json | grep -A 50 "vitest\|test"

Repository: syi0808/pubm

Length of output: 1254


🏁 Script executed:

# Look for vitest config
fd . -name "vitest.config.*" -o -name "vitest.*" 2>/dev/null

Repository: syi0808/pubm

Length of output: 38


🏁 Script executed:

# Check if bin/cli.js exists
ls -la bin/ 2>/dev/null || echo "bin directory doesn't exist or is inaccessible"

Repository: syi0808/pubm

Length of output: 102


🏁 Script executed:

# Get the actual structure of the repo root
ls -la | head -30

Repository: syi0808/pubm

Length of output: 1308


🏁 Script executed:

cat vitest.config.mts

Repository: syi0808/pubm

Length of output: 473


🏁 Script executed:

cat tsup.config.ts

Repository: syi0808/pubm

Length of output: 598


🏁 Script executed:

# Check the src directory structure
find src -type f | head -20

Repository: syi0808/pubm

Length of output: 515


🏁 Script executed:

# Look for CLI entry point
rg -n "export.*main\|bin\|#!/usr" src/ --type ts

Repository: syi0808/pubm

Length of output: 38


🏁 Script executed:

cat src/cli.ts

Repository: syi0808/pubm

Length of output: 5560


🏁 Script executed:

# Check what happens on import - any top-level errors?
cat src/index.ts | head -30

Repository: syi0808/pubm

Length of output: 665


🏁 Script executed:

# Look at package.json build scripts
grep -A 5 "build\|dist\|bin" package.json

Repository: syi0808/pubm

Length of output: 544


🏁 Script executed:

# Check GitHub Actions workflow
find .github -type f -name "*.yml" -o -name "*.yaml"

Repository: syi0808/pubm

Length of output: 111


🏁 Script executed:

# Look for any test setup files
find tests -name "setup*" -o -name "vitest.*"

Repository: syi0808/pubm

Length of output: 38


🏁 Script executed:

# Check if tinyexec is imported correctly
rg -n "tinyexec" src/ tests/ --type ts

Repository: syi0808/pubm

Length of output: 1140


🏁 Script executed:

# Verify the import.meta.dirname usage
rg -n "import.meta" src/cli.ts tests/utils/cli.ts --type ts -A 2 -B 2

Repository: syi0808/pubm

Length of output: 513


🏁 Script executed:

cat .github/workflows/ci.yaml

Repository: syi0808/pubm

Length of output: 2765


🏁 Script executed:

# Also check the release workflow
cat .github/workflows/release.yml

Repository: syi0808/pubm

Length of output: 884


🏁 Script executed:

# Check if there's a build script run before tests
grep -n "build\|test" package.json

Repository: syi0808/pubm

Length of output: 229


Critical: CLI tests are timing out because bin/cli.js is not built before running tests.

The test utility attempts to spawn the CLI from bin/cli.js (line 139 of tests/utils/cli.ts), which is generated by tsup during the build step. The CI workflow (.github/workflows/ci.yaml) runs pnpm run test directly after pnpm install --frozen-lockfile without running pnpm run build first, causing the process spawn to fail. The controller.waitForStdout() method then times out after 4000ms (or 20000ms in CI environments) waiting for output from a process that was never started.

The tests cannot pass until a build step is added to the test job in .github/workflows/ci.yaml before running pnpm run test.

🧰 Tools
🪛 GitHub Actions: CI

[error] 5-5: CLI help command shows usage timed out after 5000ms.


[error] 13-13: CLI version command shows version timed out after 5000ms.


[error] 19-19: CLI -h alias shows help timed out after 5000ms.


[error] 26-26: CLI -v alias shows version timed out after 5000ms.

🪛 GitHub Check: Coverage: node-20, ubuntu-latest

[failure] 26-26: tests/cli.test.ts > CLI Basic Commands > CLI -v alias shows version
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:26:2


[failure] 19-19: tests/cli.test.ts > CLI Basic Commands > CLI -h alias shows help
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:19:2


[failure] 13-13: tests/cli.test.ts > CLI Basic Commands > CLI version command shows version
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:13:2


[failure] 5-5: tests/cli.test.ts > CLI Basic Commands > CLI help command shows usage
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:5:2

🪛 GitHub Check: Test: node-18, ubuntu-latest

[failure] 26-26: tests/cli.test.ts > CLI Basic Commands > CLI -v alias shows version
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:26:2


[failure] 19-19: tests/cli.test.ts > CLI Basic Commands > CLI -h alias shows help
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:19:2


[failure] 13-13: tests/cli.test.ts > CLI Basic Commands > CLI version command shows version
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:13:2


[failure] 5-5: tests/cli.test.ts > CLI Basic Commands > CLI help command shows usage
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:5:2

🪛 GitHub Check: Test: node-20, macos-latest

[failure] 26-26: tests/cli.test.ts > CLI Basic Commands > CLI -v alias shows version
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:26:2


[failure] 19-19: tests/cli.test.ts > CLI Basic Commands > CLI -h alias shows help
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:19:2


[failure] 13-13: tests/cli.test.ts > CLI Basic Commands > CLI version command shows version
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:13:2


[failure] 5-5: tests/cli.test.ts > CLI Basic Commands > CLI help command shows usage
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:5:2

🪛 GitHub Check: Test: node-20, ubuntu-latest

[failure] 26-26: tests/cli.test.ts > CLI Basic Commands > CLI -v alias shows version
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:26:2


[failure] 19-19: tests/cli.test.ts > CLI Basic Commands > CLI -h alias shows help
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:19:2


[failure] 13-13: tests/cli.test.ts > CLI Basic Commands > CLI version command shows version
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:13:2


[failure] 5-5: tests/cli.test.ts > CLI Basic Commands > CLI help command shows usage
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:5:2

🪛 GitHub Check: Test: node-20, windows-latest

[failure] 26-26: tests/cli.test.ts > CLI Basic Commands > CLI -v alias shows version
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:26:2


[failure] 19-19: tests/cli.test.ts > CLI Basic Commands > CLI -h alias shows help
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:19:2


[failure] 13-13: tests/cli.test.ts > CLI Basic Commands > CLI version command shows version
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:13:2


[failure] 5-5: tests/cli.test.ts > CLI Basic Commands > CLI help command shows usage
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:5:2

🪛 GitHub Check: Test: node-22, ubuntu-latest

[failure] 26-26: tests/cli.test.ts > CLI Basic Commands > CLI -v alias shows version
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:26:2


[failure] 19-19: tests/cli.test.ts > CLI Basic Commands > CLI -h alias shows help
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:19:2


[failure] 13-13: tests/cli.test.ts > CLI Basic Commands > CLI version command shows version
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:13:2


[failure] 5-5: tests/cli.test.ts > CLI Basic Commands > CLI help command shows usage
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
❯ tests/cli.test.ts:5:2

} as never);

// Access protected method through type casting
await (registry as any).npm(['view', 'package']);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix lint error: Avoid any type when accessing protected method.

The pipeline flagged lint/suspicious/noExplicitAny. Consider creating a type-safe way to test the protected method.

🔎 Suggested fix using a narrower type
-			// Access protected method through type casting
-			await (registry as any).npm(['view', 'package']);
+			// Access protected method through type casting
+			await (registry as unknown as { npm: (args: string[]) => Promise<string> }).npm(['view', 'package']);

Alternatively, you could create a test subclass that exposes the method:

class TestableCustomRegistry extends CustomRegistry {
  public async testNpm(args: string[]): Promise<string> {
    return this.npm(args);
  }
}
📝 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
await (registry as any).npm(['view', 'package']);
await (registry as unknown as { npm: (args: string[]) => Promise<string> }).npm(['view', 'package']);
🧰 Tools
🪛 GitHub Actions: CI

[error] 43-43: lint/suspicious/noExplicitAny - Unexpected any. Specify a different type.

🤖 Prompt for AI Agents
In tests/unit/registry/custom-registry.test.ts around line 43, the test
currently uses (registry as any).npm([...]) which triggers
lint/suspicious/noExplicitAny; replace that with a type-safe approach by
creating a small TestableCustomRegistry subclass in the test that exposes the
protected npm method as a public wrapper (e.g., public async testNpm(args:
string[]): Promise<string> { return this.npm(args); }), instantiate that
subclass with the same constructor args used for CustomRegistry, and call
testNpm(['view','package']) in place of the any cast so the test remains
equivalent but avoids using any.

exitCode: 1,
} as never);

await expect((registry as any).npm(['test'])).rejects.toBe('error');
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix lint error: Same any type issue here.

Apply the same fix pattern as suggested above.

🔎 Suggested fix
-			await expect((registry as any).npm(['test'])).rejects.toBe('error');
+			await expect((registry as unknown as { npm: (args: string[]) => Promise<string> }).npm(['test'])).rejects.toBe('error');
🧰 Tools
🪛 GitHub Actions: CI

[error] 60-60: lint/suspicious/noExplicitAny - Unexpected any. Specify a different type.

🤖 Prompt for AI Agents
In tests/unit/registry/custom-registry.test.ts around line 60, the test uses an
unsafe cast "(registry as any)" causing the lint error; replace the any cast
with a proper typed cast or interface for registry (e.g., cast to the registry
type or to "unknown as { npm: (args: string[]) => Promise<unknown> }") and call
.npm with that typed reference so the expect(...).rejects assertion stays
unchanged.

@@ -0,0 +1,47 @@
import { describe, expect, test, vi, beforeEach } from 'vitest';
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Address pipeline failure: Import statements need sorting.

The CI pipeline flagged that import statements could be sorted. Run the formatter to fix the import order.

🧰 Tools
🪛 GitHub Actions: CI

Organize imports: Import statements could be sorted.

🤖 Prompt for AI Agents
In tests/unit/registry/index.test.ts around line 1, the import statements are
not in the expected sorted order and the CI pipeline is failing; reorder the
imports to match the project's import/formatter rules (e.g., alphabetically and
grouped by external vs local imports) and then run the repository formatter (npm
run format or the configured prettier/eslint --fix) to apply the correct
ordering and update the file.

@@ -0,0 +1,72 @@
import { describe, expect, test, vi, beforeEach, afterEach } from 'vitest';
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Address pipeline failure: Import statements need sorting.

The CI pipeline flagged that import statements could be sorted. Run the formatter to fix the import order.

🧰 Tools
🪛 GitHub Actions: CI

[error] 1-1: Import statements could be sorted: current order differs from recommended.

🤖 Prompt for AI Agents
In tests/unit/rollback.test.ts at line 1, the import statements are not sorted
which breaks the CI pipeline; run your project's formatter or sort imports so
they follow the configured ordering (e.g., alphabetical/grouped per lint/format
rules), then save the file and re-run tests to ensure the import order warning
is resolved.

Comment on lines +1 to +2
import { describe, expect, test } from 'vitest';
import { warningBadge, link } from '../../src/utils/cli';
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix import statement order.

The pipeline indicates that import statements should be sorted. Please run your linter/formatter to automatically organize the imports according to the project's conventions.

🧰 Tools
🪛 GitHub Actions: CI

[error] 1-1: Import statements could be sorted: current order differs from recommended.

🤖 Prompt for AI Agents
In tests/unit/utils-cli.test.ts lines 1-2 the import declarations are out of the
project's expected sort/order; run the project's linter/formatter (e.g., eslint
--fix or prettier, or your repo's configured import-sorting tool) or manually
reorder the imports to match the project's import sorting rules so they are
properly grouped and alphabetized (then save the file and re-run tests).

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant