use crate::error::{Result, ZecKitError};
use colored::*;
use std::fs;
use std::path::PathBuf;
const WORKFLOW_TEMPLATE: &str = r#"name: ZecKit E2E CI
on:
push:
branches: [ main, master ]
pull_request:
branches: [ main, master ]
workflow_dispatch:
jobs:
zeckit-e2e:
name: ZecKit E2E
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: 🚀 Start ZecKit Devnet
uses: {repo}@{branch}
with:
backend: '{backend}'
image_prefix: 'ghcr.io/intellidean/zeckit'
startup_timeout_minutes: '15'
# Add your own test steps below!
# Example:
# - name: Run Custom Smoke Test
# run: |
# chmod +x ./smoke_test.sh
# ./smoke_test.sh
"#;
fn find_toolkit_root() -> Option<PathBuf> {
let mut curr = std::env::current_dir().ok()?;
loop {
if curr.join("action.yml").exists() {
return Some(curr);
}
if !curr.pop() {
break;
}
}
None
}
fn detect_github_repo() -> String {
use std::process::Command;
let output = Command::new("git")
.args(["remote", "get-url", "origin"])
.output();
if let Ok(out) = output {
let mut url = String::from_utf8_lossy(&out.stdout).trim().to_string();
if url.contains("github.com") {
let parts: Vec<&str> = if url.contains("https://") {
if let Some(pos) = url.find('@') {
url = format!("https://{}", &url[pos+1..]);
}
url.trim_start_matches("https://github.com/").trim_end_matches(".git").split('/').collect()
} else {
url.trim_start_matches("git@github.com:").trim_end_matches(".git").split('/').collect()
};
if parts.len() >= 2 {
return format!("{}/{}", parts[0], parts[1]);
}
}
}
"intelliDean/ZecKit".to_string()
}
fn detect_git_branch() -> String {
use std::process::Command;
let output = Command::new("git")
.args(["rev-parse", "--abbrev-ref", "HEAD"])
.output();
if let Ok(out) = output {
let branch = String::from_utf8_lossy(&out.stdout).trim().to_string();
if !branch.is_empty() && branch != "HEAD" {
return branch;
}
}
"main".to_string()
}
pub async fn execute(
backend: String,
force: bool,
output: Option<String>,
project_dir: Option<String>,
) -> Result<()> {
println!("{}", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━".cyan());
println!(" {}", "ZecKit - Workflow Generator".cyan().bold());
println!("{}", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━".cyan());
let target_base = if let Some(ref dir) = project_dir {
PathBuf::from(dir)
} else {
std::env::current_dir().map_err(|e| ZecKitError::Io(e))?
};
let target_path = if let Some(out) = output {
PathBuf::from(out)
} else {
target_base.join(".github").join("workflows").join("zeckit-e2e.yml")
};
if target_path.exists() && !force {
println!("{} Workflow file already exists at {:?}", "Warning:".yellow().bold(), target_path);
println!("Use --force to overwrite it.");
return Ok(());
}
if let Some(parent) = target_path.parent() {
fs::create_dir_all(parent).map_err(|e| ZecKitError::Io(e))?;
}
let is_toolkit_repo = find_toolkit_root().is_some();
let (repo, branch) = if is_toolkit_repo {
(detect_github_repo(), detect_git_branch())
} else {
("intelliDean/ZecKit".to_string(), "main".to_string())
};
let content = WORKFLOW_TEMPLATE
.replace("{backend}", &backend)
.replace("{repo}", &repo)
.replace("{branch}", &branch);
fs::write(&target_path, content).map_err(|e| ZecKitError::Io(e))?;
println!("{} Successfully initialized ZecKit workflow!", "✓".green().bold());
println!("File created at: {}", target_path.to_string_lossy().cyan());
println!("\nNext steps:");
println!(" 1. Commit the new workflow file.");
println!(" 2. Push to GitHub to trigger your first ZecKit-powered CI run.");
println!("\nHappy private coding! 🛡️");
Ok(())
}