-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
fix(task): handle dots in monorepo directory names correctly #6571
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,113 @@ | ||
| #!/usr/bin/env bash | ||
| # Test monorepo tasks with dots in directory names | ||
| export MISE_EXPERIMENTAL=1 | ||
|
|
||
| # Create monorepo root config | ||
| cat <<'TOML' >mise.toml | ||
| experimental_monorepo_root = true | ||
|
|
||
| [tasks.root-task] | ||
| run = 'echo "root task executed"' | ||
| TOML | ||
|
|
||
| # Create projects with dots in directory names | ||
| mkdir -p "projects/my.app" | ||
| cat <<'TOML' >"projects/my.app/mise.toml" | ||
| [tasks.build] | ||
| run = 'echo "building my.app"' | ||
|
|
||
| [tasks.test] | ||
| run = 'echo "testing my.app"' | ||
| TOML | ||
|
|
||
| mkdir -p "projects/my.service.api" | ||
| cat <<'TOML' >"projects/my.service.api/mise.toml" | ||
| [tasks.build] | ||
| run = 'echo "building my.service.api"' | ||
|
|
||
| [tasks.deploy] | ||
| depends = ["//projects/my.app:build"] | ||
| run = 'echo "deploying my.service.api"' | ||
| TOML | ||
|
|
||
| mkdir -p "projects/feature.v2.beta" | ||
| cat <<'TOML' >"projects/feature.v2.beta/mise.toml" | ||
| [tasks.test] | ||
| run = 'echo "testing feature.v2.beta"' | ||
| TOML | ||
|
|
||
| # Test 1: List tasks for directory with single dot | ||
| echo "=== Test 1: List tasks for my.app ===" | ||
| assert_contains "mise tasks ls --all" "//projects/my.app:build" | ||
| assert_contains "mise tasks ls --all" "//projects/my.app:test" | ||
|
|
||
| # Test 2: List tasks for directory with multiple dots | ||
| echo "=== Test 2: List tasks for my.service.api ===" | ||
| assert_contains "mise tasks ls --all" "//projects/my.service.api:build" | ||
| assert_contains "mise tasks ls --all" "//projects/my.service.api:deploy" | ||
|
|
||
| # Test 3: List tasks for directory with version-like dots | ||
| echo "=== Test 3: List tasks for feature.v2.beta ===" | ||
| assert_contains "mise tasks ls --all" "//projects/feature.v2.beta:test" | ||
|
|
||
| # Test 4: Run task from directory with single dot | ||
| echo "=== Test 4: Run build task from my.app ===" | ||
| assert_contains "mise run '//projects/my.app:build'" "building my.app" | ||
|
|
||
| # Test 5: Run task from directory with multiple dots | ||
| echo "=== Test 5: Run build task from my.service.api ===" | ||
| assert_contains "mise run '//projects/my.service.api:build'" "building my.service.api" | ||
|
|
||
| # Test 6: Run task with dependency across directories with dots | ||
| echo "=== Test 6: Run deploy with cross-directory dependency ===" | ||
| output=$(mise run '//projects/my.service.api:deploy') | ||
| echo "$output" | ||
| echo "$output" | grep -q "building my.app" || (echo "FAIL: Dependency task from my.app not run" && exit 1) | ||
| echo "$output" | grep -q "deploying my.service.api" || (echo "FAIL: Deploy task not run" && exit 1) | ||
|
|
||
| # Test 7: Wildcard matching with dots in directory names | ||
| echo "=== Test 7: Wildcard matching ===" | ||
| assert_contains "mise run '//projects/...:build'" "building my.app" | ||
| assert_contains "mise run '//projects/...:build'" "building my.service.api" | ||
|
|
||
| # Test 8: Run all test tasks | ||
| echo "=== Test 8: Run all test tasks ===" | ||
| output=$(mise run '//...:test') | ||
| echo "$output" | ||
| echo "$output" | grep -q "testing my.app" || (echo "FAIL: my.app test not run" && exit 1) | ||
| echo "$output" | grep -q "testing feature.v2.beta" || (echo "FAIL: feature.v2.beta test not run" && exit 1) | ||
|
|
||
| # Test 9: Verify no false positive matches between different projects | ||
| echo "=== Test 9: Verify no false positive task matching ===" | ||
| # Running //projects/my.app:build should NOT run //projects/my.service.api:build | ||
| output=$(mise run '//projects/my.app:build') | ||
| echo "$output" | ||
| echo "$output" | grep -q "building my.app" || (echo "FAIL: my.app build not run" && exit 1) | ||
| # Verify my.service.api:build was NOT run (would contain "building my.service.api") | ||
| if echo "$output" | grep -q "building my.service.api"; then | ||
| echo "FAIL: False positive - my.service.api:build should not have run" | ||
| exit 1 | ||
| fi | ||
| echo "SUCCESS: No false positive matches" | ||
|
|
||
| # Test 10: Verify simple patterns can match monorepo tasks | ||
| echo "=== Test 10: Simple pattern matching ===" | ||
| # Create a simple task in the current directory for comparison | ||
| cat <<'TOML' >>mise.toml | ||
|
|
||
| [tasks.local-build] | ||
| run = 'echo "local build"' | ||
| TOML | ||
|
|
||
| # Test that pattern "build" matches monorepo tasks | ||
| output=$(mise tasks ls --all) | ||
| echo "$output" | ||
| # Count how many "build" tasks exist (should be at least 3: my.app, my.service.api, and local-build) | ||
| build_count=$(echo "$output" | grep -c ":build\|local-build" || true) | ||
| if [ "$build_count" -lt 3 ]; then | ||
| echo "FAIL: Expected at least 3 build tasks, found $build_count" | ||
| exit 1 | ||
| fi | ||
| echo "SUCCESS: Found $build_count build tasks" | ||
|
|
||
| echo "=== All tests with dots in directory names passed! ===" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -254,12 +254,24 @@ impl Task { | |||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| /// prints the task name without an extension | ||||||||||||||||||||||||||||||||||||||
| pub fn display_name(&self, all_tasks: &BTreeMap<String, Task>) -> String { | ||||||||||||||||||||||||||||||||||||||
| let display_name = self | ||||||||||||||||||||||||||||||||||||||
| .name | ||||||||||||||||||||||||||||||||||||||
| .rsplitn(2, '.') | ||||||||||||||||||||||||||||||||||||||
| .last() | ||||||||||||||||||||||||||||||||||||||
| .unwrap_or_default() | ||||||||||||||||||||||||||||||||||||||
| .to_string(); | ||||||||||||||||||||||||||||||||||||||
| // For task names, only strip extensions after the last colon (:) | ||||||||||||||||||||||||||||||||||||||
| // This handles monorepo task names like "//projects/my.app:build.sh" | ||||||||||||||||||||||||||||||||||||||
| // where we want to strip ".sh" but keep "my.app" intact | ||||||||||||||||||||||||||||||||||||||
| 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(); | ||||||||||||||||||||||||||||||||||||||
| 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() | ||||||||||||||||||||||||||||||||||||||
|
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() | |
| 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) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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_nameandis_matchfunctions incorrectly strip file extensions. Thersplitn(2, '.').last()call returns the extension (e.g., "sh" for "build.sh") instead of the base name without the extension.