Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
eb6404d
Rename config file and update log file naming convention
etcoder-642 Jun 29, 2026
72f801c
Update test configuration and execution workflow
etcoder-642 Jun 29, 2026
9f1e6c2
updated test for the session logger
etcoder-642 Jun 29, 2026
84af010
Refactor path handling and improve task reporting
etcoder-642 Jun 30, 2026
d218676
Update Task constructor arguments in unit tests
etcoder-642 Jun 30, 2026
00aec76
Refactor task identification to use paths
etcoder-642 Jun 30, 2026
8071f39
Increase default max depth for path watching
etcoder-642 Jun 30, 2026
254f2b8
Persist task state and update default watching depth
etcoder-642 Jun 30, 2026
e0af4f3
Add set command and implement task settings
etcoder-642 Jun 30, 2026
3332f3c
Added bash script to test the CLI layer
etcoder-642 Jun 30, 2026
0ed4881
Add -f flag to remove command and add unit tests
etcoder-642 Jul 1, 2026
d6b6e0f
Add check command to CLI
etcoder-642 Jul 1, 2026
bc8b238
Implement task configuration settings
etcoder-642 Jul 1, 2026
d68f330
Add CLI integration tests to CI workflow
etcoder-642 Jul 1, 2026
88828b9
Update tests.yml
etcoder-642 Jul 1, 2026
b983657
Remove redundant directory changes in test scripts
etcoder-642 Jul 1, 2026
e1e570d
Install flowhook binary to bin directory
etcoder-642 Jul 1, 2026
43d360d
Update installation targets for flowhook_cli
etcoder-642 Jul 1, 2026
3cc643a
Ignore paths matching ignore patterns
etcoder-642 Jul 1, 2026
bdac0c4
Handle missing paths during initialization
etcoder-642 Jul 1, 2026
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
12 changes: 10 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Tests

on:
push:
branches: [core, main]
branches: [dev, main, test/all]
pull_request:
branches: [main]

Expand Down Expand Up @@ -31,4 +31,12 @@ jobs:
run: cmake --build build

- name: Run tests
run: ctest --test-dir build --output-on-failure
run: cd build && ./test_config_manager && ./test_task_runner && ./test_session_logger && ./test_flowhook_core

- name: Install flowhook binary
run: |
sudo cmake --install build
which flowhook || echo "flowhook not found after install"

- name: Run CLI tests
run: bash cli/tests/test_cli.sh
9 changes: 8 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,17 @@ add_executable(flowhook_cli cli/src/main.cpp
cli/src/commands/run.cpp
cli/src/commands/init.cpp
cli/src/commands/list.cpp
cli/src/commands/remove.cpp)
cli/src/commands/remove.cpp
cli/src/commands/set.cpp
cli/src/commands/check.cpp)
target_link_libraries(flowhook_cli PRIVATE flowhook_lib)
target_include_directories(flowhook_cli PRIVATE cli/src)

install(TARGETS flowhook_cli DESTINATION bin)
install(PROGRAMS $<TARGET_FILE:flowhook_cli>
DESTINATION bin
RENAME flowhook)

enable_testing()
add_test(NAME flowhook_filewatcher_tests COMMAND test_filewatcher)
add_test(NAME flowhook_task_runner_tests COMMAND test_task_runner)
Expand Down
14 changes: 7 additions & 7 deletions cli/src/commands/add.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,15 @@ void register_add(CLI::App *app, flowhook::FlowHookCore *fh) {
if (add_n_path.empty() && add_command.empty() &&
command_on_success.empty() && command_on_failure.empty() &&
ignored_path.empty() && ignored_pattern.empty()) {
std::cerr << "At least one of --path, --command, --on-success, --on-failure, --ignored-path, or --ignored-pattern is required\n"
std::cout << "Error: At least one of --path, --command, --on-success, --on-failure, --ignored-path, or --ignored-pattern is required\n"
<< std::endl;
return;
}

if (!add_n_path.empty()) {
auto r = fh->set_task_path(task_id, add_n_path);
if (r.isErr()) {
std::cerr << "Failed to set task path: " << r.getErrMessage()
std::cout << "Failed to set task path: " << r.getErrMessage()
<< std::endl;
return;
}
Expand All @@ -61,7 +61,7 @@ void register_add(CLI::App *app, flowhook::FlowHookCore *fh) {
if (!add_command.empty()) {
auto r = fh->set_task_command(task_id, add_command);
if (r.isErr()) {
std::cerr << "Failed to set task command: " << r.getErrMessage()
std::cout << "Failed to set task command: " << r.getErrMessage()
<< std::endl;
return;
}
Expand All @@ -70,7 +70,7 @@ void register_add(CLI::App *app, flowhook::FlowHookCore *fh) {
if (!command_on_success.empty()) {
auto r = fh->set_task_on_success(task_id, command_on_success);
if (r.isErr()) {
std::cerr << "Failed to set task on success: " << r.getErrMessage()
std::cout << "Failed to set task on success: " << r.getErrMessage()
<< std::endl;
return;
}
Expand All @@ -79,7 +79,7 @@ void register_add(CLI::App *app, flowhook::FlowHookCore *fh) {
if (!command_on_failure.empty()) {
auto r = fh->set_task_on_failure(task_id, command_on_failure);
if (r.isErr()) {
std::cerr << "Failed to set task on failure: " << r.getErrMessage()
std::cout << "Failed to set task on failure: " << r.getErrMessage()
<< std::endl;
return;
}
Expand All @@ -88,7 +88,7 @@ void register_add(CLI::App *app, flowhook::FlowHookCore *fh) {
if (!ignored_path.empty()) {
auto r = fh->set_ignored_path(task_id, ignored_path);
if (r.isErr()) {
std::cerr << "Failed to set ignored path: " << r.getErrMessage()
std::cout << "Failed to set ignored path: " << r.getErrMessage()
<< std::endl;
return;
}
Expand All @@ -97,7 +97,7 @@ void register_add(CLI::App *app, flowhook::FlowHookCore *fh) {
if (!ignored_pattern.empty()) {
auto r = fh->set_ignored_pattern(task_id, ignored_pattern);
if (r.isErr()) {
std::cerr << "Failed to set ignored pattern: " << r.getErrMessage()
std::cout << "Failed to set ignored pattern: " << r.getErrMessage()
<< std::endl;
return;
}
Expand Down
78 changes: 78 additions & 0 deletions cli/src/commands/check.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#include "../../src/include/flowhook_core.h"
#include "../include/CLI11.hpp"
#include "../../src/include/macros.hpp"


namespace fs = std::filesystem;

namespace flowhook_cli {
void register_check(CLI::App* app, flowhook::FlowHookCore* fh)
{
auto* check = app->add_subcommand("check", "check the status of the task");

// this is a purely ceremonial flag(so that CLI11 won't say there is unrecognized flag)
// the actual parsing of verbose and setting of the environment variable is done in main.cpp
check->add_flag("--debug", FLOWHOOK_DEBUG, "Enable debug output. \n// is an extremely detailed output that shows every step of the process.");
check->add_flag("--verbose", FLOWHOOK_VERBOSE, "Enable verbose output. \n// shows a summary of the process.");


static bool active = false;
check->add_flag("--active", active, "Check if task is labeled as active");

static bool deactive = false;
check->add_flag("--deactive", deactive, "Check if task is labeled as deactive");

static bool depth;
check->add_flag("--depth", depth, "Outputs the watching depth set from your working directory.");

check->callback([=]() mutable {
std::string check_task_id = fs::current_path().string();

if(!active && !deactive && !depth) {
if(!fh->is_task(check_task_id)) {
std::cout << "FlowHook task has not been initialized in the current directory." << std::endl;
return;
}else {
std::cout << "FlowHook task is initialized in the current directory." << std::endl;
return;
}
}

if (active) {
auto r = fh->is_task_active(check_task_id);
if (r.isErr()) {
std::cout << "Failed to check if task is active: " << r.getErrMessage()
<< std::endl;
return;
}
if (r.unwrap()) {
std::cout << "Task " << check_task_id << " is active" << std::endl;
} else {
std::cout << "Task " << check_task_id << " is not active" << std::endl;
}
}
if(deactive) {
auto r = fh->is_task_active(check_task_id);
if (r.isErr()) {
std::cout << "Failed to check if task is deactive: " << r.getErrMessage()
<< std::endl;
return;
}
if (r.unwrap()) {
std::cout << "Task " << check_task_id << " is not deactive" << std::endl;
} else {
std::cout << "Task " << check_task_id << " is deactive" << std::endl;
}
}
if(depth) {
auto r = fh->get_task_depth(check_task_id);
if (r.isErr()) {
std::cout << "Failed to check task depth: " << r.getErrMessage()
<< std::endl;
return;
}
std::cout << "Task depth is " << r.unwrap() << std::endl;
}
});
}
}
4 changes: 2 additions & 2 deletions cli/src/commands/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ namespace flowhook_cli {

auto r = fh->create_task(task_name, n_path);
if (r.isErr()) {
std::cerr << "Failed to create task: " << r.getErrMessage() << std::endl;
std::cout << "Failed to create task: " << r.getErrMessage() << std::endl;
return;
}
auto p = fh->set_task_path(n_path, n_path);
if (p.isErr()) {
std::cerr << "Failed to set task path: " << p.getErrMessage() << std::endl;
std::cout << "Failed to set task path: " << p.getErrMessage() << std::endl;
return;
}

Expand Down
51 changes: 30 additions & 21 deletions cli/src/commands/list.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,54 +46,63 @@ namespace flowhook_cli {


if (list_tasks) {
std::cout << "All registered Tasks:\n" << std::endl;
std::cout << "\nAll registered Tasks:\n" << std::endl;
for (const auto& task : tasks) {
std::cout << "Name: " << task.name << std::endl;
std::cout << "Working Directory: " << task.id << "\n" << std::endl;
std::cout << " Name: " << task.name << std::endl;
std::cout << " Working Directory: " << task.id << "\n" << std::endl;
}
}


if (list_paths) {
if(ts == nullptr) {
std::cerr << "Error: No flowhook task found for the current directory." << std::endl;
std::cout << "Error: No flowhook task found for the current directory." << std::endl;
return;
}

std::cout << "Paths:\n" << std::endl;
for(const auto& path : ts->paths) {
std::cout << path << std::endl;
std::cout << "\nWatched Directories:\n" << std::endl;
for(const auto& path : ts->dir_paths) {
std::cout << " " << path << std::endl;
}

std::cout << "\nStandalone File:\n" << std::endl;
for(const auto& path : ts->file_paths) {
std::cout << " " << path << std::endl;
}

std::cout << "All Currently Watched Files:\n" << std::endl;
for(const auto& path : fh->get_resolved_files(ts->id)) {
std::cout << " " << path << std::endl;
}

}

if (list_commands) {
if(ts == nullptr) {
std::cerr << "Error: No flowhook task found for the current directory." << std::endl;
std::cout << "Error: No flowhook task found for the current directory." << std::endl;
return;
}

std::cout << "Commands:\n" << std::endl;
std::cout << "\nCommands:\n" << std::endl;
for(const auto& command : ts->commands) {
std::cout << command << std::endl;
std::cout << " " << command << std::endl;
}
}

if (list_ignored) {
if(ts == nullptr) {
std::cerr << "Error: No flowhook task found for the current directory." << std::endl;
std::cout << "Error: No flowhook task found for the current directory." << std::endl;
return;
}

std::cout << "Ignored paths:\n" << std::endl;
std::cout << "\nIgnored paths:\n" << std::endl;
for(const auto& ignored_paths : ts->ignored_paths) {
std::cout << ignored_paths << std::endl;
std::cout << " " << ignored_paths << std::endl;
}
std::cout << std::endl;
std::cout << "Ignored patterns:\n" << std::endl;
std::cout << "\nIgnored patterns:\n" << std::endl;
for(const auto& ignored_pattern : ts->ignored_patterns) {
std::cout << ignored_pattern << std::endl;
std::cout << " " << ignored_pattern << std::endl;
}
std::cout << std::endl;
}

if (list_on_success) {
Expand All @@ -102,9 +111,9 @@ namespace flowhook_cli {
return;
}

std::cout << "On success:\n" << std::endl;
std::cout << "\nOn success:\n" << std::endl;
for(const auto& command : ts->on_success) {
std::cout << command << std::endl;
std::cout << " " << command << std::endl;
}
std::cout << std::endl;
}
Expand All @@ -115,9 +124,9 @@ namespace flowhook_cli {
return;
}

std::cout << "On failure:\n" << std::endl;
std::cout << "\nOn failure:\n" << std::endl;
for(const auto& command : ts->on_failure) {
std::cout << command << std::endl;
std::cout << " " << command << std::endl;
}
std::cout << std::endl;
}
Expand Down
31 changes: 18 additions & 13 deletions cli/src/commands/remove.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,13 @@ void register_remove(CLI::App *app, flowhook::FlowHookCore *fh) {

// add subcommand
auto *remove =
app->add_subcommand("remove", "remove an element from a task\n");
app->add_subcommand("remove", "remove an element from a task\n // removes the task itself if no OPTION is provided");

// this is a purely ceremonial flag(so that CLI11 won't say there is unrecognized flag)
// the actual parsing of verbose and setting of the environment variable is done in main.cpp
remove->add_flag("--debug", FLOWHOOK_DEBUG, "Enable debug output. \n// is an extremely detailed output that shows every step of the process.");
remove->add_flag("--verbose", FLOWHOOK_VERBOSE, "Enable verbose output. \n// shows a summary of the process.");



static std::string task_id = "";
static std::string removed_path = "";
remove->add_option("--path", removed_path, "Path to the task [optional] \n// use to add a new path to be watched.\n");
Expand All @@ -38,29 +36,36 @@ void register_remove(CLI::App *app, flowhook::FlowHookCore *fh) {
remove->add_option("--ignored-path", ignored_path, "Ignored path [optional] \n// if not provided, all files will be watched in the working directory\n");
remove->add_option("--ignored-pattern", ignored_pattern, "Ignored pattern [optional] \n// if not provided, no pattern will be ignored in the working directory\n");

static bool force = false;
remove->add_flag("-f", force, "removes flowhook instance without providing a dialog to check certainity");

remove->callback([=]() mutable {
fs::path cwd = fs::current_path();
task_id = cwd.string();

bool remove_task = false;
if(removed_path.empty() && removed_command.empty() &&
command_on_success.empty() && command_on_failure.empty() &&
ignored_path.empty() && ignored_pattern.empty()) {
command_on_success.empty() && command_on_failure.empty() &&
ignored_path.empty() && ignored_pattern.empty()) {
remove_task = true;
}

if (remove_task) {
std::cout << "Are you sure you want to remove the flowhook instance from the current directory?" << std::endl;
std::cout << "Type 'yes' to confirm: ";
std::string confirmation;
std::cin >> confirmation;
if (confirmation != "yes") {
std::cout << "Removal cancelled." << std::endl;
return;
if(!force){
std::cout << "Are you sure you want to remove the flowhook instance from the current directory?" << std::endl;
std::cout << "Type 'yes' to confirm: ";
std::string confirmation;
std::cin >> confirmation;
if (confirmation != "yes") {
std::cout << "Removal cancelled." << std::endl;
return;
}
}
auto r = fh->delete_task(task_id);
if (r.isErr()) {
std::cerr << "Failed to remove task: " << r.getErrMessage() << std::endl;
std::cout << "Failed to remove task: " << r.getErrMessage() << std::endl;
} else {
std::cout << "Task removed successfully." << std::endl;
}
return;
}
Expand Down
Loading
Loading