Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
376 lines
12 KiB
Rust
376 lines
12 KiB
Rust
use std::sync::Arc;
|
|
|
|
use models::{CuddlePleaseArgs, CuddlePleaseSrcArgs};
|
|
use traits::CuddlePlease;
|
|
|
|
use crate::models::{LogLevel, Server, SrcServer};
|
|
|
|
pub mod models {
|
|
#[derive(Clone, Debug)]
|
|
pub struct CuddlePleaseArgs {
|
|
/// Which repository, example: "dagger-components"
|
|
pub repository: String,
|
|
|
|
/// Whom owns the repository, example: "kjuulh"
|
|
pub owner: String,
|
|
|
|
/// which branch to fetch, example: "main"
|
|
pub branch: String,
|
|
|
|
/// Which image to use for cuddle-please, example: "docker.io/kasperhermansen/cuddle-please:latest"
|
|
pub cuddle_image: String,
|
|
|
|
/// Which server to use, see inner field for more docs
|
|
pub server: Server,
|
|
|
|
/// Which log level to output for cuddle-please. Defaults to INFO
|
|
pub log_level: Option<LogLevel>,
|
|
|
|
/// Whether to include an ssh socket, if not set will default to the token set in server and http/https depending on the server
|
|
pub use_ssh_socket: bool,
|
|
}
|
|
#[derive(Clone, Debug)]
|
|
pub struct CuddlePleaseSrcArgs {
|
|
/// Which image to use for cuddle-please, example: "docker.io/kasperhermansen/cuddle-please:latest"
|
|
pub cuddle_image: String,
|
|
|
|
/// Which log level to output for cuddle-please. Defaults to INFO
|
|
pub log_level: Option<LogLevel>,
|
|
|
|
/// Which server to use, see inner field for more docs
|
|
pub server: SrcServer,
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub enum LogLevel {
|
|
Trace,
|
|
Debug,
|
|
Info,
|
|
Warn,
|
|
Error,
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub enum Server {
|
|
Gitea {
|
|
url: String,
|
|
user: String,
|
|
token: String,
|
|
insecure: Option<bool>,
|
|
},
|
|
GitHub {
|
|
token: String,
|
|
},
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub enum SrcServer {
|
|
Gitea { token: String },
|
|
GitHub { token: String },
|
|
}
|
|
}
|
|
|
|
pub mod traits {
|
|
use crate::models::{CuddlePleaseArgs, CuddlePleaseSrcArgs};
|
|
|
|
#[async_trait::async_trait]
|
|
pub trait CuddlePlease {
|
|
async fn execute(&self, args: &CuddlePleaseArgs) -> eyre::Result<()>;
|
|
async fn execute_src(&self, args: &CuddlePleaseSrcArgs) -> eyre::Result<()>;
|
|
}
|
|
}
|
|
|
|
pub struct DaggerCuddlePleaseAction(Arc<dyn CuddlePlease + Send + Sync + 'static>);
|
|
|
|
impl DaggerCuddlePleaseAction {
|
|
/// Create a [`traits::CuddlePlease`] client based on dagger
|
|
pub fn dagger(client: Arc<dagger_sdk::Query>) -> Self {
|
|
Self(Arc::new(DaggerCuddlePlease::new(client)))
|
|
}
|
|
|
|
/// Executes the cuddle-please action. This is a slow operation, but async, so make sure to not spawn blocking
|
|
/// This relies exclively on arguments, as such this pull the repository directly, instead of using local src
|
|
pub async fn execute(&self, args: &CuddlePleaseArgs) -> eyre::Result<()> {
|
|
self.0.execute(args).await
|
|
}
|
|
|
|
/// Executes the cuddle-please action. This is a slow operation, but async, so make sure to not spawn blocking
|
|
/// This function relies exclusively on cuddle.yaml and cuddle.please.yaml
|
|
/// For private repository access, make sure to use [`CuddlePleaseSrcArgs::use_ssh_agent`] otherwise we may not able to push to your repository
|
|
pub async fn execute_src(&self, args: &CuddlePleaseSrcArgs) -> eyre::Result<()> {
|
|
self.0.execute_src(args).await
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
struct DaggerCuddlePlease {
|
|
client: Arc<dagger_sdk::Query>,
|
|
}
|
|
|
|
#[async_trait::async_trait]
|
|
impl CuddlePlease for DaggerCuddlePlease {
|
|
async fn execute(&self, args: &CuddlePleaseArgs) -> eyre::Result<()> {
|
|
self.cuddle_please(self.client.clone(), args).await
|
|
}
|
|
|
|
async fn execute_src(&self, args: &CuddlePleaseSrcArgs) -> eyre::Result<()> {
|
|
self.cuddle_please_src(self.client.clone(), args).await
|
|
}
|
|
}
|
|
|
|
impl DaggerCuddlePlease {
|
|
pub fn new(client: Arc<dagger_sdk::Query>) -> Self {
|
|
Self { client }
|
|
}
|
|
|
|
pub async fn cuddle_please(
|
|
&self,
|
|
client: Arc<dagger_sdk::Query>,
|
|
args: &CuddlePleaseArgs,
|
|
) -> eyre::Result<()> {
|
|
let build_image = client.container().from(&args.cuddle_image);
|
|
|
|
let repo_url = match &args.server {
|
|
Server::Gitea {
|
|
url,
|
|
user,
|
|
token,
|
|
insecure,
|
|
} => {
|
|
if args.use_ssh_socket {
|
|
format!(
|
|
"ssh://{}:{}@{}/{}/{}",
|
|
user, token, url, &args.owner, &args.repository
|
|
)
|
|
} else {
|
|
format!(
|
|
"{}://{}:{}@{}/{}/{}",
|
|
if insecure.unwrap_or(false) {
|
|
"http"
|
|
} else {
|
|
"https"
|
|
},
|
|
user,
|
|
token,
|
|
url,
|
|
&args.owner,
|
|
&args.repository
|
|
)
|
|
}
|
|
}
|
|
Server::GitHub { .. } => {
|
|
if args.use_ssh_socket {
|
|
format!("git@github.com:{}/{}", &args.owner, &args.repository)
|
|
} else {
|
|
format!("https://github.com/{}/{}", &args.owner, &args.repository)
|
|
}
|
|
}
|
|
};
|
|
let api_url = match &args.server {
|
|
Server::Gitea { url, insecure, .. } => {
|
|
format!(
|
|
"{}://{}",
|
|
if insecure.unwrap_or(false) {
|
|
"http"
|
|
} else {
|
|
"https"
|
|
},
|
|
url,
|
|
)
|
|
}
|
|
Server::GitHub { .. } => "https://github.com".into(),
|
|
};
|
|
|
|
let src = if args.use_ssh_socket {
|
|
let socket = client
|
|
.host()
|
|
.unix_socket(std::env::var("SSH_AGENT").expect("SSH_AGENT to be set"));
|
|
|
|
client
|
|
.git_opts(
|
|
&repo_url,
|
|
dagger_sdk::QueryGitOpts {
|
|
experimental_service_host: None,
|
|
keep_git_dir: Some(true),
|
|
},
|
|
)
|
|
.branch("main")
|
|
.tree_opts(dagger_sdk::GitRefTreeOpts {
|
|
ssh_auth_socket: Some(socket.id().await?),
|
|
ssh_known_hosts: None,
|
|
})
|
|
} else {
|
|
client
|
|
.git_opts(
|
|
&repo_url,
|
|
dagger_sdk::QueryGitOpts {
|
|
experimental_service_host: None,
|
|
keep_git_dir: Some(true),
|
|
},
|
|
)
|
|
.branch("main")
|
|
.tree()
|
|
};
|
|
|
|
let res = build_image
|
|
.with_secret_variable(
|
|
"CUDDLE_PLEASE_TOKEN",
|
|
client
|
|
.set_secret(
|
|
"CUDDLE_PLEASE_TOKEN",
|
|
match &args.server {
|
|
Server::Gitea { token, .. } => token,
|
|
Server::GitHub { token } => token,
|
|
},
|
|
)
|
|
.id()
|
|
.await?,
|
|
)
|
|
.with_workdir("/mnt/app")
|
|
.with_directory(".", src.id().await?)
|
|
.with_exec(vec!["git", "remote", "set-url", "origin", &repo_url])
|
|
.with_exec(vec![
|
|
"cuddle-please",
|
|
"release",
|
|
&format!(
|
|
"--engine={}",
|
|
match &args.server {
|
|
Server::Gitea { .. } => "gitea",
|
|
Server::GitHub { .. } => "github",
|
|
}
|
|
),
|
|
"--owner",
|
|
&args.owner,
|
|
"--repo",
|
|
&args.repository,
|
|
"--branch",
|
|
&args.branch,
|
|
"--api-url",
|
|
&api_url,
|
|
"--log-level",
|
|
match args.log_level.as_ref().unwrap_or(&LogLevel::Info) {
|
|
LogLevel::Trace => "trace",
|
|
LogLevel::Debug => "debug",
|
|
LogLevel::Info => "info",
|
|
LogLevel::Warn => "warn",
|
|
LogLevel::Error => "error",
|
|
},
|
|
]);
|
|
|
|
let exit_code = res.exit_code().await?;
|
|
if exit_code != 0 {
|
|
eyre::bail!("failed to run cuddle-please");
|
|
}
|
|
|
|
let please_out = res.stdout().await?;
|
|
println!("{please_out}");
|
|
let please_out = res.stderr().await?;
|
|
println!("{please_out}");
|
|
|
|
Ok(())
|
|
}
|
|
pub async fn cuddle_please_src(
|
|
&self,
|
|
client: Arc<dagger_sdk::Query>,
|
|
args: &CuddlePleaseSrcArgs,
|
|
) -> eyre::Result<()> {
|
|
let build_image = client.container().from(&args.cuddle_image);
|
|
let res = build_image
|
|
.with_secret_variable(
|
|
"CUDDLE_PLEASE_TOKEN",
|
|
client
|
|
.set_secret(
|
|
"CUDDLE_PLEASE_TOKEN",
|
|
match &args.server {
|
|
SrcServer::Gitea { token, .. } => token,
|
|
SrcServer::GitHub { token } => token,
|
|
},
|
|
)
|
|
.id()
|
|
.await?,
|
|
)
|
|
.with_workdir("/mnt/app")
|
|
.with_directory(".", client.host().directory(".").id().await?)
|
|
.with_unix_socket(
|
|
"/tmp/ssh.sock",
|
|
client
|
|
.host()
|
|
.unix_socket(
|
|
std::env::var("SSH_AUTH_SOCK").expect("expect SSH_AUTH_SOCK to be present"),
|
|
)
|
|
.id()
|
|
.await?,
|
|
)
|
|
.with_env_variable("SSH_AUTH_SOCK", "/tmp/ssh.sock")
|
|
.with_new_file_opts(
|
|
"/root/.ssh/config",
|
|
dagger_sdk::ContainerWithNewFileOpts {
|
|
contents: Some(
|
|
"
|
|
Host *
|
|
User git
|
|
StrictHostKeyChecking no
|
|
UserKnownHostsFile /dev/null
|
|
",
|
|
),
|
|
owner: Some("root"),
|
|
permissions: Some(700),
|
|
},
|
|
);
|
|
|
|
let remote_url = res
|
|
.with_exec(vec!["git", "config", "--get", "remote.origin.url"])
|
|
.stdout()
|
|
.await?;
|
|
|
|
let res = if remote_url.starts_with("http") {
|
|
res.with_exec(vec![
|
|
"git",
|
|
"config",
|
|
"--global",
|
|
&format!(
|
|
"url.ssh://git@{}/.insteadOf",
|
|
remote_url
|
|
.trim_start_matches("https://")
|
|
.trim_start_matches("http://")
|
|
),
|
|
&remote_url,
|
|
])
|
|
} else {
|
|
res
|
|
};
|
|
|
|
let res = res.with_exec(vec![
|
|
"cuddle-please",
|
|
"release",
|
|
&format!(
|
|
"--engine={}",
|
|
match &args.server {
|
|
SrcServer::Gitea { .. } => "gitea",
|
|
SrcServer::GitHub { .. } => "github",
|
|
}
|
|
),
|
|
"--log-level",
|
|
match args.log_level.as_ref().unwrap_or(&LogLevel::Info) {
|
|
LogLevel::Trace => "trace",
|
|
LogLevel::Debug => "debug",
|
|
LogLevel::Info => "info",
|
|
LogLevel::Warn => "warn",
|
|
LogLevel::Error => "error",
|
|
},
|
|
]);
|
|
|
|
let exit_code = res.exit_code().await?;
|
|
if exit_code != 0 {
|
|
eyre::bail!("failed to run cuddle-please");
|
|
}
|
|
|
|
let please_out = res.stdout().await?;
|
|
println!("{please_out}");
|
|
let please_out = res.stderr().await?;
|
|
println!("{please_out}");
|
|
|
|
Ok(())
|
|
}
|
|
}
|