mirror of
https://github.com/bahdotsh/wrkflw.git
synced 2026-05-18 05:05:35 +02:00
renamed
This commit is contained in:
372
Cargo.lock
generated
372
Cargo.lock
generated
@@ -486,46 +486,6 @@ dependencies = [
|
|||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "evaluator"
|
|
||||||
version = "0.5.0"
|
|
||||||
dependencies = [
|
|
||||||
"colored",
|
|
||||||
"models",
|
|
||||||
"serde_yaml",
|
|
||||||
"validators",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "executor"
|
|
||||||
version = "0.5.0"
|
|
||||||
dependencies = [
|
|
||||||
"async-trait",
|
|
||||||
"bollard",
|
|
||||||
"chrono",
|
|
||||||
"dirs",
|
|
||||||
"futures",
|
|
||||||
"futures-util",
|
|
||||||
"lazy_static",
|
|
||||||
"logging",
|
|
||||||
"matrix",
|
|
||||||
"models",
|
|
||||||
"num_cpus",
|
|
||||||
"once_cell",
|
|
||||||
"parser",
|
|
||||||
"regex",
|
|
||||||
"runtime",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"serde_yaml",
|
|
||||||
"tar",
|
|
||||||
"tempfile",
|
|
||||||
"thiserror",
|
|
||||||
"tokio",
|
|
||||||
"utils",
|
|
||||||
"uuid",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fancy-regex"
|
name = "fancy-regex"
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
@@ -714,35 +674,6 @@ version = "0.31.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "github"
|
|
||||||
version = "0.5.0"
|
|
||||||
dependencies = [
|
|
||||||
"lazy_static",
|
|
||||||
"models",
|
|
||||||
"regex",
|
|
||||||
"reqwest",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"serde_yaml",
|
|
||||||
"thiserror",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "gitlab"
|
|
||||||
version = "0.5.0"
|
|
||||||
dependencies = [
|
|
||||||
"lazy_static",
|
|
||||||
"models",
|
|
||||||
"regex",
|
|
||||||
"reqwest",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"serde_yaml",
|
|
||||||
"thiserror",
|
|
||||||
"urlencoding",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "h2"
|
name = "h2"
|
||||||
version = "0.3.26"
|
version = "0.3.26"
|
||||||
@@ -1215,28 +1146,6 @@ version = "0.4.27"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
|
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "logging"
|
|
||||||
version = "0.5.0"
|
|
||||||
dependencies = [
|
|
||||||
"chrono",
|
|
||||||
"models",
|
|
||||||
"once_cell",
|
|
||||||
"serde",
|
|
||||||
"serde_yaml",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "matrix"
|
|
||||||
version = "0.5.0"
|
|
||||||
dependencies = [
|
|
||||||
"indexmap 2.8.0",
|
|
||||||
"models",
|
|
||||||
"serde",
|
|
||||||
"serde_yaml",
|
|
||||||
"thiserror",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.7.4"
|
version = "2.7.4"
|
||||||
@@ -1281,16 +1190,6 @@ dependencies = [
|
|||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "models"
|
|
||||||
version = "0.5.0"
|
|
||||||
dependencies = [
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"serde_yaml",
|
|
||||||
"thiserror",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "native-tls"
|
name = "native-tls"
|
||||||
version = "0.2.14"
|
version = "0.2.14"
|
||||||
@@ -1511,20 +1410,6 @@ dependencies = [
|
|||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "parser"
|
|
||||||
version = "0.5.0"
|
|
||||||
dependencies = [
|
|
||||||
"jsonschema",
|
|
||||||
"matrix",
|
|
||||||
"models",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"serde_yaml",
|
|
||||||
"tempfile",
|
|
||||||
"thiserror",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "paste"
|
name = "paste"
|
||||||
version = "1.0.15"
|
version = "1.0.15"
|
||||||
@@ -1731,23 +1616,6 @@ dependencies = [
|
|||||||
"winreg",
|
"winreg",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "runtime"
|
|
||||||
version = "0.5.0"
|
|
||||||
dependencies = [
|
|
||||||
"async-trait",
|
|
||||||
"futures",
|
|
||||||
"logging",
|
|
||||||
"models",
|
|
||||||
"once_cell",
|
|
||||||
"serde",
|
|
||||||
"serde_yaml",
|
|
||||||
"tempfile",
|
|
||||||
"tokio",
|
|
||||||
"utils",
|
|
||||||
"which",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-demangle"
|
name = "rustc-demangle"
|
||||||
version = "0.1.24"
|
version = "0.1.24"
|
||||||
@@ -2243,28 +2111,6 @@ version = "0.2.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ui"
|
|
||||||
version = "0.5.0"
|
|
||||||
dependencies = [
|
|
||||||
"chrono",
|
|
||||||
"crossterm 0.26.1",
|
|
||||||
"evaluator",
|
|
||||||
"executor",
|
|
||||||
"futures",
|
|
||||||
"github",
|
|
||||||
"logging",
|
|
||||||
"models",
|
|
||||||
"ratatui",
|
|
||||||
"regex",
|
|
||||||
"reqwest",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"serde_yaml",
|
|
||||||
"tokio",
|
|
||||||
"utils",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.18"
|
version = "1.0.18"
|
||||||
@@ -2324,16 +2170,6 @@ version = "0.2.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "utils"
|
|
||||||
version = "0.5.0"
|
|
||||||
dependencies = [
|
|
||||||
"models",
|
|
||||||
"nix",
|
|
||||||
"serde",
|
|
||||||
"serde_yaml",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uuid"
|
name = "uuid"
|
||||||
version = "1.16.0"
|
version = "1.16.0"
|
||||||
@@ -2343,16 +2179,6 @@ dependencies = [
|
|||||||
"getrandom 0.3.2",
|
"getrandom 0.3.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "validators"
|
|
||||||
version = "0.5.0"
|
|
||||||
dependencies = [
|
|
||||||
"matrix",
|
|
||||||
"models",
|
|
||||||
"serde",
|
|
||||||
"serde_yaml",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vcpkg"
|
name = "vcpkg"
|
||||||
version = "0.2.15"
|
version = "0.2.15"
|
||||||
@@ -2727,41 +2553,215 @@ dependencies = [
|
|||||||
"colored",
|
"colored",
|
||||||
"crossterm 0.26.1",
|
"crossterm 0.26.1",
|
||||||
"dirs",
|
"dirs",
|
||||||
"evaluator",
|
|
||||||
"executor",
|
|
||||||
"futures",
|
"futures",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"github",
|
|
||||||
"gitlab",
|
|
||||||
"indexmap 2.8.0",
|
"indexmap 2.8.0",
|
||||||
"itertools",
|
"itertools",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"logging",
|
|
||||||
"matrix",
|
|
||||||
"models",
|
|
||||||
"nix",
|
"nix",
|
||||||
"num_cpus",
|
"num_cpus",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"parser",
|
|
||||||
"ratatui",
|
"ratatui",
|
||||||
"rayon",
|
"rayon",
|
||||||
"regex",
|
"regex",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"runtime",
|
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_yaml",
|
"serde_yaml",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
"ui",
|
|
||||||
"urlencoding",
|
"urlencoding",
|
||||||
"utils",
|
|
||||||
"uuid",
|
"uuid",
|
||||||
"validators",
|
|
||||||
"walkdir",
|
"walkdir",
|
||||||
|
"wrkflw-evaluator",
|
||||||
|
"wrkflw-executor",
|
||||||
|
"wrkflw-github",
|
||||||
|
"wrkflw-gitlab",
|
||||||
|
"wrkflw-logging",
|
||||||
|
"wrkflw-matrix",
|
||||||
|
"wrkflw-models",
|
||||||
|
"wrkflw-parser",
|
||||||
|
"wrkflw-runtime",
|
||||||
|
"wrkflw-ui",
|
||||||
|
"wrkflw-utils",
|
||||||
|
"wrkflw-validators",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wrkflw-evaluator"
|
||||||
|
version = "0.5.0"
|
||||||
|
dependencies = [
|
||||||
|
"colored",
|
||||||
|
"serde_yaml",
|
||||||
|
"wrkflw-models",
|
||||||
|
"wrkflw-validators",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wrkflw-executor"
|
||||||
|
version = "0.5.0"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"bollard",
|
||||||
|
"chrono",
|
||||||
|
"dirs",
|
||||||
|
"futures",
|
||||||
|
"futures-util",
|
||||||
|
"lazy_static",
|
||||||
|
"num_cpus",
|
||||||
|
"once_cell",
|
||||||
|
"regex",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"serde_yaml",
|
||||||
|
"tar",
|
||||||
|
"tempfile",
|
||||||
|
"thiserror",
|
||||||
|
"tokio",
|
||||||
|
"uuid",
|
||||||
|
"wrkflw-logging",
|
||||||
|
"wrkflw-matrix",
|
||||||
|
"wrkflw-models",
|
||||||
|
"wrkflw-parser",
|
||||||
|
"wrkflw-runtime",
|
||||||
|
"wrkflw-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wrkflw-github"
|
||||||
|
version = "0.5.0"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
|
"regex",
|
||||||
|
"reqwest",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"serde_yaml",
|
||||||
|
"thiserror",
|
||||||
|
"wrkflw-models",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wrkflw-gitlab"
|
||||||
|
version = "0.5.0"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
|
"regex",
|
||||||
|
"reqwest",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"serde_yaml",
|
||||||
|
"thiserror",
|
||||||
|
"urlencoding",
|
||||||
|
"wrkflw-models",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wrkflw-logging"
|
||||||
|
version = "0.5.0"
|
||||||
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
|
"once_cell",
|
||||||
|
"serde",
|
||||||
|
"serde_yaml",
|
||||||
|
"wrkflw-models",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wrkflw-matrix"
|
||||||
|
version = "0.5.0"
|
||||||
|
dependencies = [
|
||||||
|
"indexmap 2.8.0",
|
||||||
|
"serde",
|
||||||
|
"serde_yaml",
|
||||||
|
"thiserror",
|
||||||
|
"wrkflw-models",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wrkflw-models"
|
||||||
|
version = "0.5.0"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"serde_yaml",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wrkflw-parser"
|
||||||
|
version = "0.5.0"
|
||||||
|
dependencies = [
|
||||||
|
"jsonschema",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"serde_yaml",
|
||||||
|
"tempfile",
|
||||||
|
"thiserror",
|
||||||
|
"wrkflw-matrix",
|
||||||
|
"wrkflw-models",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wrkflw-runtime"
|
||||||
|
version = "0.5.0"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"futures",
|
||||||
|
"once_cell",
|
||||||
|
"serde",
|
||||||
|
"serde_yaml",
|
||||||
|
"tempfile",
|
||||||
|
"tokio",
|
||||||
|
"which",
|
||||||
|
"wrkflw-logging",
|
||||||
|
"wrkflw-models",
|
||||||
|
"wrkflw-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wrkflw-ui"
|
||||||
|
version = "0.5.0"
|
||||||
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
|
"crossterm 0.26.1",
|
||||||
|
"futures",
|
||||||
|
"ratatui",
|
||||||
|
"regex",
|
||||||
|
"reqwest",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"serde_yaml",
|
||||||
|
"tokio",
|
||||||
|
"wrkflw-evaluator",
|
||||||
|
"wrkflw-executor",
|
||||||
|
"wrkflw-github",
|
||||||
|
"wrkflw-logging",
|
||||||
|
"wrkflw-models",
|
||||||
|
"wrkflw-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wrkflw-utils"
|
||||||
|
version = "0.5.0"
|
||||||
|
dependencies = [
|
||||||
|
"nix",
|
||||||
|
"serde",
|
||||||
|
"serde_yaml",
|
||||||
|
"wrkflw-models",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wrkflw-validators"
|
||||||
|
version = "0.5.0"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde_yaml",
|
||||||
|
"wrkflw-matrix",
|
||||||
|
"wrkflw-models",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@@ -1,14 +1,19 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "evaluator"
|
name = "wrkflw-evaluator"
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
description = "Workflow evaluation for wrkflw"
|
description = "Workflow evaluation functionality for wrkflw execution engine"
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
documentation.workspace = true
|
||||||
|
homepage.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
keywords.workspace = true
|
||||||
|
categories.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Internal crates
|
# Internal crates
|
||||||
models = { path = "../models" }
|
wrkflw-models = { path = "../models", version = "0.5.0" }
|
||||||
validators = { path = "../validators" }
|
wrkflw-validators = { path = "../validators", version = "0.5.0" }
|
||||||
|
|
||||||
# External dependencies
|
# External dependencies
|
||||||
colored.workspace = true
|
colored.workspace = true
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ use serde_yaml::{self, Value};
|
|||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use models::ValidationResult;
|
use wrkflw_models::ValidationResult;
|
||||||
use validators::{validate_jobs, validate_triggers};
|
use wrkflw_validators::{validate_jobs, validate_triggers};
|
||||||
|
|
||||||
pub fn evaluate_workflow_file(path: &Path, verbose: bool) -> Result<ValidationResult, String> {
|
pub fn evaluate_workflow_file(path: &Path, verbose: bool) -> Result<ValidationResult, String> {
|
||||||
let content = fs::read_to_string(path).map_err(|e| format!("Failed to read file: {}", e))?;
|
let content = fs::read_to_string(path).map_err(|e| format!("Failed to read file: {}", e))?;
|
||||||
|
|||||||
@@ -1,18 +1,23 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "executor"
|
name = "wrkflw-executor"
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
description = "Workflow executor for wrkflw"
|
description = "Workflow execution engine for wrkflw"
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
documentation.workspace = true
|
||||||
|
homepage.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
keywords.workspace = true
|
||||||
|
categories.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Internal crates
|
# Internal crates
|
||||||
models = { path = "../models" }
|
wrkflw-models = { path = "../models", version = "0.5.0" }
|
||||||
parser = { path = "../parser" }
|
wrkflw-parser = { path = "../parser", version = "0.5.0" }
|
||||||
runtime = { path = "../runtime" }
|
wrkflw-runtime = { path = "../runtime", version = "0.5.0" }
|
||||||
logging = { path = "../logging" }
|
wrkflw-logging = { path = "../logging", version = "0.5.0" }
|
||||||
matrix = { path = "../matrix" }
|
wrkflw-matrix = { path = "../matrix", version = "0.5.0" }
|
||||||
utils = { path = "../utils" }
|
wrkflw-utils = { path = "../utils", version = "0.5.0" }
|
||||||
|
|
||||||
# External dependencies
|
# External dependencies
|
||||||
async-trait.workspace = true
|
async-trait.workspace = true
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use parser::workflow::WorkflowDefinition;
|
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
use wrkflw_parser::workflow::WorkflowDefinition;
|
||||||
|
|
||||||
pub fn resolve_dependencies(workflow: &WorkflowDefinition) -> Result<Vec<Vec<String>>, String> {
|
pub fn resolve_dependencies(workflow: &WorkflowDefinition) -> Result<Vec<Vec<String>>, String> {
|
||||||
let jobs = &workflow.jobs;
|
let jobs = &workflow.jobs;
|
||||||
|
|||||||
@@ -6,14 +6,14 @@ use bollard::{
|
|||||||
Docker,
|
Docker,
|
||||||
};
|
};
|
||||||
use futures_util::StreamExt;
|
use futures_util::StreamExt;
|
||||||
use logging;
|
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use runtime::container::{ContainerError, ContainerOutput, ContainerRuntime};
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use utils;
|
use wrkflw_logging;
|
||||||
use utils::fd;
|
use wrkflw_runtime::container::{ContainerError, ContainerOutput, ContainerRuntime};
|
||||||
|
use wrkflw_utils;
|
||||||
|
use wrkflw_utils::fd;
|
||||||
|
|
||||||
static RUNNING_CONTAINERS: Lazy<Mutex<Vec<String>>> = Lazy::new(|| Mutex::new(Vec::new()));
|
static RUNNING_CONTAINERS: Lazy<Mutex<Vec<String>>> = Lazy::new(|| Mutex::new(Vec::new()));
|
||||||
static CREATED_NETWORKS: Lazy<Mutex<Vec<String>>> = Lazy::new(|| Mutex::new(Vec::new()));
|
static CREATED_NETWORKS: Lazy<Mutex<Vec<String>>> = Lazy::new(|| Mutex::new(Vec::new()));
|
||||||
@@ -50,7 +50,7 @@ impl DockerRuntime {
|
|||||||
match CUSTOMIZED_IMAGES.lock() {
|
match CUSTOMIZED_IMAGES.lock() {
|
||||||
Ok(images) => images.get(&key).cloned(),
|
Ok(images) => images.get(&key).cloned(),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
logging::error(&format!("Failed to acquire lock: {}", e));
|
wrkflw_logging::error(&format!("Failed to acquire lock: {}", e));
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -62,7 +62,7 @@ impl DockerRuntime {
|
|||||||
if let Err(e) = CUSTOMIZED_IMAGES.lock().map(|mut images| {
|
if let Err(e) = CUSTOMIZED_IMAGES.lock().map(|mut images| {
|
||||||
images.insert(key, new_image.to_string());
|
images.insert(key, new_image.to_string());
|
||||||
}) {
|
}) {
|
||||||
logging::error(&format!("Failed to acquire lock: {}", e));
|
wrkflw_logging::error(&format!("Failed to acquire lock: {}", e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,7 +72,7 @@ impl DockerRuntime {
|
|||||||
let image_keys = match CUSTOMIZED_IMAGES.lock() {
|
let image_keys = match CUSTOMIZED_IMAGES.lock() {
|
||||||
Ok(keys) => keys,
|
Ok(keys) => keys,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
logging::error(&format!("Failed to acquire lock: {}", e));
|
wrkflw_logging::error(&format!("Failed to acquire lock: {}", e));
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -107,7 +107,7 @@ impl DockerRuntime {
|
|||||||
match CUSTOMIZED_IMAGES.lock() {
|
match CUSTOMIZED_IMAGES.lock() {
|
||||||
Ok(images) => images.get(&key).cloned(),
|
Ok(images) => images.get(&key).cloned(),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
logging::error(&format!("Failed to acquire lock: {}", e));
|
wrkflw_logging::error(&format!("Failed to acquire lock: {}", e));
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -134,7 +134,7 @@ impl DockerRuntime {
|
|||||||
if let Err(e) = CUSTOMIZED_IMAGES.lock().map(|mut images| {
|
if let Err(e) = CUSTOMIZED_IMAGES.lock().map(|mut images| {
|
||||||
images.insert(key, new_image.to_string());
|
images.insert(key, new_image.to_string());
|
||||||
}) {
|
}) {
|
||||||
logging::error(&format!("Failed to acquire lock: {}", e));
|
wrkflw_logging::error(&format!("Failed to acquire lock: {}", e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -318,7 +318,7 @@ pub fn is_available() -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
logging::debug("Docker CLI is not available");
|
wrkflw_logging::debug("Docker CLI is not available");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -331,7 +331,7 @@ pub fn is_available() -> bool {
|
|||||||
{
|
{
|
||||||
Ok(rt) => rt,
|
Ok(rt) => rt,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
logging::error(&format!(
|
wrkflw_logging::error(&format!(
|
||||||
"Failed to create runtime for Docker availability check: {}",
|
"Failed to create runtime for Docker availability check: {}",
|
||||||
e
|
e
|
||||||
));
|
));
|
||||||
@@ -352,17 +352,25 @@ pub fn is_available() -> bool {
|
|||||||
{
|
{
|
||||||
Ok(Ok(_)) => true,
|
Ok(Ok(_)) => true,
|
||||||
Ok(Err(e)) => {
|
Ok(Err(e)) => {
|
||||||
logging::debug(&format!("Docker daemon ping failed: {}", e));
|
wrkflw_logging::debug(&format!(
|
||||||
|
"Docker daemon ping failed: {}",
|
||||||
|
e
|
||||||
|
));
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
logging::debug("Docker daemon ping timed out after 1 second");
|
wrkflw_logging::debug(
|
||||||
|
"Docker daemon ping timed out after 1 second",
|
||||||
|
);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
logging::debug(&format!("Docker daemon connection failed: {}", e));
|
wrkflw_logging::debug(&format!(
|
||||||
|
"Docker daemon connection failed: {}",
|
||||||
|
e
|
||||||
|
));
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -371,7 +379,7 @@ pub fn is_available() -> bool {
|
|||||||
{
|
{
|
||||||
Ok(result) => result,
|
Ok(result) => result,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
logging::debug("Docker availability check timed out");
|
wrkflw_logging::debug("Docker availability check timed out");
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -379,7 +387,9 @@ pub fn is_available() -> bool {
|
|||||||
}) {
|
}) {
|
||||||
Ok(result) => result,
|
Ok(result) => result,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
logging::debug("Failed to redirect stderr when checking Docker availability");
|
wrkflw_logging::debug(
|
||||||
|
"Failed to redirect stderr when checking Docker availability",
|
||||||
|
);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -393,7 +403,7 @@ pub fn is_available() -> bool {
|
|||||||
return match handle.join() {
|
return match handle.join() {
|
||||||
Ok(result) => result,
|
Ok(result) => result,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
logging::warning("Docker availability check thread panicked");
|
wrkflw_logging::warning("Docker availability check thread panicked");
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -401,7 +411,9 @@ pub fn is_available() -> bool {
|
|||||||
std::thread::sleep(std::time::Duration::from_millis(50));
|
std::thread::sleep(std::time::Duration::from_millis(50));
|
||||||
}
|
}
|
||||||
|
|
||||||
logging::warning("Docker availability check timed out, assuming Docker is not available");
|
wrkflw_logging::warning(
|
||||||
|
"Docker availability check timed out, assuming Docker is not available",
|
||||||
|
);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -444,19 +456,19 @@ pub async fn cleanup_resources(docker: &Docker) {
|
|||||||
tokio::join!(cleanup_containers(docker), cleanup_networks(docker));
|
tokio::join!(cleanup_containers(docker), cleanup_networks(docker));
|
||||||
|
|
||||||
if let Err(e) = container_result {
|
if let Err(e) = container_result {
|
||||||
logging::error(&format!("Error during container cleanup: {}", e));
|
wrkflw_logging::error(&format!("Error during container cleanup: {}", e));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(e) = network_result {
|
if let Err(e) = network_result {
|
||||||
logging::error(&format!("Error during network cleanup: {}", e));
|
wrkflw_logging::error(&format!("Error during network cleanup: {}", e));
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(_) => logging::debug("Docker cleanup completed within timeout"),
|
Ok(_) => wrkflw_logging::debug("Docker cleanup completed within timeout"),
|
||||||
Err(_) => {
|
Err(_) => wrkflw_logging::warning(
|
||||||
logging::warning("Docker cleanup timed out, some resources may not have been removed")
|
"Docker cleanup timed out, some resources may not have been removed",
|
||||||
}
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -468,7 +480,7 @@ pub async fn cleanup_containers(docker: &Docker) -> Result<(), String> {
|
|||||||
match RUNNING_CONTAINERS.try_lock() {
|
match RUNNING_CONTAINERS.try_lock() {
|
||||||
Ok(containers) => containers.clone(),
|
Ok(containers) => containers.clone(),
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
logging::error("Could not acquire container lock for cleanup");
|
wrkflw_logging::error("Could not acquire container lock for cleanup");
|
||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -477,7 +489,7 @@ pub async fn cleanup_containers(docker: &Docker) -> Result<(), String> {
|
|||||||
{
|
{
|
||||||
Ok(containers) => containers,
|
Ok(containers) => containers,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
logging::error("Timeout while trying to get containers for cleanup");
|
wrkflw_logging::error("Timeout while trying to get containers for cleanup");
|
||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -486,7 +498,7 @@ pub async fn cleanup_containers(docker: &Docker) -> Result<(), String> {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
logging::info(&format!(
|
wrkflw_logging::info(&format!(
|
||||||
"Cleaning up {} containers",
|
"Cleaning up {} containers",
|
||||||
containers_to_cleanup.len()
|
containers_to_cleanup.len()
|
||||||
));
|
));
|
||||||
@@ -500,11 +512,14 @@ pub async fn cleanup_containers(docker: &Docker) -> Result<(), String> {
|
|||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(Ok(_)) => logging::debug(&format!("Stopped container: {}", container_id)),
|
Ok(Ok(_)) => wrkflw_logging::debug(&format!("Stopped container: {}", container_id)),
|
||||||
Ok(Err(e)) => {
|
Ok(Err(e)) => wrkflw_logging::warning(&format!(
|
||||||
logging::warning(&format!("Error stopping container {}: {}", container_id, e))
|
"Error stopping container {}: {}",
|
||||||
|
container_id, e
|
||||||
|
)),
|
||||||
|
Err(_) => {
|
||||||
|
wrkflw_logging::warning(&format!("Timeout stopping container: {}", container_id))
|
||||||
}
|
}
|
||||||
Err(_) => logging::warning(&format!("Timeout stopping container: {}", container_id)),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Then try to remove it
|
// Then try to remove it
|
||||||
@@ -514,11 +529,14 @@ pub async fn cleanup_containers(docker: &Docker) -> Result<(), String> {
|
|||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(Ok(_)) => logging::debug(&format!("Removed container: {}", container_id)),
|
Ok(Ok(_)) => wrkflw_logging::debug(&format!("Removed container: {}", container_id)),
|
||||||
Ok(Err(e)) => {
|
Ok(Err(e)) => wrkflw_logging::warning(&format!(
|
||||||
logging::warning(&format!("Error removing container {}: {}", container_id, e))
|
"Error removing container {}: {}",
|
||||||
|
container_id, e
|
||||||
|
)),
|
||||||
|
Err(_) => {
|
||||||
|
wrkflw_logging::warning(&format!("Timeout removing container: {}", container_id))
|
||||||
}
|
}
|
||||||
Err(_) => logging::warning(&format!("Timeout removing container: {}", container_id)),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always untrack the container whether or not we succeeded to avoid future cleanup attempts
|
// Always untrack the container whether or not we succeeded to avoid future cleanup attempts
|
||||||
@@ -536,7 +554,7 @@ pub async fn cleanup_networks(docker: &Docker) -> Result<(), String> {
|
|||||||
match CREATED_NETWORKS.try_lock() {
|
match CREATED_NETWORKS.try_lock() {
|
||||||
Ok(networks) => networks.clone(),
|
Ok(networks) => networks.clone(),
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
logging::error("Could not acquire network lock for cleanup");
|
wrkflw_logging::error("Could not acquire network lock for cleanup");
|
||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -545,7 +563,7 @@ pub async fn cleanup_networks(docker: &Docker) -> Result<(), String> {
|
|||||||
{
|
{
|
||||||
Ok(networks) => networks,
|
Ok(networks) => networks,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
logging::error("Timeout while trying to get networks for cleanup");
|
wrkflw_logging::error("Timeout while trying to get networks for cleanup");
|
||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -554,7 +572,7 @@ pub async fn cleanup_networks(docker: &Docker) -> Result<(), String> {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
logging::info(&format!(
|
wrkflw_logging::info(&format!(
|
||||||
"Cleaning up {} networks",
|
"Cleaning up {} networks",
|
||||||
networks_to_cleanup.len()
|
networks_to_cleanup.len()
|
||||||
));
|
));
|
||||||
@@ -566,9 +584,13 @@ pub async fn cleanup_networks(docker: &Docker) -> Result<(), String> {
|
|||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(Ok(_)) => logging::info(&format!("Successfully removed network: {}", network_id)),
|
Ok(Ok(_)) => {
|
||||||
Ok(Err(e)) => logging::error(&format!("Error removing network {}: {}", network_id, e)),
|
wrkflw_logging::info(&format!("Successfully removed network: {}", network_id))
|
||||||
Err(_) => logging::warning(&format!("Timeout removing network: {}", network_id)),
|
}
|
||||||
|
Ok(Err(e)) => {
|
||||||
|
wrkflw_logging::error(&format!("Error removing network {}: {}", network_id, e))
|
||||||
|
}
|
||||||
|
Err(_) => wrkflw_logging::warning(&format!("Timeout removing network: {}", network_id)),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always untrack the network whether or not we succeeded
|
// Always untrack the network whether or not we succeeded
|
||||||
@@ -599,7 +621,7 @@ pub async fn create_job_network(docker: &Docker) -> Result<String, ContainerErro
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
track_network(&network_id);
|
track_network(&network_id);
|
||||||
logging::info(&format!("Created Docker network: {}", network_id));
|
wrkflw_logging::info(&format!("Created Docker network: {}", network_id));
|
||||||
|
|
||||||
Ok(network_id)
|
Ok(network_id)
|
||||||
}
|
}
|
||||||
@@ -615,7 +637,7 @@ impl ContainerRuntime for DockerRuntime {
|
|||||||
volumes: &[(&Path, &Path)],
|
volumes: &[(&Path, &Path)],
|
||||||
) -> Result<ContainerOutput, ContainerError> {
|
) -> Result<ContainerOutput, ContainerError> {
|
||||||
// Print detailed debugging info
|
// Print detailed debugging info
|
||||||
logging::info(&format!("Docker: Running container with image: {}", image));
|
wrkflw_logging::info(&format!("Docker: Running container with image: {}", image));
|
||||||
|
|
||||||
// Add a global timeout for all Docker operations to prevent freezing
|
// Add a global timeout for all Docker operations to prevent freezing
|
||||||
let timeout_duration = std::time::Duration::from_secs(360); // Increased outer timeout to 6 minutes
|
let timeout_duration = std::time::Duration::from_secs(360); // Increased outer timeout to 6 minutes
|
||||||
@@ -629,7 +651,7 @@ impl ContainerRuntime for DockerRuntime {
|
|||||||
{
|
{
|
||||||
Ok(result) => result,
|
Ok(result) => result,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
logging::error("Docker operation timed out after 360 seconds");
|
wrkflw_logging::error("Docker operation timed out after 360 seconds");
|
||||||
Err(ContainerError::ContainerExecution(
|
Err(ContainerError::ContainerExecution(
|
||||||
"Operation timed out".to_string(),
|
"Operation timed out".to_string(),
|
||||||
))
|
))
|
||||||
@@ -644,7 +666,7 @@ impl ContainerRuntime for DockerRuntime {
|
|||||||
match tokio::time::timeout(timeout_duration, self.pull_image_inner(image)).await {
|
match tokio::time::timeout(timeout_duration, self.pull_image_inner(image)).await {
|
||||||
Ok(result) => result,
|
Ok(result) => result,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
logging::warning(&format!(
|
wrkflw_logging::warning(&format!(
|
||||||
"Pull of image {} timed out, continuing with existing image",
|
"Pull of image {} timed out, continuing with existing image",
|
||||||
image
|
image
|
||||||
));
|
));
|
||||||
@@ -662,7 +684,7 @@ impl ContainerRuntime for DockerRuntime {
|
|||||||
{
|
{
|
||||||
Ok(result) => result,
|
Ok(result) => result,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
logging::error(&format!(
|
wrkflw_logging::error(&format!(
|
||||||
"Building image {} timed out after 120 seconds",
|
"Building image {} timed out after 120 seconds",
|
||||||
tag
|
tag
|
||||||
));
|
));
|
||||||
@@ -836,9 +858,9 @@ impl DockerRuntime {
|
|||||||
// Convert command vector to Vec<String>
|
// Convert command vector to Vec<String>
|
||||||
let cmd_vec: Vec<String> = cmd.iter().map(|&s| s.to_string()).collect();
|
let cmd_vec: Vec<String> = cmd.iter().map(|&s| s.to_string()).collect();
|
||||||
|
|
||||||
logging::debug(&format!("Running command in Docker: {:?}", cmd_vec));
|
wrkflw_logging::debug(&format!("Running command in Docker: {:?}", cmd_vec));
|
||||||
logging::debug(&format!("Environment: {:?}", env));
|
wrkflw_logging::debug(&format!("Environment: {:?}", env));
|
||||||
logging::debug(&format!("Working directory: {}", working_dir.display()));
|
wrkflw_logging::debug(&format!("Working directory: {}", working_dir.display()));
|
||||||
|
|
||||||
// Determine platform-specific configurations
|
// Determine platform-specific configurations
|
||||||
let is_windows_image = image.contains("windows")
|
let is_windows_image = image.contains("windows")
|
||||||
@@ -973,7 +995,7 @@ impl DockerRuntime {
|
|||||||
_ => -1,
|
_ => -1,
|
||||||
},
|
},
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
logging::warning("Container wait operation timed out, treating as failure");
|
wrkflw_logging::warning("Container wait operation timed out, treating as failure");
|
||||||
-1
|
-1
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -1003,7 +1025,7 @@ impl DockerRuntime {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logging::warning("Retrieving container logs timed out");
|
wrkflw_logging::warning("Retrieving container logs timed out");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean up container with a timeout, but preserve on failure if configured
|
// Clean up container with a timeout, but preserve on failure if configured
|
||||||
@@ -1016,7 +1038,7 @@ impl DockerRuntime {
|
|||||||
untrack_container(&container.id);
|
untrack_container(&container.id);
|
||||||
} else {
|
} else {
|
||||||
// Container failed and we want to preserve it for debugging
|
// Container failed and we want to preserve it for debugging
|
||||||
logging::info(&format!(
|
wrkflw_logging::info(&format!(
|
||||||
"Preserving container {} for debugging (exit code: {}). Use 'docker exec -it {} bash' to inspect.",
|
"Preserving container {} for debugging (exit code: {}). Use 'docker exec -it {} bash' to inspect.",
|
||||||
container.id, exit_code, container.id
|
container.id, exit_code, container.id
|
||||||
));
|
));
|
||||||
@@ -1026,13 +1048,13 @@ impl DockerRuntime {
|
|||||||
|
|
||||||
// Log detailed information about the command execution for debugging
|
// Log detailed information about the command execution for debugging
|
||||||
if exit_code != 0 {
|
if exit_code != 0 {
|
||||||
logging::info(&format!(
|
wrkflw_logging::info(&format!(
|
||||||
"Docker command failed with exit code: {}",
|
"Docker command failed with exit code: {}",
|
||||||
exit_code
|
exit_code
|
||||||
));
|
));
|
||||||
logging::debug(&format!("Failed command: {:?}", cmd));
|
wrkflw_logging::debug(&format!("Failed command: {:?}", cmd));
|
||||||
logging::debug(&format!("Working directory: {}", working_dir.display()));
|
wrkflw_logging::debug(&format!("Working directory: {}", working_dir.display()));
|
||||||
logging::debug(&format!("STDERR: {}", stderr));
|
wrkflw_logging::debug(&format!("STDERR: {}", stderr));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(ContainerOutput {
|
Ok(ContainerOutput {
|
||||||
|
|||||||
@@ -13,13 +13,13 @@ use crate::dependency;
|
|||||||
use crate::docker;
|
use crate::docker;
|
||||||
use crate::environment;
|
use crate::environment;
|
||||||
use crate::podman;
|
use crate::podman;
|
||||||
use logging;
|
use wrkflw_logging;
|
||||||
use matrix::MatrixCombination;
|
use wrkflw_matrix::MatrixCombination;
|
||||||
use models::gitlab::Pipeline;
|
use wrkflw_models::gitlab::Pipeline;
|
||||||
use parser::gitlab::{self, parse_pipeline};
|
use wrkflw_parser::gitlab::{self, parse_pipeline};
|
||||||
use parser::workflow::{self, parse_workflow, ActionInfo, Job, WorkflowDefinition};
|
use wrkflw_parser::workflow::{self, parse_workflow, ActionInfo, Job, WorkflowDefinition};
|
||||||
use runtime::container::ContainerRuntime;
|
use wrkflw_runtime::container::ContainerRuntime;
|
||||||
use runtime::emulation;
|
use wrkflw_runtime::emulation;
|
||||||
|
|
||||||
#[allow(unused_variables, unused_assignments)]
|
#[allow(unused_variables, unused_assignments)]
|
||||||
/// Execute a GitHub Actions workflow file locally
|
/// Execute a GitHub Actions workflow file locally
|
||||||
@@ -27,8 +27,8 @@ pub async fn execute_workflow(
|
|||||||
workflow_path: &Path,
|
workflow_path: &Path,
|
||||||
config: ExecutionConfig,
|
config: ExecutionConfig,
|
||||||
) -> Result<ExecutionResult, ExecutionError> {
|
) -> Result<ExecutionResult, ExecutionError> {
|
||||||
logging::info(&format!("Executing workflow: {}", workflow_path.display()));
|
wrkflw_logging::info(&format!("Executing workflow: {}", workflow_path.display()));
|
||||||
logging::info(&format!("Runtime: {:?}", config.runtime_type));
|
wrkflw_logging::info(&format!("Runtime: {:?}", config.runtime_type));
|
||||||
|
|
||||||
// Determine if this is a GitLab CI/CD pipeline or GitHub Actions workflow
|
// Determine if this is a GitLab CI/CD pipeline or GitHub Actions workflow
|
||||||
let is_gitlab = is_gitlab_pipeline(workflow_path);
|
let is_gitlab = is_gitlab_pipeline(workflow_path);
|
||||||
@@ -150,7 +150,7 @@ async fn execute_github_workflow(
|
|||||||
|
|
||||||
// If there were failures, add detailed failure information to the result
|
// If there were failures, add detailed failure information to the result
|
||||||
if has_failures {
|
if has_failures {
|
||||||
logging::error(&format!("Workflow execution failed:{}", failure_details));
|
wrkflw_logging::error(&format!("Workflow execution failed:{}", failure_details));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(ExecutionResult {
|
Ok(ExecutionResult {
|
||||||
@@ -168,7 +168,7 @@ async fn execute_gitlab_pipeline(
|
|||||||
pipeline_path: &Path,
|
pipeline_path: &Path,
|
||||||
config: ExecutionConfig,
|
config: ExecutionConfig,
|
||||||
) -> Result<ExecutionResult, ExecutionError> {
|
) -> Result<ExecutionResult, ExecutionError> {
|
||||||
logging::info("Executing GitLab CI/CD pipeline");
|
wrkflw_logging::info("Executing GitLab CI/CD pipeline");
|
||||||
|
|
||||||
// 1. Parse the GitLab pipeline file
|
// 1. Parse the GitLab pipeline file
|
||||||
let pipeline = parse_pipeline(pipeline_path)
|
let pipeline = parse_pipeline(pipeline_path)
|
||||||
@@ -244,7 +244,7 @@ async fn execute_gitlab_pipeline(
|
|||||||
|
|
||||||
// If there were failures, add detailed failure information to the result
|
// If there were failures, add detailed failure information to the result
|
||||||
if has_failures {
|
if has_failures {
|
||||||
logging::error(&format!("Pipeline execution failed:{}", failure_details));
|
wrkflw_logging::error(&format!("Pipeline execution failed:{}", failure_details));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(ExecutionResult {
|
Ok(ExecutionResult {
|
||||||
@@ -369,7 +369,7 @@ fn initialize_runtime(
|
|||||||
match docker::DockerRuntime::new_with_config(preserve_containers_on_failure) {
|
match docker::DockerRuntime::new_with_config(preserve_containers_on_failure) {
|
||||||
Ok(docker_runtime) => Ok(Box::new(docker_runtime)),
|
Ok(docker_runtime) => Ok(Box::new(docker_runtime)),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
logging::error(&format!(
|
wrkflw_logging::error(&format!(
|
||||||
"Failed to initialize Docker runtime: {}, falling back to emulation mode",
|
"Failed to initialize Docker runtime: {}, falling back to emulation mode",
|
||||||
e
|
e
|
||||||
));
|
));
|
||||||
@@ -377,7 +377,7 @@ fn initialize_runtime(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logging::error("Docker not available, falling back to emulation mode");
|
wrkflw_logging::error("Docker not available, falling back to emulation mode");
|
||||||
Ok(Box::new(emulation::EmulationRuntime::new()))
|
Ok(Box::new(emulation::EmulationRuntime::new()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -387,7 +387,7 @@ fn initialize_runtime(
|
|||||||
match podman::PodmanRuntime::new_with_config(preserve_containers_on_failure) {
|
match podman::PodmanRuntime::new_with_config(preserve_containers_on_failure) {
|
||||||
Ok(podman_runtime) => Ok(Box::new(podman_runtime)),
|
Ok(podman_runtime) => Ok(Box::new(podman_runtime)),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
logging::error(&format!(
|
wrkflw_logging::error(&format!(
|
||||||
"Failed to initialize Podman runtime: {}, falling back to emulation mode",
|
"Failed to initialize Podman runtime: {}, falling back to emulation mode",
|
||||||
e
|
e
|
||||||
));
|
));
|
||||||
@@ -395,7 +395,7 @@ fn initialize_runtime(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logging::error("Podman not available, falling back to emulation mode");
|
wrkflw_logging::error("Podman not available, falling back to emulation mode");
|
||||||
Ok(Box::new(emulation::EmulationRuntime::new()))
|
Ok(Box::new(emulation::EmulationRuntime::new()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -577,7 +577,7 @@ async fn execute_job_with_matrix(
|
|||||||
if let Some(if_condition) = &job.if_condition {
|
if let Some(if_condition) = &job.if_condition {
|
||||||
let should_run = evaluate_job_condition(if_condition, env_context, workflow);
|
let should_run = evaluate_job_condition(if_condition, env_context, workflow);
|
||||||
if !should_run {
|
if !should_run {
|
||||||
logging::info(&format!(
|
wrkflw_logging::info(&format!(
|
||||||
"⏭️ Skipping job '{}' due to condition: {}",
|
"⏭️ Skipping job '{}' due to condition: {}",
|
||||||
job_name, if_condition
|
job_name, if_condition
|
||||||
));
|
));
|
||||||
@@ -594,11 +594,11 @@ async fn execute_job_with_matrix(
|
|||||||
// Check if this is a matrix job
|
// Check if this is a matrix job
|
||||||
if let Some(matrix_config) = &job.matrix {
|
if let Some(matrix_config) = &job.matrix {
|
||||||
// Expand the matrix into combinations
|
// Expand the matrix into combinations
|
||||||
let combinations = matrix::expand_matrix(matrix_config)
|
let combinations = wrkflw_matrix::expand_matrix(matrix_config)
|
||||||
.map_err(|e| ExecutionError::Execution(format!("Failed to expand matrix: {}", e)))?;
|
.map_err(|e| ExecutionError::Execution(format!("Failed to expand matrix: {}", e)))?;
|
||||||
|
|
||||||
if combinations.is_empty() {
|
if combinations.is_empty() {
|
||||||
logging::info(&format!(
|
wrkflw_logging::info(&format!(
|
||||||
"Matrix job '{}' has no valid combinations",
|
"Matrix job '{}' has no valid combinations",
|
||||||
job_name
|
job_name
|
||||||
));
|
));
|
||||||
@@ -606,7 +606,7 @@ async fn execute_job_with_matrix(
|
|||||||
return Ok(Vec::new());
|
return Ok(Vec::new());
|
||||||
}
|
}
|
||||||
|
|
||||||
logging::info(&format!(
|
wrkflw_logging::info(&format!(
|
||||||
"Matrix job '{}' expanded to {} combinations",
|
"Matrix job '{}' expanded to {} combinations",
|
||||||
job_name,
|
job_name,
|
||||||
combinations.len()
|
combinations.len()
|
||||||
@@ -674,13 +674,13 @@ async fn execute_job(ctx: JobExecutionContext<'_>) -> Result<JobResult, Executio
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
// Copy project files to the job workspace directory
|
// Copy project files to the job workspace directory
|
||||||
logging::info(&format!(
|
wrkflw_logging::info(&format!(
|
||||||
"Copying project files to job workspace: {}",
|
"Copying project files to job workspace: {}",
|
||||||
job_dir.path().display()
|
job_dir.path().display()
|
||||||
));
|
));
|
||||||
copy_directory_contents(¤t_dir, job_dir.path())?;
|
copy_directory_contents(¤t_dir, job_dir.path())?;
|
||||||
|
|
||||||
logging::info(&format!("Executing job: {}", ctx.job_name));
|
wrkflw_logging::info(&format!("Executing job: {}", ctx.job_name));
|
||||||
|
|
||||||
let mut job_success = true;
|
let mut job_success = true;
|
||||||
|
|
||||||
@@ -780,7 +780,8 @@ async fn execute_matrix_combinations(
|
|||||||
if ctx.fail_fast && any_failed {
|
if ctx.fail_fast && any_failed {
|
||||||
// Add skipped results for remaining combinations
|
// Add skipped results for remaining combinations
|
||||||
for combination in chunk {
|
for combination in chunk {
|
||||||
let combination_name = matrix::format_combination_name(ctx.job_name, combination);
|
let combination_name =
|
||||||
|
wrkflw_matrix::format_combination_name(ctx.job_name, combination);
|
||||||
results.push(JobResult {
|
results.push(JobResult {
|
||||||
name: combination_name,
|
name: combination_name,
|
||||||
status: JobStatus::Skipped,
|
status: JobStatus::Skipped,
|
||||||
@@ -818,7 +819,7 @@ async fn execute_matrix_combinations(
|
|||||||
Err(e) => {
|
Err(e) => {
|
||||||
// On error, mark as failed and continue if not fail-fast
|
// On error, mark as failed and continue if not fail-fast
|
||||||
any_failed = true;
|
any_failed = true;
|
||||||
logging::error(&format!("Matrix job failed: {}", e));
|
wrkflw_logging::error(&format!("Matrix job failed: {}", e));
|
||||||
|
|
||||||
if ctx.fail_fast {
|
if ctx.fail_fast {
|
||||||
return Err(e);
|
return Err(e);
|
||||||
@@ -842,9 +843,9 @@ async fn execute_matrix_job(
|
|||||||
verbose: bool,
|
verbose: bool,
|
||||||
) -> Result<JobResult, ExecutionError> {
|
) -> Result<JobResult, ExecutionError> {
|
||||||
// Create the matrix-specific job name
|
// Create the matrix-specific job name
|
||||||
let matrix_job_name = matrix::format_combination_name(job_name, combination);
|
let matrix_job_name = wrkflw_matrix::format_combination_name(job_name, combination);
|
||||||
|
|
||||||
logging::info(&format!("Executing matrix job: {}", matrix_job_name));
|
wrkflw_logging::info(&format!("Executing matrix job: {}", matrix_job_name));
|
||||||
|
|
||||||
// Clone the environment and add matrix-specific values
|
// Clone the environment and add matrix-specific values
|
||||||
let mut job_env = base_env_context.clone();
|
let mut job_env = base_env_context.clone();
|
||||||
@@ -870,14 +871,14 @@ async fn execute_matrix_job(
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
// Copy project files to the job workspace directory
|
// Copy project files to the job workspace directory
|
||||||
logging::info(&format!(
|
wrkflw_logging::info(&format!(
|
||||||
"Copying project files to job workspace: {}",
|
"Copying project files to job workspace: {}",
|
||||||
job_dir.path().display()
|
job_dir.path().display()
|
||||||
));
|
));
|
||||||
copy_directory_contents(¤t_dir, job_dir.path())?;
|
copy_directory_contents(¤t_dir, job_dir.path())?;
|
||||||
|
|
||||||
let job_success = if job_template.steps.is_empty() {
|
let job_success = if job_template.steps.is_empty() {
|
||||||
logging::warning(&format!("Job '{}' has no steps", matrix_job_name));
|
wrkflw_logging::warning(&format!("Job '{}' has no steps", matrix_job_name));
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
// Execute each step
|
// Execute each step
|
||||||
@@ -971,7 +972,7 @@ async fn execute_step(ctx: StepExecutionContext<'_>) -> Result<StepResult, Execu
|
|||||||
.unwrap_or_else(|| format!("Step {}", ctx.step_idx + 1));
|
.unwrap_or_else(|| format!("Step {}", ctx.step_idx + 1));
|
||||||
|
|
||||||
if ctx.verbose {
|
if ctx.verbose {
|
||||||
logging::info(&format!(" Executing step: {}", step_name));
|
wrkflw_logging::info(&format!(" Executing step: {}", step_name));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare step environment
|
// Prepare step environment
|
||||||
@@ -1073,7 +1074,9 @@ async fn execute_step(ctx: StepExecutionContext<'_>) -> Result<StepResult, Execu
|
|||||||
|
|
||||||
// Special handling for Rust actions
|
// Special handling for Rust actions
|
||||||
if uses.starts_with("actions-rs/") {
|
if uses.starts_with("actions-rs/") {
|
||||||
logging::info("🔄 Detected Rust action - using system Rust installation");
|
wrkflw_logging::info(
|
||||||
|
"🔄 Detected Rust action - using system Rust installation",
|
||||||
|
);
|
||||||
|
|
||||||
// For toolchain action, verify Rust is installed
|
// For toolchain action, verify Rust is installed
|
||||||
if uses.starts_with("actions-rs/toolchain@") {
|
if uses.starts_with("actions-rs/toolchain@") {
|
||||||
@@ -1083,7 +1086,10 @@ async fn execute_step(ctx: StepExecutionContext<'_>) -> Result<StepResult, Execu
|
|||||||
.map(|output| String::from_utf8_lossy(&output.stdout).to_string())
|
.map(|output| String::from_utf8_lossy(&output.stdout).to_string())
|
||||||
.unwrap_or_else(|_| "not found".to_string());
|
.unwrap_or_else(|_| "not found".to_string());
|
||||||
|
|
||||||
logging::info(&format!("🔄 Using system Rust: {}", rustc_version.trim()));
|
wrkflw_logging::info(&format!(
|
||||||
|
"🔄 Using system Rust: {}",
|
||||||
|
rustc_version.trim()
|
||||||
|
));
|
||||||
|
|
||||||
// Return success since we're using system Rust
|
// Return success since we're using system Rust
|
||||||
return Ok(StepResult {
|
return Ok(StepResult {
|
||||||
@@ -1101,7 +1107,7 @@ async fn execute_step(ctx: StepExecutionContext<'_>) -> Result<StepResult, Execu
|
|||||||
.map(|output| String::from_utf8_lossy(&output.stdout).to_string())
|
.map(|output| String::from_utf8_lossy(&output.stdout).to_string())
|
||||||
.unwrap_or_else(|_| "not found".to_string());
|
.unwrap_or_else(|_| "not found".to_string());
|
||||||
|
|
||||||
logging::info(&format!(
|
wrkflw_logging::info(&format!(
|
||||||
"🔄 Using system Rust/Cargo: {}",
|
"🔄 Using system Rust/Cargo: {}",
|
||||||
cargo_version.trim()
|
cargo_version.trim()
|
||||||
));
|
));
|
||||||
@@ -1109,7 +1115,10 @@ async fn execute_step(ctx: StepExecutionContext<'_>) -> Result<StepResult, Execu
|
|||||||
// Get the command from the 'with' parameters
|
// Get the command from the 'with' parameters
|
||||||
if let Some(with_params) = &ctx.step.with {
|
if let Some(with_params) = &ctx.step.with {
|
||||||
if let Some(command) = with_params.get("command") {
|
if let Some(command) = with_params.get("command") {
|
||||||
logging::info(&format!("🔄 Found command parameter: {}", command));
|
wrkflw_logging::info(&format!(
|
||||||
|
"🔄 Found command parameter: {}",
|
||||||
|
command
|
||||||
|
));
|
||||||
|
|
||||||
// Build the actual command
|
// Build the actual command
|
||||||
let mut real_command = format!("cargo {}", command);
|
let mut real_command = format!("cargo {}", command);
|
||||||
@@ -1119,7 +1128,7 @@ async fn execute_step(ctx: StepExecutionContext<'_>) -> Result<StepResult, Execu
|
|||||||
if !args.is_empty() {
|
if !args.is_empty() {
|
||||||
// Resolve GitHub-style variables in args
|
// Resolve GitHub-style variables in args
|
||||||
let resolved_args = if args.contains("${{") {
|
let resolved_args = if args.contains("${{") {
|
||||||
logging::info(&format!(
|
wrkflw_logging::info(&format!(
|
||||||
"🔄 Resolving workflow variables in: {}",
|
"🔄 Resolving workflow variables in: {}",
|
||||||
args
|
args
|
||||||
));
|
));
|
||||||
@@ -1133,7 +1142,7 @@ async fn execute_step(ctx: StepExecutionContext<'_>) -> Result<StepResult, Execu
|
|||||||
let re_pattern =
|
let re_pattern =
|
||||||
regex::Regex::new(r"\$\{\{\s*([^}]+)\s*\}\}")
|
regex::Regex::new(r"\$\{\{\s*([^}]+)\s*\}\}")
|
||||||
.unwrap_or_else(|_| {
|
.unwrap_or_else(|_| {
|
||||||
logging::error(
|
wrkflw_logging::error(
|
||||||
"Failed to create regex pattern",
|
"Failed to create regex pattern",
|
||||||
);
|
);
|
||||||
regex::Regex::new(r"\$\{\{.*?\}\}").unwrap()
|
regex::Regex::new(r"\$\{\{.*?\}\}").unwrap()
|
||||||
@@ -1141,7 +1150,10 @@ async fn execute_step(ctx: StepExecutionContext<'_>) -> Result<StepResult, Execu
|
|||||||
|
|
||||||
let resolved =
|
let resolved =
|
||||||
re_pattern.replace_all(&resolved, "").to_string();
|
re_pattern.replace_all(&resolved, "").to_string();
|
||||||
logging::info(&format!("🔄 Resolved to: {}", resolved));
|
wrkflw_logging::info(&format!(
|
||||||
|
"🔄 Resolved to: {}",
|
||||||
|
resolved
|
||||||
|
));
|
||||||
|
|
||||||
resolved.trim().to_string()
|
resolved.trim().to_string()
|
||||||
} else {
|
} else {
|
||||||
@@ -1157,7 +1169,7 @@ async fn execute_step(ctx: StepExecutionContext<'_>) -> Result<StepResult, Execu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logging::info(&format!(
|
wrkflw_logging::info(&format!(
|
||||||
"🔄 Running actual command: {}",
|
"🔄 Running actual command: {}",
|
||||||
real_command
|
real_command
|
||||||
));
|
));
|
||||||
@@ -1239,13 +1251,13 @@ async fn execute_step(ctx: StepExecutionContext<'_>) -> Result<StepResult, Execu
|
|||||||
.cloned()
|
.cloned()
|
||||||
.unwrap_or_else(|| "not set".to_string());
|
.unwrap_or_else(|| "not set".to_string());
|
||||||
|
|
||||||
logging::debug(&format!(
|
wrkflw_logging::debug(&format!(
|
||||||
"WRKFLW_HIDE_ACTION_MESSAGES value: {}",
|
"WRKFLW_HIDE_ACTION_MESSAGES value: {}",
|
||||||
hide_action_value
|
hide_action_value
|
||||||
));
|
));
|
||||||
|
|
||||||
let hide_messages = hide_action_value == "true";
|
let hide_messages = hide_action_value == "true";
|
||||||
logging::debug(&format!("Should hide messages: {}", hide_messages));
|
wrkflw_logging::debug(&format!("Should hide messages: {}", hide_messages));
|
||||||
|
|
||||||
// Only log a message to the console if we're showing action messages
|
// Only log a message to the console if we're showing action messages
|
||||||
if !hide_messages {
|
if !hide_messages {
|
||||||
@@ -1262,7 +1274,10 @@ async fn execute_step(ctx: StepExecutionContext<'_>) -> Result<StepResult, Execu
|
|||||||
// Common GitHub action pattern: has a 'command' parameter
|
// Common GitHub action pattern: has a 'command' parameter
|
||||||
if let Some(cmd) = with_params.get("command") {
|
if let Some(cmd) = with_params.get("command") {
|
||||||
if ctx.verbose {
|
if ctx.verbose {
|
||||||
logging::info(&format!("🔄 Found command parameter: {}", cmd));
|
wrkflw_logging::info(&format!(
|
||||||
|
"🔄 Found command parameter: {}",
|
||||||
|
cmd
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert to real command based on action type patterns
|
// Convert to real command based on action type patterns
|
||||||
@@ -1302,7 +1317,7 @@ async fn execute_step(ctx: StepExecutionContext<'_>) -> Result<StepResult, Execu
|
|||||||
if !args.is_empty() {
|
if !args.is_empty() {
|
||||||
// Resolve GitHub-style variables in args
|
// Resolve GitHub-style variables in args
|
||||||
let resolved_args = if args.contains("${{") {
|
let resolved_args = if args.contains("${{") {
|
||||||
logging::info(&format!(
|
wrkflw_logging::info(&format!(
|
||||||
"🔄 Resolving workflow variables in: {}",
|
"🔄 Resolving workflow variables in: {}",
|
||||||
args
|
args
|
||||||
));
|
));
|
||||||
@@ -1315,7 +1330,7 @@ async fn execute_step(ctx: StepExecutionContext<'_>) -> Result<StepResult, Execu
|
|||||||
let re_pattern =
|
let re_pattern =
|
||||||
regex::Regex::new(r"\$\{\{\s*([^}]+)\s*\}\}")
|
regex::Regex::new(r"\$\{\{\s*([^}]+)\s*\}\}")
|
||||||
.unwrap_or_else(|_| {
|
.unwrap_or_else(|_| {
|
||||||
logging::error(
|
wrkflw_logging::error(
|
||||||
"Failed to create regex pattern",
|
"Failed to create regex pattern",
|
||||||
);
|
);
|
||||||
regex::Regex::new(r"\$\{\{.*?\}\}").unwrap()
|
regex::Regex::new(r"\$\{\{.*?\}\}").unwrap()
|
||||||
@@ -1323,7 +1338,10 @@ async fn execute_step(ctx: StepExecutionContext<'_>) -> Result<StepResult, Execu
|
|||||||
|
|
||||||
let resolved =
|
let resolved =
|
||||||
re_pattern.replace_all(&resolved, "").to_string();
|
re_pattern.replace_all(&resolved, "").to_string();
|
||||||
logging::info(&format!("🔄 Resolved to: {}", resolved));
|
wrkflw_logging::info(&format!(
|
||||||
|
"🔄 Resolved to: {}",
|
||||||
|
resolved
|
||||||
|
));
|
||||||
|
|
||||||
resolved.trim().to_string()
|
resolved.trim().to_string()
|
||||||
} else {
|
} else {
|
||||||
@@ -1342,7 +1360,10 @@ async fn execute_step(ctx: StepExecutionContext<'_>) -> Result<StepResult, Execu
|
|||||||
if should_run_real_command && !real_command_parts.is_empty() {
|
if should_run_real_command && !real_command_parts.is_empty() {
|
||||||
// Build a final command string
|
// Build a final command string
|
||||||
let command_str = real_command_parts.join(" ");
|
let command_str = real_command_parts.join(" ");
|
||||||
logging::info(&format!("🔄 Running actual command: {}", command_str));
|
wrkflw_logging::info(&format!(
|
||||||
|
"🔄 Running actual command: {}",
|
||||||
|
command_str
|
||||||
|
));
|
||||||
|
|
||||||
// Replace the emulated command with a shell command to execute our command
|
// Replace the emulated command with a shell command to execute our command
|
||||||
cmd.clear();
|
cmd.clear();
|
||||||
@@ -1737,7 +1758,7 @@ async fn prepare_runner_image(
|
|||||||
) -> Result<(), ExecutionError> {
|
) -> Result<(), ExecutionError> {
|
||||||
// Try to pull the image first
|
// Try to pull the image first
|
||||||
if let Err(e) = runtime.pull_image(image).await {
|
if let Err(e) = runtime.pull_image(image).await {
|
||||||
logging::warning(&format!("Failed to pull image {}: {}", image, e));
|
wrkflw_logging::warning(&format!("Failed to pull image {}: {}", image, e));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if this is a language-specific runner
|
// Check if this is a language-specific runner
|
||||||
@@ -1750,7 +1771,7 @@ async fn prepare_runner_image(
|
|||||||
.map_err(|e| ExecutionError::Runtime(e.to_string()))
|
.map_err(|e| ExecutionError::Runtime(e.to_string()))
|
||||||
{
|
{
|
||||||
if verbose {
|
if verbose {
|
||||||
logging::info(&format!("Using customized image: {}", custom_image));
|
wrkflw_logging::info(&format!("Using customized image: {}", custom_image));
|
||||||
}
|
}
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@@ -2028,7 +2049,7 @@ fn evaluate_job_condition(
|
|||||||
env_context: &HashMap<String, String>,
|
env_context: &HashMap<String, String>,
|
||||||
workflow: &WorkflowDefinition,
|
workflow: &WorkflowDefinition,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
logging::debug(&format!("Evaluating condition: {}", condition));
|
wrkflw_logging::debug(&format!("Evaluating condition: {}", condition));
|
||||||
|
|
||||||
// For now, implement basic pattern matching for common conditions
|
// For now, implement basic pattern matching for common conditions
|
||||||
// TODO: Implement a full GitHub Actions expression evaluator
|
// TODO: Implement a full GitHub Actions expression evaluator
|
||||||
@@ -2051,14 +2072,14 @@ fn evaluate_job_condition(
|
|||||||
if condition.contains("needs.") && condition.contains(".outputs.") {
|
if condition.contains("needs.") && condition.contains(".outputs.") {
|
||||||
// For now, simulate that outputs are available but empty
|
// For now, simulate that outputs are available but empty
|
||||||
// This means conditions like needs.changes.outputs.source-code == 'true' will be false
|
// This means conditions like needs.changes.outputs.source-code == 'true' will be false
|
||||||
logging::debug(
|
wrkflw_logging::debug(
|
||||||
"Evaluating needs.outputs condition - defaulting to false for local execution",
|
"Evaluating needs.outputs condition - defaulting to false for local execution",
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default to true for unknown conditions to avoid breaking workflows
|
// Default to true for unknown conditions to avoid breaking workflows
|
||||||
logging::warning(&format!(
|
wrkflw_logging::warning(&format!(
|
||||||
"Unknown condition pattern: '{}' - defaulting to true",
|
"Unknown condition pattern: '{}' - defaulting to true",
|
||||||
condition
|
condition
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use matrix::MatrixCombination;
|
|
||||||
use parser::workflow::WorkflowDefinition;
|
|
||||||
use serde_yaml::Value;
|
use serde_yaml::Value;
|
||||||
use std::{collections::HashMap, fs, io, path::Path};
|
use std::{collections::HashMap, fs, io, path::Path};
|
||||||
|
use wrkflw_matrix::MatrixCombination;
|
||||||
|
use wrkflw_parser::workflow::WorkflowDefinition;
|
||||||
|
|
||||||
pub fn setup_github_environment_files(workspace_dir: &Path) -> io::Result<()> {
|
pub fn setup_github_environment_files(workspace_dir: &Path) -> io::Result<()> {
|
||||||
// Create necessary directories
|
// Create necessary directories
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use logging;
|
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use runtime::container::{ContainerError, ContainerOutput, ContainerRuntime};
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::process::Stdio;
|
use std::process::Stdio;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use tempfile;
|
use tempfile;
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
use utils;
|
use wrkflw_logging;
|
||||||
use utils::fd;
|
use wrkflw_runtime::container::{ContainerError, ContainerOutput, ContainerRuntime};
|
||||||
|
use wrkflw_utils;
|
||||||
|
use wrkflw_utils::fd;
|
||||||
|
|
||||||
static RUNNING_CONTAINERS: Lazy<Mutex<Vec<String>>> = Lazy::new(|| Mutex::new(Vec::new()));
|
static RUNNING_CONTAINERS: Lazy<Mutex<Vec<String>>> = Lazy::new(|| Mutex::new(Vec::new()));
|
||||||
// Map to track customized images for a job
|
// Map to track customized images for a job
|
||||||
@@ -46,7 +46,7 @@ impl PodmanRuntime {
|
|||||||
match CUSTOMIZED_IMAGES.lock() {
|
match CUSTOMIZED_IMAGES.lock() {
|
||||||
Ok(images) => images.get(&key).cloned(),
|
Ok(images) => images.get(&key).cloned(),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
logging::error(&format!("Failed to acquire lock: {}", e));
|
wrkflw_logging::error(&format!("Failed to acquire lock: {}", e));
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -58,7 +58,7 @@ impl PodmanRuntime {
|
|||||||
if let Err(e) = CUSTOMIZED_IMAGES.lock().map(|mut images| {
|
if let Err(e) = CUSTOMIZED_IMAGES.lock().map(|mut images| {
|
||||||
images.insert(key, new_image.to_string());
|
images.insert(key, new_image.to_string());
|
||||||
}) {
|
}) {
|
||||||
logging::error(&format!("Failed to acquire lock: {}", e));
|
wrkflw_logging::error(&format!("Failed to acquire lock: {}", e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ impl PodmanRuntime {
|
|||||||
let image_keys = match CUSTOMIZED_IMAGES.lock() {
|
let image_keys = match CUSTOMIZED_IMAGES.lock() {
|
||||||
Ok(keys) => keys,
|
Ok(keys) => keys,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
logging::error(&format!("Failed to acquire lock: {}", e));
|
wrkflw_logging::error(&format!("Failed to acquire lock: {}", e));
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -103,7 +103,7 @@ impl PodmanRuntime {
|
|||||||
match CUSTOMIZED_IMAGES.lock() {
|
match CUSTOMIZED_IMAGES.lock() {
|
||||||
Ok(images) => images.get(&key).cloned(),
|
Ok(images) => images.get(&key).cloned(),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
logging::error(&format!("Failed to acquire lock: {}", e));
|
wrkflw_logging::error(&format!("Failed to acquire lock: {}", e));
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -130,7 +130,7 @@ impl PodmanRuntime {
|
|||||||
if let Err(e) = CUSTOMIZED_IMAGES.lock().map(|mut images| {
|
if let Err(e) = CUSTOMIZED_IMAGES.lock().map(|mut images| {
|
||||||
images.insert(key, new_image.to_string());
|
images.insert(key, new_image.to_string());
|
||||||
}) {
|
}) {
|
||||||
logging::error(&format!("Failed to acquire lock: {}", e));
|
wrkflw_logging::error(&format!("Failed to acquire lock: {}", e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,7 +151,7 @@ impl PodmanRuntime {
|
|||||||
}
|
}
|
||||||
cmd.stdout(Stdio::piped()).stderr(Stdio::piped());
|
cmd.stdout(Stdio::piped()).stderr(Stdio::piped());
|
||||||
|
|
||||||
logging::debug(&format!(
|
wrkflw_logging::debug(&format!(
|
||||||
"Running Podman command: podman {}",
|
"Running Podman command: podman {}",
|
||||||
args.join(" ")
|
args.join(" ")
|
||||||
));
|
));
|
||||||
@@ -192,7 +192,7 @@ impl PodmanRuntime {
|
|||||||
match result {
|
match result {
|
||||||
Ok(output) => output,
|
Ok(output) => output,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
logging::error("Podman operation timed out after 360 seconds");
|
wrkflw_logging::error("Podman operation timed out after 360 seconds");
|
||||||
Err(ContainerError::ContainerExecution(
|
Err(ContainerError::ContainerExecution(
|
||||||
"Operation timed out".to_string(),
|
"Operation timed out".to_string(),
|
||||||
))
|
))
|
||||||
@@ -244,7 +244,7 @@ pub fn is_available() -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
logging::debug("Podman CLI is not available");
|
wrkflw_logging::debug("Podman CLI is not available");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -257,7 +257,7 @@ pub fn is_available() -> bool {
|
|||||||
{
|
{
|
||||||
Ok(rt) => rt,
|
Ok(rt) => rt,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
logging::error(&format!(
|
wrkflw_logging::error(&format!(
|
||||||
"Failed to create runtime for Podman availability check: {}",
|
"Failed to create runtime for Podman availability check: {}",
|
||||||
e
|
e
|
||||||
));
|
));
|
||||||
@@ -278,16 +278,16 @@ pub fn is_available() -> bool {
|
|||||||
if output.status.success() {
|
if output.status.success() {
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
logging::debug("Podman info command failed");
|
wrkflw_logging::debug("Podman info command failed");
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Err(e)) => {
|
Ok(Err(e)) => {
|
||||||
logging::debug(&format!("Podman info command error: {}", e));
|
wrkflw_logging::debug(&format!("Podman info command error: {}", e));
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
logging::debug("Podman info command timed out after 1 second");
|
wrkflw_logging::debug("Podman info command timed out after 1 second");
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -296,7 +296,7 @@ pub fn is_available() -> bool {
|
|||||||
{
|
{
|
||||||
Ok(result) => result,
|
Ok(result) => result,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
logging::debug("Podman availability check timed out");
|
wrkflw_logging::debug("Podman availability check timed out");
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -304,7 +304,9 @@ pub fn is_available() -> bool {
|
|||||||
}) {
|
}) {
|
||||||
Ok(result) => result,
|
Ok(result) => result,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
logging::debug("Failed to redirect stderr when checking Podman availability");
|
wrkflw_logging::debug(
|
||||||
|
"Failed to redirect stderr when checking Podman availability",
|
||||||
|
);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -318,7 +320,7 @@ pub fn is_available() -> bool {
|
|||||||
return match handle.join() {
|
return match handle.join() {
|
||||||
Ok(result) => result,
|
Ok(result) => result,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
logging::warning("Podman availability check thread panicked");
|
wrkflw_logging::warning("Podman availability check thread panicked");
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -326,7 +328,9 @@ pub fn is_available() -> bool {
|
|||||||
std::thread::sleep(std::time::Duration::from_millis(50));
|
std::thread::sleep(std::time::Duration::from_millis(50));
|
||||||
}
|
}
|
||||||
|
|
||||||
logging::warning("Podman availability check timed out, assuming Podman is not available");
|
wrkflw_logging::warning(
|
||||||
|
"Podman availability check timed out, assuming Podman is not available",
|
||||||
|
);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -352,12 +356,12 @@ pub async fn cleanup_resources() {
|
|||||||
match tokio::time::timeout(cleanup_timeout, cleanup_containers()).await {
|
match tokio::time::timeout(cleanup_timeout, cleanup_containers()).await {
|
||||||
Ok(result) => {
|
Ok(result) => {
|
||||||
if let Err(e) = result {
|
if let Err(e) = result {
|
||||||
logging::error(&format!("Error during container cleanup: {}", e));
|
wrkflw_logging::error(&format!("Error during container cleanup: {}", e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => wrkflw_logging::warning(
|
||||||
logging::warning("Podman cleanup timed out, some resources may not have been removed")
|
"Podman cleanup timed out, some resources may not have been removed",
|
||||||
}
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -369,7 +373,7 @@ pub async fn cleanup_containers() -> Result<(), String> {
|
|||||||
match RUNNING_CONTAINERS.try_lock() {
|
match RUNNING_CONTAINERS.try_lock() {
|
||||||
Ok(containers) => containers.clone(),
|
Ok(containers) => containers.clone(),
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
logging::error("Could not acquire container lock for cleanup");
|
wrkflw_logging::error("Could not acquire container lock for cleanup");
|
||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -378,7 +382,7 @@ pub async fn cleanup_containers() -> Result<(), String> {
|
|||||||
{
|
{
|
||||||
Ok(containers) => containers,
|
Ok(containers) => containers,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
logging::error("Timeout while trying to get containers for cleanup");
|
wrkflw_logging::error("Timeout while trying to get containers for cleanup");
|
||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -387,7 +391,7 @@ pub async fn cleanup_containers() -> Result<(), String> {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
logging::info(&format!(
|
wrkflw_logging::info(&format!(
|
||||||
"Cleaning up {} containers",
|
"Cleaning up {} containers",
|
||||||
containers_to_cleanup.len()
|
containers_to_cleanup.len()
|
||||||
));
|
));
|
||||||
@@ -408,15 +412,18 @@ pub async fn cleanup_containers() -> Result<(), String> {
|
|||||||
match stop_result {
|
match stop_result {
|
||||||
Ok(Ok(output)) => {
|
Ok(Ok(output)) => {
|
||||||
if output.status.success() {
|
if output.status.success() {
|
||||||
logging::debug(&format!("Stopped container: {}", container_id));
|
wrkflw_logging::debug(&format!("Stopped container: {}", container_id));
|
||||||
} else {
|
} else {
|
||||||
logging::warning(&format!("Error stopping container {}", container_id));
|
wrkflw_logging::warning(&format!("Error stopping container {}", container_id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Err(e)) => {
|
Ok(Err(e)) => wrkflw_logging::warning(&format!(
|
||||||
logging::warning(&format!("Error stopping container {}: {}", container_id, e))
|
"Error stopping container {}: {}",
|
||||||
|
container_id, e
|
||||||
|
)),
|
||||||
|
Err(_) => {
|
||||||
|
wrkflw_logging::warning(&format!("Timeout stopping container: {}", container_id))
|
||||||
}
|
}
|
||||||
Err(_) => logging::warning(&format!("Timeout stopping container: {}", container_id)),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Then try to remove it
|
// Then try to remove it
|
||||||
@@ -433,15 +440,18 @@ pub async fn cleanup_containers() -> Result<(), String> {
|
|||||||
match remove_result {
|
match remove_result {
|
||||||
Ok(Ok(output)) => {
|
Ok(Ok(output)) => {
|
||||||
if output.status.success() {
|
if output.status.success() {
|
||||||
logging::debug(&format!("Removed container: {}", container_id));
|
wrkflw_logging::debug(&format!("Removed container: {}", container_id));
|
||||||
} else {
|
} else {
|
||||||
logging::warning(&format!("Error removing container {}", container_id));
|
wrkflw_logging::warning(&format!("Error removing container {}", container_id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Err(e)) => {
|
Ok(Err(e)) => wrkflw_logging::warning(&format!(
|
||||||
logging::warning(&format!("Error removing container {}: {}", container_id, e))
|
"Error removing container {}: {}",
|
||||||
|
container_id, e
|
||||||
|
)),
|
||||||
|
Err(_) => {
|
||||||
|
wrkflw_logging::warning(&format!("Timeout removing container: {}", container_id))
|
||||||
}
|
}
|
||||||
Err(_) => logging::warning(&format!("Timeout removing container: {}", container_id)),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always untrack the container whether or not we succeeded to avoid future cleanup attempts
|
// Always untrack the container whether or not we succeeded to avoid future cleanup attempts
|
||||||
@@ -462,7 +472,7 @@ impl ContainerRuntime for PodmanRuntime {
|
|||||||
volumes: &[(&Path, &Path)],
|
volumes: &[(&Path, &Path)],
|
||||||
) -> Result<ContainerOutput, ContainerError> {
|
) -> Result<ContainerOutput, ContainerError> {
|
||||||
// Print detailed debugging info
|
// Print detailed debugging info
|
||||||
logging::info(&format!("Podman: Running container with image: {}", image));
|
wrkflw_logging::info(&format!("Podman: Running container with image: {}", image));
|
||||||
|
|
||||||
let timeout_duration = std::time::Duration::from_secs(360); // 6 minutes timeout
|
let timeout_duration = std::time::Duration::from_secs(360); // 6 minutes timeout
|
||||||
|
|
||||||
@@ -475,7 +485,7 @@ impl ContainerRuntime for PodmanRuntime {
|
|||||||
{
|
{
|
||||||
Ok(result) => result,
|
Ok(result) => result,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
logging::error("Podman operation timed out after 360 seconds");
|
wrkflw_logging::error("Podman operation timed out after 360 seconds");
|
||||||
Err(ContainerError::ContainerExecution(
|
Err(ContainerError::ContainerExecution(
|
||||||
"Operation timed out".to_string(),
|
"Operation timed out".to_string(),
|
||||||
))
|
))
|
||||||
@@ -490,7 +500,7 @@ impl ContainerRuntime for PodmanRuntime {
|
|||||||
match tokio::time::timeout(timeout_duration, self.pull_image_inner(image)).await {
|
match tokio::time::timeout(timeout_duration, self.pull_image_inner(image)).await {
|
||||||
Ok(result) => result,
|
Ok(result) => result,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
logging::warning(&format!(
|
wrkflw_logging::warning(&format!(
|
||||||
"Pull of image {} timed out, continuing with existing image",
|
"Pull of image {} timed out, continuing with existing image",
|
||||||
image
|
image
|
||||||
));
|
));
|
||||||
@@ -508,7 +518,7 @@ impl ContainerRuntime for PodmanRuntime {
|
|||||||
{
|
{
|
||||||
Ok(result) => result,
|
Ok(result) => result,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
logging::error(&format!(
|
wrkflw_logging::error(&format!(
|
||||||
"Building image {} timed out after 120 seconds",
|
"Building image {} timed out after 120 seconds",
|
||||||
tag
|
tag
|
||||||
));
|
));
|
||||||
@@ -664,9 +674,9 @@ impl PodmanRuntime {
|
|||||||
working_dir: &Path,
|
working_dir: &Path,
|
||||||
volumes: &[(&Path, &Path)],
|
volumes: &[(&Path, &Path)],
|
||||||
) -> Result<ContainerOutput, ContainerError> {
|
) -> Result<ContainerOutput, ContainerError> {
|
||||||
logging::debug(&format!("Running command in Podman: {:?}", cmd));
|
wrkflw_logging::debug(&format!("Running command in Podman: {:?}", cmd));
|
||||||
logging::debug(&format!("Environment: {:?}", env_vars));
|
wrkflw_logging::debug(&format!("Environment: {:?}", env_vars));
|
||||||
logging::debug(&format!("Working directory: {}", working_dir.display()));
|
wrkflw_logging::debug(&format!("Working directory: {}", working_dir.display()));
|
||||||
|
|
||||||
// Generate a unique container name
|
// Generate a unique container name
|
||||||
let container_name = format!("wrkflw-{}", uuid::Uuid::new_v4());
|
let container_name = format!("wrkflw-{}", uuid::Uuid::new_v4());
|
||||||
@@ -742,13 +752,13 @@ impl PodmanRuntime {
|
|||||||
match cleanup_result {
|
match cleanup_result {
|
||||||
Ok(Ok(cleanup_output)) => {
|
Ok(Ok(cleanup_output)) => {
|
||||||
if !cleanup_output.status.success() {
|
if !cleanup_output.status.success() {
|
||||||
logging::debug(&format!(
|
wrkflw_logging::debug(&format!(
|
||||||
"Failed to remove successful container {}",
|
"Failed to remove successful container {}",
|
||||||
container_name
|
container_name
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => logging::debug(&format!(
|
_ => wrkflw_logging::debug(&format!(
|
||||||
"Timeout removing successful container {}",
|
"Timeout removing successful container {}",
|
||||||
container_name
|
container_name
|
||||||
)),
|
)),
|
||||||
@@ -760,7 +770,7 @@ impl PodmanRuntime {
|
|||||||
// Failed container
|
// Failed container
|
||||||
if self.preserve_containers_on_failure {
|
if self.preserve_containers_on_failure {
|
||||||
// Failed and we want to preserve - don't clean up but untrack from auto-cleanup
|
// Failed and we want to preserve - don't clean up but untrack from auto-cleanup
|
||||||
logging::info(&format!(
|
wrkflw_logging::info(&format!(
|
||||||
"Preserving failed container {} for debugging (exit code: {}). Use 'podman exec -it {} bash' to inspect.",
|
"Preserving failed container {} for debugging (exit code: {}). Use 'podman exec -it {} bash' to inspect.",
|
||||||
container_name, output.exit_code, container_name
|
container_name, output.exit_code, container_name
|
||||||
));
|
));
|
||||||
@@ -789,11 +799,11 @@ impl PodmanRuntime {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
match cleanup_result {
|
match cleanup_result {
|
||||||
Ok(Ok(_)) => logging::debug(&format!(
|
Ok(Ok(_)) => wrkflw_logging::debug(&format!(
|
||||||
"Cleaned up failed execution container {}",
|
"Cleaned up failed execution container {}",
|
||||||
container_name
|
container_name
|
||||||
)),
|
)),
|
||||||
_ => logging::debug(&format!(
|
_ => wrkflw_logging::debug(&format!(
|
||||||
"Failed to clean up execution failure container {}",
|
"Failed to clean up execution failure container {}",
|
||||||
container_name
|
container_name
|
||||||
)),
|
)),
|
||||||
@@ -806,17 +816,17 @@ impl PodmanRuntime {
|
|||||||
match &result {
|
match &result {
|
||||||
Ok(output) => {
|
Ok(output) => {
|
||||||
if output.exit_code != 0 {
|
if output.exit_code != 0 {
|
||||||
logging::info(&format!(
|
wrkflw_logging::info(&format!(
|
||||||
"Podman command failed with exit code: {}",
|
"Podman command failed with exit code: {}",
|
||||||
output.exit_code
|
output.exit_code
|
||||||
));
|
));
|
||||||
logging::debug(&format!("Failed command: {:?}", cmd));
|
wrkflw_logging::debug(&format!("Failed command: {:?}", cmd));
|
||||||
logging::debug(&format!("Working directory: {}", working_dir.display()));
|
wrkflw_logging::debug(&format!("Working directory: {}", working_dir.display()));
|
||||||
logging::debug(&format!("STDERR: {}", output.stderr));
|
wrkflw_logging::debug(&format!("STDERR: {}", output.stderr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
logging::error(&format!("Podman execution error: {}", e));
|
wrkflw_logging::error(&format!("Podman execution error: {}", e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,18 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "github"
|
name = "wrkflw-github"
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
description = "github functionality for wrkflw"
|
description = "GitHub API integration for wrkflw workflow execution engine"
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
documentation.workspace = true
|
||||||
|
homepage.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
keywords.workspace = true
|
||||||
|
categories.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Add other crate dependencies as needed
|
# Internal crates
|
||||||
models = { path = "../models" }
|
wrkflw-models = { path = "../models", version = "0.5.0" }
|
||||||
|
|
||||||
# External dependencies from workspace
|
# External dependencies from workspace
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
|
|||||||
@@ -1,13 +1,18 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "gitlab"
|
name = "wrkflw-gitlab"
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
description = "gitlab functionality for wrkflw"
|
description = "GitLab API integration for wrkflw workflow execution engine"
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
documentation.workspace = true
|
||||||
|
homepage.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
keywords.workspace = true
|
||||||
|
categories.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Internal crates
|
# Internal crates
|
||||||
models = { path = "../models" }
|
wrkflw-models = { path = "../models", version = "0.5.0" }
|
||||||
|
|
||||||
# External dependencies
|
# External dependencies
|
||||||
lazy_static.workspace = true
|
lazy_static.workspace = true
|
||||||
|
|||||||
@@ -1,13 +1,18 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "logging"
|
name = "wrkflw-logging"
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
description = "logging functionality for wrkflw"
|
description = "Logging functionality for wrkflw workflow execution engine"
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
documentation.workspace = true
|
||||||
|
homepage.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
keywords.workspace = true
|
||||||
|
categories.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Internal crates
|
# Internal crates
|
||||||
models = { path = "../models" }
|
wrkflw-models = { path = "../models", version = "0.5.0" }
|
||||||
|
|
||||||
# External dependencies
|
# External dependencies
|
||||||
chrono.workspace = true
|
chrono.workspace = true
|
||||||
|
|||||||
@@ -1,13 +1,18 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "matrix"
|
name = "wrkflw-matrix"
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
description = "matrix functionality for wrkflw"
|
description = "Matrix job parallelization for wrkflw workflow execution engine"
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
documentation.workspace = true
|
||||||
|
homepage.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
keywords.workspace = true
|
||||||
|
categories.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Internal crates
|
# Internal crates
|
||||||
models = { path = "../models" }
|
wrkflw-models = { path = "../models", version = "0.5.0" }
|
||||||
|
|
||||||
# External dependencies
|
# External dependencies
|
||||||
indexmap.workspace = true
|
indexmap.workspace = true
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "models"
|
name = "wrkflw-models"
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
description = "Data models for wrkflw"
|
description = "Data models and structures for wrkflw workflow execution engine"
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
documentation.workspace = true
|
||||||
|
homepage.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
keywords.workspace = true
|
||||||
|
categories.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
|
|||||||
@@ -1,14 +1,19 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "parser"
|
name = "wrkflw-parser"
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
description = "Parser functionality for wrkflw"
|
description = "Workflow parsing functionality for wrkflw execution engine"
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
documentation.workspace = true
|
||||||
|
homepage.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
keywords.workspace = true
|
||||||
|
categories.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Internal crates
|
# Internal crates
|
||||||
models = { path = "../models" }
|
wrkflw-models = { path = "../models", version = "0.5.0" }
|
||||||
matrix = { path = "../matrix" }
|
wrkflw-matrix = { path = "../matrix", version = "0.5.0" }
|
||||||
|
|
||||||
# External dependencies
|
# External dependencies
|
||||||
jsonschema.workspace = true
|
jsonschema.workspace = true
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
use crate::schema::{SchemaType, SchemaValidator};
|
use crate::schema::{SchemaType, SchemaValidator};
|
||||||
use crate::workflow;
|
use crate::workflow;
|
||||||
use models::gitlab::Pipeline;
|
|
||||||
use models::ValidationResult;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
use wrkflw_models::gitlab::Pipeline;
|
||||||
|
use wrkflw_models::ValidationResult;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum GitlabParserError {
|
pub enum GitlabParserError {
|
||||||
@@ -204,8 +204,8 @@ pub fn convert_to_workflow_format(pipeline: &Pipeline) -> workflow::WorkflowDefi
|
|||||||
for (i, service) in services.iter().enumerate() {
|
for (i, service) in services.iter().enumerate() {
|
||||||
let service_name = format!("service-{}", i);
|
let service_name = format!("service-{}", i);
|
||||||
let service_image = match service {
|
let service_image = match service {
|
||||||
models::gitlab::Service::Simple(name) => name.clone(),
|
wrkflw_models::gitlab::Service::Simple(name) => name.clone(),
|
||||||
models::gitlab::Service::Detailed { name, .. } => name.clone(),
|
wrkflw_models::gitlab::Service::Detailed { name, .. } => name.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let service = workflow::Service {
|
let service = workflow::Service {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use matrix::MatrixConfig;
|
|
||||||
use serde::{Deserialize, Deserializer, Serialize};
|
use serde::{Deserialize, Deserializer, Serialize};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
use wrkflw_matrix::MatrixConfig;
|
||||||
|
|
||||||
use super::schema::SchemaValidator;
|
use super::schema::SchemaValidator;
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,19 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "runtime"
|
name = "wrkflw-runtime"
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
description = "Runtime environment for wrkflw"
|
description = "Runtime execution environment for wrkflw workflow engine"
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
documentation.workspace = true
|
||||||
|
homepage.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
keywords.workspace = true
|
||||||
|
categories.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Internal crates
|
# Internal crates
|
||||||
models = { path = "../models" }
|
wrkflw-models = { path = "../models", version = "0.5.0" }
|
||||||
logging = { path = "../logging", version = "0.5.0" }
|
wrkflw-logging = { path = "../logging", version = "0.5.0" }
|
||||||
|
|
||||||
# External dependencies
|
# External dependencies
|
||||||
async-trait.workspace = true
|
async-trait.workspace = true
|
||||||
@@ -18,5 +23,5 @@ serde_yaml.workspace = true
|
|||||||
tempfile = "3.9"
|
tempfile = "3.9"
|
||||||
tokio.workspace = true
|
tokio.workspace = true
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
utils = { path = "../utils", version = "0.5.0" }
|
wrkflw-utils = { path = "../utils", version = "0.5.0" }
|
||||||
which = "4.4"
|
which = "4.4"
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use crate::container::{ContainerError, ContainerOutput, ContainerRuntime};
|
use crate::container::{ContainerError, ContainerOutput, ContainerRuntime};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use logging;
|
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
@@ -9,6 +8,7 @@ use std::process::Command;
|
|||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
use which;
|
use which;
|
||||||
|
use wrkflw_logging;
|
||||||
|
|
||||||
// Global collection of resources to clean up
|
// Global collection of resources to clean up
|
||||||
static EMULATION_WORKSPACES: Lazy<Mutex<Vec<PathBuf>>> = Lazy::new(|| Mutex::new(Vec::new()));
|
static EMULATION_WORKSPACES: Lazy<Mutex<Vec<PathBuf>>> = Lazy::new(|| Mutex::new(Vec::new()));
|
||||||
@@ -162,9 +162,9 @@ impl ContainerRuntime for EmulationRuntime {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Log more detailed debugging information
|
// Log more detailed debugging information
|
||||||
logging::info(&format!("Executing command in container: {}", command_str));
|
wrkflw_logging::info(&format!("Executing command in container: {}", command_str));
|
||||||
logging::info(&format!("Working directory: {}", working_dir.display()));
|
wrkflw_logging::info(&format!("Working directory: {}", working_dir.display()));
|
||||||
logging::info(&format!("Command length: {}", command.len()));
|
wrkflw_logging::info(&format!("Command length: {}", command.len()));
|
||||||
|
|
||||||
if command.is_empty() {
|
if command.is_empty() {
|
||||||
return Err(ContainerError::ContainerExecution(
|
return Err(ContainerError::ContainerExecution(
|
||||||
@@ -174,13 +174,13 @@ impl ContainerRuntime for EmulationRuntime {
|
|||||||
|
|
||||||
// Print each command part separately for debugging
|
// Print each command part separately for debugging
|
||||||
for (i, part) in command.iter().enumerate() {
|
for (i, part) in command.iter().enumerate() {
|
||||||
logging::info(&format!("Command part {}: '{}'", i, part));
|
wrkflw_logging::info(&format!("Command part {}: '{}'", i, part));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log environment variables
|
// Log environment variables
|
||||||
logging::info("Environment variables:");
|
wrkflw_logging::info("Environment variables:");
|
||||||
for (key, value) in env_vars {
|
for (key, value) in env_vars {
|
||||||
logging::info(&format!(" {}={}", key, value));
|
wrkflw_logging::info(&format!(" {}={}", key, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find actual working directory - determine if we should use the current directory instead
|
// Find actual working directory - determine if we should use the current directory instead
|
||||||
@@ -197,7 +197,7 @@ impl ContainerRuntime for EmulationRuntime {
|
|||||||
// If found, use that as the working directory
|
// If found, use that as the working directory
|
||||||
if let Some(path) = workspace_path {
|
if let Some(path) = workspace_path {
|
||||||
if path.exists() {
|
if path.exists() {
|
||||||
logging::info(&format!(
|
wrkflw_logging::info(&format!(
|
||||||
"Using environment-defined workspace: {}",
|
"Using environment-defined workspace: {}",
|
||||||
path.display()
|
path.display()
|
||||||
));
|
));
|
||||||
@@ -206,7 +206,7 @@ impl ContainerRuntime for EmulationRuntime {
|
|||||||
// Fallback to current directory
|
// Fallback to current directory
|
||||||
let current_dir =
|
let current_dir =
|
||||||
std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
|
std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
|
||||||
logging::info(&format!(
|
wrkflw_logging::info(&format!(
|
||||||
"Using current directory: {}",
|
"Using current directory: {}",
|
||||||
current_dir.display()
|
current_dir.display()
|
||||||
));
|
));
|
||||||
@@ -215,7 +215,7 @@ impl ContainerRuntime for EmulationRuntime {
|
|||||||
} else {
|
} else {
|
||||||
// Fallback to current directory
|
// Fallback to current directory
|
||||||
let current_dir = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
|
let current_dir = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
|
||||||
logging::info(&format!(
|
wrkflw_logging::info(&format!(
|
||||||
"Using current directory: {}",
|
"Using current directory: {}",
|
||||||
current_dir.display()
|
current_dir.display()
|
||||||
));
|
));
|
||||||
@@ -225,7 +225,7 @@ impl ContainerRuntime for EmulationRuntime {
|
|||||||
working_dir.to_path_buf()
|
working_dir.to_path_buf()
|
||||||
};
|
};
|
||||||
|
|
||||||
logging::info(&format!(
|
wrkflw_logging::info(&format!(
|
||||||
"Using actual working directory: {}",
|
"Using actual working directory: {}",
|
||||||
actual_working_dir.display()
|
actual_working_dir.display()
|
||||||
));
|
));
|
||||||
@@ -233,8 +233,8 @@ impl ContainerRuntime for EmulationRuntime {
|
|||||||
// Check if path contains the command (for shell script execution)
|
// Check if path contains the command (for shell script execution)
|
||||||
let command_path = which::which(command[0]);
|
let command_path = which::which(command[0]);
|
||||||
match &command_path {
|
match &command_path {
|
||||||
Ok(path) => logging::info(&format!("Found command at: {}", path.display())),
|
Ok(path) => wrkflw_logging::info(&format!("Found command at: {}", path.display())),
|
||||||
Err(e) => logging::error(&format!(
|
Err(e) => wrkflw_logging::error(&format!(
|
||||||
"Command not found in PATH: {} - Error: {}",
|
"Command not found in PATH: {} - Error: {}",
|
||||||
command[0], e
|
command[0], e
|
||||||
)),
|
)),
|
||||||
@@ -246,7 +246,7 @@ impl ContainerRuntime for EmulationRuntime {
|
|||||||
|| command_str.starts_with("mkdir ")
|
|| command_str.starts_with("mkdir ")
|
||||||
|| command_str.starts_with("mv ")
|
|| command_str.starts_with("mv ")
|
||||||
{
|
{
|
||||||
logging::info("Executing as shell command");
|
wrkflw_logging::info("Executing as shell command");
|
||||||
// Execute as a shell command
|
// Execute as a shell command
|
||||||
let mut cmd = Command::new("sh");
|
let mut cmd = Command::new("sh");
|
||||||
cmd.arg("-c");
|
cmd.arg("-c");
|
||||||
@@ -264,7 +264,7 @@ impl ContainerRuntime for EmulationRuntime {
|
|||||||
let output = String::from_utf8_lossy(&output_result.stdout).to_string();
|
let output = String::from_utf8_lossy(&output_result.stdout).to_string();
|
||||||
let error = String::from_utf8_lossy(&output_result.stderr).to_string();
|
let error = String::from_utf8_lossy(&output_result.stderr).to_string();
|
||||||
|
|
||||||
logging::debug(&format!(
|
wrkflw_logging::debug(&format!(
|
||||||
"Shell command completed with exit code: {}",
|
"Shell command completed with exit code: {}",
|
||||||
exit_code
|
exit_code
|
||||||
));
|
));
|
||||||
@@ -314,7 +314,7 @@ impl ContainerRuntime for EmulationRuntime {
|
|||||||
|
|
||||||
// Always use the current directory for cargo/rust commands rather than the temporary directory
|
// Always use the current directory for cargo/rust commands rather than the temporary directory
|
||||||
let current_dir = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
|
let current_dir = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
|
||||||
logging::info(&format!(
|
wrkflw_logging::info(&format!(
|
||||||
"Using project directory for Rust command: {}",
|
"Using project directory for Rust command: {}",
|
||||||
current_dir.display()
|
current_dir.display()
|
||||||
));
|
));
|
||||||
@@ -326,7 +326,7 @@ impl ContainerRuntime for EmulationRuntime {
|
|||||||
if *key == "CARGO_HOME" && value.contains("${CI_PROJECT_DIR}") {
|
if *key == "CARGO_HOME" && value.contains("${CI_PROJECT_DIR}") {
|
||||||
let cargo_home =
|
let cargo_home =
|
||||||
value.replace("${CI_PROJECT_DIR}", ¤t_dir.to_string_lossy());
|
value.replace("${CI_PROJECT_DIR}", ¤t_dir.to_string_lossy());
|
||||||
logging::info(&format!("Setting CARGO_HOME to: {}", cargo_home));
|
wrkflw_logging::info(&format!("Setting CARGO_HOME to: {}", cargo_home));
|
||||||
cmd.env(key, cargo_home);
|
cmd.env(key, cargo_home);
|
||||||
} else {
|
} else {
|
||||||
cmd.env(key, value);
|
cmd.env(key, value);
|
||||||
@@ -338,7 +338,7 @@ impl ContainerRuntime for EmulationRuntime {
|
|||||||
cmd.args(&parts[1..]);
|
cmd.args(&parts[1..]);
|
||||||
}
|
}
|
||||||
|
|
||||||
logging::debug(&format!(
|
wrkflw_logging::debug(&format!(
|
||||||
"Executing Rust command: {} in {}",
|
"Executing Rust command: {} in {}",
|
||||||
command_str,
|
command_str,
|
||||||
current_dir.display()
|
current_dir.display()
|
||||||
@@ -350,7 +350,7 @@ impl ContainerRuntime for EmulationRuntime {
|
|||||||
let output = String::from_utf8_lossy(&output_result.stdout).to_string();
|
let output = String::from_utf8_lossy(&output_result.stdout).to_string();
|
||||||
let error = String::from_utf8_lossy(&output_result.stderr).to_string();
|
let error = String::from_utf8_lossy(&output_result.stderr).to_string();
|
||||||
|
|
||||||
logging::debug(&format!("Command exit code: {}", exit_code));
|
wrkflw_logging::debug(&format!("Command exit code: {}", exit_code));
|
||||||
|
|
||||||
if exit_code != 0 {
|
if exit_code != 0 {
|
||||||
let mut error_details = format!(
|
let mut error_details = format!(
|
||||||
@@ -405,7 +405,7 @@ impl ContainerRuntime for EmulationRuntime {
|
|||||||
let output = String::from_utf8_lossy(&output_result.stdout).to_string();
|
let output = String::from_utf8_lossy(&output_result.stdout).to_string();
|
||||||
let error = String::from_utf8_lossy(&output_result.stderr).to_string();
|
let error = String::from_utf8_lossy(&output_result.stderr).to_string();
|
||||||
|
|
||||||
logging::debug(&format!("Command completed with exit code: {}", exit_code));
|
wrkflw_logging::debug(&format!("Command completed with exit code: {}", exit_code));
|
||||||
|
|
||||||
if exit_code != 0 {
|
if exit_code != 0 {
|
||||||
let mut error_details = format!(
|
let mut error_details = format!(
|
||||||
@@ -443,12 +443,12 @@ impl ContainerRuntime for EmulationRuntime {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn pull_image(&self, image: &str) -> Result<(), ContainerError> {
|
async fn pull_image(&self, image: &str) -> Result<(), ContainerError> {
|
||||||
logging::info(&format!("🔄 Emulation: Pretending to pull image {}", image));
|
wrkflw_logging::info(&format!("🔄 Emulation: Pretending to pull image {}", image));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn build_image(&self, dockerfile: &Path, tag: &str) -> Result<(), ContainerError> {
|
async fn build_image(&self, dockerfile: &Path, tag: &str) -> Result<(), ContainerError> {
|
||||||
logging::info(&format!(
|
wrkflw_logging::info(&format!(
|
||||||
"🔄 Emulation: Pretending to build image {} from {}",
|
"🔄 Emulation: Pretending to build image {} from {}",
|
||||||
tag,
|
tag,
|
||||||
dockerfile.display()
|
dockerfile.display()
|
||||||
@@ -543,14 +543,14 @@ pub async fn handle_special_action(action: &str) -> Result<(), ContainerError> {
|
|||||||
"latest"
|
"latest"
|
||||||
};
|
};
|
||||||
|
|
||||||
logging::info(&format!(
|
wrkflw_logging::info(&format!(
|
||||||
"🔄 Processing action: {} @ {}",
|
"🔄 Processing action: {} @ {}",
|
||||||
action_name, action_version
|
action_name, action_version
|
||||||
));
|
));
|
||||||
|
|
||||||
// Handle specific known actions with special requirements
|
// Handle specific known actions with special requirements
|
||||||
if action.starts_with("cachix/install-nix-action") {
|
if action.starts_with("cachix/install-nix-action") {
|
||||||
logging::info("🔄 Emulating cachix/install-nix-action");
|
wrkflw_logging::info("🔄 Emulating cachix/install-nix-action");
|
||||||
|
|
||||||
// In emulation mode, check if nix is installed
|
// In emulation mode, check if nix is installed
|
||||||
let nix_installed = Command::new("which")
|
let nix_installed = Command::new("which")
|
||||||
@@ -560,56 +560,56 @@ pub async fn handle_special_action(action: &str) -> Result<(), ContainerError> {
|
|||||||
.unwrap_or(false);
|
.unwrap_or(false);
|
||||||
|
|
||||||
if !nix_installed {
|
if !nix_installed {
|
||||||
logging::info("🔄 Emulation: Nix is required but not installed.");
|
wrkflw_logging::info("🔄 Emulation: Nix is required but not installed.");
|
||||||
logging::info(
|
wrkflw_logging::info(
|
||||||
"🔄 To use this workflow, please install Nix: https://nixos.org/download.html",
|
"🔄 To use this workflow, please install Nix: https://nixos.org/download.html",
|
||||||
);
|
);
|
||||||
logging::info("🔄 Continuing emulation, but nix commands will fail.");
|
wrkflw_logging::info("🔄 Continuing emulation, but nix commands will fail.");
|
||||||
} else {
|
} else {
|
||||||
logging::info("🔄 Emulation: Using system-installed Nix");
|
wrkflw_logging::info("🔄 Emulation: Using system-installed Nix");
|
||||||
}
|
}
|
||||||
} else if action.starts_with("actions-rs/cargo@") {
|
} else if action.starts_with("actions-rs/cargo@") {
|
||||||
// For actions-rs/cargo action, ensure Rust is available
|
// For actions-rs/cargo action, ensure Rust is available
|
||||||
logging::info(&format!("🔄 Detected Rust cargo action: {}", action));
|
wrkflw_logging::info(&format!("🔄 Detected Rust cargo action: {}", action));
|
||||||
|
|
||||||
// Verify Rust/cargo is installed
|
// Verify Rust/cargo is installed
|
||||||
check_command_available("cargo", "Rust/Cargo", "https://rustup.rs/");
|
check_command_available("cargo", "Rust/Cargo", "https://rustup.rs/");
|
||||||
} else if action.starts_with("actions-rs/toolchain@") {
|
} else if action.starts_with("actions-rs/toolchain@") {
|
||||||
// For actions-rs/toolchain action, check for Rust installation
|
// For actions-rs/toolchain action, check for Rust installation
|
||||||
logging::info(&format!("🔄 Detected Rust toolchain action: {}", action));
|
wrkflw_logging::info(&format!("🔄 Detected Rust toolchain action: {}", action));
|
||||||
|
|
||||||
check_command_available("rustc", "Rust", "https://rustup.rs/");
|
check_command_available("rustc", "Rust", "https://rustup.rs/");
|
||||||
} else if action.starts_with("actions-rs/fmt@") {
|
} else if action.starts_with("actions-rs/fmt@") {
|
||||||
// For actions-rs/fmt action, check if rustfmt is available
|
// For actions-rs/fmt action, check if rustfmt is available
|
||||||
logging::info(&format!("🔄 Detected Rust formatter action: {}", action));
|
wrkflw_logging::info(&format!("🔄 Detected Rust formatter action: {}", action));
|
||||||
|
|
||||||
check_command_available("rustfmt", "rustfmt", "rustup component add rustfmt");
|
check_command_available("rustfmt", "rustfmt", "rustup component add rustfmt");
|
||||||
} else if action.starts_with("actions/setup-node@") {
|
} else if action.starts_with("actions/setup-node@") {
|
||||||
// Node.js setup action
|
// Node.js setup action
|
||||||
logging::info(&format!("🔄 Detected Node.js setup action: {}", action));
|
wrkflw_logging::info(&format!("🔄 Detected Node.js setup action: {}", action));
|
||||||
|
|
||||||
check_command_available("node", "Node.js", "https://nodejs.org/");
|
check_command_available("node", "Node.js", "https://nodejs.org/");
|
||||||
} else if action.starts_with("actions/setup-python@") {
|
} else if action.starts_with("actions/setup-python@") {
|
||||||
// Python setup action
|
// Python setup action
|
||||||
logging::info(&format!("🔄 Detected Python setup action: {}", action));
|
wrkflw_logging::info(&format!("🔄 Detected Python setup action: {}", action));
|
||||||
|
|
||||||
check_command_available("python", "Python", "https://www.python.org/downloads/");
|
check_command_available("python", "Python", "https://www.python.org/downloads/");
|
||||||
} else if action.starts_with("actions/setup-java@") {
|
} else if action.starts_with("actions/setup-java@") {
|
||||||
// Java setup action
|
// Java setup action
|
||||||
logging::info(&format!("🔄 Detected Java setup action: {}", action));
|
wrkflw_logging::info(&format!("🔄 Detected Java setup action: {}", action));
|
||||||
|
|
||||||
check_command_available("java", "Java", "https://adoptium.net/");
|
check_command_available("java", "Java", "https://adoptium.net/");
|
||||||
} else if action.starts_with("actions/checkout@") {
|
} else if action.starts_with("actions/checkout@") {
|
||||||
// Git checkout action - this is handled implicitly by our workspace setup
|
// Git checkout action - this is handled implicitly by our workspace setup
|
||||||
logging::info("🔄 Detected checkout action - workspace files are already prepared");
|
wrkflw_logging::info("🔄 Detected checkout action - workspace files are already prepared");
|
||||||
} else if action.starts_with("actions/cache@") {
|
} else if action.starts_with("actions/cache@") {
|
||||||
// Cache action - can't really emulate caching effectively
|
// Cache action - can't really emulate caching effectively
|
||||||
logging::info(
|
wrkflw_logging::info(
|
||||||
"🔄 Detected cache action - caching is not fully supported in emulation mode",
|
"🔄 Detected cache action - caching is not fully supported in emulation mode",
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// Generic action we don't have special handling for
|
// Generic action we don't have special handling for
|
||||||
logging::info(&format!(
|
wrkflw_logging::info(&format!(
|
||||||
"🔄 Action '{}' has no special handling in emulation mode",
|
"🔄 Action '{}' has no special handling in emulation mode",
|
||||||
action_name
|
action_name
|
||||||
));
|
));
|
||||||
@@ -628,12 +628,12 @@ fn check_command_available(command: &str, name: &str, install_url: &str) {
|
|||||||
.unwrap_or(false);
|
.unwrap_or(false);
|
||||||
|
|
||||||
if !is_available {
|
if !is_available {
|
||||||
logging::warning(&format!("{} is required but not found on the system", name));
|
wrkflw_logging::warning(&format!("{} is required but not found on the system", name));
|
||||||
logging::info(&format!(
|
wrkflw_logging::info(&format!(
|
||||||
"To use this action, please install {}: {}",
|
"To use this action, please install {}: {}",
|
||||||
name, install_url
|
name, install_url
|
||||||
));
|
));
|
||||||
logging::info(&format!(
|
wrkflw_logging::info(&format!(
|
||||||
"Continuing emulation, but {} commands will fail",
|
"Continuing emulation, but {} commands will fail",
|
||||||
name
|
name
|
||||||
));
|
));
|
||||||
@@ -642,7 +642,7 @@ fn check_command_available(command: &str, name: &str, install_url: &str) {
|
|||||||
if let Ok(output) = Command::new(command).arg("--version").output() {
|
if let Ok(output) = Command::new(command).arg("--version").output() {
|
||||||
if output.status.success() {
|
if output.status.success() {
|
||||||
let version = String::from_utf8_lossy(&output.stdout);
|
let version = String::from_utf8_lossy(&output.stdout);
|
||||||
logging::info(&format!("🔄 Using system {}: {}", name, version.trim()));
|
wrkflw_logging::info(&format!("🔄 Using system {}: {}", name, version.trim()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -708,7 +708,7 @@ async fn cleanup_processes() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
for pid in processes_to_cleanup {
|
for pid in processes_to_cleanup {
|
||||||
logging::info(&format!("Cleaning up emulated process: {}", pid));
|
wrkflw_logging::info(&format!("Cleaning up emulated process: {}", pid));
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
{
|
{
|
||||||
@@ -747,7 +747,7 @@ async fn cleanup_workspaces() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
for workspace_path in workspaces_to_cleanup {
|
for workspace_path in workspaces_to_cleanup {
|
||||||
logging::info(&format!(
|
wrkflw_logging::info(&format!(
|
||||||
"Cleaning up emulation workspace: {}",
|
"Cleaning up emulation workspace: {}",
|
||||||
workspace_path.display()
|
workspace_path.display()
|
||||||
));
|
));
|
||||||
@@ -755,8 +755,8 @@ async fn cleanup_workspaces() {
|
|||||||
// Only attempt to remove if it exists
|
// Only attempt to remove if it exists
|
||||||
if workspace_path.exists() {
|
if workspace_path.exists() {
|
||||||
match fs::remove_dir_all(&workspace_path) {
|
match fs::remove_dir_all(&workspace_path) {
|
||||||
Ok(_) => logging::info("Successfully removed workspace directory"),
|
Ok(_) => wrkflw_logging::info("Successfully removed workspace directory"),
|
||||||
Err(e) => logging::error(&format!("Error removing workspace: {}", e)),
|
Err(e) => wrkflw_logging::error(&format!("Error removing workspace: {}", e)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,23 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ui"
|
name = "wrkflw-ui"
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
description = "user interface functionality for wrkflw"
|
description = "Terminal user interface for wrkflw workflow execution engine"
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
documentation.workspace = true
|
||||||
|
homepage.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
keywords.workspace = true
|
||||||
|
categories.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Internal crates
|
# Internal crates
|
||||||
models = { path = "../models" }
|
wrkflw-models = { path = "../models", version = "0.5.0" }
|
||||||
evaluator = { path = "../evaluator" }
|
wrkflw-evaluator = { path = "../evaluator", version = "0.5.0" }
|
||||||
executor = { path = "../executor" }
|
wrkflw-executor = { path = "../executor", version = "0.5.0" }
|
||||||
logging = { path = "../logging" }
|
wrkflw-logging = { path = "../logging", version = "0.5.0" }
|
||||||
utils = { path = "../utils" }
|
wrkflw-utils = { path = "../utils", version = "0.5.0" }
|
||||||
github = { path = "../github" }
|
wrkflw-github = { path = "../github", version = "0.5.0" }
|
||||||
|
|
||||||
# External dependencies
|
# External dependencies
|
||||||
chrono.workspace = true
|
chrono.workspace = true
|
||||||
|
|||||||
@@ -11,12 +11,12 @@ use crossterm::{
|
|||||||
execute,
|
execute,
|
||||||
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
||||||
};
|
};
|
||||||
use executor::RuntimeType;
|
|
||||||
use ratatui::{backend::CrosstermBackend, Terminal};
|
use ratatui::{backend::CrosstermBackend, Terminal};
|
||||||
use std::io::{self, stdout};
|
use std::io::{self, stdout};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
use wrkflw_executor::RuntimeType;
|
||||||
|
|
||||||
pub use state::App;
|
pub use state::App;
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ pub async fn run_wrkflw_tui(
|
|||||||
|
|
||||||
if app.validation_mode {
|
if app.validation_mode {
|
||||||
app.logs.push("Starting in validation mode".to_string());
|
app.logs.push("Starting in validation mode".to_string());
|
||||||
logging::info("Starting in validation mode");
|
wrkflw_logging::info("Starting in validation mode");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load workflows
|
// Load workflows
|
||||||
@@ -108,13 +108,13 @@ pub async fn run_wrkflw_tui(
|
|||||||
Ok(_) => Ok(()),
|
Ok(_) => Ok(()),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
// If the TUI fails to initialize or crashes, fall back to CLI mode
|
// If the TUI fails to initialize or crashes, fall back to CLI mode
|
||||||
logging::error(&format!("Failed to start UI: {}", e));
|
wrkflw_logging::error(&format!("Failed to start UI: {}", e));
|
||||||
|
|
||||||
// Only for 'tui' command should we fall back to CLI mode for files
|
// Only for 'tui' command should we fall back to CLI mode for files
|
||||||
// For other commands, return the error
|
// For other commands, return the error
|
||||||
if let Some(path) = path {
|
if let Some(path) = path {
|
||||||
if path.is_file() {
|
if path.is_file() {
|
||||||
logging::error("Falling back to CLI mode...");
|
wrkflw_logging::error("Falling back to CLI mode...");
|
||||||
crate::handlers::workflow::execute_workflow_cli(path, runtime_type, verbose)
|
crate::handlers::workflow::execute_workflow_cli(path, runtime_type, verbose)
|
||||||
.await
|
.await
|
||||||
} else if path.is_dir() {
|
} else if path.is_dir() {
|
||||||
@@ -273,7 +273,7 @@ fn run_tui_event_loop(
|
|||||||
"[{}] DEBUG: Shift+r detected - this should be uppercase R",
|
"[{}] DEBUG: Shift+r detected - this should be uppercase R",
|
||||||
timestamp
|
timestamp
|
||||||
));
|
));
|
||||||
logging::info(
|
wrkflw_logging::info(
|
||||||
"Shift+r detected as lowercase - this should be uppercase R",
|
"Shift+r detected as lowercase - this should be uppercase R",
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -329,7 +329,7 @@ fn run_tui_event_loop(
|
|||||||
"[{}] DEBUG: Reset key 'Shift+R' pressed",
|
"[{}] DEBUG: Reset key 'Shift+R' pressed",
|
||||||
timestamp
|
timestamp
|
||||||
));
|
));
|
||||||
logging::info("Reset key 'Shift+R' pressed");
|
wrkflw_logging::info("Reset key 'Shift+R' pressed");
|
||||||
|
|
||||||
if !app.running {
|
if !app.running {
|
||||||
// Reset workflow status
|
// Reset workflow status
|
||||||
@@ -367,7 +367,7 @@ fn run_tui_event_loop(
|
|||||||
"Workflow '{}' is already running",
|
"Workflow '{}' is already running",
|
||||||
workflow.name
|
workflow.name
|
||||||
));
|
));
|
||||||
logging::warning(&format!(
|
wrkflw_logging::warning(&format!(
|
||||||
"Workflow '{}' is already running",
|
"Workflow '{}' is already running",
|
||||||
workflow.name
|
workflow.name
|
||||||
));
|
));
|
||||||
@@ -408,7 +408,7 @@ fn run_tui_event_loop(
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
logging::warning(&format!(
|
wrkflw_logging::warning(&format!(
|
||||||
"Cannot trigger workflow in {} state",
|
"Cannot trigger workflow in {} state",
|
||||||
status_text
|
status_text
|
||||||
));
|
));
|
||||||
@@ -416,20 +416,22 @@ fn run_tui_event_loop(
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
app.logs.push("No workflow selected to trigger".to_string());
|
app.logs.push("No workflow selected to trigger".to_string());
|
||||||
logging::warning("No workflow selected to trigger");
|
wrkflw_logging::warning("No workflow selected to trigger");
|
||||||
}
|
}
|
||||||
} else if app.running {
|
} else if app.running {
|
||||||
app.logs.push(
|
app.logs.push(
|
||||||
"Cannot trigger workflow while another operation is in progress"
|
"Cannot trigger workflow while another operation is in progress"
|
||||||
.to_string(),
|
.to_string(),
|
||||||
);
|
);
|
||||||
logging::warning(
|
wrkflw_logging::warning(
|
||||||
"Cannot trigger workflow while another operation is in progress",
|
"Cannot trigger workflow while another operation is in progress",
|
||||||
);
|
);
|
||||||
} else if app.selected_tab != 0 {
|
} else if app.selected_tab != 0 {
|
||||||
app.logs
|
app.logs
|
||||||
.push("Switch to Workflows tab to trigger a workflow".to_string());
|
.push("Switch to Workflows tab to trigger a workflow".to_string());
|
||||||
logging::warning("Switch to Workflows tab to trigger a workflow");
|
wrkflw_logging::warning(
|
||||||
|
"Switch to Workflows tab to trigger a workflow",
|
||||||
|
);
|
||||||
// For better UX, we could also automatically switch to the Workflows tab here
|
// For better UX, we could also automatically switch to the Workflows tab here
|
||||||
app.switch_tab(0);
|
app.switch_tab(0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ use crate::models::{
|
|||||||
};
|
};
|
||||||
use chrono::Local;
|
use chrono::Local;
|
||||||
use crossterm::event::KeyCode;
|
use crossterm::event::KeyCode;
|
||||||
use executor::{JobStatus, RuntimeType, StepStatus};
|
|
||||||
use ratatui::widgets::{ListState, TableState};
|
use ratatui::widgets::{ListState, TableState};
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
use wrkflw_executor::{JobStatus, RuntimeType, StepStatus};
|
||||||
|
|
||||||
/// Application state
|
/// Application state
|
||||||
pub struct App {
|
pub struct App {
|
||||||
@@ -69,8 +69,10 @@ impl App {
|
|||||||
// Use a very short timeout to prevent blocking the UI
|
// Use a very short timeout to prevent blocking the UI
|
||||||
let result = std::thread::scope(|s| {
|
let result = std::thread::scope(|s| {
|
||||||
let handle = s.spawn(|| {
|
let handle = s.spawn(|| {
|
||||||
utils::fd::with_stderr_to_null(executor::docker::is_available)
|
wrkflw_utils::fd::with_stderr_to_null(
|
||||||
.unwrap_or(false)
|
wrkflw_executor::docker::is_available,
|
||||||
|
)
|
||||||
|
.unwrap_or(false)
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set a short timeout for the thread
|
// Set a short timeout for the thread
|
||||||
@@ -85,7 +87,7 @@ impl App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If we reach here, the check took too long
|
// If we reach here, the check took too long
|
||||||
logging::warning(
|
wrkflw_logging::warning(
|
||||||
"Docker availability check timed out, falling back to emulation mode",
|
"Docker availability check timed out, falling back to emulation mode",
|
||||||
);
|
);
|
||||||
false
|
false
|
||||||
@@ -94,7 +96,7 @@ impl App {
|
|||||||
}) {
|
}) {
|
||||||
Ok(result) => result,
|
Ok(result) => result,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
logging::warning("Docker availability check failed with panic, falling back to emulation mode");
|
wrkflw_logging::warning("Docker availability check failed with panic, falling back to emulation mode");
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -104,12 +106,12 @@ impl App {
|
|||||||
"Docker is not available or unresponsive. Using emulation mode instead."
|
"Docker is not available or unresponsive. Using emulation mode instead."
|
||||||
.to_string(),
|
.to_string(),
|
||||||
);
|
);
|
||||||
logging::warning(
|
wrkflw_logging::warning(
|
||||||
"Docker is not available or unresponsive. Using emulation mode instead.",
|
"Docker is not available or unresponsive. Using emulation mode instead.",
|
||||||
);
|
);
|
||||||
RuntimeType::Emulation
|
RuntimeType::Emulation
|
||||||
} else {
|
} else {
|
||||||
logging::info("Docker is available, using Docker runtime");
|
wrkflw_logging::info("Docker is available, using Docker runtime");
|
||||||
RuntimeType::Docker
|
RuntimeType::Docker
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -119,8 +121,10 @@ impl App {
|
|||||||
// Use a very short timeout to prevent blocking the UI
|
// Use a very short timeout to prevent blocking the UI
|
||||||
let result = std::thread::scope(|s| {
|
let result = std::thread::scope(|s| {
|
||||||
let handle = s.spawn(|| {
|
let handle = s.spawn(|| {
|
||||||
utils::fd::with_stderr_to_null(executor::podman::is_available)
|
wrkflw_utils::fd::with_stderr_to_null(
|
||||||
.unwrap_or(false)
|
wrkflw_executor::podman::is_available,
|
||||||
|
)
|
||||||
|
.unwrap_or(false)
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set a short timeout for the thread
|
// Set a short timeout for the thread
|
||||||
@@ -135,7 +139,7 @@ impl App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If we reach here, the check took too long
|
// If we reach here, the check took too long
|
||||||
logging::warning(
|
wrkflw_logging::warning(
|
||||||
"Podman availability check timed out, falling back to emulation mode",
|
"Podman availability check timed out, falling back to emulation mode",
|
||||||
);
|
);
|
||||||
false
|
false
|
||||||
@@ -144,7 +148,7 @@ impl App {
|
|||||||
}) {
|
}) {
|
||||||
Ok(result) => result,
|
Ok(result) => result,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
logging::warning("Podman availability check failed with panic, falling back to emulation mode");
|
wrkflw_logging::warning("Podman availability check failed with panic, falling back to emulation mode");
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -154,12 +158,12 @@ impl App {
|
|||||||
"Podman is not available or unresponsive. Using emulation mode instead."
|
"Podman is not available or unresponsive. Using emulation mode instead."
|
||||||
.to_string(),
|
.to_string(),
|
||||||
);
|
);
|
||||||
logging::warning(
|
wrkflw_logging::warning(
|
||||||
"Podman is not available or unresponsive. Using emulation mode instead.",
|
"Podman is not available or unresponsive. Using emulation mode instead.",
|
||||||
);
|
);
|
||||||
RuntimeType::Emulation
|
RuntimeType::Emulation
|
||||||
} else {
|
} else {
|
||||||
logging::info("Podman is available, using Podman runtime");
|
wrkflw_logging::info("Podman is available, using Podman runtime");
|
||||||
RuntimeType::Podman
|
RuntimeType::Podman
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -227,7 +231,7 @@ impl App {
|
|||||||
let timestamp = Local::now().format("%H:%M:%S").to_string();
|
let timestamp = Local::now().format("%H:%M:%S").to_string();
|
||||||
self.logs
|
self.logs
|
||||||
.push(format!("[{}] Switched to {} mode", timestamp, mode));
|
.push(format!("[{}] Switched to {} mode", timestamp, mode));
|
||||||
logging::info(&format!("Switched to {} mode", mode));
|
wrkflw_logging::info(&format!("Switched to {} mode", mode));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn runtime_type_name(&self) -> &str {
|
pub fn runtime_type_name(&self) -> &str {
|
||||||
@@ -445,7 +449,7 @@ impl App {
|
|||||||
let timestamp = Local::now().format("%H:%M:%S").to_string();
|
let timestamp = Local::now().format("%H:%M:%S").to_string();
|
||||||
self.logs
|
self.logs
|
||||||
.push(format!("[{}] Starting workflow execution...", timestamp));
|
.push(format!("[{}] Starting workflow execution...", timestamp));
|
||||||
logging::info("Starting workflow execution...");
|
wrkflw_logging::info("Starting workflow execution...");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -453,7 +457,7 @@ impl App {
|
|||||||
pub fn process_execution_result(
|
pub fn process_execution_result(
|
||||||
&mut self,
|
&mut self,
|
||||||
workflow_idx: usize,
|
workflow_idx: usize,
|
||||||
result: Result<(Vec<executor::JobResult>, ()), String>,
|
result: Result<(Vec<wrkflw_executor::JobResult>, ()), String>,
|
||||||
) {
|
) {
|
||||||
if workflow_idx >= self.workflows.len() {
|
if workflow_idx >= self.workflows.len() {
|
||||||
let timestamp = Local::now().format("%H:%M:%S").to_string();
|
let timestamp = Local::now().format("%H:%M:%S").to_string();
|
||||||
@@ -461,7 +465,7 @@ impl App {
|
|||||||
"[{}] Error: Invalid workflow index received",
|
"[{}] Error: Invalid workflow index received",
|
||||||
timestamp
|
timestamp
|
||||||
));
|
));
|
||||||
logging::error("Invalid workflow index received in process_execution_result");
|
wrkflw_logging::error("Invalid workflow index received in process_execution_result");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -490,15 +494,15 @@ impl App {
|
|||||||
.push(format!("[{}] Operation completed successfully.", timestamp));
|
.push(format!("[{}] Operation completed successfully.", timestamp));
|
||||||
execution_details.progress = 1.0;
|
execution_details.progress = 1.0;
|
||||||
|
|
||||||
// Convert executor::JobResult to our JobExecution struct
|
// Convert wrkflw_executor::JobResult to our JobExecution struct
|
||||||
execution_details.jobs = jobs
|
execution_details.jobs = jobs
|
||||||
.iter()
|
.iter()
|
||||||
.map(|job_result| JobExecution {
|
.map(|job_result| JobExecution {
|
||||||
name: job_result.name.clone(),
|
name: job_result.name.clone(),
|
||||||
status: match job_result.status {
|
status: match job_result.status {
|
||||||
executor::JobStatus::Success => JobStatus::Success,
|
wrkflw_executor::JobStatus::Success => JobStatus::Success,
|
||||||
executor::JobStatus::Failure => JobStatus::Failure,
|
wrkflw_executor::JobStatus::Failure => JobStatus::Failure,
|
||||||
executor::JobStatus::Skipped => JobStatus::Skipped,
|
wrkflw_executor::JobStatus::Skipped => JobStatus::Skipped,
|
||||||
},
|
},
|
||||||
steps: job_result
|
steps: job_result
|
||||||
.steps
|
.steps
|
||||||
@@ -506,9 +510,9 @@ impl App {
|
|||||||
.map(|step_result| StepExecution {
|
.map(|step_result| StepExecution {
|
||||||
name: step_result.name.clone(),
|
name: step_result.name.clone(),
|
||||||
status: match step_result.status {
|
status: match step_result.status {
|
||||||
executor::StepStatus::Success => StepStatus::Success,
|
wrkflw_executor::StepStatus::Success => StepStatus::Success,
|
||||||
executor::StepStatus::Failure => StepStatus::Failure,
|
wrkflw_executor::StepStatus::Failure => StepStatus::Failure,
|
||||||
executor::StepStatus::Skipped => StepStatus::Skipped,
|
wrkflw_executor::StepStatus::Skipped => StepStatus::Skipped,
|
||||||
},
|
},
|
||||||
output: step_result.output.clone(),
|
output: step_result.output.clone(),
|
||||||
})
|
})
|
||||||
@@ -547,7 +551,7 @@ impl App {
|
|||||||
"[{}] Workflow '{}' completed successfully!",
|
"[{}] Workflow '{}' completed successfully!",
|
||||||
timestamp, workflow.name
|
timestamp, workflow.name
|
||||||
));
|
));
|
||||||
logging::info(&format!(
|
wrkflw_logging::info(&format!(
|
||||||
"[{}] Workflow '{}' completed successfully!",
|
"[{}] Workflow '{}' completed successfully!",
|
||||||
timestamp, workflow.name
|
timestamp, workflow.name
|
||||||
));
|
));
|
||||||
@@ -559,7 +563,7 @@ impl App {
|
|||||||
"[{}] Workflow '{}' failed: {}",
|
"[{}] Workflow '{}' failed: {}",
|
||||||
timestamp, workflow.name, e
|
timestamp, workflow.name, e
|
||||||
));
|
));
|
||||||
logging::error(&format!(
|
wrkflw_logging::error(&format!(
|
||||||
"[{}] Workflow '{}' failed: {}",
|
"[{}] Workflow '{}' failed: {}",
|
||||||
timestamp, workflow.name, e
|
timestamp, workflow.name, e
|
||||||
));
|
));
|
||||||
@@ -585,7 +589,7 @@ impl App {
|
|||||||
self.current_execution = Some(next);
|
self.current_execution = Some(next);
|
||||||
self.logs
|
self.logs
|
||||||
.push(format!("Executing workflow: {}", self.workflows[next].name));
|
.push(format!("Executing workflow: {}", self.workflows[next].name));
|
||||||
logging::info(&format!(
|
wrkflw_logging::info(&format!(
|
||||||
"Executing workflow: {}",
|
"Executing workflow: {}",
|
||||||
self.workflows[next].name
|
self.workflows[next].name
|
||||||
));
|
));
|
||||||
@@ -688,7 +692,7 @@ impl App {
|
|||||||
for log in &self.logs {
|
for log in &self.logs {
|
||||||
all_logs.push(log.clone());
|
all_logs.push(log.clone());
|
||||||
}
|
}
|
||||||
for log in logging::get_logs() {
|
for log in wrkflw_logging::get_logs() {
|
||||||
all_logs.push(log.clone());
|
all_logs.push(log.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -780,7 +784,7 @@ impl App {
|
|||||||
// Scroll logs down
|
// Scroll logs down
|
||||||
pub fn scroll_logs_down(&mut self) {
|
pub fn scroll_logs_down(&mut self) {
|
||||||
// Get total log count including system logs
|
// Get total log count including system logs
|
||||||
let total_logs = self.logs.len() + logging::get_logs().len();
|
let total_logs = self.logs.len() + wrkflw_logging::get_logs().len();
|
||||||
if total_logs > 0 {
|
if total_logs > 0 {
|
||||||
self.log_scroll = (self.log_scroll + 1).min(total_logs - 1);
|
self.log_scroll = (self.log_scroll + 1).min(total_logs - 1);
|
||||||
}
|
}
|
||||||
@@ -834,7 +838,9 @@ impl App {
|
|||||||
let timestamp = Local::now().format("%H:%M:%S").to_string();
|
let timestamp = Local::now().format("%H:%M:%S").to_string();
|
||||||
self.logs
|
self.logs
|
||||||
.push(format!("[{}] Error: Invalid workflow selection", timestamp));
|
.push(format!("[{}] Error: Invalid workflow selection", timestamp));
|
||||||
logging::error("Invalid workflow selection in trigger_selected_workflow");
|
wrkflw_logging::error(
|
||||||
|
"Invalid workflow selection in trigger_selected_workflow",
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -844,7 +850,7 @@ impl App {
|
|||||||
"[{}] Triggering workflow: {}",
|
"[{}] Triggering workflow: {}",
|
||||||
timestamp, workflow.name
|
timestamp, workflow.name
|
||||||
));
|
));
|
||||||
logging::info(&format!("Triggering workflow: {}", workflow.name));
|
wrkflw_logging::info(&format!("Triggering workflow: {}", workflow.name));
|
||||||
|
|
||||||
// Clone necessary values for the async task
|
// Clone necessary values for the async task
|
||||||
let workflow_name = workflow.name.clone();
|
let workflow_name = workflow.name.clone();
|
||||||
@@ -877,19 +883,19 @@ impl App {
|
|||||||
|
|
||||||
// Send the result back to the main thread
|
// Send the result back to the main thread
|
||||||
if let Err(e) = tx_clone.send((selected_idx, result)) {
|
if let Err(e) = tx_clone.send((selected_idx, result)) {
|
||||||
logging::error(&format!("Error sending trigger result: {}", e));
|
wrkflw_logging::error(&format!("Error sending trigger result: {}", e));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
let timestamp = Local::now().format("%H:%M:%S").to_string();
|
let timestamp = Local::now().format("%H:%M:%S").to_string();
|
||||||
self.logs
|
self.logs
|
||||||
.push(format!("[{}] No workflow selected to trigger", timestamp));
|
.push(format!("[{}] No workflow selected to trigger", timestamp));
|
||||||
logging::warning("No workflow selected to trigger");
|
wrkflw_logging::warning("No workflow selected to trigger");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.logs
|
self.logs
|
||||||
.push("No workflow selected to trigger".to_string());
|
.push("No workflow selected to trigger".to_string());
|
||||||
logging::warning("No workflow selected to trigger");
|
wrkflw_logging::warning("No workflow selected to trigger");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -902,7 +908,7 @@ impl App {
|
|||||||
"[{}] Debug: No workflow selected for reset",
|
"[{}] Debug: No workflow selected for reset",
|
||||||
timestamp
|
timestamp
|
||||||
));
|
));
|
||||||
logging::warning("No workflow selected for reset");
|
wrkflw_logging::warning("No workflow selected for reset");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -939,7 +945,7 @@ impl App {
|
|||||||
"[{}] Reset workflow '{}' from {} state to NotStarted - status is now {:?}",
|
"[{}] Reset workflow '{}' from {} state to NotStarted - status is now {:?}",
|
||||||
timestamp, workflow.name, old_status, workflow.status
|
timestamp, workflow.name, old_status, workflow.status
|
||||||
));
|
));
|
||||||
logging::info(&format!(
|
wrkflw_logging::info(&format!(
|
||||||
"Reset workflow '{}' from {} state to NotStarted - status is now {:?}",
|
"Reset workflow '{}' from {} state to NotStarted - status is now {:?}",
|
||||||
workflow.name, old_status, workflow.status
|
workflow.name, old_status, workflow.status
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -2,12 +2,12 @@
|
|||||||
use crate::app::App;
|
use crate::app::App;
|
||||||
use crate::models::{ExecutionResultMsg, WorkflowExecution, WorkflowStatus};
|
use crate::models::{ExecutionResultMsg, WorkflowExecution, WorkflowStatus};
|
||||||
use chrono::Local;
|
use chrono::Local;
|
||||||
use evaluator::evaluate_workflow_file;
|
|
||||||
use executor::{self, JobStatus, RuntimeType, StepStatus};
|
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
use wrkflw_evaluator::evaluate_workflow_file;
|
||||||
|
use wrkflw_executor::{self, JobStatus, RuntimeType, StepStatus};
|
||||||
|
|
||||||
// Validate a workflow or directory containing workflows
|
// Validate a workflow or directory containing workflows
|
||||||
pub fn validate_workflow(path: &Path, verbose: bool) -> io::Result<()> {
|
pub fn validate_workflow(path: &Path, verbose: bool) -> io::Result<()> {
|
||||||
@@ -20,7 +20,7 @@ pub fn validate_workflow(path: &Path, verbose: bool) -> io::Result<()> {
|
|||||||
let entry = entry?;
|
let entry = entry?;
|
||||||
let entry_path = entry.path();
|
let entry_path = entry.path();
|
||||||
|
|
||||||
if entry_path.is_file() && utils::is_workflow_file(&entry_path) {
|
if entry_path.is_file() && wrkflw_utils::is_workflow_file(&entry_path) {
|
||||||
workflows.push(entry_path);
|
workflows.push(entry_path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -105,18 +105,18 @@ pub async fn execute_workflow_cli(
|
|||||||
// Check container runtime availability if container runtime is selected
|
// Check container runtime availability if container runtime is selected
|
||||||
let runtime_type = match runtime_type {
|
let runtime_type = match runtime_type {
|
||||||
RuntimeType::Docker => {
|
RuntimeType::Docker => {
|
||||||
if !executor::docker::is_available() {
|
if !wrkflw_executor::docker::is_available() {
|
||||||
println!("⚠️ Docker is not available. Using emulation mode instead.");
|
println!("⚠️ Docker is not available. Using emulation mode instead.");
|
||||||
logging::warning("Docker is not available. Using emulation mode instead.");
|
wrkflw_logging::warning("Docker is not available. Using emulation mode instead.");
|
||||||
RuntimeType::Emulation
|
RuntimeType::Emulation
|
||||||
} else {
|
} else {
|
||||||
RuntimeType::Docker
|
RuntimeType::Docker
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RuntimeType::Podman => {
|
RuntimeType::Podman => {
|
||||||
if !executor::podman::is_available() {
|
if !wrkflw_executor::podman::is_available() {
|
||||||
println!("⚠️ Podman is not available. Using emulation mode instead.");
|
println!("⚠️ Podman is not available. Using emulation mode instead.");
|
||||||
logging::warning("Podman is not available. Using emulation mode instead.");
|
wrkflw_logging::warning("Podman is not available. Using emulation mode instead.");
|
||||||
RuntimeType::Emulation
|
RuntimeType::Emulation
|
||||||
} else {
|
} else {
|
||||||
RuntimeType::Podman
|
RuntimeType::Podman
|
||||||
@@ -129,20 +129,20 @@ pub async fn execute_workflow_cli(
|
|||||||
println!("Runtime mode: {:?}", runtime_type);
|
println!("Runtime mode: {:?}", runtime_type);
|
||||||
|
|
||||||
// Log the start of the execution in debug mode with more details
|
// Log the start of the execution in debug mode with more details
|
||||||
logging::debug(&format!(
|
wrkflw_logging::debug(&format!(
|
||||||
"Starting workflow execution: path={}, runtime={:?}, verbose={}",
|
"Starting workflow execution: path={}, runtime={:?}, verbose={}",
|
||||||
path.display(),
|
path.display(),
|
||||||
runtime_type,
|
runtime_type,
|
||||||
verbose
|
verbose
|
||||||
));
|
));
|
||||||
|
|
||||||
let config = executor::ExecutionConfig {
|
let config = wrkflw_executor::ExecutionConfig {
|
||||||
runtime_type,
|
runtime_type,
|
||||||
verbose,
|
verbose,
|
||||||
preserve_containers_on_failure: false, // Default for this path
|
preserve_containers_on_failure: false, // Default for this path
|
||||||
};
|
};
|
||||||
|
|
||||||
match executor::execute_workflow(path, config).await {
|
match wrkflw_executor::execute_workflow(path, config).await {
|
||||||
Ok(result) => {
|
Ok(result) => {
|
||||||
println!("\nWorkflow execution results:");
|
println!("\nWorkflow execution results:");
|
||||||
|
|
||||||
@@ -166,7 +166,7 @@ pub async fn execute_workflow_cli(
|
|||||||
println!("-------------------------");
|
println!("-------------------------");
|
||||||
|
|
||||||
// Log the job details for debug purposes
|
// Log the job details for debug purposes
|
||||||
logging::debug(&format!("Job: {}, Status: {:?}", job.name, job.status));
|
wrkflw_logging::debug(&format!("Job: {}, Status: {:?}", job.name, job.status));
|
||||||
|
|
||||||
for step in job.steps.iter() {
|
for step in job.steps.iter() {
|
||||||
match step.status {
|
match step.status {
|
||||||
@@ -202,7 +202,7 @@ pub async fn execute_workflow_cli(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Show command/run details in debug mode
|
// Show command/run details in debug mode
|
||||||
if logging::get_log_level() <= logging::LogLevel::Debug {
|
if wrkflw_logging::get_log_level() <= wrkflw_logging::LogLevel::Debug {
|
||||||
if let Some(cmd_output) = step
|
if let Some(cmd_output) = step
|
||||||
.output
|
.output
|
||||||
.lines()
|
.lines()
|
||||||
@@ -242,7 +242,7 @@ pub async fn execute_workflow_cli(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Always log the step details for debug purposes
|
// Always log the step details for debug purposes
|
||||||
logging::debug(&format!(
|
wrkflw_logging::debug(&format!(
|
||||||
"Step: {}, Status: {:?}, Output length: {} lines",
|
"Step: {}, Status: {:?}, Output length: {} lines",
|
||||||
step.name,
|
step.name,
|
||||||
step.status,
|
step.status,
|
||||||
@@ -250,10 +250,10 @@ pub async fn execute_workflow_cli(
|
|||||||
));
|
));
|
||||||
|
|
||||||
// In debug mode, log all step output
|
// In debug mode, log all step output
|
||||||
if logging::get_log_level() == logging::LogLevel::Debug
|
if wrkflw_logging::get_log_level() == wrkflw_logging::LogLevel::Debug
|
||||||
&& !step.output.trim().is_empty()
|
&& !step.output.trim().is_empty()
|
||||||
{
|
{
|
||||||
logging::debug(&format!(
|
wrkflw_logging::debug(&format!(
|
||||||
"Step output for '{}': \n{}",
|
"Step output for '{}': \n{}",
|
||||||
step.name, step.output
|
step.name, step.output
|
||||||
));
|
));
|
||||||
@@ -265,7 +265,7 @@ pub async fn execute_workflow_cli(
|
|||||||
println!("\n❌ Workflow completed with failures");
|
println!("\n❌ Workflow completed with failures");
|
||||||
// In the case of failure, we'll also inform the user about the debug option
|
// In the case of failure, we'll also inform the user about the debug option
|
||||||
// if they're not already using it
|
// if they're not already using it
|
||||||
if logging::get_log_level() > logging::LogLevel::Debug {
|
if wrkflw_logging::get_log_level() > wrkflw_logging::LogLevel::Debug {
|
||||||
println!(" Run with --debug for more detailed output");
|
println!(" Run with --debug for more detailed output");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -276,7 +276,7 @@ pub async fn execute_workflow_cli(
|
|||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("❌ Failed to execute workflow: {}", e);
|
println!("❌ Failed to execute workflow: {}", e);
|
||||||
logging::error(&format!("Failed to execute workflow: {}", e));
|
wrkflw_logging::error(&format!("Failed to execute workflow: {}", e));
|
||||||
Err(io::Error::other(e))
|
Err(io::Error::other(e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -286,7 +286,7 @@ pub async fn execute_workflow_cli(
|
|||||||
pub async fn execute_curl_trigger(
|
pub async fn execute_curl_trigger(
|
||||||
workflow_name: &str,
|
workflow_name: &str,
|
||||||
branch: Option<&str>,
|
branch: Option<&str>,
|
||||||
) -> Result<(Vec<executor::JobResult>, ()), String> {
|
) -> Result<(Vec<wrkflw_executor::JobResult>, ()), String> {
|
||||||
// Get GitHub token
|
// Get GitHub token
|
||||||
let token = std::env::var("GITHUB_TOKEN").map_err(|_| {
|
let token = std::env::var("GITHUB_TOKEN").map_err(|_| {
|
||||||
"GitHub token not found. Please set GITHUB_TOKEN environment variable".to_string()
|
"GitHub token not found. Please set GITHUB_TOKEN environment variable".to_string()
|
||||||
@@ -294,13 +294,13 @@ pub async fn execute_curl_trigger(
|
|||||||
|
|
||||||
// Debug log to check if GITHUB_TOKEN is set
|
// Debug log to check if GITHUB_TOKEN is set
|
||||||
match std::env::var("GITHUB_TOKEN") {
|
match std::env::var("GITHUB_TOKEN") {
|
||||||
Ok(token) => logging::info(&format!("GITHUB_TOKEN is set: {}", &token[..5])), // Log first 5 characters for security
|
Ok(token) => wrkflw_logging::info(&format!("GITHUB_TOKEN is set: {}", &token[..5])), // Log first 5 characters for security
|
||||||
Err(_) => logging::error("GITHUB_TOKEN is not set"),
|
Err(_) => wrkflw_logging::error("GITHUB_TOKEN is not set"),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get repository information
|
// Get repository information
|
||||||
let repo_info =
|
let repo_info = wrkflw_github::get_repo_info()
|
||||||
github::get_repo_info().map_err(|e| format!("Failed to get repository info: {}", e))?;
|
.map_err(|e| format!("Failed to get repository info: {}", e))?;
|
||||||
|
|
||||||
// Determine branch to use
|
// Determine branch to use
|
||||||
let branch_ref = branch.unwrap_or(&repo_info.default_branch);
|
let branch_ref = branch.unwrap_or(&repo_info.default_branch);
|
||||||
@@ -315,7 +315,7 @@ pub async fn execute_curl_trigger(
|
|||||||
workflow_name
|
workflow_name
|
||||||
};
|
};
|
||||||
|
|
||||||
logging::info(&format!("Using workflow name: {}", workflow_name));
|
wrkflw_logging::info(&format!("Using workflow name: {}", workflow_name));
|
||||||
|
|
||||||
// Construct JSON payload
|
// Construct JSON payload
|
||||||
let payload = serde_json::json!({
|
let payload = serde_json::json!({
|
||||||
@@ -328,7 +328,7 @@ pub async fn execute_curl_trigger(
|
|||||||
repo_info.owner, repo_info.repo, workflow_name
|
repo_info.owner, repo_info.repo, workflow_name
|
||||||
);
|
);
|
||||||
|
|
||||||
logging::info(&format!("Triggering workflow at URL: {}", url));
|
wrkflw_logging::info(&format!("Triggering workflow at URL: {}", url));
|
||||||
|
|
||||||
// Create a reqwest client
|
// Create a reqwest client
|
||||||
let client = reqwest::Client::new();
|
let client = reqwest::Client::new();
|
||||||
@@ -362,12 +362,12 @@ pub async fn execute_curl_trigger(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Create a job result structure
|
// Create a job result structure
|
||||||
let job_result = executor::JobResult {
|
let job_result = wrkflw_executor::JobResult {
|
||||||
name: "GitHub Trigger".to_string(),
|
name: "GitHub Trigger".to_string(),
|
||||||
status: executor::JobStatus::Success,
|
status: wrkflw_executor::JobStatus::Success,
|
||||||
steps: vec![executor::StepResult {
|
steps: vec![wrkflw_executor::StepResult {
|
||||||
name: "Remote Trigger".to_string(),
|
name: "Remote Trigger".to_string(),
|
||||||
status: executor::StepStatus::Success,
|
status: wrkflw_executor::StepStatus::Success,
|
||||||
output: success_msg,
|
output: success_msg,
|
||||||
}],
|
}],
|
||||||
logs: "Workflow triggered remotely on GitHub".to_string(),
|
logs: "Workflow triggered remotely on GitHub".to_string(),
|
||||||
@@ -391,13 +391,13 @@ pub fn start_next_workflow_execution(
|
|||||||
if verbose {
|
if verbose {
|
||||||
app.logs
|
app.logs
|
||||||
.push("Verbose mode: Step outputs will be displayed in full".to_string());
|
.push("Verbose mode: Step outputs will be displayed in full".to_string());
|
||||||
logging::info("Verbose mode: Step outputs will be displayed in full");
|
wrkflw_logging::info("Verbose mode: Step outputs will be displayed in full");
|
||||||
} else {
|
} else {
|
||||||
app.logs.push(
|
app.logs.push(
|
||||||
"Standard mode: Only step status will be shown (use --verbose for full output)"
|
"Standard mode: Only step status will be shown (use --verbose for full output)"
|
||||||
.to_string(),
|
.to_string(),
|
||||||
);
|
);
|
||||||
logging::info(
|
wrkflw_logging::info(
|
||||||
"Standard mode: Only step status will be shown (use --verbose for full output)",
|
"Standard mode: Only step status will be shown (use --verbose for full output)",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -406,21 +406,24 @@ pub fn start_next_workflow_execution(
|
|||||||
let runtime_type = match app.runtime_type {
|
let runtime_type = match app.runtime_type {
|
||||||
RuntimeType::Docker => {
|
RuntimeType::Docker => {
|
||||||
// Use safe FD redirection to check Docker availability
|
// Use safe FD redirection to check Docker availability
|
||||||
let is_docker_available =
|
let is_docker_available = match wrkflw_utils::fd::with_stderr_to_null(
|
||||||
match utils::fd::with_stderr_to_null(executor::docker::is_available) {
|
wrkflw_executor::docker::is_available,
|
||||||
Ok(result) => result,
|
) {
|
||||||
Err(_) => {
|
Ok(result) => result,
|
||||||
logging::debug(
|
Err(_) => {
|
||||||
"Failed to redirect stderr when checking Docker availability.",
|
wrkflw_logging::debug(
|
||||||
);
|
"Failed to redirect stderr when checking Docker availability.",
|
||||||
false
|
);
|
||||||
}
|
false
|
||||||
};
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if !is_docker_available {
|
if !is_docker_available {
|
||||||
app.logs
|
app.logs
|
||||||
.push("Docker is not available. Using emulation mode instead.".to_string());
|
.push("Docker is not available. Using emulation mode instead.".to_string());
|
||||||
logging::warning("Docker is not available. Using emulation mode instead.");
|
wrkflw_logging::warning(
|
||||||
|
"Docker is not available. Using emulation mode instead.",
|
||||||
|
);
|
||||||
RuntimeType::Emulation
|
RuntimeType::Emulation
|
||||||
} else {
|
} else {
|
||||||
RuntimeType::Docker
|
RuntimeType::Docker
|
||||||
@@ -428,21 +431,24 @@ pub fn start_next_workflow_execution(
|
|||||||
}
|
}
|
||||||
RuntimeType::Podman => {
|
RuntimeType::Podman => {
|
||||||
// Use safe FD redirection to check Podman availability
|
// Use safe FD redirection to check Podman availability
|
||||||
let is_podman_available =
|
let is_podman_available = match wrkflw_utils::fd::with_stderr_to_null(
|
||||||
match utils::fd::with_stderr_to_null(executor::podman::is_available) {
|
wrkflw_executor::podman::is_available,
|
||||||
Ok(result) => result,
|
) {
|
||||||
Err(_) => {
|
Ok(result) => result,
|
||||||
logging::debug(
|
Err(_) => {
|
||||||
"Failed to redirect stderr when checking Podman availability.",
|
wrkflw_logging::debug(
|
||||||
);
|
"Failed to redirect stderr when checking Podman availability.",
|
||||||
false
|
);
|
||||||
}
|
false
|
||||||
};
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if !is_podman_available {
|
if !is_podman_available {
|
||||||
app.logs
|
app.logs
|
||||||
.push("Podman is not available. Using emulation mode instead.".to_string());
|
.push("Podman is not available. Using emulation mode instead.".to_string());
|
||||||
logging::warning("Podman is not available. Using emulation mode instead.");
|
wrkflw_logging::warning(
|
||||||
|
"Podman is not available. Using emulation mode instead.",
|
||||||
|
);
|
||||||
RuntimeType::Emulation
|
RuntimeType::Emulation
|
||||||
} else {
|
} else {
|
||||||
RuntimeType::Podman
|
RuntimeType::Podman
|
||||||
@@ -487,21 +493,21 @@ pub fn start_next_workflow_execution(
|
|||||||
Ok(validation_result) => {
|
Ok(validation_result) => {
|
||||||
// Create execution result based on validation
|
// Create execution result based on validation
|
||||||
let status = if validation_result.is_valid {
|
let status = if validation_result.is_valid {
|
||||||
executor::JobStatus::Success
|
wrkflw_executor::JobStatus::Success
|
||||||
} else {
|
} else {
|
||||||
executor::JobStatus::Failure
|
wrkflw_executor::JobStatus::Failure
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create a synthetic job result for validation
|
// Create a synthetic job result for validation
|
||||||
let jobs = vec![executor::JobResult {
|
let jobs = vec![wrkflw_executor::JobResult {
|
||||||
name: "Validation".to_string(),
|
name: "Validation".to_string(),
|
||||||
status,
|
status,
|
||||||
steps: vec![executor::StepResult {
|
steps: vec![wrkflw_executor::StepResult {
|
||||||
name: "Validator".to_string(),
|
name: "Validator".to_string(),
|
||||||
status: if validation_result.is_valid {
|
status: if validation_result.is_valid {
|
||||||
executor::StepStatus::Success
|
wrkflw_executor::StepStatus::Success
|
||||||
} else {
|
} else {
|
||||||
executor::StepStatus::Failure
|
wrkflw_executor::StepStatus::Failure
|
||||||
},
|
},
|
||||||
output: validation_result.issues.join("\n"),
|
output: validation_result.issues.join("\n"),
|
||||||
}],
|
}],
|
||||||
@@ -521,15 +527,15 @@ pub fn start_next_workflow_execution(
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Use safe FD redirection for execution
|
// Use safe FD redirection for execution
|
||||||
let config = executor::ExecutionConfig {
|
let config = wrkflw_executor::ExecutionConfig {
|
||||||
runtime_type,
|
runtime_type,
|
||||||
verbose,
|
verbose,
|
||||||
preserve_containers_on_failure,
|
preserve_containers_on_failure,
|
||||||
};
|
};
|
||||||
|
|
||||||
let execution_result = utils::fd::with_stderr_to_null(|| {
|
let execution_result = wrkflw_utils::fd::with_stderr_to_null(|| {
|
||||||
futures::executor::block_on(async {
|
futures::executor::block_on(async {
|
||||||
executor::execute_workflow(&workflow_path, config).await
|
wrkflw_executor::execute_workflow(&workflow_path, config).await
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.map_err(|e| format!("Failed to redirect stderr during execution: {}", e))?;
|
.map_err(|e| format!("Failed to redirect stderr during execution: {}", e))?;
|
||||||
@@ -546,7 +552,7 @@ pub fn start_next_workflow_execution(
|
|||||||
|
|
||||||
// Only send if we get a valid result
|
// Only send if we get a valid result
|
||||||
if let Err(e) = tx_clone_inner.send((next_idx, result)) {
|
if let Err(e) = tx_clone_inner.send((next_idx, result)) {
|
||||||
logging::error(&format!("Error sending execution result: {}", e));
|
wrkflw_logging::error(&format!("Error sending execution result: {}", e));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -554,6 +560,6 @@ pub fn start_next_workflow_execution(
|
|||||||
let timestamp = Local::now().format("%H:%M:%S").to_string();
|
let timestamp = Local::now().format("%H:%M:%S").to_string();
|
||||||
app.logs
|
app.logs
|
||||||
.push(format!("[{}] All workflows completed execution", timestamp));
|
.push(format!("[{}] All workflows completed execution", timestamp));
|
||||||
logging::info("All workflows completed execution");
|
wrkflw_logging::info("All workflows completed execution");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
// UI Models for wrkflw
|
// UI Models for wrkflw
|
||||||
use chrono::Local;
|
use chrono::Local;
|
||||||
use executor::{JobStatus, StepStatus};
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use wrkflw_executor::{JobStatus, StepStatus};
|
||||||
|
|
||||||
/// Type alias for the complex execution result type
|
/// Type alias for the complex execution result type
|
||||||
pub type ExecutionResultMsg = (usize, Result<(Vec<executor::JobResult>, ()), String>);
|
pub type ExecutionResultMsg = (usize, Result<(Vec<wrkflw_executor::JobResult>, ()), String>);
|
||||||
|
|
||||||
/// Represents an individual workflow file
|
/// Represents an individual workflow file
|
||||||
pub struct Workflow {
|
pub struct Workflow {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// UI utilities
|
// UI utilities
|
||||||
use crate::models::{Workflow, WorkflowStatus};
|
use crate::models::{Workflow, WorkflowStatus};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use utils::is_workflow_file;
|
use wrkflw_utils::is_workflow_file;
|
||||||
|
|
||||||
/// Find and load all workflow files in a directory
|
/// Find and load all workflow files in a directory
|
||||||
pub fn load_workflows(dir_path: &Path) -> Vec<Workflow> {
|
pub fn load_workflows(dir_path: &Path) -> Vec<Workflow> {
|
||||||
|
|||||||
@@ -145,15 +145,17 @@ pub fn render_execution_tab(
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|job| {
|
.map(|job| {
|
||||||
let status_symbol = match job.status {
|
let status_symbol = match job.status {
|
||||||
executor::JobStatus::Success => "✅",
|
wrkflw_executor::JobStatus::Success => "✅",
|
||||||
executor::JobStatus::Failure => "❌",
|
wrkflw_executor::JobStatus::Failure => "❌",
|
||||||
executor::JobStatus::Skipped => "⏭",
|
wrkflw_executor::JobStatus::Skipped => "⏭",
|
||||||
};
|
};
|
||||||
|
|
||||||
let status_style = match job.status {
|
let status_style = match job.status {
|
||||||
executor::JobStatus::Success => Style::default().fg(Color::Green),
|
wrkflw_executor::JobStatus::Success => {
|
||||||
executor::JobStatus::Failure => Style::default().fg(Color::Red),
|
Style::default().fg(Color::Green)
|
||||||
executor::JobStatus::Skipped => Style::default().fg(Color::Gray),
|
}
|
||||||
|
wrkflw_executor::JobStatus::Failure => Style::default().fg(Color::Red),
|
||||||
|
wrkflw_executor::JobStatus::Skipped => Style::default().fg(Color::Gray),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Count completed and total steps
|
// Count completed and total steps
|
||||||
@@ -162,8 +164,8 @@ pub fn render_execution_tab(
|
|||||||
.steps
|
.steps
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|s| {
|
.filter(|s| {
|
||||||
s.status == executor::StepStatus::Success
|
s.status == wrkflw_executor::StepStatus::Success
|
||||||
|| s.status == executor::StepStatus::Failure
|
|| s.status == wrkflw_executor::StepStatus::Failure
|
||||||
})
|
})
|
||||||
.count();
|
.count();
|
||||||
|
|
||||||
|
|||||||
@@ -46,15 +46,15 @@ pub fn render_job_detail_view(
|
|||||||
|
|
||||||
// Job title section
|
// Job title section
|
||||||
let status_text = match job.status {
|
let status_text = match job.status {
|
||||||
executor::JobStatus::Success => "Success",
|
wrkflw_executor::JobStatus::Success => "Success",
|
||||||
executor::JobStatus::Failure => "Failed",
|
wrkflw_executor::JobStatus::Failure => "Failed",
|
||||||
executor::JobStatus::Skipped => "Skipped",
|
wrkflw_executor::JobStatus::Skipped => "Skipped",
|
||||||
};
|
};
|
||||||
|
|
||||||
let status_style = match job.status {
|
let status_style = match job.status {
|
||||||
executor::JobStatus::Success => Style::default().fg(Color::Green),
|
wrkflw_executor::JobStatus::Success => Style::default().fg(Color::Green),
|
||||||
executor::JobStatus::Failure => Style::default().fg(Color::Red),
|
wrkflw_executor::JobStatus::Failure => Style::default().fg(Color::Red),
|
||||||
executor::JobStatus::Skipped => Style::default().fg(Color::Yellow),
|
wrkflw_executor::JobStatus::Skipped => Style::default().fg(Color::Yellow),
|
||||||
};
|
};
|
||||||
|
|
||||||
let job_title = Paragraph::new(vec![
|
let job_title = Paragraph::new(vec![
|
||||||
@@ -101,15 +101,19 @@ pub fn render_job_detail_view(
|
|||||||
|
|
||||||
let rows = job.steps.iter().map(|step| {
|
let rows = job.steps.iter().map(|step| {
|
||||||
let status_symbol = match step.status {
|
let status_symbol = match step.status {
|
||||||
executor::StepStatus::Success => "✅",
|
wrkflw_executor::StepStatus::Success => "✅",
|
||||||
executor::StepStatus::Failure => "❌",
|
wrkflw_executor::StepStatus::Failure => "❌",
|
||||||
executor::StepStatus::Skipped => "⏭",
|
wrkflw_executor::StepStatus::Skipped => "⏭",
|
||||||
};
|
};
|
||||||
|
|
||||||
let status_style = match step.status {
|
let status_style = match step.status {
|
||||||
executor::StepStatus::Success => Style::default().fg(Color::Green),
|
wrkflw_executor::StepStatus::Success => {
|
||||||
executor::StepStatus::Failure => Style::default().fg(Color::Red),
|
Style::default().fg(Color::Green)
|
||||||
executor::StepStatus::Skipped => Style::default().fg(Color::Gray),
|
}
|
||||||
|
wrkflw_executor::StepStatus::Failure => Style::default().fg(Color::Red),
|
||||||
|
wrkflw_executor::StepStatus::Skipped => {
|
||||||
|
Style::default().fg(Color::Gray)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Row::new(vec![
|
Row::new(vec![
|
||||||
@@ -147,15 +151,21 @@ pub fn render_job_detail_view(
|
|||||||
|
|
||||||
// Show step output with proper styling
|
// Show step output with proper styling
|
||||||
let status_text = match step.status {
|
let status_text = match step.status {
|
||||||
executor::StepStatus::Success => "Success",
|
wrkflw_executor::StepStatus::Success => "Success",
|
||||||
executor::StepStatus::Failure => "Failed",
|
wrkflw_executor::StepStatus::Failure => "Failed",
|
||||||
executor::StepStatus::Skipped => "Skipped",
|
wrkflw_executor::StepStatus::Skipped => "Skipped",
|
||||||
};
|
};
|
||||||
|
|
||||||
let status_style = match step.status {
|
let status_style = match step.status {
|
||||||
executor::StepStatus::Success => Style::default().fg(Color::Green),
|
wrkflw_executor::StepStatus::Success => {
|
||||||
executor::StepStatus::Failure => Style::default().fg(Color::Red),
|
Style::default().fg(Color::Green)
|
||||||
executor::StepStatus::Skipped => Style::default().fg(Color::Yellow),
|
}
|
||||||
|
wrkflw_executor::StepStatus::Failure => {
|
||||||
|
Style::default().fg(Color::Red)
|
||||||
|
}
|
||||||
|
wrkflw_executor::StepStatus::Skipped => {
|
||||||
|
Style::default().fg(Color::Yellow)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut output_text = step.output.clone();
|
let mut output_text = step.output.clone();
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ pub fn render_logs_tab(f: &mut Frame<CrosstermBackend<io::Stdout>>, app: &App, a
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Process system logs
|
// Process system logs
|
||||||
for log in logging::get_logs() {
|
for log in wrkflw_logging::get_logs() {
|
||||||
all_logs.push(log.clone());
|
all_logs.push(log.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
// Status bar rendering
|
// Status bar rendering
|
||||||
use crate::app::App;
|
use crate::app::App;
|
||||||
use executor::RuntimeType;
|
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
backend::CrosstermBackend,
|
backend::CrosstermBackend,
|
||||||
layout::{Alignment, Rect},
|
layout::{Alignment, Rect},
|
||||||
@@ -10,6 +9,7 @@ use ratatui::{
|
|||||||
Frame,
|
Frame,
|
||||||
};
|
};
|
||||||
use std::io;
|
use std::io;
|
||||||
|
use wrkflw_executor::RuntimeType;
|
||||||
|
|
||||||
// Render the status bar
|
// Render the status bar
|
||||||
pub fn render_status_bar(f: &mut Frame<CrosstermBackend<io::Stdout>>, app: &App, area: Rect) {
|
pub fn render_status_bar(f: &mut Frame<CrosstermBackend<io::Stdout>>, app: &App, area: Rect) {
|
||||||
@@ -50,16 +50,17 @@ pub fn render_status_bar(f: &mut Frame<CrosstermBackend<io::Stdout>>, app: &App,
|
|||||||
match app.runtime_type {
|
match app.runtime_type {
|
||||||
RuntimeType::Docker => {
|
RuntimeType::Docker => {
|
||||||
// Check Docker silently using safe FD redirection
|
// Check Docker silently using safe FD redirection
|
||||||
let is_docker_available =
|
let is_docker_available = match wrkflw_utils::fd::with_stderr_to_null(
|
||||||
match utils::fd::with_stderr_to_null(executor::docker::is_available) {
|
wrkflw_executor::docker::is_available,
|
||||||
Ok(result) => result,
|
) {
|
||||||
Err(_) => {
|
Ok(result) => result,
|
||||||
logging::debug(
|
Err(_) => {
|
||||||
"Failed to redirect stderr when checking Docker availability.",
|
wrkflw_logging::debug(
|
||||||
);
|
"Failed to redirect stderr when checking Docker availability.",
|
||||||
false
|
);
|
||||||
}
|
false
|
||||||
};
|
}
|
||||||
|
};
|
||||||
|
|
||||||
status_items.push(Span::raw(" "));
|
status_items.push(Span::raw(" "));
|
||||||
status_items.push(Span::styled(
|
status_items.push(Span::styled(
|
||||||
@@ -79,16 +80,17 @@ pub fn render_status_bar(f: &mut Frame<CrosstermBackend<io::Stdout>>, app: &App,
|
|||||||
}
|
}
|
||||||
RuntimeType::Podman => {
|
RuntimeType::Podman => {
|
||||||
// Check Podman silently using safe FD redirection
|
// Check Podman silently using safe FD redirection
|
||||||
let is_podman_available =
|
let is_podman_available = match wrkflw_utils::fd::with_stderr_to_null(
|
||||||
match utils::fd::with_stderr_to_null(executor::podman::is_available) {
|
wrkflw_executor::podman::is_available,
|
||||||
Ok(result) => result,
|
) {
|
||||||
Err(_) => {
|
Ok(result) => result,
|
||||||
logging::debug(
|
Err(_) => {
|
||||||
"Failed to redirect stderr when checking Podman availability.",
|
wrkflw_logging::debug(
|
||||||
);
|
"Failed to redirect stderr when checking Podman availability.",
|
||||||
false
|
);
|
||||||
}
|
false
|
||||||
};
|
}
|
||||||
|
};
|
||||||
|
|
||||||
status_items.push(Span::raw(" "));
|
status_items.push(Span::raw(" "));
|
||||||
status_items.push(Span::styled(
|
status_items.push(Span::styled(
|
||||||
@@ -159,7 +161,7 @@ pub fn render_status_bar(f: &mut Frame<CrosstermBackend<io::Stdout>>, app: &App,
|
|||||||
}
|
}
|
||||||
2 => {
|
2 => {
|
||||||
// For logs tab, show scrolling instructions
|
// For logs tab, show scrolling instructions
|
||||||
let log_count = app.logs.len() + logging::get_logs().len();
|
let log_count = app.logs.len() + wrkflw_logging::get_logs().len();
|
||||||
if log_count > 0 {
|
if log_count > 0 {
|
||||||
// Convert to a static string for consistent return type
|
// Convert to a static string for consistent return type
|
||||||
let scroll_text = format!(
|
let scroll_text = format!(
|
||||||
|
|||||||
@@ -1,13 +1,18 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "utils"
|
name = "wrkflw-utils"
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
description = "utility functions for wrkflw"
|
description = "Utility functions for wrkflw workflow execution engine"
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
documentation.workspace = true
|
||||||
|
homepage.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
keywords.workspace = true
|
||||||
|
categories.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Internal crates
|
# Internal crates
|
||||||
models = { path = "../models" }
|
wrkflw-models = { path = "../models", version = "0.5.0" }
|
||||||
|
|
||||||
# External dependencies
|
# External dependencies
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
|
|||||||
@@ -1,14 +1,19 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "validators"
|
name = "wrkflw-validators"
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
description = "validation functionality for wrkflw"
|
description = "Workflow validation functionality for wrkflw execution engine"
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
documentation.workspace = true
|
||||||
|
homepage.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
keywords.workspace = true
|
||||||
|
categories.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Internal crates
|
# Internal crates
|
||||||
models = { path = "../models" }
|
wrkflw-models = { path = "../models", version = "0.5.0" }
|
||||||
matrix = { path = "../matrix" }
|
wrkflw-matrix = { path = "../matrix", version = "0.5.0" }
|
||||||
|
|
||||||
# External dependencies
|
# External dependencies
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use models::ValidationResult;
|
use wrkflw_models::ValidationResult;
|
||||||
|
|
||||||
pub fn validate_action_reference(
|
pub fn validate_action_reference(
|
||||||
action_ref: &str,
|
action_ref: &str,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use models::gitlab::{Job, Pipeline};
|
|
||||||
use models::ValidationResult;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use wrkflw_models::gitlab::{Job, Pipeline};
|
||||||
|
use wrkflw_models::ValidationResult;
|
||||||
|
|
||||||
/// Validate a GitLab CI/CD pipeline
|
/// Validate a GitLab CI/CD pipeline
|
||||||
pub fn validate_gitlab_pipeline(pipeline: &Pipeline) -> ValidationResult {
|
pub fn validate_gitlab_pipeline(pipeline: &Pipeline) -> ValidationResult {
|
||||||
@@ -65,7 +65,7 @@ fn validate_jobs(jobs: &HashMap<String, Job>, result: &mut ValidationResult) {
|
|||||||
// Check retry configuration
|
// Check retry configuration
|
||||||
if let Some(retry) = &job.retry {
|
if let Some(retry) = &job.retry {
|
||||||
match retry {
|
match retry {
|
||||||
models::gitlab::Retry::MaxAttempts(attempts) => {
|
wrkflw_models::gitlab::Retry::MaxAttempts(attempts) => {
|
||||||
if *attempts > 10 {
|
if *attempts > 10 {
|
||||||
result.add_issue(format!(
|
result.add_issue(format!(
|
||||||
"Job '{}' has excessive retry count: {}. Consider reducing to avoid resource waste",
|
"Job '{}' has excessive retry count: {}. Consider reducing to avoid resource waste",
|
||||||
@@ -73,7 +73,7 @@ fn validate_jobs(jobs: &HashMap<String, Job>, result: &mut ValidationResult) {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
models::gitlab::Retry::Detailed { max, when: _ } => {
|
wrkflw_models::gitlab::Retry::Detailed { max, when: _ } => {
|
||||||
if *max > 10 {
|
if *max > 10 {
|
||||||
result.add_issue(format!(
|
result.add_issue(format!(
|
||||||
"Job '{}' has excessive retry count: {}. Consider reducing to avoid resource waste",
|
"Job '{}' has excessive retry count: {}. Consider reducing to avoid resource waste",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::{validate_matrix, validate_steps};
|
use crate::{validate_matrix, validate_steps};
|
||||||
use models::ValidationResult;
|
|
||||||
use serde_yaml::Value;
|
use serde_yaml::Value;
|
||||||
|
use wrkflw_models::ValidationResult;
|
||||||
|
|
||||||
pub fn validate_jobs(jobs: &Value, result: &mut ValidationResult) {
|
pub fn validate_jobs(jobs: &Value, result: &mut ValidationResult) {
|
||||||
if let Value::Mapping(jobs_map) = jobs {
|
if let Value::Mapping(jobs_map) = jobs {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use models::ValidationResult;
|
|
||||||
use serde_yaml::Value;
|
use serde_yaml::Value;
|
||||||
|
use wrkflw_models::ValidationResult;
|
||||||
|
|
||||||
pub fn validate_matrix(matrix: &Value, result: &mut ValidationResult) {
|
pub fn validate_matrix(matrix: &Value, result: &mut ValidationResult) {
|
||||||
// Check if matrix is a mapping
|
// Check if matrix is a mapping
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use crate::validate_action_reference;
|
use crate::validate_action_reference;
|
||||||
use models::ValidationResult;
|
|
||||||
use serde_yaml::Value;
|
use serde_yaml::Value;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
use wrkflw_models::ValidationResult;
|
||||||
|
|
||||||
pub fn validate_steps(steps: &[Value], job_name: &str, result: &mut ValidationResult) {
|
pub fn validate_steps(steps: &[Value], job_name: &str, result: &mut ValidationResult) {
|
||||||
let mut step_ids: HashSet<String> = HashSet::new();
|
let mut step_ids: HashSet<String> = HashSet::new();
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use models::ValidationResult;
|
|
||||||
use serde_yaml::Value;
|
use serde_yaml::Value;
|
||||||
|
use wrkflw_models::ValidationResult;
|
||||||
|
|
||||||
pub fn validate_triggers(on: &Value, result: &mut ValidationResult) {
|
pub fn validate_triggers(on: &Value, result: &mut ValidationResult) {
|
||||||
let valid_events = vec![
|
let valid_events = vec![
|
||||||
|
|||||||
@@ -12,18 +12,18 @@ license.workspace = true
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Workspace crates
|
# Workspace crates
|
||||||
models = { path = "../models" }
|
wrkflw-models = { path = "../models", version = "0.5.0" }
|
||||||
executor = { path = "../executor" }
|
wrkflw-executor = { path = "../executor", version = "0.5.0" }
|
||||||
github = { path = "../github" }
|
wrkflw-github = { path = "../github", version = "0.5.0" }
|
||||||
gitlab = { path = "../gitlab" }
|
wrkflw-gitlab = { path = "../gitlab", version = "0.5.0" }
|
||||||
logging = { path = "../logging" }
|
wrkflw-logging = { path = "../logging", version = "0.5.0" }
|
||||||
matrix = { path = "../matrix" }
|
wrkflw-matrix = { path = "../matrix", version = "0.5.0" }
|
||||||
parser = { path = "../parser" }
|
wrkflw-parser = { path = "../parser", version = "0.5.0" }
|
||||||
runtime = { path = "../runtime" }
|
wrkflw-runtime = { path = "../runtime", version = "0.5.0" }
|
||||||
ui = { path = "../ui" }
|
wrkflw-ui = { path = "../ui", version = "0.5.0" }
|
||||||
utils = { path = "../utils" }
|
wrkflw-utils = { path = "../utils", version = "0.5.0" }
|
||||||
validators = { path = "../validators" }
|
wrkflw-validators = { path = "../validators", version = "0.5.0" }
|
||||||
evaluator = { path = "../evaluator" }
|
wrkflw-evaluator = { path = "../evaluator", version = "0.5.0" }
|
||||||
|
|
||||||
# External dependencies
|
# External dependencies
|
||||||
clap.workspace = true
|
clap.workspace = true
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
pub use evaluator;
|
pub use wrkflw_evaluator as evaluator;
|
||||||
pub use executor;
|
pub use wrkflw_executor as executor;
|
||||||
pub use github;
|
pub use wrkflw_github as github;
|
||||||
pub use gitlab;
|
pub use wrkflw_gitlab as gitlab;
|
||||||
pub use logging;
|
pub use wrkflw_logging as logging;
|
||||||
pub use matrix;
|
pub use wrkflw_matrix as matrix;
|
||||||
pub use models;
|
pub use wrkflw_models as models;
|
||||||
pub use parser;
|
pub use wrkflw_parser as parser;
|
||||||
pub use runtime;
|
pub use wrkflw_runtime as runtime;
|
||||||
pub use ui;
|
pub use wrkflw_ui as ui;
|
||||||
pub use utils;
|
pub use wrkflw_utils as utils;
|
||||||
pub use validators;
|
pub use wrkflw_validators as validators;
|
||||||
|
|||||||
@@ -14,12 +14,12 @@ enum RuntimeChoice {
|
|||||||
Emulation,
|
Emulation,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<RuntimeChoice> for executor::RuntimeType {
|
impl From<RuntimeChoice> for wrkflw_executor::RuntimeType {
|
||||||
fn from(choice: RuntimeChoice) -> Self {
|
fn from(choice: RuntimeChoice) -> Self {
|
||||||
match choice {
|
match choice {
|
||||||
RuntimeChoice::Docker => executor::RuntimeType::Docker,
|
RuntimeChoice::Docker => wrkflw_executor::RuntimeType::Docker,
|
||||||
RuntimeChoice::Podman => executor::RuntimeType::Podman,
|
RuntimeChoice::Podman => wrkflw_executor::RuntimeType::Podman,
|
||||||
RuntimeChoice::Emulation => executor::RuntimeType::Emulation,
|
RuntimeChoice::Emulation => wrkflw_executor::RuntimeType::Emulation,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -143,7 +143,7 @@ fn parse_key_val(s: &str) -> Result<(String, String), String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Make this function public for testing? Or move to a utils/cleanup mod?
|
// Make this function public for testing? Or move to a utils/cleanup mod?
|
||||||
// Or call executor::cleanup and runtime::cleanup directly?
|
// Or call wrkflw_executor::cleanup and wrkflw_runtime::cleanup directly?
|
||||||
// Let's try calling them directly for now.
|
// Let's try calling them directly for now.
|
||||||
async fn cleanup_on_exit() {
|
async fn cleanup_on_exit() {
|
||||||
// Clean up Docker resources if available, but don't let it block indefinitely
|
// Clean up Docker resources if available, but don't let it block indefinitely
|
||||||
@@ -151,35 +151,35 @@ async fn cleanup_on_exit() {
|
|||||||
match Docker::connect_with_local_defaults() {
|
match Docker::connect_with_local_defaults() {
|
||||||
Ok(docker) => {
|
Ok(docker) => {
|
||||||
// Assuming cleanup_resources exists in executor crate
|
// Assuming cleanup_resources exists in executor crate
|
||||||
executor::cleanup_resources(&docker).await;
|
wrkflw_executor::cleanup_resources(&docker).await;
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
// Docker not available
|
// Docker not available
|
||||||
logging::info("Docker not available, skipping Docker cleanup");
|
wrkflw_logging::info("Docker not available, skipping Docker cleanup");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(_) => logging::debug("Docker cleanup completed successfully"),
|
Ok(_) => wrkflw_logging::debug("Docker cleanup completed successfully"),
|
||||||
Err(_) => {
|
Err(_) => wrkflw_logging::warning(
|
||||||
logging::warning("Docker cleanup timed out after 3 seconds, continuing with shutdown")
|
"Docker cleanup timed out after 3 seconds, continuing with shutdown",
|
||||||
}
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always clean up emulation resources
|
// Always clean up emulation resources
|
||||||
match tokio::time::timeout(
|
match tokio::time::timeout(
|
||||||
std::time::Duration::from_secs(2),
|
std::time::Duration::from_secs(2),
|
||||||
// Assuming cleanup_resources exists in runtime::emulation module
|
// Assuming cleanup_resources exists in wrkflw_runtime::emulation module
|
||||||
runtime::emulation::cleanup_resources(),
|
wrkflw_runtime::emulation::cleanup_resources(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(_) => logging::debug("Emulation cleanup completed successfully"),
|
Ok(_) => wrkflw_logging::debug("Emulation cleanup completed successfully"),
|
||||||
Err(_) => logging::warning("Emulation cleanup timed out, continuing with shutdown"),
|
Err(_) => wrkflw_logging::warning("Emulation cleanup timed out, continuing with shutdown"),
|
||||||
}
|
}
|
||||||
|
|
||||||
logging::info("Resource cleanup completed");
|
wrkflw_logging::info("Resource cleanup completed");
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_signals() {
|
async fn handle_signals() {
|
||||||
@@ -207,7 +207,7 @@ async fn handle_signals() {
|
|||||||
"Cleanup taking too long (over {} seconds), forcing exit...",
|
"Cleanup taking too long (over {} seconds), forcing exit...",
|
||||||
hard_exit_time.as_secs()
|
hard_exit_time.as_secs()
|
||||||
);
|
);
|
||||||
logging::error("Forced exit due to cleanup timeout");
|
wrkflw_logging::error("Forced exit due to cleanup timeout");
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -272,13 +272,13 @@ async fn main() {
|
|||||||
|
|
||||||
// Set log level based on command line flags
|
// Set log level based on command line flags
|
||||||
if debug {
|
if debug {
|
||||||
logging::set_log_level(logging::LogLevel::Debug);
|
wrkflw_logging::set_log_level(wrkflw_logging::LogLevel::Debug);
|
||||||
logging::debug("Debug mode enabled - showing detailed logs");
|
wrkflw_logging::debug("Debug mode enabled - showing detailed logs");
|
||||||
} else if verbose {
|
} else if verbose {
|
||||||
logging::set_log_level(logging::LogLevel::Info);
|
wrkflw_logging::set_log_level(wrkflw_logging::LogLevel::Info);
|
||||||
logging::info("Verbose mode enabled");
|
wrkflw_logging::info("Verbose mode enabled");
|
||||||
} else {
|
} else {
|
||||||
logging::set_log_level(logging::LogLevel::Warning);
|
wrkflw_logging::set_log_level(wrkflw_logging::LogLevel::Warning);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup a Ctrl+C handler that runs in the background
|
// Setup a Ctrl+C handler that runs in the background
|
||||||
@@ -360,7 +360,7 @@ async fn main() {
|
|||||||
gitlab,
|
gitlab,
|
||||||
}) => {
|
}) => {
|
||||||
// Create execution configuration
|
// Create execution configuration
|
||||||
let config = executor::ExecutionConfig {
|
let config = wrkflw_executor::ExecutionConfig {
|
||||||
runtime_type: runtime.clone().into(),
|
runtime_type: runtime.clone().into(),
|
||||||
verbose,
|
verbose,
|
||||||
preserve_containers_on_failure: *preserve_containers_on_failure,
|
preserve_containers_on_failure: *preserve_containers_on_failure,
|
||||||
@@ -374,10 +374,10 @@ async fn main() {
|
|||||||
"GitHub workflow"
|
"GitHub workflow"
|
||||||
};
|
};
|
||||||
|
|
||||||
logging::info(&format!("Running {} at: {}", workflow_type, path.display()));
|
wrkflw_logging::info(&format!("Running {} at: {}", workflow_type, path.display()));
|
||||||
|
|
||||||
// Execute the workflow
|
// Execute the workflow
|
||||||
let result = executor::execute_workflow(path, config)
|
let result = wrkflw_executor::execute_workflow(path, config)
|
||||||
.await
|
.await
|
||||||
.unwrap_or_else(|e| {
|
.unwrap_or_else(|e| {
|
||||||
eprintln!("Error executing workflow: {}", e);
|
eprintln!("Error executing workflow: {}", e);
|
||||||
@@ -419,15 +419,15 @@ async fn main() {
|
|||||||
println!(
|
println!(
|
||||||
" {} {} ({})",
|
" {} {} ({})",
|
||||||
match job.status {
|
match job.status {
|
||||||
executor::JobStatus::Success => "✅",
|
wrkflw_executor::JobStatus::Success => "✅",
|
||||||
executor::JobStatus::Failure => "❌",
|
wrkflw_executor::JobStatus::Failure => "❌",
|
||||||
executor::JobStatus::Skipped => "⏭️",
|
wrkflw_executor::JobStatus::Skipped => "⏭️",
|
||||||
},
|
},
|
||||||
job.name,
|
job.name,
|
||||||
match job.status {
|
match job.status {
|
||||||
executor::JobStatus::Success => "success",
|
wrkflw_executor::JobStatus::Success => "success",
|
||||||
executor::JobStatus::Failure => "failure",
|
wrkflw_executor::JobStatus::Failure => "failure",
|
||||||
executor::JobStatus::Skipped => "skipped",
|
wrkflw_executor::JobStatus::Skipped => "skipped",
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -435,15 +435,15 @@ async fn main() {
|
|||||||
println!(" Steps:");
|
println!(" Steps:");
|
||||||
for step in job.steps {
|
for step in job.steps {
|
||||||
let step_status = match step.status {
|
let step_status = match step.status {
|
||||||
executor::StepStatus::Success => "✅",
|
wrkflw_executor::StepStatus::Success => "✅",
|
||||||
executor::StepStatus::Failure => "❌",
|
wrkflw_executor::StepStatus::Failure => "❌",
|
||||||
executor::StepStatus::Skipped => "⏭️",
|
wrkflw_executor::StepStatus::Skipped => "⏭️",
|
||||||
};
|
};
|
||||||
|
|
||||||
println!(" {} {}", step_status, step.name);
|
println!(" {} {}", step_status, step.name);
|
||||||
|
|
||||||
// If step failed and we're not in verbose mode, show condensed error info
|
// If step failed and we're not in verbose mode, show condensed error info
|
||||||
if step.status == executor::StepStatus::Failure && !verbose {
|
if step.status == wrkflw_executor::StepStatus::Failure && !verbose {
|
||||||
// Extract error information from step output
|
// Extract error information from step output
|
||||||
let error_lines = step
|
let error_lines = step
|
||||||
.output
|
.output
|
||||||
@@ -482,7 +482,7 @@ async fn main() {
|
|||||||
.map(|v| v.iter().cloned().collect::<HashMap<String, String>>());
|
.map(|v| v.iter().cloned().collect::<HashMap<String, String>>());
|
||||||
|
|
||||||
// Trigger the pipeline
|
// Trigger the pipeline
|
||||||
if let Err(e) = gitlab::trigger_pipeline(branch.as_deref(), variables).await {
|
if let Err(e) = wrkflw_gitlab::trigger_pipeline(branch.as_deref(), variables).await {
|
||||||
eprintln!("Error triggering GitLab pipeline: {}", e);
|
eprintln!("Error triggering GitLab pipeline: {}", e);
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
@@ -497,7 +497,7 @@ async fn main() {
|
|||||||
let runtime_type = runtime.clone().into();
|
let runtime_type = runtime.clone().into();
|
||||||
|
|
||||||
// Call the TUI implementation from the ui crate
|
// Call the TUI implementation from the ui crate
|
||||||
if let Err(e) = ui::run_wrkflw_tui(
|
if let Err(e) = wrkflw_ui::run_wrkflw_tui(
|
||||||
path.as_ref(),
|
path.as_ref(),
|
||||||
runtime_type,
|
runtime_type,
|
||||||
verbose,
|
verbose,
|
||||||
@@ -520,7 +520,9 @@ async fn main() {
|
|||||||
.map(|i| i.iter().cloned().collect::<HashMap<String, String>>());
|
.map(|i| i.iter().cloned().collect::<HashMap<String, String>>());
|
||||||
|
|
||||||
// Trigger the workflow
|
// Trigger the workflow
|
||||||
if let Err(e) = github::trigger_workflow(workflow, branch.as_deref(), inputs).await {
|
if let Err(e) =
|
||||||
|
wrkflw_github::trigger_workflow(workflow, branch.as_deref(), inputs).await
|
||||||
|
{
|
||||||
eprintln!("Error triggering GitHub workflow: {}", e);
|
eprintln!("Error triggering GitHub workflow: {}", e);
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
@@ -530,10 +532,10 @@ async fn main() {
|
|||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
// Launch TUI by default when no command is provided
|
// Launch TUI by default when no command is provided
|
||||||
let runtime_type = executor::RuntimeType::Docker;
|
let runtime_type = wrkflw_executor::RuntimeType::Docker;
|
||||||
|
|
||||||
// Call the TUI implementation from the ui crate with default path
|
// Call the TUI implementation from the ui crate with default path
|
||||||
if let Err(e) = ui::run_wrkflw_tui(None, runtime_type, verbose, false).await {
|
if let Err(e) = wrkflw_ui::run_wrkflw_tui(None, runtime_type, verbose, false).await {
|
||||||
eprintln!("Error running TUI: {}", e);
|
eprintln!("Error running TUI: {}", e);
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
@@ -547,13 +549,13 @@ fn validate_github_workflow(path: &Path, verbose: bool) -> bool {
|
|||||||
print!("Validating GitHub workflow file: {}... ", path.display());
|
print!("Validating GitHub workflow file: {}... ", path.display());
|
||||||
|
|
||||||
// Use the ui crate's validate_workflow function
|
// Use the ui crate's validate_workflow function
|
||||||
match ui::validate_workflow(path, verbose) {
|
match wrkflw_ui::validate_workflow(path, verbose) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
// The detailed validation output is already printed by the function
|
// The detailed validation output is already printed by the function
|
||||||
// We need to check if there were validation issues
|
// We need to check if there were validation issues
|
||||||
// Since ui::validate_workflow doesn't return the validation result directly,
|
// Since wrkflw_ui::validate_workflow doesn't return the validation result directly,
|
||||||
// we need to call the evaluator directly to get the result
|
// we need to call the evaluator directly to get the result
|
||||||
match evaluator::evaluate_workflow_file(path, verbose) {
|
match wrkflw_evaluator::evaluate_workflow_file(path, verbose) {
|
||||||
Ok(result) => !result.is_valid,
|
Ok(result) => !result.is_valid,
|
||||||
Err(_) => true, // Parse errors count as validation failure
|
Err(_) => true, // Parse errors count as validation failure
|
||||||
}
|
}
|
||||||
@@ -571,12 +573,12 @@ fn validate_gitlab_pipeline(path: &Path, verbose: bool) -> bool {
|
|||||||
print!("Validating GitLab CI pipeline file: {}... ", path.display());
|
print!("Validating GitLab CI pipeline file: {}... ", path.display());
|
||||||
|
|
||||||
// Parse and validate the pipeline file
|
// Parse and validate the pipeline file
|
||||||
match parser::gitlab::parse_pipeline(path) {
|
match wrkflw_parser::gitlab::parse_pipeline(path) {
|
||||||
Ok(pipeline) => {
|
Ok(pipeline) => {
|
||||||
println!("✅ Valid syntax");
|
println!("✅ Valid syntax");
|
||||||
|
|
||||||
// Additional structural validation
|
// Additional structural validation
|
||||||
let validation_result = validators::validate_gitlab_pipeline(&pipeline);
|
let validation_result = wrkflw_validators::validate_gitlab_pipeline(&pipeline);
|
||||||
|
|
||||||
if !validation_result.is_valid {
|
if !validation_result.is_valid {
|
||||||
println!("⚠️ Validation issues:");
|
println!("⚠️ Validation issues:");
|
||||||
|
|||||||
71
publish_crates.sh
Executable file
71
publish_crates.sh
Executable file
@@ -0,0 +1,71 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Simple script to publish all wrkflw crates to crates.io in dependency order
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
DRY_RUN=${1:-""}
|
||||||
|
|
||||||
|
if [[ "$DRY_RUN" == "--dry-run" ]]; then
|
||||||
|
echo "🧪 DRY RUN: Testing wrkflw crates publication"
|
||||||
|
else
|
||||||
|
echo "🚀 Publishing wrkflw crates to crates.io"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if we're logged in to crates.io
|
||||||
|
if [ ! -f ~/.cargo/credentials.toml ] && [ ! -f ~/.cargo/credentials ]; then
|
||||||
|
echo "❌ Not logged in to crates.io. Please run: cargo login <your-token>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Publication order (respecting dependencies)
|
||||||
|
CRATES=(
|
||||||
|
"models"
|
||||||
|
"logging"
|
||||||
|
"utils"
|
||||||
|
"matrix"
|
||||||
|
"validators"
|
||||||
|
"github"
|
||||||
|
"gitlab"
|
||||||
|
"parser"
|
||||||
|
"runtime"
|
||||||
|
"evaluator"
|
||||||
|
"executor"
|
||||||
|
"ui"
|
||||||
|
"wrkflw"
|
||||||
|
)
|
||||||
|
|
||||||
|
echo "📦 Publishing crates in dependency order..."
|
||||||
|
|
||||||
|
for crate in "${CRATES[@]}"; do
|
||||||
|
if [[ "$DRY_RUN" == "--dry-run" ]]; then
|
||||||
|
echo "Testing $crate..."
|
||||||
|
cd "crates/$crate"
|
||||||
|
cargo publish --dry-run --allow-dirty
|
||||||
|
echo "✅ $crate dry-run successful"
|
||||||
|
else
|
||||||
|
echo "Publishing $crate..."
|
||||||
|
cd "crates/$crate"
|
||||||
|
cargo publish --allow-dirty
|
||||||
|
echo "✅ Published $crate"
|
||||||
|
fi
|
||||||
|
cd - > /dev/null
|
||||||
|
|
||||||
|
# Small delay to avoid rate limiting (except for the last crate and in dry-run)
|
||||||
|
if [[ "$crate" != "wrkflw" ]] && [[ "$DRY_RUN" != "--dry-run" ]]; then
|
||||||
|
echo " Waiting 10 seconds to avoid rate limits..."
|
||||||
|
sleep 10
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ "$DRY_RUN" == "--dry-run" ]]; then
|
||||||
|
echo "🎉 All crates passed dry-run tests!"
|
||||||
|
echo ""
|
||||||
|
echo "To actually publish, run:"
|
||||||
|
echo " ./publish_crates.sh"
|
||||||
|
else
|
||||||
|
echo "🎉 All crates published successfully!"
|
||||||
|
echo ""
|
||||||
|
echo "Users can now install wrkflw with:"
|
||||||
|
echo " cargo install wrkflw"
|
||||||
|
fi
|
||||||
Reference in New Issue
Block a user