Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions e2e/tasks/test_task_monorepo_includes
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#!/usr/bin/env bash

# Test monorepo task edge cases: multiple dot extensions
export MISE_EXPERIMENTAL=1

# Create monorepo root config
cat <<EOF >mise.toml
experimental_monorepo_root = true

[tasks.root-task]
run = 'echo "root task"'
EOF

# Create project structure with task_config.includes
mkdir -p projects/frontend/tasks
cat <<EOF >projects/frontend/mise.toml
[task_config]
includes = ["tasks.toml", "tasks"]
EOF

# Create included TOML file with a task
cat <<EOF >projects/frontend/tasks.toml
[toml-task]
run = 'echo "task from toml"'
EOF

# Create included script task
cat <<'EOF' >projects/frontend/tasks/script-task.sh
#!/usr/bin/env bash
echo "task from script"
EOF
chmod +x projects/frontend/tasks/script-task.sh

# Create included script task with multiple extensions
cat <<'EOF' >projects/frontend/tasks/another.long-name.sh
#!/usr/bin/env bash
echo "task with long name"
EOF
chmod +x projects/frontend/tasks/another.long-name.sh

# --- Assertions ---

# Verify all tasks are discovered
assert_contains "mise tasks ls --debug --all" "//:root-task"
assert_contains "mise tasks ls --all" "//projects/frontend:toml-task"
assert_contains "mise tasks ls --all" "//projects/frontend:script-task"
assert_contains "mise tasks ls --all" "//projects/frontend:another.long-name"

# Verify running tasks from root
assert_contains "mise run '//projects/frontend:toml-task'" "task from toml"
assert_contains "mise run '//projects/frontend:script-task'" "task from script"
assert_contains "mise run '//projects/frontend:another.long-name'" "task with long name"
assert_contains "mise run '//projects/frontend:another.long-name.sh'" "task with long name"

# Verify running from a subdirectory
cd projects/frontend
assert_contains "mise run :toml-task" "task from toml"
assert_contains "mise run :script-task" "task from script"
assert_contains "mise run :another.long-name" "task with long name"

# Verify wildcard execution from subdirectory
output=$(mise run ':*')
assert_contains "echo '$output'" "task from toml"
assert_contains "echo '$output'" "task from script"
assert_contains "echo '$output'" "task with long name"

cd ../..

# Verify wildcard execution from root
output=$(mise run '//projects/frontend:*')
assert_contains "echo '$output'" "task from toml"
assert_contains "echo '$output'" "task from script"
assert_contains "echo '$output'" "task with long name"
78 changes: 34 additions & 44 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1611,36 +1611,39 @@ async fn load_tasks_includes(
root: &Path,
config_root: &Path,
) -> Result<Vec<Task>> {
if !root.is_dir() {
return Ok(vec![]);
}
let files = WalkDir::new(root)
.follow_links(true)
.into_iter()
// skip hidden directories (if the root is hidden that's ok)
.filter_entry(|e| e.path() == root || !e.file_name().to_string_lossy().starts_with('.'))
.filter_ok(|e| e.file_type().is_file())
.map_ok(|e| e.path().to_path_buf())
.try_collect::<_, Vec<PathBuf>, _>()?
.into_iter()
.filter(|p| file::is_executable(p))
.filter(|p| {
!Settings::get()
.task_disable_paths
.iter()
.any(|d| p.starts_with(d))
})
.collect::<Vec<_>>();
let mut tasks = vec![];
let root = Arc::new(root.to_path_buf());
let config_root = Arc::new(config_root.to_path_buf());
for path in files {
let root = root.clone();
let config_root = config_root.clone();
let config = config.clone();
tasks.push(Task::from_path(&config, &path, &root, &config_root).await?);
if root.is_file() {
load_task_file(config, root, config_root).await
Comment thread
risu729 marked this conversation as resolved.
} else if root.is_dir() {
Comment thread
risu729 marked this conversation as resolved.
let files = WalkDir::new(root)
.follow_links(true)
.into_iter()
// skip hidden directories (if the root is hidden that's ok)
.filter_entry(|e| e.path() == root || !e.file_name().to_string_lossy().starts_with('.'))
.filter_ok(|e| e.file_type().is_file())
.map_ok(|e| e.path().to_path_buf())
.try_collect::<_, Vec<PathBuf>, _>()?
.into_iter()
.filter(|p| file::is_executable(p))
.filter(|p| {
!Settings::get()
.task_disable_paths
.iter()
.any(|d| p.starts_with(d))
})
.collect::<Vec<_>>();
let mut tasks = vec![];
let root = Arc::new(root.to_path_buf());
let config_root = Arc::new(config_root.to_path_buf());
for path in files {
let root = root.clone();
let config_root = config_root.clone();
let config = config.clone();
tasks.push(Task::from_path(&config, &path, &root, &config_root).await?);
}
Ok(tasks)
} else {
Ok(vec![])
}
Ok(tasks)
}

async fn load_file_tasks(
Expand Down Expand Up @@ -1692,22 +1695,9 @@ pub async fn load_tasks_in_dir(
let dir = dir.to_path_buf();
config_tasks.extend(load_config_tasks(config, cf.clone(), &dir).await?);
}
let includes = task_includes_for_dir(dir, config_files);
let extra_tasks = includes
.iter()
.filter(|p| p.is_file() && p.extension().unwrap_or_default().to_string_lossy() == "toml");
for p in extra_tasks {
let p = p.clone();
let dir = dir.to_path_buf();
let config = config.clone();
config_tasks.extend(load_task_file(&config, &p, &dir).await?);
}
let mut file_tasks = vec![];
for p in includes {
let dir = dir.to_path_buf();
let p = p.clone();
let config = config.clone();
file_tasks.extend(load_tasks_includes(&config, &p, &dir).await?);
for p in task_includes_for_dir(dir, config_files) {
file_tasks.extend(load_tasks_includes(config, &p, dir).await?);
}
let mut tasks = file_tasks
.into_iter()
Expand Down
Loading