feat: with init command
Signed-off-by: kjuulh <contact@kjuulh.io>
This commit is contained in:
parent
91ee9d4387
commit
6c5fed87b1
1898
Cargo.lock
generated
1898
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -1,2 +1,3 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
members = ["cuddle_cli", "examples/base"]
|
members = ["cuddle_cli", "examples/base", "ci"]
|
||||||
|
resolver = "2"
|
||||||
|
11
ci/Cargo.toml
Normal file
11
ci/Cargo.toml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
[package]
|
||||||
|
name = "ci"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
dagger-sdk = "0.2.22"
|
||||||
|
eyre = "0.6.8"
|
||||||
|
tokio = { version = "1.28.2", features = ["full"] }
|
112
ci/src/main.rs
Normal file
112
ci/src/main.rs
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use dagger_sdk::{
|
||||||
|
Container, ContainerPublishOptsBuilder, Directory, HostDirectoryOptsBuilder, Query,
|
||||||
|
QueryContainerOptsBuilder,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> eyre::Result<()> {
|
||||||
|
let client = dagger_sdk::connect().await?;
|
||||||
|
|
||||||
|
let src = client.host().directory_opts(
|
||||||
|
".",
|
||||||
|
HostDirectoryOptsBuilder::default()
|
||||||
|
.include(vec![
|
||||||
|
"ci/",
|
||||||
|
"cuddle_cli/",
|
||||||
|
"examples",
|
||||||
|
"Cargo.lock",
|
||||||
|
"Cargo.toml",
|
||||||
|
])
|
||||||
|
.build()?,
|
||||||
|
);
|
||||||
|
|
||||||
|
client
|
||||||
|
.container()
|
||||||
|
.publish_opts(
|
||||||
|
"kasperhermansen/cuddle:dev",
|
||||||
|
ContainerPublishOptsBuilder::default()
|
||||||
|
.platform_variants(vec![
|
||||||
|
dind_image(client.clone(), src.clone(), "x86_64", "linux/amd64")
|
||||||
|
.await?
|
||||||
|
.id()
|
||||||
|
.await?,
|
||||||
|
dind_image(client.clone(), src.clone(), "aarch64", "linux/arm64/v8")
|
||||||
|
.await?
|
||||||
|
.id()
|
||||||
|
.await?,
|
||||||
|
])
|
||||||
|
.build()?,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn dind_image(
|
||||||
|
client: Arc<Query>,
|
||||||
|
src: Directory,
|
||||||
|
architecture: &str,
|
||||||
|
platform: &str,
|
||||||
|
) -> eyre::Result<Container> {
|
||||||
|
let rust_bin = client
|
||||||
|
.container_opts(
|
||||||
|
QueryContainerOptsBuilder::default()
|
||||||
|
.platform(platform)
|
||||||
|
.build()?,
|
||||||
|
)
|
||||||
|
.from("rust:1.70.0-slim-bullseye")
|
||||||
|
.with_exec(vec![
|
||||||
|
"rustup",
|
||||||
|
"target",
|
||||||
|
"add",
|
||||||
|
&format!("{architecture}-unknown-linux-musl"),
|
||||||
|
])
|
||||||
|
.with_exec(vec!["update-ca-certificates"])
|
||||||
|
.with_exec(vec!["apt-get", "update"])
|
||||||
|
.with_exec(vec!["apt-get", "upgrade", "-y"])
|
||||||
|
.with_exec(vec![
|
||||||
|
"apt-get",
|
||||||
|
"install",
|
||||||
|
"-y",
|
||||||
|
"-q",
|
||||||
|
"build-essential",
|
||||||
|
"curl",
|
||||||
|
"git",
|
||||||
|
"musl-tools",
|
||||||
|
"musl-dev",
|
||||||
|
"libz-dev",
|
||||||
|
])
|
||||||
|
.with_workdir("/app/cuddle/")
|
||||||
|
.with_directory(".", src.id().await?)
|
||||||
|
.with_exec(vec![
|
||||||
|
"cargo",
|
||||||
|
"install",
|
||||||
|
"--target",
|
||||||
|
&format!("{architecture}-unknown-linux-musl"),
|
||||||
|
"--path",
|
||||||
|
"cuddle_cli",
|
||||||
|
"--profile=release",
|
||||||
|
]);
|
||||||
|
|
||||||
|
let final_image = client
|
||||||
|
.container_opts(
|
||||||
|
QueryContainerOptsBuilder::default()
|
||||||
|
.platform(platform)
|
||||||
|
.build()?,
|
||||||
|
)
|
||||||
|
.from("docker:dind")
|
||||||
|
.with_directory(
|
||||||
|
"/usr/local/cargo/bin/",
|
||||||
|
rust_bin.directory("/usr/local/cargo/bin/").id().await?,
|
||||||
|
);
|
||||||
|
|
||||||
|
let path_env = final_image.env_variable("PATH").await?;
|
||||||
|
|
||||||
|
let final_image = final_image
|
||||||
|
.with_env_variable("PATH", format!("{path_env}:/usr/local/cargo/bin"))
|
||||||
|
.with_exec(vec![""]);
|
||||||
|
|
||||||
|
Ok(final_image)
|
||||||
|
}
|
@ -15,12 +15,22 @@ anyhow = "1.0.60"
|
|||||||
serde = { version = "1.0.143", features = ["derive"] }
|
serde = { version = "1.0.143", features = ["derive"] }
|
||||||
serde_yaml = "0.9.4"
|
serde_yaml = "0.9.4"
|
||||||
walkdir = "2.3.2"
|
walkdir = "2.3.2"
|
||||||
git2 = { version = "0.15.0", features = ["ssh"] }
|
git2 = { version = "0.17.2", default-features = false, features = [
|
||||||
clap = "3.2.16"
|
"vendored-libgit2",
|
||||||
|
"vendored-openssl",
|
||||||
|
] }
|
||||||
|
clap = { version = "4.3.4", features = ["env", "string"] }
|
||||||
envconfig = "0.10.0"
|
envconfig = "0.10.0"
|
||||||
dirs = "4.0.0"
|
dirs = "5.0.1"
|
||||||
tracing = "0.1.36"
|
tracing = "0.1.36"
|
||||||
tracing-subscriber = { version = "0.3.15", features = ["json"] }
|
tracing-subscriber = { version = "0.3.15", features = ["json"] }
|
||||||
log = { version = "0.4.17", features = ["std", "kv_unstable"] }
|
log = { version = "0.4.17", features = ["std", "kv_unstable"] }
|
||||||
openssl = { version = "0.10", features = ["vendored"] }
|
|
||||||
tera = "1.17.0"
|
tera = "1.17.0"
|
||||||
|
openssl = { version = "0.10.54", features = ["vendored"] }
|
||||||
|
libz-sys = { version = "1.1.9", default-features = false, features = [
|
||||||
|
"libc",
|
||||||
|
"static",
|
||||||
|
] }
|
||||||
|
inquire = { version = "0.6.2", features = ["console"] }
|
||||||
|
tempfile = { version = "3.6.0" }
|
||||||
|
serde_json = "1.0.97"
|
||||||
|
@ -18,20 +18,20 @@ use crate::{
|
|||||||
use self::subcommands::render_template::RenderTemplateCommand;
|
use self::subcommands::render_template::RenderTemplateCommand;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct CuddleCli<'a> {
|
pub struct CuddleCli {
|
||||||
scripts: Vec<CuddleAction>,
|
scripts: Vec<CuddleAction>,
|
||||||
variables: Vec<CuddleVariable>,
|
variables: Vec<CuddleVariable>,
|
||||||
context: Arc<Mutex<Vec<CuddleContext>>>,
|
context: Arc<Mutex<Vec<CuddleContext>>>,
|
||||||
command: Option<Command<'a>>,
|
command: Option<Command>,
|
||||||
tmp_dir: Option<PathBuf>,
|
tmp_dir: Option<PathBuf>,
|
||||||
config: CuddleConfig,
|
config: CuddleConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> CuddleCli<'a> {
|
impl CuddleCli {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
context: Arc<Mutex<Vec<CuddleContext>>>,
|
context: Arc<Mutex<Vec<CuddleContext>>>,
|
||||||
config: CuddleConfig,
|
config: CuddleConfig,
|
||||||
) -> anyhow::Result<CuddleCli<'a>> {
|
) -> anyhow::Result<CuddleCli> {
|
||||||
let mut cli = CuddleCli {
|
let mut cli = CuddleCli {
|
||||||
scripts: vec![],
|
scripts: vec![],
|
||||||
variables: vec![],
|
variables: vec![],
|
||||||
@ -159,6 +159,7 @@ impl<'a> CuddleCli<'a> {
|
|||||||
|
|
||||||
root_cmd = subcommands::x::build_command(root_cmd, self.clone());
|
root_cmd = subcommands::x::build_command(root_cmd, self.clone());
|
||||||
root_cmd = subcommands::render_template::build_command(root_cmd);
|
root_cmd = subcommands::render_template::build_command(root_cmd);
|
||||||
|
root_cmd = subcommands::init::build_command(root_cmd, self.clone());
|
||||||
|
|
||||||
self.command = Some(root_cmd);
|
self.command = Some(root_cmd);
|
||||||
|
|
||||||
@ -176,13 +177,15 @@ impl<'a> CuddleCli<'a> {
|
|||||||
.and_then(|cmd| cmd.execute())?;
|
.and_then(|cmd| cmd.execute())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Some(("init", sub_matches)) => {
|
||||||
|
subcommands::init::execute_init(sub_matches, self.clone())
|
||||||
|
}
|
||||||
_ => Err(anyhow::anyhow!("could not find a match")),
|
_ => Err(anyhow::anyhow!("could not find a match")),
|
||||||
};
|
};
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(()) => {}
|
Ok(()) => {}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let _ = cli.print_long_help();
|
|
||||||
return Err(e);
|
return Err(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
147
cuddle_cli/src/cli/subcommands/init.rs
Normal file
147
cuddle_cli/src/cli/subcommands/init.rs
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
use std::fs::{create_dir_all, read, read_dir};
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
use clap::{ArgMatches, Command};
|
||||||
|
|
||||||
|
use crate::cli::CuddleCli;
|
||||||
|
|
||||||
|
pub fn build_command(root_cmd: Command, cli: CuddleCli) -> Command {
|
||||||
|
let mut repo_url = clap::Arg::new("repo").long("repo").short('r');
|
||||||
|
|
||||||
|
if let Ok(cuddle_template_url) = std::env::var("CUDDLE_TEMPLATE_URL") {
|
||||||
|
repo_url = repo_url.default_value(cuddle_template_url);
|
||||||
|
} else {
|
||||||
|
repo_url = repo_url.required(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut execute_cmd = Command::new("init")
|
||||||
|
.about("init bootstraps a repository from a template")
|
||||||
|
.arg(repo_url)
|
||||||
|
.arg(clap::Arg::new("name"))
|
||||||
|
.arg(clap::Arg::new("path"));
|
||||||
|
|
||||||
|
root_cmd.subcommand(execute_cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn execute_init(exe_submatch: &ArgMatches, cli: CuddleCli) -> anyhow::Result<()> {
|
||||||
|
let repo = exe_submatch.get_one::<String>("repo").unwrap();
|
||||||
|
let name = exe_submatch.get_one::<String>("name");
|
||||||
|
let path = exe_submatch.get_one::<String>("path");
|
||||||
|
|
||||||
|
tracing::info!("Downloading: {}", repo);
|
||||||
|
|
||||||
|
create_dir_all(std::env::temp_dir())?;
|
||||||
|
|
||||||
|
let tmpdir = tempfile::tempdir()?;
|
||||||
|
|
||||||
|
let tmpdir_path = tmpdir.path().canonicalize()?;
|
||||||
|
|
||||||
|
let output = std::process::Command::new("git")
|
||||||
|
.args(&["clone", repo, "."])
|
||||||
|
.current_dir(tmpdir_path)
|
||||||
|
.output()?;
|
||||||
|
|
||||||
|
std::io::stdout().write_all(&output.stdout)?;
|
||||||
|
std::io::stderr().write_all(&output.stderr)?;
|
||||||
|
|
||||||
|
let templates_path = tmpdir.path().join("cuddle-templates.json");
|
||||||
|
let template_path = tmpdir.path().join("cuddle-template.json");
|
||||||
|
let templates = if templates_path.exists() {
|
||||||
|
let templates = read(templates_path)?;
|
||||||
|
let templates: CuddleTemplates = serde_json::from_slice(&templates)?;
|
||||||
|
|
||||||
|
let mut single_templates = Vec::new();
|
||||||
|
|
||||||
|
for template_name in templates.templates.iter() {
|
||||||
|
let template = read(
|
||||||
|
tmpdir
|
||||||
|
.path()
|
||||||
|
.join(template_name)
|
||||||
|
.join("cuddle-template.json"),
|
||||||
|
)?;
|
||||||
|
let template = serde_json::from_slice::<CuddleTemplate>(&template)?;
|
||||||
|
|
||||||
|
single_templates.push((template_name, template))
|
||||||
|
}
|
||||||
|
|
||||||
|
single_templates
|
||||||
|
.into_iter()
|
||||||
|
.map(|(name, template)| (name.clone(), tmpdir.path().join(name), template))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
} else if template_path.exists() {
|
||||||
|
let template = read(template_path)?;
|
||||||
|
let template = serde_json::from_slice::<CuddleTemplate>(&template)?;
|
||||||
|
vec![(template.clone().name, tmpdir.path().to_path_buf(), template)]
|
||||||
|
} else {
|
||||||
|
anyhow::bail!("No cuddle-template.json or cuddle-templates.json found");
|
||||||
|
};
|
||||||
|
|
||||||
|
let template = match name {
|
||||||
|
Some(name) => {
|
||||||
|
let template = read(tmpdir.path().join(name).join("cuddle-template.json"))?;
|
||||||
|
let template = serde_json::from_slice::<CuddleTemplate>(&template)?;
|
||||||
|
Ok((name.clone(), tmpdir.path().join(name), template))
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
if templates.len() > 1 {
|
||||||
|
let name = inquire::Select::new(
|
||||||
|
"template",
|
||||||
|
templates.iter().map(|t| t.0.clone()).collect(),
|
||||||
|
)
|
||||||
|
.with_help_message("name of which template to use")
|
||||||
|
.prompt()?;
|
||||||
|
|
||||||
|
let found_template = templates
|
||||||
|
.iter()
|
||||||
|
.find(|item| item.0 == name)
|
||||||
|
.ok_or(anyhow::anyhow!("could not find an item with that name"))?;
|
||||||
|
|
||||||
|
Ok(found_template.clone())
|
||||||
|
} else if templates.len() == 1 {
|
||||||
|
Ok(templates[0].clone())
|
||||||
|
} else {
|
||||||
|
Err(anyhow::anyhow!("No templates found, with any valid names"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let (name, template_dir, template) = template?;
|
||||||
|
|
||||||
|
let path = match path {
|
||||||
|
Some(path) => path.clone(),
|
||||||
|
None => inquire::Text::new("path")
|
||||||
|
.with_help_message("to where it should be placed")
|
||||||
|
.with_default(".")
|
||||||
|
.prompt()?,
|
||||||
|
};
|
||||||
|
|
||||||
|
create_dir_all(&path)?;
|
||||||
|
let dir = std::fs::read_dir(&path)?;
|
||||||
|
if dir.count() != 0 {
|
||||||
|
anyhow::bail!("Directory {} is not empty", &path);
|
||||||
|
}
|
||||||
|
|
||||||
|
for entry in read_dir(template_dir)? {
|
||||||
|
let entry = entry?;
|
||||||
|
let entry_path = entry.path();
|
||||||
|
let name = entry.file_name();
|
||||||
|
|
||||||
|
if name == "cuddle-template.json" || name == "cuddle-templates.json" {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::fs::rename(entry_path, std::path::PathBuf::from(&path).join(name))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||||
|
struct CuddleTemplates {
|
||||||
|
pub templates: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||||
|
struct CuddleTemplate {
|
||||||
|
pub name: String,
|
||||||
|
}
|
@ -1,2 +1,3 @@
|
|||||||
|
pub mod init;
|
||||||
pub mod render_template;
|
pub mod render_template;
|
||||||
pub mod x;
|
pub mod x;
|
||||||
|
@ -4,7 +4,7 @@ use clap::{Arg, ArgMatches, Command};
|
|||||||
|
|
||||||
use crate::{cli::CuddleCli, model::CuddleVariable};
|
use crate::{cli::CuddleCli, model::CuddleVariable};
|
||||||
|
|
||||||
pub fn build_command<'a>(root_cmd: Command<'a>) -> Command<'a> {
|
pub fn build_command(root_cmd: Command) -> Command {
|
||||||
root_cmd.subcommand(
|
root_cmd.subcommand(
|
||||||
Command::new("render_template")
|
Command::new("render_template")
|
||||||
.about("renders a jinja compatible template")
|
.about("renders a jinja compatible template")
|
||||||
|
@ -2,14 +2,15 @@ use clap::{ArgMatches, Command};
|
|||||||
|
|
||||||
use crate::cli::CuddleCli;
|
use crate::cli::CuddleCli;
|
||||||
|
|
||||||
pub fn build_command<'a>(root_cmd: Command<'a>, cli: CuddleCli<'a>) -> Command<'a> {
|
pub fn build_command(root_cmd: Command, cli: CuddleCli) -> Command {
|
||||||
if cli.scripts.len() > 0 {
|
if cli.scripts.len() > 0 {
|
||||||
let mut execute_cmd = Command::new("x").about("x is your entry into your domains scripts, scripts inherited from parents will also be present here").subcommand_required(true);
|
let execute_cmd_about = "x is your entry into your domains scripts, scripts inherited from parents will also be present here";
|
||||||
|
let mut execute_cmd = Command::new("x")
|
||||||
|
.about(execute_cmd_about)
|
||||||
|
.subcommand_required(true);
|
||||||
|
|
||||||
for script in cli.scripts.iter() {
|
for script in cli.scripts.iter() {
|
||||||
let action_cmd = Command::new(script.name.clone());
|
let action_cmd = Command::new(&script.name).about(&script.name);
|
||||||
|
|
||||||
// TODO: Some way to add an about for clap, requires conversion from String -> &str
|
|
||||||
|
|
||||||
execute_cmd = execute_cmd.subcommand(action_cmd);
|
execute_cmd = execute_cmd.subcommand(action_cmd);
|
||||||
}
|
}
|
||||||
|
@ -20,10 +20,7 @@ fn main() -> anyhow::Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn init_logging() -> anyhow::Result<()> {
|
fn init_logging() -> anyhow::Result<()> {
|
||||||
tracing_subscriber::fmt()
|
tracing_subscriber::fmt().with_max_level(Level::INFO).init();
|
||||||
.pretty()
|
|
||||||
.with_max_level(Level::INFO)
|
|
||||||
.init();
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
FROM rust:1.62.1-slim-bullseye as base
|
FROM rust:1.70-slim-bullseye as base
|
||||||
|
|
||||||
RUN rustup target add x86_64-unknown-linux-musl
|
RUN rustup target add x86_64-unknown-linux-musl
|
||||||
|
|
||||||
|
@ -1,2 +1,3 @@
|
|||||||
.cuddle/
|
.cuddle/
|
||||||
.git/
|
.git/
|
||||||
|
target/
|
||||||
|
Loading…
Reference in New Issue
Block a user