Skip to content

Refactor cloneTemplate to use execFile and improve error handling#17645

Open
DawitMengistu wants to merge 2 commits into
mastra-ai:mainfrom
DawitMengistu:patch-2
Open

Refactor cloneTemplate to use execFile and improve error handling#17645
DawitMengistu wants to merge 2 commits into
mastra-ai:mainfrom
DawitMengistu:patch-2

Conversation

@DawitMengistu

@DawitMengistu DawitMengistu commented Jun 6, 2026

Copy link
Copy Markdown

Fixes #17646

This PR fixes a Windows-specific failure when cloning template repositories in create-mastra.

The issue occurs because shell-quote is used together with child_process.exec, which causes Git URLs to be incorrectly escaped (e.g. https:// becomes https://). This results in git clone failing with:

fatal: Too many arguments
Root cause

Using shell-quote.quote() to build a command string for exec() introduces unsafe shell escaping behavior for Git URLs, which is not cross-platform reliable.

Fix

Replaced exec + shellQuote with child_process.execFile, passing arguments as an array instead of a shell string. This removes shell parsing entirely and ensures consistent behavior across Windows, macOS, and Linux.

Changes
Removed shell-quote dependency for command execution
Replaced exec with execFile
Updated:
git clone
npx degit
package manager install
Ensured all commands use argument arrays instead of shell strings
Verification

Reproduced issue on Windows by logging the generated command, confirming:

git clone https://github.com/...

After fix, commands execute correctly with no escaping issues.

Related issue(s)

Fixes # (you can open one first, then paste the number here)

ELI5

This PR fixes a bug where cloning templates on Windows would fail because the code was trying to use shell escaping on command URLs, which broke them (turning https:// into https\://). Instead of escaping commands for a shell, the fix passes commands directly as a list of arguments, which works correctly on all operating systems.

Summary

This PR refactors the template cloning functionality to replace unsafe shell command construction with direct file execution. The core issue was that child_process.exec() combined with shell-quote caused platform-specific escaping problems on Windows when handling Git URLs. By switching to child_process.execFile(), commands are passed as argument arrays, eliminating shell parsing entirely and providing cross-platform reliability.

Key Changes

Command Execution Refactor:

  • Replaces execFile import using util.promisify(child_process.execFile) instead of building shell command strings
  • Updates cloneRepositoryWithoutGit() to use array-based arguments:
    • npx degit invocation: execFile('npx', ['degit', repoWithBranch, targetPath])
    • Git clone fallback: execFile('git', ['clone', '--branch', branch, repoUrl, targetPath]) (branch conditional)
  • Updates installDependencies() to use: execFile(pm, ['install'], { cwd: projectPath })
  • Removes dependency on shell-quote for command construction

Directory and Environment Handling:

  • Pre-creates target directory with fs.mkdir() before attempting clones
  • Maintains .env file logic with MODEL= replacement
  • Preserves conditional logging for missing model identifiers
  • Removes .git directory after successful git clone

Error Handling and UX:

  • Maintains exception propagation through spinner feedback
  • Preserves try-catch control flow for degit fallback to git
  • Consistent error message formatting in success/failure scenarios

Testing

Comprehensive test suite included covering:

  • Successful cloning with degit
  • Fallback to git clone when degit fails
  • Branch-specific cloning scenarios
  • Custom target directory paths
  • .env file updates with LLM provider configuration
  • Package manager detection and dependency installation
  • Error scenarios (missing directories, failed installations)

No changes to exported/public API signatures.

@changeset-bot

changeset-bot Bot commented Jun 6, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: b1450b8

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@vercel

vercel Bot commented Jun 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

2 Skipped Deployments
Project Deployment Actions Updated (UTC)
mastra-docs-1.x Skipped Skipped Jun 9, 2026 12:36pm
mastra-playground-ui Skipped Skipped Jun 9, 2026 12:36pm

Request Review

@vercel vercel Bot temporarily deployed to Preview – mastra-docs-1.x June 6, 2026 06:00 Inactive
@vercel vercel Bot temporarily deployed to Preview – mastra-playground-ui June 6, 2026 06:00 Inactive
@coderabbitai

coderabbitai Bot commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

Looking for one thing? Review this PR in Change Stack to search files, summaries, diffs, and code without losing your place.

Review Change Stack

Walkthrough

clone-template.ts transitions from shell-quote-based command construction to structured execFile invocation throughout its template cloning workflow. The import chain is simplified, helper functions are refactored to use explicit argument arrays, and the overall execution model moves from shell-interpolated strings to direct process spawning for robustness and clarity.

Changes

Template cloning refactor to execFile

Layer / File(s) Summary
Import changes and execFile helper introduction
packages/cli/src/utils/clone-template.ts
shell-quote import is removed. A promisified execFile helper is introduced to replace the previous promisified exec pattern for structured argument-based process execution.
cloneTemplate entry point refactoring
packages/cli/src/utils/clone-template.ts
cloneTemplate function is reformatted with multiline projectPath construction and updated multiline error message generation while preserving the existence check → clone → configuration update → success control flow.
Repository cloning with structured execFile invocation
packages/cli/src/utils/clone-template.ts
cloneRepositoryWithoutGit now creates the target directory upfront, attempts npx degit with simplified owner/repo#branch selector, falls back to git clone using explicit argument arrays, and removes the .git directory after clone completion.
Package and environment configuration helpers
packages/cli/src/utils/clone-template.ts
updatePackageJson and updateEnvFile preserve existing behavior—JSON read/parse/write and MODEL= line replacement—with revised error-handling structure and adjusted warning messages when the model identifier is missing.
Dependency installation via execFile
packages/cli/src/utils/clone-template.ts
installDependencies is rewritten to invoke the package manager install command via execFile(pm, ['install']) with cwd set to projectPath, replacing the previous shell-quoted command string approach.

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.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
Title check ✅ Passed The title is fully related to the main change: replacing exec+shell-quote with execFile for safer command execution and improving error handling in cloneTemplate utility.
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@dane-ai-mastra dane-ai-mastra Bot added the needs-issue PR is missing a linked issue label Jun 6, 2026
@dane-ai-mastra

dane-ai-mastra Bot commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

PR triage

Linked issue check passed (#17646).

Mastra uses CodeRabbit for automated code reviews. Please address all feedback from CodeRabbit by either making changes to your PR or leaving a comment explaining why you disagree with the feedback. Since CodeRabbit is an AI, it may occasionally provide incorrect feedback.


PR complexity score

Factor Value Score impact
Files changed 1 +2
Lines changed 143 +6
Author merged PRs 0 -0
Test files changed No -0
Final score 8

Applied label: complexity: low


Changed test gate

Changed Test Gate is pending. The Changed Test Gate / changed-tests check will update the test label when it completes.

@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.

Actionable comments posted: 1

🤖 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.

Inline comments:
In `@packages/cli/src/utils/clone-template.ts`:
- Around line 35-37: The clone flow leaves targetPath behind on failure causing
the directoryExists guard in the projectPath/projectName check to fail on
retries; update the cloning logic in clone-template.ts to either (a) perform the
clone into a temporary directory (e.g., tmpTarget) and rename/move it to
targetPath only after the clone succeeds, or (b) ensure any created targetPath
is removed in all failure paths (catch blocks and both degit and git fallback
code paths) before rethrowing; reference the projectPath/projectName checks,
targetPath creation, and the degit/git fallback branches so cleanup is executed
consistently on errors and spinner.error is still called.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8c41d630-f107-46af-adc0-1f1e47d4491f

📥 Commits

Reviewing files that changed from the base of the PR and between ede1fa9 and 8cfccd9.

📒 Files selected for processing (1)
  • packages/cli/src/utils/clone-template.ts

Comment on lines 35 to 37
if (await directoryExists(projectPath)) {
spinner.error(`Directory ${projectName} already exists`);
throw new Error(`Directory ${projectName} already exists`);

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Clean up targetPath when cloning fails.

Because Line 92 creates targetPath before either clone command runs, any failure path leaves that directory behind. The next retry then immediately trips the existing-directory guard at Lines 35-37, and the git fallback is also fragile if degit wrote anything before throwing. Remove targetPath on failure, or clone into a temp directory and rename only after success.

Also applies to: 92-119

🤖 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/cli/src/utils/clone-template.ts` around lines 35 - 37, The clone
flow leaves targetPath behind on failure causing the directoryExists guard in
the projectPath/projectName check to fail on retries; update the cloning logic
in clone-template.ts to either (a) perform the clone into a temporary directory
(e.g., tmpTarget) and rename/move it to targetPath only after the clone
succeeds, or (b) ensure any created targetPath is removed in all failure paths
(catch blocks and both degit and git fallback code paths) before rethrowing;
reference the projectPath/projectName checks, targetPath creation, and the
degit/git fallback branches so cleanup is executed consistently on errors and
spinner.error is still called.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@DawitMengistu can you address this comment? Thanks!

@dane-ai-mastra dane-ai-mastra Bot added complexity: low Low-complexity PR and removed needs-issue PR is missing a linked issue labels Jun 6, 2026
@vercel vercel Bot temporarily deployed to Preview – mastra-playground-ui June 9, 2026 12:36 Inactive
@vercel vercel Bot temporarily deployed to Preview – mastra-docs-1.x June 9, 2026 12:36 Inactive
@wardpeet

wardpeet commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Tests seem to fail as well

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

Labels

complexity: low Low-complexity PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Windows fails to clone templates due to shell-quote escaping in create-mastra

3 participants