feat(dry-run): Add worktree-based dry-run mode with real diff output - #692

Merged
BYK merged 15 commits into
masterfrom
byk/feat/worktree-dry-run
Jan 9, 2026
Merged

feat(dry-run): Add worktree-based dry-run mode with real diff output#692
BYK merged 15 commits into
masterfrom
byk/feat/worktree-dry-run

Conversation

@BYK

@BYKBYK commented Dec 31, 2025

Copy link
Copy Markdown
Member

Summary

Implements an enhanced --dry-run mode that creates a temporary git worktree, allowing local operations to execute in isolation while showing a real diff of what would change.

Changes

New Worktree-Based Dry-Run Flow

  • Creates a temporary git worktree at /tmp/craft-dry-run-*
  • Pre-release commands execute in the worktree (isolated from main repo)
  • File writes and git commits happen in the worktree
  • Shows a formatted git diff at the end showing exactly what would change
  • Only blocks remote operations (push, GitHub API mutations)

Key Features

  • Real preview: Users can see actual file changes (e.g., version bumps, changelog updates)
  • Isolated environment: Original repository is never modified
  • Clean cleanup: Signal handlers ensure worktree is removed on Ctrl+C
  • Symlinked dependencies: node_modules, vendor, etc. are symlinked for pre-release scripts

Example Output

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Dry-run complete. Here's what would change:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Files changed: 1
M package.json
diff --git a/package.json b/package.json
- "version": "1.0.0"
+ "version": "1.0.1"
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[dry-run] No actual changes were made to your repository.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Testing

  • Added comprehensive unit tests for worktree utilities
  • Added e2e tests with snapshot testing for dry-run output
  • All 699 tests pass

Implement an enhanced dry-run mode that creates a temporary git worktree,
allowing local operations to execute in isolation while showing a real
diff of what would change.
Key changes:
- Create temporary worktree for dry-run operations
- Allow pre-release commands and file writes in worktree mode
- Show formatted git diff at the end of dry-run
- Block only remote operations (push, GitHub API mutations)
- Add signal handlers for cleanup on Ctrl+C
- Add comprehensive unit and e2e tests with snapshot testing
@github-actions

github-actionsBot commented Dec 31, 2025

Copy link
Copy Markdown
Contributor

Semver Impact of This PR

🟡 Minor (new features)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


New Features ✨

  • (dry-run) Add worktree-based dry-run mode with real diff output by BYK in #692

Bug Fixes 🐛

  • (brew) Skip prereleases and add mustache templating for formula names by BYK in #682

Build / dependencies / internal 🔧

  • Add comprehensive Sentry integration with tracing and source maps by BYK in #697
  • Migrate from Yarn to pnpm 10.27.0 by BYK in #693

🤖 This preview updates automatically when you update the PR.

@github-actions

github-actionsBot commented Dec 31, 2025

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.0
Preview removed because the pull request was closed.
2026-01-09 14:57 UTC

…ation API
- Move all worktree logic from worktree.ts into dryRun.ts
- Create unified createDryRunIsolation() function that:
- Returns null if not in dry-run mode (no isDryRun() check needed)
- Handles worktree creation, mode enabling, and cleanup internally
- Provides git client, showDiff(), and cleanup() methods
- Update prepare.ts to use the new API, removing explicit isDryRun() checks
- Delete worktree.ts and worktree.test.ts (merged into dryRun.ts)
This simplifies the API surface: consumers just call createDryRunIsolation()
and use the returned object, rather than stitching together multiple functions
from different modules.
Instead of returning null in non-dry-run mode, return a passthrough
object with no-op methods. This eliminates conditional checks around
isolation usage - just call the methods unconditionally.
Instead of hiding success messages in dry-run mode, show them to give
users a complete picture of what the output would look like.
Added isIsolated property to DryRunIsolation to allow checking if
we're in an isolated worktree (needed to skip switchToDefaultBranch
which would fail since main is already checked out in the original repo).
execPublish was being called while worktree mode was still active,
which would allow safeExec() calls to actually run (npm publish, etc.).
Now we skip execPublish entirely in isolated mode and show a message
indicating what would have run.
@BYK
BYK marked this pull request as ready for review January 7, 2026 21:58
Comment threadsrc/utils/dryRun.ts
- Replace npx ts-node with locally installed tsx for e2e tests
- Add tsx as devDependency
- Rename vitest.config.ts to vitest.config.mts for ESM compatibility
- Fix signal handler race condition: don't call process.exit() immediately
after cleanup to give Sentry time to flush telemetry
- Fix removeAllListeners bug: only remove our handlers, not Sentry's
Comment threadsrc/utils/dryRun.ts
Comment on lines 133 to -140
return function (...args: unknown[]) {
if (isDryRun()) {
const argsStr = args
.map(a => (typeof a === 'string' ? a : JSON.stringify(a)))
.join(' ');
logDryRun(`git.${prop}(${argsStr})`);
// Return a mock result if the method's return value is accessed,
// otherwise return the proxy directly for chaining compatibility.

This comment was marked as outdated.

BYK added 2 commits January 9, 2026 14:48
E2E tests should test the actual built artifact, not transpiled source.
This is faster and more realistic.
- Remove tsx dependency
- Update tests to use dist/craft binary
- Add beforeAll hook to build if binary doesn't exist
@BYK
BYK merged commit 373929f into masterJan 9, 2026
17 checks passed
@BYK
BYK deleted the byk/feat/worktree-dry-run branch January 9, 2026 14:57
Sign up for freeto 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.

2 participants

@BYK@Lms24
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all \u003cpre\u003e\u003ccode\u003e blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks"); } } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); } })(); (function(){ try { var __m = "github.com"; var __re = new RegExp('^' + "github\\.com" + '
Skip to content

feat(dry-run): Add worktree-based dry-run mode with real diff output - #692

Merged
BYK merged 15 commits into
masterfrom
byk/feat/worktree-dry-run
Jan 9, 2026
Merged

feat(dry-run): Add worktree-based dry-run mode with real diff output#692
BYK merged 15 commits into
masterfrom
byk/feat/worktree-dry-run

Conversation

@BYK

@BYKBYK commented Dec 31, 2025

Copy link
Copy Markdown
Member

Summary

Implements an enhanced --dry-run mode that creates a temporary git worktree, allowing local operations to execute in isolation while showing a real diff of what would change.

Changes

New Worktree-Based Dry-Run Flow

  • Creates a temporary git worktree at /tmp/craft-dry-run-*
  • Pre-release commands execute in the worktree (isolated from main repo)
  • File writes and git commits happen in the worktree
  • Shows a formatted git diff at the end showing exactly what would change
  • Only blocks remote operations (push, GitHub API mutations)

Key Features

  • Real preview: Users can see actual file changes (e.g., version bumps, changelog updates)
  • Isolated environment: Original repository is never modified
  • Clean cleanup: Signal handlers ensure worktree is removed on Ctrl+C
  • Symlinked dependencies: node_modules, vendor, etc. are symlinked for pre-release scripts

Example Output

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Dry-run complete. Here's what would change:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Files changed: 1
M package.json
diff --git a/package.json b/package.json
- "version": "1.0.0"
+ "version": "1.0.1"
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[dry-run] No actual changes were made to your repository.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Testing

  • Added comprehensive unit tests for worktree utilities
  • Added e2e tests with snapshot testing for dry-run output
  • All 699 tests pass

Implement an enhanced dry-run mode that creates a temporary git worktree,
allowing local operations to execute in isolation while showing a real
diff of what would change.
Key changes:
- Create temporary worktree for dry-run operations
- Allow pre-release commands and file writes in worktree mode
- Show formatted git diff at the end of dry-run
- Block only remote operations (push, GitHub API mutations)
- Add signal handlers for cleanup on Ctrl+C
- Add comprehensive unit and e2e tests with snapshot testing
@github-actions

github-actionsBot commented Dec 31, 2025

Copy link
Copy Markdown
Contributor

Semver Impact of This PR

🟡 Minor (new features)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


New Features ✨

  • (dry-run) Add worktree-based dry-run mode with real diff output by BYK in #692

Bug Fixes 🐛

  • (brew) Skip prereleases and add mustache templating for formula names by BYK in #682

Build / dependencies / internal 🔧

  • Add comprehensive Sentry integration with tracing and source maps by BYK in #697
  • Migrate from Yarn to pnpm 10.27.0 by BYK in #693

🤖 This preview updates automatically when you update the PR.

@github-actions

github-actionsBot commented Dec 31, 2025

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.0
Preview removed because the pull request was closed.
2026-01-09 14:57 UTC

…ation API
- Move all worktree logic from worktree.ts into dryRun.ts
- Create unified createDryRunIsolation() function that:
- Returns null if not in dry-run mode (no isDryRun() check needed)
- Handles worktree creation, mode enabling, and cleanup internally
- Provides git client, showDiff(), and cleanup() methods
- Update prepare.ts to use the new API, removing explicit isDryRun() checks
- Delete worktree.ts and worktree.test.ts (merged into dryRun.ts)
This simplifies the API surface: consumers just call createDryRunIsolation()
and use the returned object, rather than stitching together multiple functions
from different modules.
Instead of returning null in non-dry-run mode, return a passthrough
object with no-op methods. This eliminates conditional checks around
isolation usage - just call the methods unconditionally.
Instead of hiding success messages in dry-run mode, show them to give
users a complete picture of what the output would look like.
Added isIsolated property to DryRunIsolation to allow checking if
we're in an isolated worktree (needed to skip switchToDefaultBranch
which would fail since main is already checked out in the original repo).
execPublish was being called while worktree mode was still active,
which would allow safeExec() calls to actually run (npm publish, etc.).
Now we skip execPublish entirely in isolated mode and show a message
indicating what would have run.
@BYK
BYK marked this pull request as ready for review January 7, 2026 21:58
Comment threadsrc/utils/dryRun.ts
- Replace npx ts-node with locally installed tsx for e2e tests
- Add tsx as devDependency
- Rename vitest.config.ts to vitest.config.mts for ESM compatibility
- Fix signal handler race condition: don't call process.exit() immediately
after cleanup to give Sentry time to flush telemetry
- Fix removeAllListeners bug: only remove our handlers, not Sentry's
Comment threadsrc/utils/dryRun.ts
Comment on lines 133 to -140
return function (...args: unknown[]) {
if (isDryRun()) {
const argsStr = args
.map(a => (typeof a === 'string' ? a : JSON.stringify(a)))
.join(' ');
logDryRun(`git.${prop}(${argsStr})`);
// Return a mock result if the method's return value is accessed,
// otherwise return the proxy directly for chaining compatibility.

This comment was marked as outdated.

BYK added 2 commits January 9, 2026 14:48
E2E tests should test the actual built artifact, not transpiled source.
This is faster and more realistic.
- Remove tsx dependency
- Update tests to use dist/craft binary
- Add beforeAll hook to build if binary doesn't exist
@BYK
BYK merged commit 373929f into masterJan 9, 2026
17 checks passed
@BYK
BYK deleted the byk/feat/worktree-dry-run branch January 9, 2026 14:57
Sign up for freeto 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.

2 participants

@BYK@Lms24
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(dry-run): Add worktree-based dry-run mode with real diff output - #692

Merged
BYK merged 15 commits into
masterfrom
byk/feat/worktree-dry-run
Jan 9, 2026
Merged

feat(dry-run): Add worktree-based dry-run mode with real diff output#692
BYK merged 15 commits into
masterfrom
byk/feat/worktree-dry-run

Conversation

@BYK

@BYKBYK commented Dec 31, 2025

Copy link
Copy Markdown
Member

Summary

Implements an enhanced --dry-run mode that creates a temporary git worktree, allowing local operations to execute in isolation while showing a real diff of what would change.

Changes

New Worktree-Based Dry-Run Flow

  • Creates a temporary git worktree at /tmp/craft-dry-run-*
  • Pre-release commands execute in the worktree (isolated from main repo)
  • File writes and git commits happen in the worktree
  • Shows a formatted git diff at the end showing exactly what would change
  • Only blocks remote operations (push, GitHub API mutations)

Key Features

  • Real preview: Users can see actual file changes (e.g., version bumps, changelog updates)
  • Isolated environment: Original repository is never modified
  • Clean cleanup: Signal handlers ensure worktree is removed on Ctrl+C
  • Symlinked dependencies: node_modules, vendor, etc. are symlinked for pre-release scripts

Example Output

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Dry-run complete. Here's what would change:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Files changed: 1
M package.json
diff --git a/package.json b/package.json
- "version": "1.0.0"
+ "version": "1.0.1"
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[dry-run] No actual changes were made to your repository.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Testing

  • Added comprehensive unit tests for worktree utilities
  • Added e2e tests with snapshot testing for dry-run output
  • All 699 tests pass

Implement an enhanced dry-run mode that creates a temporary git worktree,
allowing local operations to execute in isolation while showing a real
diff of what would change.
Key changes:
- Create temporary worktree for dry-run operations
- Allow pre-release commands and file writes in worktree mode
- Show formatted git diff at the end of dry-run
- Block only remote operations (push, GitHub API mutations)
- Add signal handlers for cleanup on Ctrl+C
- Add comprehensive unit and e2e tests with snapshot testing
@github-actions

github-actionsBot commented Dec 31, 2025

Copy link
Copy Markdown
Contributor

Semver Impact of This PR

🟡 Minor (new features)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


New Features ✨

  • (dry-run) Add worktree-based dry-run mode with real diff output by BYK in #692

Bug Fixes 🐛

  • (brew) Skip prereleases and add mustache templating for formula names by BYK in #682

Build / dependencies / internal 🔧

  • Add comprehensive Sentry integration with tracing and source maps by BYK in #697
  • Migrate from Yarn to pnpm 10.27.0 by BYK in #693

🤖 This preview updates automatically when you update the PR.

@github-actions

github-actionsBot commented Dec 31, 2025

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.0
Preview removed because the pull request was closed.
2026-01-09 14:57 UTC

…ation API
- Move all worktree logic from worktree.ts into dryRun.ts
- Create unified createDryRunIsolation() function that:
- Returns null if not in dry-run mode (no isDryRun() check needed)
- Handles worktree creation, mode enabling, and cleanup internally
- Provides git client, showDiff(), and cleanup() methods
- Update prepare.ts to use the new API, removing explicit isDryRun() checks
- Delete worktree.ts and worktree.test.ts (merged into dryRun.ts)
This simplifies the API surface: consumers just call createDryRunIsolation()
and use the returned object, rather than stitching together multiple functions
from different modules.
Instead of returning null in non-dry-run mode, return a passthrough
object with no-op methods. This eliminates conditional checks around
isolation usage - just call the methods unconditionally.
Instead of hiding success messages in dry-run mode, show them to give
users a complete picture of what the output would look like.
Added isIsolated property to DryRunIsolation to allow checking if
we're in an isolated worktree (needed to skip switchToDefaultBranch
which would fail since main is already checked out in the original repo).
execPublish was being called while worktree mode was still active,
which would allow safeExec() calls to actually run (npm publish, etc.).
Now we skip execPublish entirely in isolated mode and show a message
indicating what would have run.
@BYK
BYK marked this pull request as ready for review January 7, 2026 21:58
Comment threadsrc/utils/dryRun.ts
- Replace npx ts-node with locally installed tsx for e2e tests
- Add tsx as devDependency
- Rename vitest.config.ts to vitest.config.mts for ESM compatibility
- Fix signal handler race condition: don't call process.exit() immediately
after cleanup to give Sentry time to flush telemetry
- Fix removeAllListeners bug: only remove our handlers, not Sentry's
Comment threadsrc/utils/dryRun.ts
Comment on lines 133 to -140
return function (...args: unknown[]) {
if (isDryRun()) {
const argsStr = args
.map(a => (typeof a === 'string' ? a : JSON.stringify(a)))
.join(' ');
logDryRun(`git.${prop}(${argsStr})`);
// Return a mock result if the method's return value is accessed,
// otherwise return the proxy directly for chaining compatibility.

This comment was marked as outdated.

BYK added 2 commits January 9, 2026 14:48
E2E tests should test the actual built artifact, not transpiled source.
This is faster and more realistic.
- Remove tsx dependency
- Update tests to use dist/craft binary
- Add beforeAll hook to build if binary doesn't exist
@BYK
BYK merged commit 373929f into masterJan 9, 2026
17 checks passed
@BYK
BYK deleted the byk/feat/worktree-dry-run branch January 9, 2026 14:57
Sign up for freeto 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.

2 participants

@BYK@Lms24
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length \u003e 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(dry-run): Add worktree-based dry-run mode with real diff output - #692

Merged
BYK merged 15 commits into
masterfrom
byk/feat/worktree-dry-run
Jan 9, 2026
Merged

feat(dry-run): Add worktree-based dry-run mode with real diff output#692
BYK merged 15 commits into
masterfrom
byk/feat/worktree-dry-run

Conversation

@BYK

@BYKBYK commented Dec 31, 2025

Copy link
Copy Markdown
Member

Summary

Implements an enhanced --dry-run mode that creates a temporary git worktree, allowing local operations to execute in isolation while showing a real diff of what would change.

Changes

New Worktree-Based Dry-Run Flow

  • Creates a temporary git worktree at /tmp/craft-dry-run-*
  • Pre-release commands execute in the worktree (isolated from main repo)
  • File writes and git commits happen in the worktree
  • Shows a formatted git diff at the end showing exactly what would change
  • Only blocks remote operations (push, GitHub API mutations)

Key Features

  • Real preview: Users can see actual file changes (e.g., version bumps, changelog updates)
  • Isolated environment: Original repository is never modified
  • Clean cleanup: Signal handlers ensure worktree is removed on Ctrl+C
  • Symlinked dependencies: node_modules, vendor, etc. are symlinked for pre-release scripts

Example Output

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Dry-run complete. Here's what would change:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Files changed: 1
M package.json
diff --git a/package.json b/package.json
- "version": "1.0.0"
+ "version": "1.0.1"
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[dry-run] No actual changes were made to your repository.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Testing

  • Added comprehensive unit tests for worktree utilities
  • Added e2e tests with snapshot testing for dry-run output
  • All 699 tests pass

Implement an enhanced dry-run mode that creates a temporary git worktree,
allowing local operations to execute in isolation while showing a real
diff of what would change.
Key changes:
- Create temporary worktree for dry-run operations
- Allow pre-release commands and file writes in worktree mode
- Show formatted git diff at the end of dry-run
- Block only remote operations (push, GitHub API mutations)
- Add signal handlers for cleanup on Ctrl+C
- Add comprehensive unit and e2e tests with snapshot testing
@github-actions

github-actionsBot commented Dec 31, 2025

Copy link
Copy Markdown
Contributor

Semver Impact of This PR

🟡 Minor (new features)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


New Features ✨

  • (dry-run) Add worktree-based dry-run mode with real diff output by BYK in #692

Bug Fixes 🐛

  • (brew) Skip prereleases and add mustache templating for formula names by BYK in #682

Build / dependencies / internal 🔧

  • Add comprehensive Sentry integration with tracing and source maps by BYK in #697
  • Migrate from Yarn to pnpm 10.27.0 by BYK in #693

🤖 This preview updates automatically when you update the PR.

@github-actions

github-actionsBot commented Dec 31, 2025

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.0
Preview removed because the pull request was closed.
2026-01-09 14:57 UTC

…ation API
- Move all worktree logic from worktree.ts into dryRun.ts
- Create unified createDryRunIsolation() function that:
- Returns null if not in dry-run mode (no isDryRun() check needed)
- Handles worktree creation, mode enabling, and cleanup internally
- Provides git client, showDiff(), and cleanup() methods
- Update prepare.ts to use the new API, removing explicit isDryRun() checks
- Delete worktree.ts and worktree.test.ts (merged into dryRun.ts)
This simplifies the API surface: consumers just call createDryRunIsolation()
and use the returned object, rather than stitching together multiple functions
from different modules.
Instead of returning null in non-dry-run mode, return a passthrough
object with no-op methods. This eliminates conditional checks around
isolation usage - just call the methods unconditionally.
Instead of hiding success messages in dry-run mode, show them to give
users a complete picture of what the output would look like.
Added isIsolated property to DryRunIsolation to allow checking if
we're in an isolated worktree (needed to skip switchToDefaultBranch
which would fail since main is already checked out in the original repo).
execPublish was being called while worktree mode was still active,
which would allow safeExec() calls to actually run (npm publish, etc.).
Now we skip execPublish entirely in isolated mode and show a message
indicating what would have run.
@BYK
BYK marked this pull request as ready for review January 7, 2026 21:58
Comment threadsrc/utils/dryRun.ts
- Replace npx ts-node with locally installed tsx for e2e tests
- Add tsx as devDependency
- Rename vitest.config.ts to vitest.config.mts for ESM compatibility
- Fix signal handler race condition: don't call process.exit() immediately
after cleanup to give Sentry time to flush telemetry
- Fix removeAllListeners bug: only remove our handlers, not Sentry's
Comment threadsrc/utils/dryRun.ts
Comment on lines 133 to -140
return function (...args: unknown[]) {
if (isDryRun()) {
const argsStr = args
.map(a => (typeof a === 'string' ? a : JSON.stringify(a)))
.join(' ');
logDryRun(`git.${prop}(${argsStr})`);
// Return a mock result if the method's return value is accessed,
// otherwise return the proxy directly for chaining compatibility.

This comment was marked as outdated.

BYK added 2 commits January 9, 2026 14:48
E2E tests should test the actual built artifact, not transpiled source.
This is faster and more realistic.
- Remove tsx dependency
- Update tests to use dist/craft binary
- Add beforeAll hook to build if binary doesn't exist
@BYK
BYK merged commit 373929f into masterJan 9, 2026
17 checks passed
@BYK
BYK deleted the byk/feat/worktree-dry-run branch January 9, 2026 14:57
Sign up for freeto 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.

2 participants

@BYK@Lms24
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

feat(dry-run): Add worktree-based dry-run mode with real diff output - #692

Merged
BYK merged 15 commits into
masterfrom
byk/feat/worktree-dry-run
Jan 9, 2026
Merged

feat(dry-run): Add worktree-based dry-run mode with real diff output#692
BYK merged 15 commits into
masterfrom
byk/feat/worktree-dry-run

Conversation

@BYK

@BYKBYK commented Dec 31, 2025

Copy link
Copy Markdown
Member

Summary

Implements an enhanced --dry-run mode that creates a temporary git worktree, allowing local operations to execute in isolation while showing a real diff of what would change.

Changes

New Worktree-Based Dry-Run Flow

  • Creates a temporary git worktree at /tmp/craft-dry-run-*
  • Pre-release commands execute in the worktree (isolated from main repo)
  • File writes and git commits happen in the worktree
  • Shows a formatted git diff at the end showing exactly what would change
  • Only blocks remote operations (push, GitHub API mutations)

Key Features

  • Real preview: Users can see actual file changes (e.g., version bumps, changelog updates)
  • Isolated environment: Original repository is never modified
  • Clean cleanup: Signal handlers ensure worktree is removed on Ctrl+C
  • Symlinked dependencies: node_modules, vendor, etc. are symlinked for pre-release scripts

Example Output

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Dry-run complete. Here's what would change:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Files changed: 1
M package.json
diff --git a/package.json b/package.json
- "version": "1.0.0"
+ "version": "1.0.1"
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[dry-run] No actual changes were made to your repository.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Testing

  • Added comprehensive unit tests for worktree utilities
  • Added e2e tests with snapshot testing for dry-run output
  • All 699 tests pass

Implement an enhanced dry-run mode that creates a temporary git worktree,
allowing local operations to execute in isolation while showing a real
diff of what would change.
Key changes:
- Create temporary worktree for dry-run operations
- Allow pre-release commands and file writes in worktree mode
- Show formatted git diff at the end of dry-run
- Block only remote operations (push, GitHub API mutations)
- Add signal handlers for cleanup on Ctrl+C
- Add comprehensive unit and e2e tests with snapshot testing
@github-actions

github-actionsBot commented Dec 31, 2025

Copy link
Copy Markdown
Contributor

Semver Impact of This PR

🟡 Minor (new features)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


New Features ✨

  • (dry-run) Add worktree-based dry-run mode with real diff output by BYK in #692

Bug Fixes 🐛

  • (brew) Skip prereleases and add mustache templating for formula names by BYK in #682

Build / dependencies / internal 🔧

  • Add comprehensive Sentry integration with tracing and source maps by BYK in #697
  • Migrate from Yarn to pnpm 10.27.0 by BYK in #693

🤖 This preview updates automatically when you update the PR.

@github-actions

github-actionsBot commented Dec 31, 2025

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.0
Preview removed because the pull request was closed.
2026-01-09 14:57 UTC

…ation API
- Move all worktree logic from worktree.ts into dryRun.ts
- Create unified createDryRunIsolation() function that:
- Returns null if not in dry-run mode (no isDryRun() check needed)
- Handles worktree creation, mode enabling, and cleanup internally
- Provides git client, showDiff(), and cleanup() methods
- Update prepare.ts to use the new API, removing explicit isDryRun() checks
- Delete worktree.ts and worktree.test.ts (merged into dryRun.ts)
This simplifies the API surface: consumers just call createDryRunIsolation()
and use the returned object, rather than stitching together multiple functions
from different modules.
Instead of returning null in non-dry-run mode, return a passthrough
object with no-op methods. This eliminates conditional checks around
isolation usage - just call the methods unconditionally.
Instead of hiding success messages in dry-run mode, show them to give
users a complete picture of what the output would look like.
Added isIsolated property to DryRunIsolation to allow checking if
we're in an isolated worktree (needed to skip switchToDefaultBranch
which would fail since main is already checked out in the original repo).
execPublish was being called while worktree mode was still active,
which would allow safeExec() calls to actually run (npm publish, etc.).
Now we skip execPublish entirely in isolated mode and show a message
indicating what would have run.
@BYK
BYK marked this pull request as ready for review January 7, 2026 21:58
Comment threadsrc/utils/dryRun.ts
- Replace npx ts-node with locally installed tsx for e2e tests
- Add tsx as devDependency
- Rename vitest.config.ts to vitest.config.mts for ESM compatibility
- Fix signal handler race condition: don't call process.exit() immediately
after cleanup to give Sentry time to flush telemetry
- Fix removeAllListeners bug: only remove our handlers, not Sentry's
Comment threadsrc/utils/dryRun.ts
Comment on lines 133 to -140
return function (...args: unknown[]) {
if (isDryRun()) {
const argsStr = args
.map(a => (typeof a === 'string' ? a : JSON.stringify(a)))
.join(' ');
logDryRun(`git.${prop}(${argsStr})`);
// Return a mock result if the method's return value is accessed,
// otherwise return the proxy directly for chaining compatibility.

This comment was marked as outdated.

BYK added 2 commits January 9, 2026 14:48
E2E tests should test the actual built artifact, not transpiled source.
This is faster and more realistic.
- Remove tsx dependency
- Update tests to use dist/craft binary
- Add beforeAll hook to build if binary doesn't exist
@BYK
BYK merged commit 373929f into masterJan 9, 2026
17 checks passed
@BYK
BYK deleted the byk/feat/worktree-dry-run branch January 9, 2026 14:57
Sign up for freeto 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.

2 participants

@BYK@Lms24
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(dry-run): Add worktree-based dry-run mode with real diff output - #692

Merged
BYK merged 15 commits into
masterfrom
byk/feat/worktree-dry-run
Jan 9, 2026
Merged

feat(dry-run): Add worktree-based dry-run mode with real diff output#692
BYK merged 15 commits into
masterfrom
byk/feat/worktree-dry-run

Conversation

@BYK

@BYKBYK commented Dec 31, 2025

Copy link
Copy Markdown
Member

Summary

Implements an enhanced --dry-run mode that creates a temporary git worktree, allowing local operations to execute in isolation while showing a real diff of what would change.

Changes

New Worktree-Based Dry-Run Flow

  • Creates a temporary git worktree at /tmp/craft-dry-run-*
  • Pre-release commands execute in the worktree (isolated from main repo)
  • File writes and git commits happen in the worktree
  • Shows a formatted git diff at the end showing exactly what would change
  • Only blocks remote operations (push, GitHub API mutations)

Key Features

  • Real preview: Users can see actual file changes (e.g., version bumps, changelog updates)
  • Isolated environment: Original repository is never modified
  • Clean cleanup: Signal handlers ensure worktree is removed on Ctrl+C
  • Symlinked dependencies: node_modules, vendor, etc. are symlinked for pre-release scripts

Example Output

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Dry-run complete. Here's what would change:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Files changed: 1
M package.json
diff --git a/package.json b/package.json
- "version": "1.0.0"
+ "version": "1.0.1"
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[dry-run] No actual changes were made to your repository.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Testing

  • Added comprehensive unit tests for worktree utilities
  • Added e2e tests with snapshot testing for dry-run output
  • All 699 tests pass

Implement an enhanced dry-run mode that creates a temporary git worktree,
allowing local operations to execute in isolation while showing a real
diff of what would change.
Key changes:
- Create temporary worktree for dry-run operations
- Allow pre-release commands and file writes in worktree mode
- Show formatted git diff at the end of dry-run
- Block only remote operations (push, GitHub API mutations)
- Add signal handlers for cleanup on Ctrl+C
- Add comprehensive unit and e2e tests with snapshot testing
@github-actions

github-actionsBot commented Dec 31, 2025

Copy link
Copy Markdown
Contributor

Semver Impact of This PR

🟡 Minor (new features)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


New Features ✨

  • (dry-run) Add worktree-based dry-run mode with real diff output by BYK in #692

Bug Fixes 🐛

  • (brew) Skip prereleases and add mustache templating for formula names by BYK in #682

Build / dependencies / internal 🔧

  • Add comprehensive Sentry integration with tracing and source maps by BYK in #697
  • Migrate from Yarn to pnpm 10.27.0 by BYK in #693

🤖 This preview updates automatically when you update the PR.

@github-actions

github-actionsBot commented Dec 31, 2025

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.0
Preview removed because the pull request was closed.
2026-01-09 14:57 UTC

…ation API
- Move all worktree logic from worktree.ts into dryRun.ts
- Create unified createDryRunIsolation() function that:
- Returns null if not in dry-run mode (no isDryRun() check needed)
- Handles worktree creation, mode enabling, and cleanup internally
- Provides git client, showDiff(), and cleanup() methods
- Update prepare.ts to use the new API, removing explicit isDryRun() checks
- Delete worktree.ts and worktree.test.ts (merged into dryRun.ts)
This simplifies the API surface: consumers just call createDryRunIsolation()
and use the returned object, rather than stitching together multiple functions
from different modules.
Instead of returning null in non-dry-run mode, return a passthrough
object with no-op methods. This eliminates conditional checks around
isolation usage - just call the methods unconditionally.
Instead of hiding success messages in dry-run mode, show them to give
users a complete picture of what the output would look like.
Added isIsolated property to DryRunIsolation to allow checking if
we're in an isolated worktree (needed to skip switchToDefaultBranch
which would fail since main is already checked out in the original repo).
execPublish was being called while worktree mode was still active,
which would allow safeExec() calls to actually run (npm publish, etc.).
Now we skip execPublish entirely in isolated mode and show a message
indicating what would have run.
@BYK
BYK marked this pull request as ready for review January 7, 2026 21:58
Comment threadsrc/utils/dryRun.ts
- Replace npx ts-node with locally installed tsx for e2e tests
- Add tsx as devDependency
- Rename vitest.config.ts to vitest.config.mts for ESM compatibility
- Fix signal handler race condition: don't call process.exit() immediately
after cleanup to give Sentry time to flush telemetry
- Fix removeAllListeners bug: only remove our handlers, not Sentry's
Comment threadsrc/utils/dryRun.ts
Comment on lines 133 to -140
return function (...args: unknown[]) {
if (isDryRun()) {
const argsStr = args
.map(a => (typeof a === 'string' ? a : JSON.stringify(a)))
.join(' ');
logDryRun(`git.${prop}(${argsStr})`);
// Return a mock result if the method's return value is accessed,
// otherwise return the proxy directly for chaining compatibility.

This comment was marked as outdated.

BYK added 2 commits January 9, 2026 14:48
E2E tests should test the actual built artifact, not transpiled source.
This is faster and more realistic.
- Remove tsx dependency
- Update tests to use dist/craft binary
- Add beforeAll hook to build if binary doesn't exist
@BYK
BYK merged commit 373929f into masterJan 9, 2026
17 checks passed
@BYK
BYK deleted the byk/feat/worktree-dry-run branch January 9, 2026 14:57
Sign up for freeto 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.

2 participants

@BYK@Lms24
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(dry-run): Add worktree-based dry-run mode with real diff output - #692

Merged
BYK merged 15 commits into
masterfrom
byk/feat/worktree-dry-run
Jan 9, 2026
Merged

feat(dry-run): Add worktree-based dry-run mode with real diff output#692
BYK merged 15 commits into
masterfrom
byk/feat/worktree-dry-run

Conversation

@BYK

@BYKBYK commented Dec 31, 2025

Copy link
Copy Markdown
Member

Summary

Implements an enhanced --dry-run mode that creates a temporary git worktree, allowing local operations to execute in isolation while showing a real diff of what would change.

Changes

New Worktree-Based Dry-Run Flow

  • Creates a temporary git worktree at /tmp/craft-dry-run-*
  • Pre-release commands execute in the worktree (isolated from main repo)
  • File writes and git commits happen in the worktree
  • Shows a formatted git diff at the end showing exactly what would change
  • Only blocks remote operations (push, GitHub API mutations)

Key Features

  • Real preview: Users can see actual file changes (e.g., version bumps, changelog updates)
  • Isolated environment: Original repository is never modified
  • Clean cleanup: Signal handlers ensure worktree is removed on Ctrl+C
  • Symlinked dependencies: node_modules, vendor, etc. are symlinked for pre-release scripts

Example Output

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Dry-run complete. Here's what would change:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Files changed: 1
M package.json
diff --git a/package.json b/package.json
- "version": "1.0.0"
+ "version": "1.0.1"
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[dry-run] No actual changes were made to your repository.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Testing

  • Added comprehensive unit tests for worktree utilities
  • Added e2e tests with snapshot testing for dry-run output
  • All 699 tests pass

Implement an enhanced dry-run mode that creates a temporary git worktree,
allowing local operations to execute in isolation while showing a real
diff of what would change.
Key changes:
- Create temporary worktree for dry-run operations
- Allow pre-release commands and file writes in worktree mode
- Show formatted git diff at the end of dry-run
- Block only remote operations (push, GitHub API mutations)
- Add signal handlers for cleanup on Ctrl+C
- Add comprehensive unit and e2e tests with snapshot testing
@github-actions

github-actionsBot commented Dec 31, 2025

Copy link
Copy Markdown
Contributor

Semver Impact of This PR

🟡 Minor (new features)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


New Features ✨

  • (dry-run) Add worktree-based dry-run mode with real diff output by BYK in #692

Bug Fixes 🐛

  • (brew) Skip prereleases and add mustache templating for formula names by BYK in #682

Build / dependencies / internal 🔧

  • Add comprehensive Sentry integration with tracing and source maps by BYK in #697
  • Migrate from Yarn to pnpm 10.27.0 by BYK in #693

🤖 This preview updates automatically when you update the PR.

@github-actions

github-actionsBot commented Dec 31, 2025

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.0
Preview removed because the pull request was closed.
2026-01-09 14:57 UTC

…ation API
- Move all worktree logic from worktree.ts into dryRun.ts
- Create unified createDryRunIsolation() function that:
- Returns null if not in dry-run mode (no isDryRun() check needed)
- Handles worktree creation, mode enabling, and cleanup internally
- Provides git client, showDiff(), and cleanup() methods
- Update prepare.ts to use the new API, removing explicit isDryRun() checks
- Delete worktree.ts and worktree.test.ts (merged into dryRun.ts)
This simplifies the API surface: consumers just call createDryRunIsolation()
and use the returned object, rather than stitching together multiple functions
from different modules.
Instead of returning null in non-dry-run mode, return a passthrough
object with no-op methods. This eliminates conditional checks around
isolation usage - just call the methods unconditionally.
Instead of hiding success messages in dry-run mode, show them to give
users a complete picture of what the output would look like.
Added isIsolated property to DryRunIsolation to allow checking if
we're in an isolated worktree (needed to skip switchToDefaultBranch
which would fail since main is already checked out in the original repo).
execPublish was being called while worktree mode was still active,
which would allow safeExec() calls to actually run (npm publish, etc.).
Now we skip execPublish entirely in isolated mode and show a message
indicating what would have run.
@BYK
BYK marked this pull request as ready for review January 7, 2026 21:58
Comment threadsrc/utils/dryRun.ts
- Replace npx ts-node with locally installed tsx for e2e tests
- Add tsx as devDependency
- Rename vitest.config.ts to vitest.config.mts for ESM compatibility
- Fix signal handler race condition: don't call process.exit() immediately
after cleanup to give Sentry time to flush telemetry
- Fix removeAllListeners bug: only remove our handlers, not Sentry's
Comment threadsrc/utils/dryRun.ts
Comment on lines 133 to -140
return function (...args: unknown[]) {
if (isDryRun()) {
const argsStr = args
.map(a => (typeof a === 'string' ? a : JSON.stringify(a)))
.join(' ');
logDryRun(`git.${prop}(${argsStr})`);
// Return a mock result if the method's return value is accessed,
// otherwise return the proxy directly for chaining compatibility.

This comment was marked as outdated.

BYK added 2 commits January 9, 2026 14:48
E2E tests should test the actual built artifact, not transpiled source.
This is faster and more realistic.
- Remove tsx dependency
- Update tests to use dist/craft binary
- Add beforeAll hook to build if binary doesn't exist
@BYK
BYK merged commit 373929f into masterJan 9, 2026
17 checks passed
@BYK
BYK deleted the byk/feat/worktree-dry-run branch January 9, 2026 14:57
Sign up for freeto 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.

2 participants

@BYK@Lms24
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

feat(dry-run): Add worktree-based dry-run mode with real diff output - #692

Merged
BYK merged 15 commits into
masterfrom
byk/feat/worktree-dry-run
Jan 9, 2026
Merged

feat(dry-run): Add worktree-based dry-run mode with real diff output#692
BYK merged 15 commits into
masterfrom
byk/feat/worktree-dry-run

Conversation

@BYK

@BYKBYK commented Dec 31, 2025

Copy link
Copy Markdown
Member

Summary

Implements an enhanced --dry-run mode that creates a temporary git worktree, allowing local operations to execute in isolation while showing a real diff of what would change.

Changes

New Worktree-Based Dry-Run Flow

  • Creates a temporary git worktree at /tmp/craft-dry-run-*
  • Pre-release commands execute in the worktree (isolated from main repo)
  • File writes and git commits happen in the worktree
  • Shows a formatted git diff at the end showing exactly what would change
  • Only blocks remote operations (push, GitHub API mutations)

Key Features

  • Real preview: Users can see actual file changes (e.g., version bumps, changelog updates)
  • Isolated environment: Original repository is never modified
  • Clean cleanup: Signal handlers ensure worktree is removed on Ctrl+C
  • Symlinked dependencies: node_modules, vendor, etc. are symlinked for pre-release scripts

Example Output

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Dry-run complete. Here's what would change:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Files changed: 1
M package.json
diff --git a/package.json b/package.json
- "version": "1.0.0"
+ "version": "1.0.1"
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[dry-run] No actual changes were made to your repository.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Testing

  • Added comprehensive unit tests for worktree utilities
  • Added e2e tests with snapshot testing for dry-run output
  • All 699 tests pass

Implement an enhanced dry-run mode that creates a temporary git worktree,
allowing local operations to execute in isolation while showing a real
diff of what would change.
Key changes:
- Create temporary worktree for dry-run operations
- Allow pre-release commands and file writes in worktree mode
- Show formatted git diff at the end of dry-run
- Block only remote operations (push, GitHub API mutations)
- Add signal handlers for cleanup on Ctrl+C
- Add comprehensive unit and e2e tests with snapshot testing
@github-actions

github-actionsBot commented Dec 31, 2025

Copy link
Copy Markdown
Contributor

Semver Impact of This PR

🟡 Minor (new features)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


New Features ✨

  • (dry-run) Add worktree-based dry-run mode with real diff output by BYK in #692

Bug Fixes 🐛

  • (brew) Skip prereleases and add mustache templating for formula names by BYK in #682

Build / dependencies / internal 🔧

  • Add comprehensive Sentry integration with tracing and source maps by BYK in #697
  • Migrate from Yarn to pnpm 10.27.0 by BYK in #693

🤖 This preview updates automatically when you update the PR.

@github-actions

github-actionsBot commented Dec 31, 2025

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.0
Preview removed because the pull request was closed.
2026-01-09 14:57 UTC

…ation API
- Move all worktree logic from worktree.ts into dryRun.ts
- Create unified createDryRunIsolation() function that:
- Returns null if not in dry-run mode (no isDryRun() check needed)
- Handles worktree creation, mode enabling, and cleanup internally
- Provides git client, showDiff(), and cleanup() methods
- Update prepare.ts to use the new API, removing explicit isDryRun() checks
- Delete worktree.ts and worktree.test.ts (merged into dryRun.ts)
This simplifies the API surface: consumers just call createDryRunIsolation()
and use the returned object, rather than stitching together multiple functions
from different modules.
Instead of returning null in non-dry-run mode, return a passthrough
object with no-op methods. This eliminates conditional checks around
isolation usage - just call the methods unconditionally.
Instead of hiding success messages in dry-run mode, show them to give
users a complete picture of what the output would look like.
Added isIsolated property to DryRunIsolation to allow checking if
we're in an isolated worktree (needed to skip switchToDefaultBranch
which would fail since main is already checked out in the original repo).
execPublish was being called while worktree mode was still active,
which would allow safeExec() calls to actually run (npm publish, etc.).
Now we skip execPublish entirely in isolated mode and show a message
indicating what would have run.
@BYK
BYK marked this pull request as ready for review January 7, 2026 21:58
Comment threadsrc/utils/dryRun.ts
- Replace npx ts-node with locally installed tsx for e2e tests
- Add tsx as devDependency
- Rename vitest.config.ts to vitest.config.mts for ESM compatibility
- Fix signal handler race condition: don't call process.exit() immediately
after cleanup to give Sentry time to flush telemetry
- Fix removeAllListeners bug: only remove our handlers, not Sentry's
Comment threadsrc/utils/dryRun.ts
Comment on lines 133 to -140
return function (...args: unknown[]) {
if (isDryRun()) {
const argsStr = args
.map(a => (typeof a === 'string' ? a : JSON.stringify(a)))
.join(' ');
logDryRun(`git.${prop}(${argsStr})`);
// Return a mock result if the method's return value is accessed,
// otherwise return the proxy directly for chaining compatibility.

This comment was marked as outdated.

BYK added 2 commits January 9, 2026 14:48
E2E tests should test the actual built artifact, not transpiled source.
This is faster and more realistic.
- Remove tsx dependency
- Update tests to use dist/craft binary
- Add beforeAll hook to build if binary doesn't exist
@BYK
BYK merged commit 373929f into masterJan 9, 2026
17 checks passed
@BYK
BYK deleted the byk/feat/worktree-dry-run branch January 9, 2026 14:57
Sign up for freeto 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.

2 participants

@BYK@Lms24