Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
35 changes: 35 additions & 0 deletions e2e/tasks/test_task_non_executable_error
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/usr/bin/env bash

# Test that non-executable files in mise-tasks/ give a helpful error message
# Issue: https://github.com/jdx/mise/discussions/8614

# Create a mise-tasks directory with non-executable files
mkdir -p mise-tasks
cat <<'EOF' >mise-tasks/build
#!/usr/bin/env bash
echo "building"
EOF
cat <<'EOF' >mise-tasks/test
#!/usr/bin/env bash
echo "testing"
EOF

# Ensure files are NOT executable
chmod -x mise-tasks/build mise-tasks/test

# Create a minimal mise.toml so the directory is recognized as a project
cat <<EOF >mise.toml
EOF

# Running a task should give a helpful error mentioning non-executable files
assert_fail_contains "mise run build" "non-executable"
assert_fail_contains "mise run build" "chmod +x"

# mise tasks ls should warn about non-executable files
assert_contains "mise tasks 2>&1" "non-executable"
Comment thread
greptile-apps[bot] marked this conversation as resolved.
Resolved

# After making files executable, tasks should work
chmod +x mise-tasks/build mise-tasks/test
assert_contains "mise tasks" "build"
assert_contains "mise tasks" "test"
assert "mise run build" "building"
22 changes: 20 additions & 2 deletions src/cli/tasks/ls.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use std::sync::Arc;

use crate::config::Config;
use crate::file::display_rel_path;
use crate::config::{self, Config};
use crate::dirs;
use crate::file::{self, display_rel_path};
use crate::task::Task;
use crate::task::task_fetcher::TaskFetcher;
use crate::ui::table::MiseTable;
Expand Down Expand Up @@ -122,6 +123,23 @@ impl TasksLs {
// MISE_TASK_REMOTE_NO_CACHE env var is still respected if set
TaskFetcher::new(false).fetch_tasks(&mut tasks).await?;

if tasks.is_empty() && !cfg!(windows) {
if let Some(cwd) = &*dirs::CWD {
let includes = config::task_includes_for_dir(cwd, &config.config_files);
let has_non_exec = includes.iter().filter(|d| d.is_dir()).any(|d| {
walkdir::WalkDir::new(d)
.into_iter()
.filter_map(|e| e.ok())
.any(|e| e.file_type().is_file() && !file::is_executable(e.path()))
});
if has_non_exec {
warn!(
"no tasks found, but non-executable files exist in task directories.\nFiles must be executable to be detected as tasks. Run `chmod +x` on the task files to fix this."
);
}
}
}
Comment thread
greptile-apps[bot] marked this conversation as resolved.
Resolved
Comment thread
cursor[bot] marked this conversation as resolved.
Resolved

if self.complete {
return self.complete(tasks);
} else if self.usage {
Expand Down
39 changes: 39 additions & 0 deletions src/task/task_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,45 @@ async fn err_no_task(config: &Config, name: &str) -> Result<()> {
}
}

// Check if there are non-executable files in task include directories
if !cfg!(windows) {
if let Some(cwd) = &*dirs::CWD {
let includes = config::task_includes_for_dir(cwd, &config.config_files);
let non_exec_files: Vec<PathBuf> = includes
.iter()
.filter(|d| d.is_dir())
.flat_map(|d| {
walkdir::WalkDir::new(d)
.into_iter()
.filter_map(|e| e.ok())
.filter(|e| e.file_type().is_file() && !file::is_executable(e.path()))
.map(|e| e.path().to_path_buf())
})
.collect();
Comment thread
greptile-apps[bot] marked this conversation as resolved.
Resolved
if !non_exec_files.is_empty() {
let dirs_with_files: Vec<String> = includes
.iter()
.filter(|d| d.is_dir())
.map(|d| display_path(d))
.collect();
bail!(
"no tasks defined in {}, but found {} non-executable file(s) in {}.\n\
Files must be executable to be detected as tasks.\n\
Run `chmod +x` on the task files to fix this, e.g.:\n chmod +x {}",
display_path(dirs::CWD.clone().unwrap_or_default()),
non_exec_files.len(),
dirs_with_files.join(", "),
non_exec_files
.iter()
.take(5)
.map(|p| display_path(p))
.collect::<Vec<_>>()
.join(" "),
);
Comment thread
greptile-apps[bot] marked this conversation as resolved.
Resolved
}
Comment thread
greptile-apps[bot] marked this conversation as resolved.
Resolved
}
}
Comment thread
cursor[bot] marked this conversation as resolved.
Resolved

bail!(
"no tasks defined in {}. Are you in a project directory?",
display_path(dirs::CWD.clone().unwrap_or_default())
Expand Down
Loading