Files
wrkflw/crates
Gokul 14d30b6b57 feat(ui): add job selection mode to TUI for running individual jobs (#80)
* feat(ui): add job selection mode to TUI for running individual jobs

The CLI already has --job to run a single job, but the TUI had no
way to do this. You could only run entire workflows, which is the
kind of all-or-nothing approach that gets old fast when you have a
workflow with 12 jobs and you just want to re-run "lint".

Add a job selection sub-view to the Workflows tab. Press Enter on a
workflow to drill into its jobs (parsed via parse_workflow), then
Enter on a job to run just that one (with its transitive deps), or
'a' to run all, or Esc to go back. The selected job flows through
as target_job in ExecutionConfig, reusing the exact same filtering
logic the CLI --job flag already uses.

While at it, updated the status bar hints and help overlay to
document the new keybindings.

* fix(ui): stop target_job from leaking across queued workflows

The job selection feature added in 8406c60 stored target_job as a
single global field on App, shared across all queued executions.
It turns out that when you use a global mutable field as a
communication channel between "user picked a job" and "executor
should run this job", *any* workflow queued after the first one
silently inherits whatever target_job was set. Confusion ensues.

While at it, the Enter key was repurposed from "run this workflow"
to "enter job selection mode", which means the most common operation
now takes two keypresses instead of one. That's not great.

Also, pressing 'r' during job selection mode would bypass it
entirely and start execution with target_job still set from the
previous selection, leaving the UI in a corrupt state. Please don't
ship unguarded key handlers in modal UIs.

The fix:

- Replace the global target_job with a per-entry QueuedExecution
  struct that carries its own target_job. Each queued workflow now
  owns its execution config. No more ambient mutable state.

- Cache parsed job names in Workflow at discovery time instead of
  re-parsing the YAML file on every Enter keypress.

- Restore Enter to its original "run the workflow" behavior. Job
  selection is now Shift+J, which doesn't steal the primary action
  key.

- Guard 'r' and other keys properly in job_selection_mode.

- Deduplicate select_job_and_run/run_all_jobs into a single
  run_from_job_selection(target: Option<String>) method.

* fix(ui): fix job selection bugs and add tests

The single-file CLI path (`wrkflw --tui <file>`) was hardcoding
job_names to Vec::new(), which means Shift+J would always report
"No jobs found" even when the workflow *obviously* has jobs. Not
great when the whole point of this feature is selecting jobs.

The dedup logic in run_from_job_selection() was checking
workflow_idx equality only, so queueing the same workflow with
different target jobs (run "build", then run "test") would silently
drop the second one. The user clicks a thing, nothing happens.
Confusion ensues.

While at it, extract the job name parsing into a shared
extract_job_names() helper — the same six lines were copy-pasted
in three places, which is exactly how you end up with three
slightly different bugs later.

Add 12 unit tests covering the job selection state machine:
enter/exit mode, navigation wrapping, target_job threading through
the execution queue, and the dedup fix.

* fix(ui): deduplicate job selection cleanup and add edge case tests

run_from_job_selection was manually inlining the same three lines
that exit_job_selection_mode already does. That's the kind of thing
that *will* drift the moment someone adds cleanup logic to one path
but not the other. Let's just call the existing method.

While at it, add tests for two untested edge cases: calling
run_from_job_selection when no workflow is selected (should be a
safe no-op), and entering job selection mode with an out-of-bounds
index (should also be a no-op). Also document the precondition that
callers must check !self.running before calling run_from_job_selection.
2026-04-02 15:38:58 +05:30
..
2025-09-05 08:22:15 +05:30
2025-09-05 08:22:15 +05:30

Wrkflw Crates

This directory contains the Rust crates that make up the Wrkflw project. The project has been restructured to use a workspace-based approach with individual crates for better modularity and maintainability.

Crate Structure

  • wrkflw: Main binary crate and entry point for the application
  • models: Data models and structures used throughout the application
  • evaluator: Workflow evaluation functionality
  • executor: Workflow execution engine
  • github: GitHub API integration
  • gitlab: GitLab API integration
  • logging: Logging functionality
  • matrix: Matrix-based parallelization support
  • parser: Workflow parsing functionality
  • runtime: Runtime execution environment
  • ui: User interface components
  • utils: Utility functions
  • validators: Validation functionality

Dependencies

Each crate has its own Cargo.toml file that defines its dependencies. The root Cargo.toml file defines the workspace and shared dependencies.

Build Instructions

To build the entire project:

cargo build

To build a specific crate:

cargo build -p <crate-name>

Testing

To run tests for the entire project:

cargo test

To run tests for a specific crate:

cargo test -p <crate-name>

Rust Best Practices

When contributing to wrkflw, please follow these Rust best practices:

Code Organization

  • Place modules in their respective crates to maintain separation of concerns
  • Use pub selectively to expose only the necessary APIs
  • Follow the Rust module system conventions (use mod and pub mod appropriately)

Errors and Error Handling

  • Prefer using the thiserror crate for defining custom error types
  • Use the ? operator for error propagation instead of match statements when appropriate
  • Implement custom error types that provide context for the error
  • Avoid using .unwrap() and .expect() in production code

Performance

  • Profile code before optimizing using tools like cargo flamegraph
  • Use Arc and Mutex judiciously for shared mutable state
  • Leverage Rust's zero-cost abstractions (iterators, closures)
  • Consider adding benchmark tests using the criterion crate for performance-critical code

Security

  • Validate all input, especially from external sources
  • Avoid using unsafe code unless absolutely necessary
  • Handle secrets securely using environment variables
  • Check for integer overflows with checked_ operations

Testing

  • Write unit tests for all public functions
  • Use integration tests to verify crate-to-crate interactions
  • Consider property-based testing for complex logic
  • Structure tests with clear preparation, execution, and verification phases

Tooling

  • Run cargo clippy before committing changes to catch common mistakes
  • Use cargo fmt to maintain consistent code formatting
  • Enable compiler warnings with #![warn(clippy::all)]

For more detailed guidance, refer to the project's best practices documentation.