Compare commits

..

3 Commits

Author SHA1 Message Date
e39be808a7 feat: add github action 2023-03-07 23:35:43 +01:00
b60410276f feat: absolute path run 2023-03-07 23:30:56 +01:00
2053220d01 feat: add ci 2023-03-07 23:20:06 +01:00
11 changed files with 1129 additions and 71 deletions

37
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,37 @@
name: release
on:
pull_request:
push:
branches:
- "main"
env:
CARGO_TERM_COLOR: always
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
_EXPERIMENTAL_DAGGER_CACHE_CONFIG: type=gha;mode=max
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Update rust toolchain
run: rustup update stable && rustup default stable
- name: Set up cargo cache
uses: actions/cache@v3
continue-on-error: false
with:
path: "~/.cargo/bin/\n~/.cargo/registry/index/\n~/.cargo/registry/cache/\n~/.cargo/git/db/\ntarget/"
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: ${{ runner.os }}-cargo
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2.0.0
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Run dagger [CI]
run: cargo run -p ci

855
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -30,3 +30,8 @@ command = "cargo"
args = ["install", "--path", "crates/biteme"] args = ["install", "--path", "crates/biteme"]
workspace = false workspace = false
install_crate = "cargo-all-features" 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 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 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. din gæster... både de unge og de gamle.
time: 2023-03-06 time: 2025-03-06
--- ---
Some article 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!["/app/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 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
gitevents_sdk = { git = "https://github.com/kjuulh/gitevents.git", branch = "main" }
cached = "0.42.0" cached = "0.42.0"
chrono = { version = "0.4.23", features = ["serde"] } chrono = { version = "0.4.23", features = ["serde"] }
domain = { path = "../domain" } domain = { path = "../domain" }
@@ -18,3 +19,4 @@ serde_json = "1.0.94"
serde_yaml = "0.9.19" serde_yaml = "0.9.19"
tokio = { version = "1.26.0", features = ["full"] } tokio = { version = "1.26.0", features = ["full"] }
uuid = { version = "1.3.0", features = ["v4", "serde"] } 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::path::PathBuf;
use std::sync::Arc; 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)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct RawImage { pub struct RawImage {
pub url: String, 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 { impl EventStore {
pub fn new(path: PathBuf) -> Self { 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 { Self {
inner: Arc::new(InnerEventStore {
url: article_repo_url,
path, path,
events: Default::default(), 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>> { pub async fn get_upcoming_events(&self) -> eyre::Result<Vec<Event>> {
let mut event_path = self.path.clone(); let events = self.inner.events.read().await.clone();
event_path.push("events");
let events = fetch_events(event_path).await?;
let mut e = self.events.write().await;
*e = events.clone();
Ok(events) Ok(events)
} }
pub async fn get_event(&self, event_id: uuid::Uuid) -> eyre::Result<Option<Event>> { 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); 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>> { pub async fn fetch_events(event_path: PathBuf) -> eyre::Result<Vec<Event>> {
let mut dir = tokio::fs::read_dir(event_path).await?; 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 { impl Default for EventStore {
fn default() -> Self { fn default() -> Self {
Self { Self {
inner: Arc::new(InnerEventStore {
url: Default::default(),
path: PathBuf::from("articles"), path: PathBuf::from("articles"),
events: Default::default(), events: Default::default(),
}),
} }
} }
} }

View File

@@ -1,3 +1,5 @@
use std::path::PathBuf;
use cfg_if::cfg_if; use cfg_if::cfg_if;
use leptos::*; use leptos::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@@ -10,11 +12,9 @@ cfg_if! {
use lazy_static::lazy_static; use lazy_static::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> { async fn get_upcoming_events_fn() -> Result<UpcomingEventsOverview, ServerFnError> {
let current_time = chrono::Utc::now();
let mut events: Vec<EventOverview> = EVENTSTORE let mut events: Vec<EventOverview> = EVENTSTORE
.get_upcoming_events() .get_upcoming_events()
.await .await
@@ -33,9 +33,12 @@ cfg_if! {
.get_event(event_id) .get_event(event_id)
.await .await
.map_err(|e| ServerFnError::ServerError(e.to_string()))?; .map_err(|e| ServerFnError::ServerError(e.to_string()))?;
Ok(event) 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")] #[cfg(feature = "ssr")]
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
use axum::{ use axum::{extract::Extension, routing::post, Router};
extract::{Extension, Path},
routing::{get, post},
Router,
};
use leptos::*; use leptos::*;
use leptos_axum::{generate_route_list, LeptosRoutes}; use leptos_axum::{generate_route_list, LeptosRoutes};
use ssr_modes_axum::app::*; use ssr_modes_axum::app::*;
use ssr_modes_axum::fallback::file_and_error_handler; use ssr_modes_axum::fallback::file_and_error_handler;
use std::sync::Arc; 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 conf = get_configuration(None).await.unwrap();
let addr = conf.leptos_options.site_addr; let addr = conf.leptos_options.site_addr;