Skip to content

fix(task): handle dots in monorepo directory names correctly#6571

Merged
jdx merged 3 commits intomainfrom
fix-monorepo-tasks-with-dots
Oct 6, 2025
Merged

fix(task): handle dots in monorepo directory names correctly#6571
jdx merged 3 commits intomainfrom
fix-monorepo-tasks-with-dots

Conversation

@jdx
Copy link
Copy Markdown
Owner

@jdx jdx commented Oct 6, 2025

Summary

Fixes a bug where monorepo task names with dots in directory paths were incorrectly truncated when displayed. For example, //projects/my.app:build would appear as //projects/my instead of the full path.

Changes

  • Modified Task::display_name() to only strip file extensions from the task name portion after the last colon (:)
  • Updated Task::is_match() to handle colon-separated task names consistently
  • Added comprehensive e2e test test_task_monorepo_dots_in_dir covering various edge cases

Test Coverage

The new test covers:

  • Single dots in directory names (e.g., my.app)
  • Multiple dots (e.g., my.service.api)
  • Version-like dots (e.g., feature.v2.beta)
  • Cross-directory task dependencies
  • Wildcard matching with //...:task patterns

Example

Before: //projects/my.app:build → displayed as //projects/my
After: //projects/my.app:build → displayed as //projects/my.app:build

🤖 Generated with Claude Code


Note

Fixes display and pattern matching for monorepo tasks with dots by stripping extensions only from the task segment and adds comprehensive e2e tests.

  • Task resolution/display:
    • Task::display_name(): Strip file extensions only after the last : so monorepo paths with dots (e.g., //projects/my.app:build.sh) display correctly.
    • Task::is_match(): Align matching logic to handle colon-separated names and extension stripping, supporting simple patterns (e.g., build) against monorepo tasks.
  • Tests:
    • Add e2e/tasks/test_task_monorepo_dots_in_dir covering directories with dots, cross-project dependencies, wildcard //...:task matching, and false-positive checks.

Written by Cursor Bugbot for commit 6f765fc. This will update automatically on new commits. Configure here.

Previously, the `display_name()` method would strip extensions from the
entire task name by splitting on the last dot. This caused issues with
monorepo tasks where directory names contained dots (e.g.,
"//projects/my.app:build" would become "//projects/my").

Now the method only strips extensions from the task name portion after
the last colon, preserving dots in the path prefix. This allows monorepo
subdirectories to have dots in their names without breaking task display
and matching.

Also updated `is_match()` to handle colon-separated names consistently.

Includes comprehensive e2e test for monorepo tasks with dots in directory
names, covering:
- Single dots (my.app)
- Multiple dots (my.service.api)
- Version-like dots (feature.v2.beta)
- Cross-directory dependencies
- Wildcard matching

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings October 6, 2025 19:18
cursor[bot]

This comment was marked as outdated.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

Fixes a bug where monorepo task names with dots in directory paths were incorrectly truncated when displayed, ensuring full task names are preserved while only stripping file extensions from the task portion.

  • Modified Task::display_name() to only strip file extensions after the last colon separator
  • Updated Task::is_match() to handle colon-separated task names consistently for pattern matching
  • Added comprehensive e2e test covering various edge cases with dots in directory names

Reviewed Changes

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

File Description
src/task/mod.rs Improved task name parsing to handle dots in monorepo directory paths correctly
e2e/tasks/test_task_monorepo_dots_in_dir Added comprehensive e2e test for various dot patterns in directory names

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment thread src/task/mod.rs
Comment on lines +263 to +271
let task_without_ext = task_part.rsplitn(2, '.').last().unwrap_or_default();
format!("{}:{}", prefix, task_without_ext)
} else {
// No colon separator (e.g., "build.sh")
// Strip extension from the whole name
self.name
.rsplitn(2, '.')
.last()
.unwrap_or_default()
Copy link

Copilot AI Oct 6, 2025

Choose a reason for hiding this comment

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

The logic is incorrect. rsplitn(2, '.') followed by last() returns the part before the last dot, not after removing the extension. For 'build.sh', this returns 'build.sh' instead of 'build'. Use rsplitn(2, '.').nth(1).unwrap_or(task_part) to get the part before the extension.

Suggested change
let task_without_ext = task_part.rsplitn(2, '.').last().unwrap_or_default();
format!("{}:{}", prefix, task_without_ext)
} else {
// No colon separator (e.g., "build.sh")
// Strip extension from the whole name
self.name
.rsplitn(2, '.')
.last()
.unwrap_or_default()
let task_without_ext = task_part.rsplitn(2, '.').nth(1).unwrap_or(task_part);
format!("{}:{}", prefix, task_without_ext)
} else {
// No colon separator (e.g., "build.sh")
// Strip extension from the whole name
self.name
.rsplitn(2, '.')
.nth(1)
.unwrap_or(&self.name)

Copilot uses AI. Check for mistakes.
Comment thread src/task/mod.rs Outdated
Comment on lines +292 to +297
let name_ext_stripped = task_part.rsplitn(2, '.').last().unwrap_or_default();
let pat_ext_stripped = if let Some((_, pat_task)) = pat.rsplit_once(':') {
pat_task.rsplitn(2, '.').last().unwrap_or_default()
} else {
pat.rsplitn(2, '.').last().unwrap_or_default()
};
Copy link

Copilot AI Oct 6, 2025

Choose a reason for hiding this comment

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

Same logic error as in display_name(). The rsplitn(2, '.').last() pattern returns the original string instead of stripping the extension. Should use rsplitn(2, '.').nth(1).unwrap_or(original) to properly remove extensions.

Copilot uses AI. Check for mistakes.
Comment thread src/task/mod.rs Outdated
Comment on lines +302 to +303
self.name.rsplitn(2, '.').last().unwrap_or_default(),
pat.rsplitn(2, '.').last().unwrap_or_default(),
Copy link

Copilot AI Oct 6, 2025

Choose a reason for hiding this comment

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

Same logic error repeated in the else branch. These lines also incorrectly use rsplitn(2, '.').last() which doesn't strip extensions properly.

Copilot uses AI. Check for mistakes.
The previous implementation of is_match() only compared the task name
portion after the colon, ignoring the path prefix. This caused false
positive matches where `//projects/frontend:build` would match
`//projects/backend:build`.

Now the method preserves the full path prefix when comparing task names,
only stripping file extensions from the task portion. This ensures tasks
from different projects/directories don't incorrectly match each other.

Added test case to verify no false positive matches occur between tasks
from different directories with the same task name.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
cursor[bot]

This comment was marked as outdated.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Oct 6, 2025

Hyperfine Performance

mise x -- echo

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2025.10.4 x -- echo 18.1 ± 0.4 17.6 22.6 1.00
mise x -- echo 18.2 ± 0.3 17.7 19.2 1.00 ± 0.02

mise env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2025.10.4 env 17.5 ± 0.3 17.0 20.1 1.00
mise env 17.7 ± 0.4 17.1 20.7 1.01 ± 0.03

mise hook-env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2025.10.4 hook-env 17.2 ± 0.3 16.8 19.0 1.00
mise hook-env 17.3 ± 0.3 16.8 18.4 1.01 ± 0.02

mise ls

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2025.10.4 ls 15.2 ± 0.2 14.7 16.4 1.00
mise ls 15.4 ± 0.3 14.9 17.2 1.01 ± 0.02

xtasks/test/perf

Command mise-2025.10.4 mise Variance
install (cached) 199ms ✅ 106ms +87%
ls (cached) 63ms 63ms +0%
bin-paths (cached) 70ms 70ms +0%
task-ls (cached) 481ms 469ms +2%

✅ Performance improvement: install cached is 87%

The previous fix prevented simple patterns from matching monorepo tasks.
When a pattern lacked a colon (e.g., "build"), it would only compare
against the stripped pattern, not the full task name with its prefix.
This broke the ability to run monorepo tasks using just their task name.

Now the matching logic properly handles:
1. Simple pattern (e.g., "build") matches monorepo tasks by comparing
   only the task portion after the colon
2. Full pattern (e.g., "//projects/my.app:build") matches only tasks
   with the exact path prefix
3. Extensions are stripped for all comparisons

Added test case to verify simple pattern matching works correctly.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Comment thread src/task/mod.rs
let display_name = if let Some((prefix, task_part)) = self.name.rsplit_once(':') {
// Has a colon separator (e.g., "//projects/my.app:build.sh")
// Strip extension from the task part only
let task_without_ext = task_part.rsplitn(2, '.').last().unwrap_or_default();
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bug: File Extension Stripping Bug

The display_name and is_match functions incorrectly strip file extensions. The rsplitn(2, '.').last() call returns the extension (e.g., "sh" for "build.sh") instead of the base name without the extension.

Fix in Cursor Fix in Web

@jdx jdx enabled auto-merge (squash) October 6, 2025 19:45
@jdx jdx merged commit 8a201fd into main Oct 6, 2025
26 checks passed
@jdx jdx deleted the fix-monorepo-tasks-with-dots branch October 6, 2025 19:50
jdx pushed a commit that referenced this pull request Oct 7, 2025
### 📦 Registry

- add jules by @alefteris in
[#6568](#6568)

### 🐛 Bug Fixes

- **(docs)** improve favicon support for Safari by @jdx in
[#6567](#6567)
- **(github)** download assets via API to respect GITHUB_TOKEN by @roele
in [#6496](#6496)
- **(task)** load toml tasks in `task_config.includes` in system/global
config and monorepo subdirs by @risu729 in
[#6545](#6545)
- **(task)** handle dots in monorepo directory names correctly by @jdx
in [#6571](#6571)

### 📚 Documentation

- **(readme)** add GitHub Issues & Discussions section by @rsyring in
[#6573](#6573)
- **(tasks)** create dedicated monorepo tasks documentation by @jdx in
[#6561](#6561)
- **(tasks)** enhance monorepo documentation with tool comparisons by
@jdx in [#6563](#6563)
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.

2 participants