feat: add base ci

Signed-off-by: kjuulh <contact@kjuulh.io>
This commit is contained in:
Kasper Juul Hermansen 2023-08-11 17:23:57 +02:00
parent 675cb01d15
commit 2c102bc9b5
Signed by: kjuulh
GPG Key ID: 9AA7BC13CE474394
6 changed files with 237 additions and 421 deletions

170
.drone.yml Normal file
View File

@ -0,0 +1,170 @@
kind: pipeline
name: default
type: docker
steps:
- name: build ci
image: rustlang/rust:nightly
volumes:
- name: ci
path: /mnt/ci
environment:
PKG_CONFIG_SYSROOT_DIR: "/"
CI_PREFIX: "/mnt/ci"
commands:
- set -e
- apt update
- apt install musl-tools pkg-config libssl-dev openssl build-essential musl-dev -y
- rustup target add x86_64-unknown-linux-musl
- cargo build --target=x86_64-unknown-linux-musl -p ci
#- cargo build -p ci
- mv target/x86_64-unknown-linux-musl/debug/ci "$CI_PREFIX/ci"
#- mv target/debug/ci $CI_PREFIX/ci
- name: load_secret
image: debian:buster-slim
volumes:
- name: ssh
path: /root/.ssh/
environment:
SSH_KEY:
from_secret: gitea_id_ed25519
commands:
- mkdir -p $HOME/.ssh/
- echo "$SSH_KEY" | base64 -d > $HOME/.ssh/id_ed25519
- name: build pr
image: kasperhermansen/cuddle:latest
pull: always
volumes:
- name: ssh
path: /root/.ssh/
- name: dockersock
path: /var/run
- name: ci
path: /mnt/ci
commands:
- eval `ssh-agent`
- chmod -R 600 ~/.ssh
- ssh-add
- echo "$DOCKER_PASSWORD" | docker login --password-stdin --username="$DOCKER_USERNAME" docker.io
- ldd $CI_PREFIX
- apk add git
- cuddle x ci:pr
environment:
DOCKER_BUILDKIT: 1
DOCKER_PASSWORD:
from_secret: docker_password
DOCKER_USERNAME:
from_secret: docker_username
CUDDLE_SECRETS_PROVIDER: 1password
CUDDLE_ONE_PASSWORD_DOT_ENV: ".env.ci"
CUDDLE_SSH_AGENT: "true"
CI_PREFIX: "/mnt/ci/ci"
CUDDLE_PLEASE_TOKEN:
from_secret: cuddle_please_token
OP_SERVICE_ACCOUNT_TOKEN:
from_secret: op_service_account_token
when:
event:
- pull_request
exclude:
- main
- master
depends_on:
- "load_secret"
- "build ci"
- name: build main
image: kasperhermansen/cuddle:latest
pull: always
volumes:
- name: ssh
path: /root/.ssh/
- name: dockersock
path: /var/run
- name: ci
path: /mnt/ci
commands:
- eval `ssh-agent`
- chmod -R 600 ~/.ssh
- ssh-add
- echo "$DOCKER_PASSWORD" | docker login --password-stdin --username="$DOCKER_USERNAME" docker.io
- ldd $CI_PREFIX
- apk add git
- cuddle x ci:main
environment:
DOCKER_BUILDKIT: 1
DOCKER_PASSWORD:
from_secret: docker_password
DOCKER_USERNAME:
from_secret: docker_username
CUDDLE_SECRETS_PROVIDER: 1password
CUDDLE_ONE_PASSWORD_DOT_ENV: ".env.ci"
CUDDLE_SSH_AGENT: "true"
CI_PREFIX: "/mnt/ci/ci"
CUDDLE_PLEASE_TOKEN:
from_secret: cuddle_please_token
OP_SERVICE_ACCOUNT_TOKEN:
from_secret: op_service_account_token
when:
event:
- push
branch:
- main
- master
exclude:
- pull_request
depends_on:
- "load_secret"
- "build ci"
- name: deploy release
image: kasperhermansen/cuddle:latest
pull: always
volumes:
- name: ssh
path: /root/.ssh/
- name: dockersock
path: /var/run
commands:
- eval `ssh-agent`
- chmod -R 600 ~/.ssh
- ssh-add
- cuddle x build:release:all
- cuddle x deploy:docs:preview
environment:
DOCKER_BUILDKIT: 1
CUDDLE_SECRETS_PROVIDER: 1password
CUDDLE_ONE_PASSWORD_DOT_ENV: ".env.ci"
CUDDLE_SSH_AGENT: "true"
CUDDLE_CI: "true"
CUDDLE_PLEASE_TOKEN:
from_secret: cuddle_please_token
OP_SERVICE_ACCOUNT_TOKEN:
from_secret: op_service_account_token
when:
event:
- tag
ref:
include:
- refs/tags/v*
depends_on:
- "load_secret"
- "build ci"
services:
- name: docker
image: docker:dind
privileged: true
volumes:
- name: dockersock
path: /var/run
volumes:
- name: ssh
temp: {}
- name: dockersock
temp: {}
- name: ci
temp: {}

2
.gitignore vendored
View File

@ -1 +1,3 @@
/target /target
.env
.cuddle/

View File

@ -7,10 +7,6 @@ use clap::Parser;
use clap::Subcommand; use clap::Subcommand;
use clap::ValueEnum; use clap::ValueEnum;
use dagger_sdk::Platform;
use dagger_sdk::QueryContainerOpts;
use futures::StreamExt;
use crate::please_release::run_release_please; use crate::please_release::run_release_please;
#[derive(Parser, Clone)] #[derive(Parser, Clone)]
@ -30,44 +26,15 @@ pub enum Commands {
#[command(subcommand)] #[command(subcommand)]
command: LocalCommands, command: LocalCommands,
}, },
PullRequest { PullRequest {},
#[arg(long)] Main {},
image: String,
#[arg(long)]
tag: String,
#[arg(long)]
bin_name: String,
},
Main {
#[arg(long)]
image: String,
#[arg(long)]
tag: String,
#[arg(long)]
bin_name: String,
},
Release, Release,
} }
#[derive(Subcommand, Clone)] #[derive(Subcommand, Clone)]
pub enum LocalCommands { pub enum LocalCommands {
Build {
#[arg(long, default_value = "debug")]
profile: BuildProfile,
#[arg(long)]
bin_name: String,
},
Test, Test,
DockerImage {
#[arg(long)]
image: String,
#[arg(long)]
tag: String,
#[arg(long)]
bin_name: String,
},
PleaseRelease, PleaseRelease,
BuildDocs {},
} }
#[derive(Debug, Clone, ValueEnum)] #[derive(Debug, Clone, ValueEnum)]
@ -84,23 +51,8 @@ pub struct GlobalArgs {
#[arg(long, global = true, help_heading = "Global")] #[arg(long, global = true, help_heading = "Global")]
rust_builder_image: Option<String>, rust_builder_image: Option<String>,
#[arg(long, global = true, help_heading = "Global")]
production_image: Option<String>,
#[arg(long, global = true, help_heading = "Global")]
mkdocs_image: Option<String>,
#[arg(long, global = true, help_heading = "Global")]
caddy_image: Option<String>,
#[arg(long, global = true, help_heading = "Global")] #[arg(long, global = true, help_heading = "Global")]
source: Option<PathBuf>, source: Option<PathBuf>,
#[arg(long, global = true, help_heading = "Global")]
docs_image: Option<String>,
#[arg(long, global = true, help_heading = "Global")]
docs_image_tag: Option<String>,
} }
#[tokio::main] #[tokio::main]
@ -114,118 +66,35 @@ async fn main() -> eyre::Result<()> {
match &cli.commands { match &cli.commands {
Commands::Local { command } => match command { Commands::Local { command } => match command {
LocalCommands::Build {
profile: _,
bin_name,
} => {
let base_image = base_rust_image(
client.clone(),
&cli.global,
&None,
&bin_name.clone(),
&"release".into(),
)
.await?;
let prod_image = get_base_debian_image(client.clone(), &cli.global, None).await?;
build::execute(
client,
&cli.global,
&base_image,
&prod_image,
bin_name,
&None,
)
.await?;
}
LocalCommands::Test => { LocalCommands::Test => {
let base_image = base_rust_image( let base_image =
client.clone(), base_rust_image(client.clone(), &cli.global, &None, &"debug".into()).await?;
&cli.global,
&None,
&"cuddle-please".into(),
&"debug".into(),
)
.await?;
test::execute(client, &cli.global, base_image).await?; test::execute(client, &cli.global, base_image).await?;
} }
LocalCommands::DockerImage {
tag,
image,
bin_name,
} => {
build::build_and_deploy(client, &cli.global, bin_name, image, tag).await?;
}
LocalCommands::PleaseRelease => todo!(), LocalCommands::PleaseRelease => todo!(),
LocalCommands::BuildDocs {} => {
let _image = docs::execute(
client.clone(),
&cli.global,
&Some("linux/amd64".to_string()),
)
.await?;
}
}, },
Commands::PullRequest { Commands::PullRequest {} => {
image, async fn test(client: Arc<dagger_sdk::Query>, cli: &Command) {
tag,
bin_name,
} => {
async fn test(client: Arc<dagger_sdk::Query>, cli: &Command, bin_name: &String) {
let args = &cli.global; let args = &cli.global;
let base_image = let base_image = base_rust_image(client.clone(), args, &None, &"debug".into())
base_rust_image(client.clone(), args, &None, bin_name, &"debug".into()) .await
.await .unwrap();
.unwrap();
test::execute(client.clone(), args, base_image) test::execute(client.clone(), args, base_image)
.await .await
.unwrap(); .unwrap();
} }
async fn build(
client: Arc<dagger_sdk::Query>,
cli: &Command,
bin_name: &String,
image: &String,
tag: &String,
) {
let args = &cli.global;
build::build(client.clone(), args, bin_name, image, tag) tokio::join!(test(client.clone(), &cli),);
.await
.unwrap();
}
tokio::join!(
test(client.clone(), &cli, bin_name),
build(client.clone(), &cli, bin_name, image, tag),
);
} }
Commands::Main { Commands::Main {} => {
image, async fn test(client: Arc<dagger_sdk::Query>, cli: &Command) {
tag,
bin_name,
} => {
async fn test(client: Arc<dagger_sdk::Query>, cli: &Command, bin_name: &String) {
let args = &cli.global; let args = &cli.global;
let base_image = let base_image = base_rust_image(client.clone(), args, &None, &"debug".into())
base_rust_image(client.clone(), args, &None, bin_name, &"debug".into())
.await
.unwrap();
test::execute(client.clone(), args, base_image)
.await .await
.unwrap(); .unwrap();
} test::execute(client.clone(), args, base_image)
async fn build(
client: Arc<dagger_sdk::Query>,
cli: &Command,
bin_name: &String,
image: &String,
tag: &String,
) {
let args = &cli.global;
build::build_and_deploy(client.clone(), args, bin_name, image, tag)
.await .await
.unwrap(); .unwrap();
} }
@ -237,8 +106,7 @@ async fn main() -> eyre::Result<()> {
} }
tokio::join!( tokio::join!(
test(client.clone(), &cli, bin_name), test(client.clone(), &cli),
build(client.clone(), &cli, bin_name, image, tag),
cuddle_please(client.clone(), &cli) cuddle_please(client.clone(), &cli)
); );
} }
@ -249,7 +117,6 @@ async fn main() -> eyre::Result<()> {
} }
mod please_release { mod please_release {
use std::sync::Arc; use std::sync::Arc;
use crate::{base_rust_image, GlobalArgs}; use crate::{base_rust_image, GlobalArgs};
@ -258,26 +125,13 @@ mod please_release {
client: Arc<dagger_sdk::Query>, client: Arc<dagger_sdk::Query>,
args: &GlobalArgs, args: &GlobalArgs,
) -> eyre::Result<()> { ) -> eyre::Result<()> {
let base_image = base_rust_image( let build_image = client
client.clone(), .container()
args, .from("kasperhermansen/cuddle-please:main-1691504183");
&Some("linux/amd64".to_string()),
&"cuddle-please".to_string(),
&"release".into(),
)
.await?;
let build_image = base_image.with_exec(vec![
"cargo",
"install",
"--target",
"x86_64-unknown-linux-gnu",
"--path=crates/cuddle-please",
]);
let src = client let src = client
.git_opts( .git_opts(
"https://git.front.kjuulh.io/kjuulh/cuddle-please", "https://git.front.kjuulh.io/kjuulh/dagger-components",
dagger_sdk::QueryGitOpts { dagger_sdk::QueryGitOpts {
experimental_service_host: None, experimental_service_host: None,
keep_git_dir: Some(true), keep_git_dir: Some(true),
@ -302,7 +156,7 @@ mod please_release {
"set-url", "set-url",
"origin", "origin",
&format!( &format!(
"https://git:{}@git.front.kjuulh.io/kjuulh/cuddle-please.git", "https://git:{}@git.front.kjuulh.io/kjuulh/dagger-components.git",
std::env::var("CUDDLE_PLEASE_TOKEN")? std::env::var("CUDDLE_PLEASE_TOKEN")?
), ),
]) ])
@ -311,7 +165,7 @@ mod please_release {
"release", "release",
"--engine=gitea", "--engine=gitea",
"--owner=kjuulh", "--owner=kjuulh",
"--repo=cuddle-please", "--repo=dagger-components",
"--branch=main", "--branch=main",
"--api-url=https://git.front.kjuulh.io", "--api-url=https://git.front.kjuulh.io",
"--log-level=debug", "--log-level=debug",
@ -331,225 +185,6 @@ mod please_release {
} }
} }
mod docs {
use std::sync::Arc;
use dagger_sdk::Container;
use crate::GlobalArgs;
pub fn get_docs_src(client: Arc<dagger_sdk::Query>) -> eyre::Result<dagger_sdk::Directory> {
let docs_content = client.host().directory_opts(
".",
dagger_sdk::HostDirectoryOpts {
exclude: None,
include: Some(vec!["mkdocs.yml", "docs/"]),
},
);
Ok(docs_content)
}
pub async fn execute(
client: Arc<dagger_sdk::Query>,
args: &GlobalArgs,
_platform: &Option<String>,
) -> eyre::Result<Container> {
let mkdocs_container = client.container().from(
args.mkdocs_image
.as_ref()
.expect("--mkdocs-image to be set"),
);
let built_mkdocs_container = mkdocs_container
.with_directory("/docs", get_docs_src(client.clone())?.id().await?)
.with_exec(vec!["build"]);
let site_output = built_mkdocs_container.directory("/docs/site").id().await?;
let caddy_file = client.host().directory("templates").file("Caddyfile");
let dep_image = client
.container()
.from(args.caddy_image.as_ref().expect("--caddy-image to be set"))
.with_directory("/usr/share/caddy", site_output)
.with_file("/etc/caddy/Caddyfile", caddy_file.id().await?)
.with_exec(vec!["echo", "caddy"]);
Ok(dep_image)
}
pub async fn publish(
client: Arc<dagger_sdk::Query>,
args: &GlobalArgs,
containers: &Vec<dagger_sdk::Container>,
) -> eyre::Result<()> {
let container_ids =
futures::future::join_all(containers.iter().map(|c| c.id()).collect::<Vec<_>>()).await;
let container_ids = container_ids
.into_iter()
.collect::<eyre::Result<Vec<dagger_sdk::ContainerId>>>()?;
client
.container()
.publish_opts(
format!(
"{}:{}",
args.docs_image.as_ref().expect("--docs-image to be set"),
args.docs_image_tag
.as_ref()
.expect("--docs-image-tag to be set")
),
dagger_sdk::ContainerPublishOpts {
platform_variants: Some(container_ids),
},
)
.await?;
Ok(())
}
}
mod build {
use std::sync::Arc;
use dagger_sdk::Container;
use crate::{base_rust_image, get_base_debian_image, GlobalArgs};
pub async fn build_and_deploy(
client: Arc<dagger_sdk::Query>,
args: &GlobalArgs,
bin_name: &String,
image: &String,
tag: &String,
) -> eyre::Result<()> {
// let containers = vec!["linux/amd64", "linux/arm64"];
let base_image = get_base_debian_image(
client.clone(),
&args.clone(),
Some("linux/amd64".to_string()),
)
.await?;
let container = base_rust_image(
client.clone(),
args,
&Some("linux/amd64".to_string()),
&bin_name.clone(),
&"release".into(),
)
.await?;
let build_image = execute(
client.clone(),
args,
&container,
&base_image,
bin_name,
&Some("linux/amd64".to_string()),
)
.await?;
let build_id = build_image.id().await?;
let _container = client
.clone()
.container()
.publish_opts(
format!("{image}:{tag}"),
dagger_sdk::ContainerPublishOpts {
platform_variants: Some(vec![build_id]),
},
)
.await?;
Ok(())
}
pub async fn build(
client: Arc<dagger_sdk::Query>,
args: &GlobalArgs,
bin_name: &String,
_image: &String,
_tag: &String,
) -> eyre::Result<()> {
// let containers = vec!["linux/amd64", "linux/arm64"];
let base_image = get_base_debian_image(
client.clone(),
&args.clone(),
Some("linux/amd64".to_string()),
)
.await?;
let container = base_rust_image(
client.clone(),
args,
&Some("linux/amd64".to_string()),
&bin_name.clone(),
&"release".into(),
)
.await?;
let build_image = execute(
client.clone(),
args,
&container,
&base_image,
bin_name,
&Some("linux/amd64".to_string()),
)
.await?;
build_image.exit_code().await?;
Ok(())
}
pub async fn execute(
_client: Arc<dagger_sdk::Query>,
_args: &GlobalArgs,
container: &dagger_sdk::Container,
base_image: &dagger_sdk::Container,
bin_name: &String,
platform: &Option<String>,
) -> eyre::Result<Container> {
let rust_target = match platform
.clone()
.unwrap_or("linux/amd64".to_string())
.as_str()
{
"linux/amd64" => "x86_64-unknown-linux-gnu",
"linux/arm64" => "aarch64-unknown-linux-gnu",
_ => eyre::bail!("architecture not supported"),
};
let build_image = container.with_exec(vec![
"cargo",
"build",
"--target",
rust_target,
"--release",
"-p",
bin_name,
]);
let final_image = base_image
.with_file(
format!("/usr/local/bin/{}", &bin_name),
build_image
.file(format!("target/{}/release/{}", rust_target, &bin_name))
.id()
.await?,
)
.with_exec(vec![bin_name, "--help"]);
let output = final_image.stdout().await?;
println!("{output}");
//.with_entrypoint(vec![&bin_name, "--log-level=debug"]);
Ok(final_image)
}
}
mod test { mod test {
use std::sync::Arc; use std::sync::Arc;
@ -566,45 +201,17 @@ mod test {
.with_exec(vec!["apt", "install", "-y", "git"]) .with_exec(vec!["apt", "install", "-y", "git"])
.with_exec(vec!["cargo", "test"]); .with_exec(vec!["cargo", "test"]);
let please_out = test_image.stdout().await?;
println!("{please_out}");
let please_out = test_image.stderr().await?;
println!("{please_out}");
test_image.exit_code().await?; test_image.exit_code().await?;
Ok(()) Ok(())
} }
} }
pub async fn get_base_debian_image(
client: Arc<dagger_sdk::Query>,
args: &GlobalArgs,
platform: Option<String>,
) -> eyre::Result<dagger_sdk::Container> {
let default_platform = client.default_platform().await?;
let platform = platform.map(Platform).unwrap_or(default_platform);
let image = client
.container_opts(QueryContainerOpts {
id: None,
platform: Some(platform),
})
.from(
args.production_image
.clone()
.unwrap_or("debian:bullseye".to_string()),
);
let base_image = image.with_exec(vec!["apt", "update"]).with_exec(vec![
"apt",
"install",
"-y",
"libssl-dev",
"pkg-config",
"openssl",
"git",
"jq",
]);
Ok(base_image)
}
pub fn get_src( pub fn get_src(
client: Arc<dagger_sdk::Query>, client: Arc<dagger_sdk::Query>,
args: &GlobalArgs, args: &GlobalArgs,
@ -695,7 +302,6 @@ pub async fn base_rust_image(
client: Arc<dagger_sdk::Query>, client: Arc<dagger_sdk::Query>,
args: &GlobalArgs, args: &GlobalArgs,
platform: &Option<String>, platform: &Option<String>,
bin_name: &String,
profile: &String, profile: &String,
) -> eyre::Result<dagger_sdk::Container> { ) -> eyre::Result<dagger_sdk::Container> {
let dep_src = get_rust_dep_src(client.clone(), args).await?; let dep_src = get_rust_dep_src(client.clone(), args).await?;
@ -725,7 +331,7 @@ pub async fn base_rust_image(
.with_exec(vec!["apt", "install", "-y", "jq"]); .with_exec(vec!["apt", "install", "-y", "jq"]);
let target_cache = client.cache_volume(format!("rust_target_{}", profile)); let target_cache = client.cache_volume(format!("rust_target_{}", profile));
let mut build_options = vec!["cargo", "build", "--target", rust_target, "-p", bin_name]; let mut build_options = vec!["cargo", "build", "--target", rust_target];
if profile == "release" { if profile == "release" {
build_options.push("--release"); build_options.push("--release");

View File

@ -13,3 +13,12 @@ please:
branch: main branch: main
settings: settings:
api_url: https://git.front.kjuulh.io api_url: https://git.front.kjuulh.io
scripts:
"ci:main":
type: shell
"ci:pr":
type: shell
"ci:release":
type: shell

12
scripts/ci:main.sh Executable file
View File

@ -0,0 +1,12 @@
#!/usr/bin/env bash
set -e
CMD_PREFIX="cargo run -p ci --"
if [[ -n "$CI_PREFIX" ]]; then
CMD_PREFIX="$CI_PREFIX"
fi
$CMD_PREFIX main

17
scripts/ci:pr.sh Executable file
View File

@ -0,0 +1,17 @@
#!/usr/bin/env bash
set -e
CMD_PREFIX="cargo run -p ci --"
if [[ -n "$CI_PREFIX" ]]; then
CMD_PREFIX="$CI_PREFIX"
fi
$CMD_PREFIX pull-request \
--mkdocs-image "$MKDOCS_IMAGE" \
--caddy-image "$CADDY_IMAGE" \
--image "$REGISTRY/$SERVICE" \
--tag "main-$(date +%s)" \
--bin-name "$SERVICE"