Something went wrong. Try again.
gitops as CI
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219#[link(wasm_import_module = "sip:workflow/env")]extern "C" { #[link_name = "workflow_file_len"] fn workflow_file_len(path_ptr: *const u8, path_len: i32) -> i32; #[link_name = "read_workflow_file"] fn read_workflow_file( path_ptr: *const u8, path_len: i32, dst_ptr: *mut u8, dst_len: i32, ) -> i32; #[link_name = "declare_job"] fn declare_job( name_ptr: *const u8, name_len: i32, parent_ptr: *const u8, parent_len: i32, requires_ptr: *const u8, requires_len: i32, executor_ptr: *const u8, executor_len: i32, command_ptr: *const u8, command_len: i32, ) -> i32;}
const EXECUTOR: &str = "unsafe-host-shell";
#[no_mangle]pub extern "C" fn _start() { let yaml = read_yaml(); declare_jobs(&yaml);}
fn read_yaml() -> String { for path in [ ".sip/workflows/sip.yml", ".sip/workflows/sip.yaml", ".sip/workflows/ci.yml", ".sip/workflows/ci.yaml", ] { let len = unsafe { workflow_file_len(path.as_ptr(), path.len() as i32) }; if len <= 0 { continue; } let mut bytes = vec![0; len as usize]; let read = unsafe { read_workflow_file( path.as_ptr(), path.len() as i32, bytes.as_mut_ptr(), len, ) }; if read == len { return String::from_utf8(bytes).unwrap_or_default(); } } String::new()}
fn declare_jobs(yaml: &str) { if yaml.lines().any(|line| line.trim() == "tasks:") { declare_task_list(yaml); } else { declare_job_map(yaml); }}
fn declare_job_map(yaml: &str) { let mut in_jobs = false; let mut current_name: Option<String> = None; let mut current_requires = Vec::new(); let mut current_commands = Vec::new();
for raw in yaml.lines() { let trimmed = raw.trim(); if trimmed.is_empty() || trimmed.starts_with('#') { continue; } if trimmed == "jobs:" { in_jobs = true; continue; } if !in_jobs { continue; }
let indent = raw.len() - raw.trim_start().len(); if indent == 0 && !trimmed.starts_with("jobs:") { flush_job( &mut current_name, &mut current_requires, &mut current_commands, ); in_jobs = false; continue; }
if indent == 2 && trimmed.ends_with(':') && !trimmed.starts_with('-') { flush_job( &mut current_name, &mut current_requires, &mut current_commands, ); current_name = Some(trimmed.trim_end_matches(':').trim().to_string()); continue; }
if indent >= 4 && trimmed.starts_with("needs:") { let value = trimmed.trim_start_matches("needs:").trim(); current_requires = parse_needs(value); } if indent >= 6 && trimmed.starts_with("- ") { current_commands.push(trimmed.trim_start_matches("- ").trim().to_string()); } }
flush_job( &mut current_name, &mut current_requires, &mut current_commands, );}
fn declare_task_list(yaml: &str) { let mut in_tasks = false; let mut previous: Option<String> = None;
for raw in yaml.lines() { let trimmed = raw.trim(); if trimmed.is_empty() || trimmed.starts_with('#') { continue; } if trimmed == "tasks:" { in_tasks = true; continue; } if !in_tasks { continue; } let indent = raw.len() - raw.trim_start().len(); if indent == 0 && !trimmed.starts_with("tasks:") { break; } let Some(task) = trimmed.strip_prefix("- ") else { continue; }; let Some((name, _command)) = task.split_once(':') else { continue; }; let name = name.trim(); let command = _command.trim(); if name.is_empty() { continue; } let requires = previous.clone().unwrap_or_default(); unsafe { declare_job( name.as_ptr(), name.len() as i32, core::ptr::null(), 0, requires.as_ptr(), requires.len() as i32, EXECUTOR.as_ptr(), EXECUTOR.len() as i32, command.as_ptr(), command.len() as i32, ); } previous = Some(name.to_string()); }}
fn flush_job(name: &mut Option<String>, requires: &mut Vec<String>, commands: &mut Vec<String>) { let Some(name) = name.take() else { return; }; let joined_requires = requires.join("\n"); let command = commands.join("\n"); unsafe { declare_job( name.as_ptr(), name.len() as i32, core::ptr::null(), 0, joined_requires.as_ptr(), joined_requires.len() as i32, EXECUTOR.as_ptr(), EXECUTOR.len() as i32, command.as_ptr(), command.len() as i32, ); } requires.clear(); commands.clear();}
fn parse_needs(value: &str) -> Vec<String> { if value.starts_with('[') && value.ends_with(']') { return value .trim_start_matches('[') .trim_end_matches(']') .split(',') .map(|name| name.trim().trim_matches('"').trim_matches('\'')) .filter(|name| !name.is_empty()) .map(str::to_string) .collect(); } value .trim_matches('"') .trim_matches('\'') .split_whitespace() .filter(|name| !name.is_empty()) .map(str::to_string) .collect()}