mirror of
https://github.com/bahdotsh/wrkflw.git
synced 2025-12-31 17:36:15 +01:00
Compare commits
7 Commits
wrkflw-par
...
wrkflw-uti
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7ac18f3715 | ||
|
|
1f3fee7373 | ||
|
|
f49ccd70d9 | ||
|
|
5161882989 | ||
|
|
5e9658c885 | ||
|
|
aa9da33b30 | ||
|
|
dff3697052 |
16
.github/workflows/build.yml
vendored
16
.github/workflows/build.yml
vendored
@@ -3,7 +3,7 @@ name: Build
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [ main ]
|
||||
branches: [main]
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
@@ -12,12 +12,14 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
target: x86_64-unknown-linux-gnu
|
||||
- os: macos-latest
|
||||
target: x86_64-apple-darwin
|
||||
- os: windows-latest
|
||||
target: x86_64-pc-windows-msvc
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
@@ -31,27 +33,27 @@ jobs:
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
components: clippy, rustfmt
|
||||
|
||||
|
||||
- name: Check formatting
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: -- --check
|
||||
|
||||
|
||||
- name: Run clippy
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: clippy
|
||||
args: -- -D warnings
|
||||
|
||||
|
||||
- name: Build
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --target ${{ matrix.target }}
|
||||
|
||||
|
||||
- name: Run tests
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --target ${{ matrix.target }}
|
||||
args: --target ${{ matrix.target }}
|
||||
|
||||
26
Cargo.lock
generated
26
Cargo.lock
generated
@@ -3080,7 +3080,7 @@ checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb"
|
||||
|
||||
[[package]]
|
||||
name = "wrkflw"
|
||||
version = "0.7.1"
|
||||
version = "0.7.2"
|
||||
dependencies = [
|
||||
"bollard",
|
||||
"chrono",
|
||||
@@ -3127,7 +3127,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wrkflw-evaluator"
|
||||
version = "0.7.1"
|
||||
version = "0.7.2"
|
||||
dependencies = [
|
||||
"colored",
|
||||
"serde_yaml",
|
||||
@@ -3137,7 +3137,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wrkflw-executor"
|
||||
version = "0.7.1"
|
||||
version = "0.7.2"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bollard",
|
||||
@@ -3169,7 +3169,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wrkflw-github"
|
||||
version = "0.7.1"
|
||||
version = "0.7.2"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"regex",
|
||||
@@ -3183,7 +3183,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wrkflw-gitlab"
|
||||
version = "0.7.1"
|
||||
version = "0.7.2"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"regex",
|
||||
@@ -3198,7 +3198,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wrkflw-logging"
|
||||
version = "0.7.1"
|
||||
version = "0.7.2"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"once_cell",
|
||||
@@ -3209,7 +3209,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wrkflw-matrix"
|
||||
version = "0.7.1"
|
||||
version = "0.7.2"
|
||||
dependencies = [
|
||||
"indexmap 2.10.0",
|
||||
"serde",
|
||||
@@ -3220,7 +3220,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wrkflw-models"
|
||||
version = "0.7.1"
|
||||
version = "0.7.2"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -3230,7 +3230,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wrkflw-parser"
|
||||
version = "0.7.1"
|
||||
version = "0.7.2"
|
||||
dependencies = [
|
||||
"jsonschema",
|
||||
"serde",
|
||||
@@ -3244,7 +3244,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wrkflw-runtime"
|
||||
version = "0.7.1"
|
||||
version = "0.7.2"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"futures",
|
||||
@@ -3293,7 +3293,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wrkflw-ui"
|
||||
version = "0.7.1"
|
||||
version = "0.7.2"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"crossterm 0.26.1",
|
||||
@@ -3315,7 +3315,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wrkflw-utils"
|
||||
version = "0.7.1"
|
||||
version = "0.7.2"
|
||||
dependencies = [
|
||||
"nix",
|
||||
"serde",
|
||||
@@ -3325,7 +3325,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wrkflw-validators"
|
||||
version = "0.7.1"
|
||||
version = "0.7.2"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_yaml",
|
||||
|
||||
@@ -5,7 +5,7 @@ members = [
|
||||
resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
version = "0.7.1"
|
||||
version = "0.7.2"
|
||||
edition = "2021"
|
||||
description = "A GitHub Actions workflow validator and executor"
|
||||
documentation = "https://github.com/bahdotsh/wrkflw"
|
||||
|
||||
@@ -793,7 +793,7 @@ async fn cleanup_processes() {
|
||||
let _ = Command::new("taskkill")
|
||||
.arg("/F")
|
||||
.arg("/PID")
|
||||
.arg(&pid.to_string())
|
||||
.arg(pid.to_string())
|
||||
.output();
|
||||
}
|
||||
|
||||
|
||||
@@ -17,4 +17,6 @@ wrkflw-models = { path = "../models", version = "0.7.0" }
|
||||
# External dependencies
|
||||
serde.workspace = true
|
||||
serde_yaml.workspace = true
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
nix.workspace = true
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
Shared helpers used across crates.
|
||||
|
||||
- Workflow file detection (`.github/workflows/*.yml`, `.gitlab-ci.yml`)
|
||||
- File-descriptor redirection utilities for silencing noisy subprocess output
|
||||
- File-descriptor redirection utilities for silencing noisy subprocess output (Unix only; Windows support is limited)
|
||||
|
||||
### Example
|
||||
|
||||
@@ -14,7 +14,7 @@ use wrkflw_utils::{is_workflow_file, fd::with_stderr_to_null};
|
||||
assert!(is_workflow_file(Path::new(".github/workflows/ci.yml")));
|
||||
|
||||
let value = with_stderr_to_null(|| {
|
||||
eprintln!("this is hidden");
|
||||
eprintln!("this is hidden on Unix, visible on Windows");
|
||||
42
|
||||
}).unwrap();
|
||||
assert_eq!(value, 42);
|
||||
|
||||
@@ -35,78 +35,145 @@ pub fn is_workflow_file(path: &Path) -> bool {
|
||||
}
|
||||
|
||||
/// Module for safely handling file descriptor redirection
|
||||
///
|
||||
/// On Unix systems (Linux, macOS), this module provides true file descriptor
|
||||
/// redirection by duplicating stderr and redirecting it to /dev/null.
|
||||
///
|
||||
/// On Windows systems, the redirection functionality is limited due to platform
|
||||
/// differences in file descriptor handling. The functions will execute without
|
||||
/// error but stderr may not be fully suppressed.
|
||||
pub mod fd {
|
||||
use nix::fcntl::{open, OFlag};
|
||||
use nix::sys::stat::Mode;
|
||||
use nix::unistd::{close, dup, dup2};
|
||||
use std::io::{self, Result};
|
||||
use std::os::unix::io::RawFd;
|
||||
use std::path::Path;
|
||||
|
||||
/// Standard file descriptors
|
||||
const STDERR_FILENO: RawFd = 2;
|
||||
use std::io::Result;
|
||||
|
||||
/// Represents a redirected stderr that can be restored
|
||||
pub struct RedirectedStderr {
|
||||
original_fd: Option<RawFd>,
|
||||
null_fd: Option<RawFd>,
|
||||
#[cfg(unix)]
|
||||
original_fd: Option<std::os::unix::io::RawFd>,
|
||||
#[cfg(unix)]
|
||||
null_fd: Option<std::os::unix::io::RawFd>,
|
||||
#[cfg(windows)]
|
||||
_phantom: std::marker::PhantomData<()>,
|
||||
}
|
||||
|
||||
impl RedirectedStderr {
|
||||
/// Creates a new RedirectedStderr that redirects stderr to /dev/null
|
||||
pub fn to_null() -> Result<Self> {
|
||||
// Duplicate the current stderr fd
|
||||
let stderr_backup = match dup(STDERR_FILENO) {
|
||||
Ok(fd) => fd,
|
||||
Err(e) => return Err(io::Error::other(e)),
|
||||
};
|
||||
#[cfg(unix)]
|
||||
mod unix_impl {
|
||||
use super::*;
|
||||
use nix::fcntl::{open, OFlag};
|
||||
use nix::sys::stat::Mode;
|
||||
use nix::unistd::{close, dup, dup2};
|
||||
use std::io;
|
||||
use std::os::unix::io::RawFd;
|
||||
use std::path::Path;
|
||||
|
||||
// Open /dev/null
|
||||
let null_fd = match open(Path::new("/dev/null"), OFlag::O_WRONLY, Mode::empty()) {
|
||||
Ok(fd) => fd,
|
||||
Err(e) => {
|
||||
/// Standard file descriptors
|
||||
const STDERR_FILENO: RawFd = 2;
|
||||
|
||||
impl RedirectedStderr {
|
||||
/// Creates a new RedirectedStderr that redirects stderr to /dev/null
|
||||
pub fn to_null() -> Result<Self> {
|
||||
// Duplicate the current stderr fd
|
||||
let stderr_backup = match dup(STDERR_FILENO) {
|
||||
Ok(fd) => fd,
|
||||
Err(e) => return Err(io::Error::other(e)),
|
||||
};
|
||||
|
||||
// Open /dev/null
|
||||
let null_fd = match open(Path::new("/dev/null"), OFlag::O_WRONLY, Mode::empty()) {
|
||||
Ok(fd) => fd,
|
||||
Err(e) => {
|
||||
let _ = close(stderr_backup); // Clean up on error
|
||||
return Err(io::Error::other(e));
|
||||
}
|
||||
};
|
||||
|
||||
// Redirect stderr to /dev/null
|
||||
if let Err(e) = dup2(null_fd, STDERR_FILENO) {
|
||||
let _ = close(stderr_backup); // Clean up on error
|
||||
let _ = close(null_fd);
|
||||
return Err(io::Error::other(e));
|
||||
}
|
||||
};
|
||||
|
||||
// Redirect stderr to /dev/null
|
||||
if let Err(e) = dup2(null_fd, STDERR_FILENO) {
|
||||
let _ = close(stderr_backup); // Clean up on error
|
||||
let _ = close(null_fd);
|
||||
return Err(io::Error::other(e));
|
||||
Ok(RedirectedStderr {
|
||||
original_fd: Some(stderr_backup),
|
||||
null_fd: Some(null_fd),
|
||||
})
|
||||
}
|
||||
|
||||
Ok(RedirectedStderr {
|
||||
original_fd: Some(stderr_backup),
|
||||
null_fd: Some(null_fd),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for RedirectedStderr {
|
||||
/// Automatically restores stderr when the RedirectedStderr is dropped
|
||||
fn drop(&mut self) {
|
||||
if let Some(orig_fd) = self.original_fd.take() {
|
||||
// Restore the original stderr
|
||||
let _ = dup2(orig_fd, STDERR_FILENO);
|
||||
let _ = close(orig_fd);
|
||||
}
|
||||
impl Drop for RedirectedStderr {
|
||||
/// Automatically restores stderr when the RedirectedStderr is dropped
|
||||
fn drop(&mut self) {
|
||||
if let Some(orig_fd) = self.original_fd.take() {
|
||||
// Restore the original stderr
|
||||
let _ = dup2(orig_fd, STDERR_FILENO);
|
||||
let _ = close(orig_fd);
|
||||
}
|
||||
|
||||
// Close the null fd
|
||||
if let Some(null_fd) = self.null_fd.take() {
|
||||
let _ = close(null_fd);
|
||||
// Close the null fd
|
||||
if let Some(null_fd) = self.null_fd.take() {
|
||||
let _ = close(null_fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Run a function with stderr redirected to /dev/null, then restore stderr
|
||||
#[cfg(windows)]
|
||||
mod windows_impl {
|
||||
use super::*;
|
||||
|
||||
impl RedirectedStderr {
|
||||
/// Creates a new RedirectedStderr that redirects stderr to NUL on Windows
|
||||
pub fn to_null() -> Result<Self> {
|
||||
// On Windows, we can't easily redirect stderr at the file descriptor level
|
||||
// like we can on Unix systems. This is a simplified implementation that
|
||||
// doesn't actually redirect but provides the same interface.
|
||||
// The actual stderr suppression will need to be handled differently on Windows.
|
||||
Ok(RedirectedStderr {
|
||||
_phantom: std::marker::PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for RedirectedStderr {
|
||||
/// No-op drop implementation for Windows
|
||||
fn drop(&mut self) {
|
||||
// Nothing to restore on Windows in this simplified implementation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Run a function with stderr redirected to /dev/null (Unix) or suppressed (Windows), then restore stderr
|
||||
///
|
||||
/// # Platform Support
|
||||
/// - **Unix (Linux, macOS)**: Fully supported - stderr is redirected to /dev/null
|
||||
/// - **Windows**: Limited support - function executes but stderr may be visible
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use wrkflw_utils::fd::with_stderr_to_null;
|
||||
///
|
||||
/// let result = with_stderr_to_null(|| {
|
||||
/// eprintln!("This will be hidden on Unix");
|
||||
/// 42
|
||||
/// }).unwrap();
|
||||
/// assert_eq!(result, 42);
|
||||
/// ```
|
||||
pub fn with_stderr_to_null<F, T>(f: F) -> Result<T>
|
||||
where
|
||||
F: FnOnce() -> T,
|
||||
{
|
||||
let _redirected = RedirectedStderr::to_null()?;
|
||||
Ok(f())
|
||||
#[cfg(unix)]
|
||||
{
|
||||
let _redirected = RedirectedStderr::to_null()?;
|
||||
Ok(f())
|
||||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
// On Windows, we can't easily redirect stderr at the FD level,
|
||||
// so we just run the function without redirection.
|
||||
// This means stderr won't be suppressed on Windows, but the function will work.
|
||||
Ok(f())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,15 +183,16 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_fd_redirection() {
|
||||
// This test will write to stderr, which should be redirected
|
||||
// This test will write to stderr, which should be redirected on Unix
|
||||
// On Windows, it will just run normally without redirection
|
||||
let result = fd::with_stderr_to_null(|| {
|
||||
// This would normally appear in stderr
|
||||
eprintln!("This should be redirected to /dev/null");
|
||||
// This would normally appear in stderr (suppressed on Unix, visible on Windows)
|
||||
eprintln!("This should be redirected to /dev/null on Unix");
|
||||
// Return a test value to verify the function passes through the result
|
||||
42
|
||||
});
|
||||
|
||||
// The function should succeed and return our test value
|
||||
// The function should succeed and return our test value on both platforms
|
||||
assert!(result.is_ok());
|
||||
assert_eq!(result.unwrap(), 42);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user