Skip to content

feat(plugin-dryrun): implement dry-run extension#568

Merged
kazupon merged 1 commit into
mainfrom
feat/plugin-dryrun
Jun 6, 2026
Merged

feat(plugin-dryrun): implement dry-run extension#568
kazupon merged 1 commit into
mainfrom
feat/plugin-dryrun

Conversation

@kazupon

@kazupon kazupon commented Jun 6, 2026

Copy link
Copy Markdown
Owner

Description

Linked Issues

Additional context

Summary by CodeRabbit

  • New Features

    • Added @gunshi/plugin-dryrun plugin providing dry-run support. Enables selective skipping of wrapped side-effect operations via --dry-run flag while allowing the rest of the command to execute normally.
  • Documentation

    • Added comprehensive plugin documentation including usage guide, installation instructions, and API reference.
    • Added plugin to official plugins list.

@coderabbitai

coderabbitai Bot commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR implements a complete dry-run plugin for the Gunshi CLI framework. It enables commands to selectively skip side-effectful operations via a --dry-run option by wrapping them with context extension methods (run() and wrap()). The feature includes full TypeScript type safety, comprehensive tests, auto-generated API documentation, and user guides.

Changes

Dry-Run Plugin Feature

Layer / File(s) Summary
Plugin contract and type exports
packages/plugin-dryrun/src/index.ts (lines 1–151)
Exports pluginId and DryRunExtension interface with enabled flag, run<R>() and wrap<A, R>() methods for side-effect handling, plus DryRunPluginOptions, DryRunMessage, and conditional tuple types enforcing fallback options for non-void operations.
Plugin implementation and helpers
packages/plugin-dryrun/src/index.ts (lines 159–281)
dryrun() factory function configures the global --dry-run option (with customizable name/description/prefix), implements extension.enabled tracking, provides run() and wrap() logic to skip/log operations or return configured fallbacks, integrates optional i18n plugin, and includes description resolution helpers.
Type safety and runtime tests
packages/plugin-dryrun/src/index.test-d.ts, packages/plugin-dryrun/src/index.test.ts
Type tests validate run and wrap require fallback options for non-void returns; runtime tests verify global option registration, i18n resource setup, dry-run mode behavior (skipping void tasks, returning fallback results), fallback resolution patterns, and logging with custom prefixes.
Documentation generation tooling
packages/plugin-dryrun/typedoc.config.mjs, packages/plugin-dryrun/package.json (scripts & devDependencies)
TypeDoc configuration with Markdown plugin for auto-generating API documentation from TypeScript definitions; build:docs script and typedoc dependencies added to package metadata.
Auto-generated API documentation
packages/plugin-dryrun/docs/ (index, functions, interfaces, type aliases, variables)
Markdown pages documenting the plugin entry point, factory function signature, exported interfaces (DryRunExtension, DryRunMessage, DryRunPluginOptions), type aliases (result/fallback shapes, option tuples), and plugin identifier variable.
User guides and plugin README
packages/plugin-dryrun/README.md, packages/docs/src/guide/essentials/plugin-system.md, packages/docs/src/guide/plugin/list.md, packages/docs/src/guide/plugin/introduction.md
Comprehensive README with installation instructions, usage examples showing ctx.extensions['g:dryrun'].run() wrapping, explanation of selective skipping, configuration options, i18n integration, and API reference links; guide documentation lists the plugin and provides a practical example of wrapping a publish command.
Build scripts and package configuration
package.json, packages/plugin-dryrun/package.json, packages/plugin-dryrun/jsr.json
Root monorepo scripts added for building and testing the plugin; package metadata updated (spelling dry-run, keywords, doc build scripts, TypeDoc dependencies); JSR config description synchronized.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

feature

Poem

🐰 A dry-run plugin hops into view,
Side effects wrapped, they skip on cue,
With types so tight and tests so true,
Docs auto-bloom from code's debut,
Now users run commands without the "boo"!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(plugin-dryrun): implement dry-run extension' clearly and directly summarizes the main change: implementing the dry-run extension for the plugin-dryrun package across the entire changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/plugin-dryrun

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.

@kazupon kazupon added the feature Includes new features label Jun 6, 2026
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying gunshi with  Cloudflare Pages  Cloudflare Pages

Latest commit: 7e1b8fe
Status: ✅  Deploy successful!
Preview URL: https://c179c9e1.gunshi.pages.dev
Branch Preview URL: https://feat-plugin-dryrun.gunshi.pages.dev

View logs

@pkg-pr-new

pkg-pr-new Bot commented Jun 6, 2026

Copy link
Copy Markdown

Open in StackBlitz

@gunshi/bone

npm i https://pkg.pr.new/@gunshi/bone@568

@gunshi/combinators

npm i https://pkg.pr.new/@gunshi/combinators@568

@gunshi/definition

npm i https://pkg.pr.new/@gunshi/definition@568

@gunshi/docs

npm i https://pkg.pr.new/@gunshi/docs@568

gunshi

npm i https://pkg.pr.new/gunshi@568

@gunshi/plugin

npm i https://pkg.pr.new/@gunshi/plugin@568

@gunshi/plugin-completion

npm i https://pkg.pr.new/@gunshi/plugin-completion@568

@gunshi/plugin-dryrun

npm i https://pkg.pr.new/@gunshi/plugin-dryrun@568

@gunshi/plugin-global

npm i https://pkg.pr.new/@gunshi/plugin-global@568

@gunshi/plugin-i18n

npm i https://pkg.pr.new/@gunshi/plugin-i18n@568

@gunshi/plugin-renderer

npm i https://pkg.pr.new/@gunshi/plugin-renderer@568

@gunshi/resources

npm i https://pkg.pr.new/@gunshi/resources@568

@gunshi/shared

npm i https://pkg.pr.new/@gunshi/shared@568

commit: 7e1b8fe

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
packages/plugin-dryrun/src/index.ts (3)

195-200: 💤 Low value

Document the precedence of resolve over result.

While the type definition (DryRunRunResult) is a union ensuring only one field exists, at runtime an object could contain both resolve and result. The code prioritizes resolve (line 195-196) over result (line 198-199), but this precedence is not documented in the JSDoc or comments.

Consider adding a brief comment explaining this behavior, or asserting mutual exclusivity if stricter validation is desired.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/plugin-dryrun/src/index.ts` around lines 195 - 200, Document that
when both fields are present at runtime the function will prefer the resolve
path: add a brief JSDoc or inline comment in packages/plugin-dryrun/src/index.ts
near the branch handling options (the block checking 'resolve' in options and
'result' in options) stating that resolve takes precedence over result, and
optionally add a runtime assertion or validation (e.g., in the same function
that handles DryRunRunResult) to enforce mutual exclusivity if stricter behavior
is desired; reference the options object and the DryRunRunResult union in the
comment so readers know which symbols/types the precedence applies to.

204-218: ⚖️ Poor tradeoff

Same defensive consideration applies to resolveWrapResult.

Like resolveRunResult, this function returns undefined as R when no fallback is provided (lines 209, 217). The same type-safety considerations and defensive programming suggestions apply here.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/plugin-dryrun/src/index.ts` around lines 204 - 218, The
resolveWrapResult function currently returns undefined cast to R when no options
or no fallback is provided, which is unsafe; update resolveWrapResult (and its
signature if needed) to mirror the defensive fix used for resolveRunResult: when
options is missing or neither resolve nor result are present, either return a
well-typed explicit fallback (e.g., a provided default value or
Promise.resolve(default)) or throw a clear error; ensure you reference and
adjust the DryRunMessage/DryRunWrapResult types if necessary so the return type
no longer relies on casting undefined to R and callers cannot receive an
unexpected undefined.

189-202: ⚖️ Poor tradeoff

Consider defensive guard for non-void fallback resolution.

The function returns undefined as R when no fallback is provided (lines 193, 201). While the public API type DryRunOptionsArg enforces that non-void operations must provide result or resolve, the internal parameter type Partial<DryRunRunResult<R>> allows both fields to be absent.

If a caller bypasses type checking (e.g., via as any), or if there's a generic instantiation edge case, a non-void task could receive undefined instead of a proper value, breaking type safety at runtime.

🛡️ Consider adding a runtime guard
 function resolveRunResult<R>(
   options?: DryRunMessage & Partial<DryRunRunResult<R>>
 ): Awaitable<R> {
   if (!options) {
     return undefined as R
   }
   if ('resolve' in options && options.resolve) {
     return options.resolve()
   }
   if ('result' in options) {
     return options.result as R
   }
+  // This path should be unreachable for non-void R due to DryRunOptionsArg,
+  // but provides a runtime safeguard if type constraints are bypassed
   return undefined as R
 }

Note: This is a defense-in-depth measure; the current implementation is type-safe when used correctly through the public API.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/plugin-dryrun/src/index.ts` around lines 189 - 202, The
resolveRunResult function currently returns undefined as R in two fall-through
cases which can leak undefined at runtime; change those fall-through returns to
throw a clear runtime error (e.g., "DryRun: missing result/resolve for non-void
run") so callers cannot receive undefined when no result/resolve was provided;
update the function resolveRunResult<R>(options?: DryRunMessage &
Partial<DryRunRunResult<R>>) to throw when options is falsy or when neither
'resolve' nor 'result' is present, leaving the existing branches for
options.resolve() and options.result intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/plugin-dryrun/src/index.ts`:
- Around line 195-200: Document that when both fields are present at runtime the
function will prefer the resolve path: add a brief JSDoc or inline comment in
packages/plugin-dryrun/src/index.ts near the branch handling options (the block
checking 'resolve' in options and 'result' in options) stating that resolve
takes precedence over result, and optionally add a runtime assertion or
validation (e.g., in the same function that handles DryRunRunResult) to enforce
mutual exclusivity if stricter behavior is desired; reference the options object
and the DryRunRunResult union in the comment so readers know which symbols/types
the precedence applies to.
- Around line 204-218: The resolveWrapResult function currently returns
undefined cast to R when no options or no fallback is provided, which is unsafe;
update resolveWrapResult (and its signature if needed) to mirror the defensive
fix used for resolveRunResult: when options is missing or neither resolve nor
result are present, either return a well-typed explicit fallback (e.g., a
provided default value or Promise.resolve(default)) or throw a clear error;
ensure you reference and adjust the DryRunMessage/DryRunWrapResult types if
necessary so the return type no longer relies on casting undefined to R and
callers cannot receive an unexpected undefined.
- Around line 189-202: The resolveRunResult function currently returns undefined
as R in two fall-through cases which can leak undefined at runtime; change those
fall-through returns to throw a clear runtime error (e.g., "DryRun: missing
result/resolve for non-void run") so callers cannot receive undefined when no
result/resolve was provided; update the function resolveRunResult<R>(options?:
DryRunMessage & Partial<DryRunRunResult<R>>) to throw when options is falsy or
when neither 'resolve' nor 'result' is present, leaving the existing branches
for options.resolve() and options.result intact.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d1ffb1c7-cb1d-4abe-8095-66cdff39b80f

📥 Commits

Reviewing files that changed from the base of the PR and between 2cee5f4 and 7e1b8fe.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (21)
  • package.json
  • packages/docs/src/guide/essentials/plugin-system.md
  • packages/docs/src/guide/plugin/introduction.md
  • packages/docs/src/guide/plugin/list.md
  • packages/plugin-dryrun/README.md
  • packages/plugin-dryrun/docs/functions/default.md
  • packages/plugin-dryrun/docs/index.md
  • packages/plugin-dryrun/docs/interfaces/DryRunExtension.md
  • packages/plugin-dryrun/docs/interfaces/DryRunMessage.md
  • packages/plugin-dryrun/docs/interfaces/DryRunPluginOptions.md
  • packages/plugin-dryrun/docs/type-aliases/DryRunOptionsArg.md
  • packages/plugin-dryrun/docs/type-aliases/DryRunRunResult.md
  • packages/plugin-dryrun/docs/type-aliases/DryRunWrapResult.md
  • packages/plugin-dryrun/docs/type-aliases/PluginId.md
  • packages/plugin-dryrun/docs/variables/pluginId.md
  • packages/plugin-dryrun/jsr.json
  • packages/plugin-dryrun/package.json
  • packages/plugin-dryrun/src/index.test-d.ts
  • packages/plugin-dryrun/src/index.test.ts
  • packages/plugin-dryrun/src/index.ts
  • packages/plugin-dryrun/typedoc.config.mjs

@kazupon kazupon merged commit b79cad8 into main Jun 6, 2026
10 checks passed
@kazupon kazupon deleted the feat/plugin-dryrun branch June 6, 2026 13:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature Includes new features

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant