feat: add ci

This commit is contained in:
Kasper Juul Hermansen 2023-03-07 23:20:06 +01:00
parent f56f8c6818
commit 2053220d01
Signed by: kjuulh
GPG Key ID: 57B6E1465221F912
10 changed files with 1092 additions and 71 deletions

855
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
[workspace]
members = [".", "crates/services", "crates/domain", "crates/biteme"]
members = [".", "crates/services", "crates/domain", "crates/biteme", "ci"]
[workspace.dependencies]
domain = { path = "crates/domain" }
@ -35,6 +35,8 @@ tower = { version = "0.4.13", optional = true }
tower-http = { version = "0.3.4", features = ["fs"], optional = true }
tokio = { version = "1", features = ["time"], optional = true }
wasm-bindgen = "0.2"
tracing-subscriber = { version = "0.3.16", optional = true }
tracing = { version = "0.1.37", features = ["log"], optional = true }
serde = { workspace = true }
chrono = { workspace = true }
@ -55,6 +57,8 @@ ssr = [
"leptos_router/ssr",
"dep:leptos_axum",
"dep:services",
"dep:tracing-subscriber",
"dep:tracing",
]
[package.metadata.leptos]

View File

@ -30,3 +30,8 @@ command = "cargo"
args = ["install", "--path", "crates/biteme"]
workspace = false
install_crate = "cargo-all-features"
[tasks.ci]
command = "cargo"
args = ["run", "-p", "ci"]
workspace = false

View File

@ -7,7 +7,7 @@ description: |
God gammeldags oksesteg med en intens og fyldig brun sauce. Gammeldags oksesteg
er rigtig simremad som gør de fleste glade. Så server en gammeldags oksesteg for
din gæster... både de unge og de gamle.
time: 2023-03-06
time: 2025-03-06
---
Some article

13
ci/Cargo.toml Normal file
View File

@ -0,0 +1,13 @@
[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.15"
eyre = "0.6.8"
color-eyre = "0.6.2"
tokio = { version = "1.26.0", features = ["full"] }
chrono.workspace = true

147
ci/src/main.rs Normal file
View File

@ -0,0 +1,147 @@
#[tokio::main]
async fn main() -> eyre::Result<()> {
let rust_image = "rustlang/rust:nightly";
let client = dagger_sdk::connect().await?;
let workdir = client.host().directory_opts(
".",
dagger_sdk::HostDirectoryOpts {
exclude: Some(vec!["target/", ".git/"]),
include: None,
},
);
let minio_url = "https://github.com/mozilla/sccache/releases/download/v0.3.3/sccache-v0.3.3-x86_64-unknown-linux-musl.tar.gz";
// Main container
let rust_base = client
.container()
.from(rust_image)
.with_exec(vec!["apt-get", "update"])
.with_exec(vec!["apt-get", "install", "--yes", "libpq-dev", "wget"])
.with_exec(vec!["wget", minio_url])
.with_exec(vec![
"tar",
"xzf",
"sccache-v0.3.3-x86_64-unknown-linux-musl.tar.gz",
])
.with_exec(vec![
"mv",
"sccache-v0.3.3-x86_64-unknown-linux-musl/sccache",
"/usr/local/bin/sccache",
])
.with_exec(vec!["chmod", "+x", "/usr/local/bin/sccache"])
.with_env_variable("RUSTC_WRAPPER", "/usr/local/bin/sccache")
.with_env_variable(
"AWS_ACCESS_KEY_ID",
std::env::var("AWS_ACCESS_KEY_ID").unwrap_or("".into()),
)
.with_env_variable(
"AWS_SECRET_ACCESS_KEY",
std::env::var("AWS_SECRET_ACCESS_KEY").unwrap_or("".into()),
)
.with_env_variable("SCCACHE_BUCKET", "sccache")
.with_env_variable("SCCACHE_REGION", "auto")
.with_env_variable("SCCACHE_ENDPOINT", "https://api-minio.front.kjuulh.io")
.with_exec(vec!["cargo", "install", "cargo-chef"])
.with_exec(vec!["cargo", "install", "cargo-leptos"])
.with_workdir("/app");
let exit_code = rust_base.exit_code().await?;
if exit_code != 0 {
eyre::bail!("could not build base");
}
let rust_prepare = rust_base
.with_mounted_directory(".", workdir.id().await?)
.with_exec(vec![
"cargo",
"chef",
"prepare",
"--recipe-path",
"recipe.json",
]);
let exit_code = rust_prepare.exit_code().await?;
if exit_code != 0 {
eyre::bail!("could not build prepare");
}
let rust_cacher = rust_base
.with_exec(vec!["apt", "update"])
.with_exec(vec![
"apt",
"install",
"pkg-config",
"openssl",
"libssl-dev",
"-y",
])
.with_exec(vec!["rustup", "target", "add", "wasm32-unknown-unknown"])
.with_file(
"/recipe.json",
rust_prepare.file("./recipe.json").id().await?,
)
.with_mounted_directory(".", workdir.id().await?)
.with_exec(vec![
"cargo",
"chef",
"cook",
"--release",
"--recipe-path",
"/recipe.json",
]);
let exit_code = rust_cacher.exit_code().await?;
if exit_code != 0 {
eyre::bail!("could not build cacher");
}
let rust_builder = rust_base
.with_exec(vec![
"curl",
"-sL",
"https://deb.nodesource.com/setup_12.x",
"-o",
"/node_12.txt",
])
.with_exec(vec!["chmod", "+x", "/node_12.txt"])
.with_exec(vec!["bash", "-c", "/node_12.txt"])
.with_exec(vec!["apt-get", "update"])
.with_exec(vec!["apt-get", "install", "nodejs"])
.with_mounted_directory(".", workdir.id().await?)
.with_directory(
"/app/target",
rust_cacher.directory("/app/target").id().await?,
)
.with_directory(
"/usr/local/cargo",
rust_cacher.directory("/usr/local/cargo").id().await?,
)
.with_exec(vec!["rustup", "target", "add", "wasm32-unknown-unknown"])
.with_exec(vec!["npm", "install", "-g", "sass"])
.with_env_variable("LEPTOS_BROWSERQUERY", "defaults")
.with_exec(vec!["cargo", "leptos", "build", "--release"]);
let exit_code = rust_builder.exit_code().await?;
if exit_code != 0 {
eyre::bail!("could not build builder");
}
let tag = chrono::Utc::now().timestamp();
let prod_image = "gcr.io/distroless/cc-debian11";
let prod = client
.container()
.from(prod_image)
.with_workdir("/app")
.with_directory("/app", rust_builder.directory("/app/target").id().await?)
.with_env_variable("LEPTOS_SITE_ADDRESS", "0.0.0.0:3000")
.with_entrypoint(vec!["./server/release/ssr_mode_axum"]);
prod.publish(format!("docker.io/kasperhermansen/bitebuds:{tag}"))
.await?;
Ok(())
}

View File

@ -9,6 +9,7 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
gitevents_sdk = { git = "https://github.com/kjuulh/gitevents.git", branch = "main" }
cached = "0.42.0"
chrono = { version = "0.4.23", features = ["serde"] }
domain = { path = "../domain" }
@ -18,3 +19,4 @@ serde_json = "1.0.94"
serde_yaml = "0.9.19"
tokio = { version = "1.26.0", features = ["full"] }
uuid = { version = "1.3.0", features = ["v4", "serde"] }
tracing = { version = "0.1.37", features = ["log"] }

View File

@ -1,15 +1,10 @@
use cached::proc_macro::once;
use domain::{Event, Image, Metadata};
use gitevents_sdk::events::EventResponse;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use std::sync::Arc;
use cached::proc_macro::{cached, once};
use domain::{Event, Image, Metadata};
use serde::{Deserialize, Serialize};
pub struct EventStore {
pub path: PathBuf,
events: Arc<tokio::sync::RwLock<Vec<Event>>>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct RawImage {
pub url: String,
@ -81,28 +76,89 @@ impl From<RawImage> for Image {
}
}
struct InnerEventStore {
url: Option<String>,
pub path: PathBuf,
events: Arc<tokio::sync::RwLock<Vec<Event>>>,
}
#[derive(Clone)]
pub struct EventStore {
inner: Arc<InnerEventStore>,
}
impl EventStore {
pub fn new(path: PathBuf) -> Self {
let article_repo_url = std::env::var("BITE_ARTICLE_REPO_URL")
.map(|a| (a != "").then(|| a))
.unwrap_or(None);
Self {
inner: Arc::new(InnerEventStore {
url: article_repo_url,
path,
events: Default::default(),
}),
}
}
pub async fn bootstrap(&self) -> eyre::Result<()> {
tracing::info!("boostrapping event_store");
//let mut event_path = self.inner.path.clone();
//event_path.push("events");
//let events = fetch_events(event_path.clone()).await?;
//let mut e = self.inner.events.write().await;
//*e = events;
if let Some(repo_url) = self.inner.url.clone() {
tracing::info!(repo_url = repo_url, "subscribing to repo");
let inner = self.inner.clone();
tokio::task::spawn(async move {
gitevents_sdk::builder::Builder::new()
.set_generic_git_url(repo_url)
.set_scheduler_opts(&gitevents_sdk::cron::SchedulerOpts {
duration: std::time::Duration::from_secs(30),
})
.action(move |req| {
let inner = inner.clone();
async move {
tracing::info!("updating articles");
let mut event_path = req.git.path.clone();
event_path.push("articles/events");
tracing::debug!(
path = event_path.display().to_string(),
"reading from"
);
let events = fetch_events(event_path).await.unwrap();
let mut e = inner.events.write().await;
*e = events.clone();
Ok(EventResponse {})
}
})
.execute()
.await
.unwrap();
});
}
Ok(())
}
pub async fn get_upcoming_events(&self) -> eyre::Result<Vec<Event>> {
let mut event_path = self.path.clone();
event_path.push("events");
let events = fetch_events(event_path).await?;
let mut e = self.events.write().await;
*e = events.clone();
let events = self.inner.events.read().await.clone();
Ok(events)
}
pub async fn get_event(&self, event_id: uuid::Uuid) -> eyre::Result<Option<Event>> {
let events = self.events.read().await;
let events = self.inner.events.read().await;
let event = events.iter().find(|e| e.id == event_id);
@ -110,7 +166,6 @@ impl EventStore {
}
}
#[once(time = 60, result = true, sync_writes = true)]
pub async fn fetch_events(event_path: PathBuf) -> eyre::Result<Vec<Event>> {
let mut dir = tokio::fs::read_dir(event_path).await?;
@ -140,8 +195,11 @@ pub async fn fetch_events(event_path: PathBuf) -> eyre::Result<Vec<Event>> {
impl Default for EventStore {
fn default() -> Self {
Self {
inner: Arc::new(InnerEventStore {
url: Default::default(),
path: PathBuf::from("articles"),
events: Default::default(),
}),
}
}
}

View File

@ -1,3 +1,5 @@
use std::path::PathBuf;
use cfg_if::cfg_if;
use leptos::*;
use serde::{Deserialize, Serialize};
@ -10,11 +12,9 @@ cfg_if! {
use lazy_static::lazy_static;
lazy_static! {
static ref EVENTSTORE: EventStore = EventStore::default();
static ref EVENTSTORE: EventStore = EventStore::new(PathBuf::from("articles"));
}
async fn get_upcoming_events_fn() -> Result<UpcomingEventsOverview, ServerFnError> {
let current_time = chrono::Utc::now();
let mut events: Vec<EventOverview> = EVENTSTORE
.get_upcoming_events()
.await
@ -24,7 +24,7 @@ cfg_if! {
.map(|data| data.clone().into())
.collect();
events.sort_by(|a, b| a.time.cmp(&b.time));
events.sort_by(|a, b| a.time.cmp(&b.time)) ;
Ok(UpcomingEventsOverview { events })
}
@ -33,9 +33,12 @@ cfg_if! {
.get_event(event_id)
.await
.map_err(|e| ServerFnError::ServerError(e.to_string()))?;
Ok(event)
}
pub async fn boostrap() -> Result<(), ServerFnError> {
EVENTSTORE.bootstrap().await.map_err(|e| ServerFnError::ServerError(e.to_string()))
}
}
}

View File

@ -1,16 +1,24 @@
#[cfg(feature = "ssr")]
#[tokio::main]
async fn main() {
use axum::{
extract::{Extension, Path},
routing::{get, post},
Router,
};
use axum::{extract::Extension, routing::post, Router};
use leptos::*;
use leptos_axum::{generate_route_list, LeptosRoutes};
use ssr_modes_axum::app::*;
use ssr_modes_axum::fallback::file_and_error_handler;
use std::sync::Arc;
use tracing::metadata::LevelFilter;
std::env::set_var(
"BITE_ARTICLE_REPO_URL",
"git@git.front.kjuulh.io:kjuulh/articles.git",
);
tracing_subscriber::fmt()
.with_max_level(LevelFilter::TRACE)
.init();
ssr_modes_axum::api::events::boostrap().await.unwrap();
let conf = get_configuration(None).await.unwrap();
let addr = conf.leptos_options.site_addr;