From d2a90591bf53dd75fa719a295dc5819846fe90cd Mon Sep 17 00:00:00 2001 From: Vivek Chavan <111511821+vivekchavan14@users.noreply.github.com> Date: Wed, 22 Oct 2025 11:51:38 +0530 Subject: [PATCH] fix(parser): Handle CRLF line endings in bash and PowerShell parsers (#6889) Fixes #6867 The bash and PowerShell parsers were not properly handling CRLF (Windows-style) line endings when parsing script arguments. The regex patterns were only matching LF line endings, causing scripts with CRLF to fail metadata generation. Changes: - Updated RE_BASH regex to optionally match \r before line end (\r?$) - Updated RE_POWERSHELL_ARGS regex to optionally match \r (\r?) - Added test case test_parse_bash_sig_with_crlf() to verify CRLF handling This fix ensures that bash scripts created on Windows (or with editors using CRLF) will correctly parse arguments and generate proper metadata via 'wmill script generate-metadata' command. Python parser was verified to work correctly as it uses an AST parser that inherently handles line endings properly. --- .../parsers/windmill-parser-bash/src/lib.rs | 63 ++++++++++++++++++- 1 file changed, 61 insertions(+), 2 deletions(-) diff --git a/backend/parsers/windmill-parser-bash/src/lib.rs b/backend/parsers/windmill-parser-bash/src/lib.rs index f179243d68..4e3d70c038 100644 --- a/backend/parsers/windmill-parser-bash/src/lib.rs +++ b/backend/parsers/windmill-parser-bash/src/lib.rs @@ -45,10 +45,10 @@ pub fn parse_powershell_sig(code: &str) -> anyhow::Result { } lazy_static::lazy_static! { - static ref RE_BASH: Regex = Regex::new(r#"(?m)^(\w+)="\$(?:(\d+)|\{(\d+)\}|\{(\d+):-(.*)\})"(?:[\t ]*)?(?:#.*)?$"#).unwrap(); + static ref RE_BASH: Regex = Regex::new(r#"(?m)^(\w+)="\$(?:(\d+)|\{(\d+)\}|\{(\d+):-(.*)\})"(?:[\t ]*)?(?:#.*)?\r?$"#).unwrap(); pub static ref RE_POWERSHELL_PARAM: Regex = Regex::new(r#"(?m)param[\t ]*\(([^)]*)\)"#).unwrap(); - static ref RE_POWERSHELL_ARGS: Regex = Regex::new(r#"(?:\[([\w\[\]]+)\])?\$(\w+)[\t ]*(?:=[\t ]*(?:(?:(?:"|')([^"\n\r\$]*)(?:"|'))|([\d.]+)))?"#).unwrap(); + static ref RE_POWERSHELL_ARGS: Regex = Regex::new(r#"(?:\[([\w\[\]]+)\])?\$(\w+)[\t ]*(?:=[\t ]*(?:(?:(?:"|')([^"\n\r\$]*)(?:"|'))|([\d.]+)))?\r?"#).unwrap(); } fn parse_bash_file(code: &str) -> anyhow::Result>> { @@ -352,4 +352,63 @@ non_required="${5:-}" ); Ok(()) } + + #[test] + fn test_parse_bash_sig_with_crlf() -> anyhow::Result<()> { + // Test with CRLF line endings (Windows-style) + let code = "\r\ntoken=\"$1\"\r\nimage=\"$2\"\r\ndigest=\"${3:-latest with spaces}\"\r\ntext=\"$4\" # with comment\r\nnon_required=\"${5:-}\"\r\n\r\n\r\n"; + assert_eq!( + parse_bash_sig(code)?, + MainArgSignature { + star_args: false, + star_kwargs: false, + args: vec![ + Arg { + otyp: None, + name: "token".to_string(), + typ: Typ::Str(None), + default: None, + has_default: false, + oidx: None + }, + Arg { + otyp: None, + name: "image".to_string(), + typ: Typ::Str(None), + default: None, + has_default: false, + oidx: None + }, + Arg { + otyp: None, + name: "digest".to_string(), + typ: Typ::Str(None), + default: Some(json!("latest with spaces")), + has_default: true, + oidx: None + }, + Arg { + otyp: None, + name: "text".to_string(), + typ: Typ::Str(None), + default: None, + has_default: false, + oidx: None + }, + Arg { + otyp: None, + name: "non_required".to_string(), + typ: Typ::Str(None), + default: Some(json!("")), + has_default: true, + oidx: None + } + ], + no_main_func: None, + has_preprocessor: None + } + ); + + Ok(()) + } }