Compare commits
17 Commits
6266d97fc1
...
renovate/a
| Author | SHA1 | Date | |
|---|---|---|---|
| ed2d4e989e | |||
|
9086f7833d
|
|||
|
cc5b165822
|
|||
|
0cf84ffe0e
|
|||
| 61e98064f6 | |||
| f35fbe21c2 | |||
| 47dfec5e26 | |||
| e3056382d3 | |||
| a24a726a9b | |||
| ccfcd42d1e | |||
| 592afe4914 | |||
| bf39c5f42f | |||
| f575351335 | |||
| b1a13dad9b | |||
| 97986b1bef | |||
| 3274120012 | |||
| ec9bc7eed8 |
@@ -70,6 +70,9 @@ steps:
|
||||
CUDDLE_SECRETS_PROVIDER: 1password
|
||||
CUDDLE_ONE_PASSWORD_DOT_ENV: ".env.ci"
|
||||
CUDDLE_SSH_AGENT: "true"
|
||||
GIT_USERNAME: "kjuulh"
|
||||
GIT_PASSWORD:
|
||||
from_secret: git_password
|
||||
CI_PREFIX: "/mnt/ci/ci"
|
||||
CUDDLE_PLEASE_TOKEN:
|
||||
from_secret: cuddle_please_token
|
||||
@@ -123,8 +126,11 @@ steps:
|
||||
CUDDLE_SECRETS_PROVIDER: 1password
|
||||
CUDDLE_ONE_PASSWORD_DOT_ENV: ".env.ci"
|
||||
CUDDLE_SSH_AGENT: "true"
|
||||
GIT_USERNAME: "kjuulh"
|
||||
GIT_PASSWORD:
|
||||
from_secret: git_password
|
||||
SSH_KEY:
|
||||
from_secret: gitea_id_ed25519
|
||||
CI_PREFIX: "/mnt/ci/ci"
|
||||
DOCKER_HOST: "tcp://192.168.1.155:2376"
|
||||
CUDDLE_PLEASE_TOKEN:
|
||||
|
||||
2574
Cargo.lock
generated
Normal file
2574
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
3
Cargo.toml
Normal file
3
Cargo.toml
Normal file
@@ -0,0 +1,3 @@
|
||||
[workspace]
|
||||
members = ["ci/"]
|
||||
resolver = "2"
|
||||
84
README.md
Normal file
84
README.md
Normal file
@@ -0,0 +1,84 @@
|
||||
# Kasper Hermansen's Blog
|
||||
|
||||
A personal blog built with Zola (static site generator) and deployed using containerized CI/CD with Dagger.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- **mise** - Development environment manager
|
||||
- **Rust** - For running Dagger CI
|
||||
- **Docker** - For containerization
|
||||
|
||||
## Development
|
||||
|
||||
### Quick Start
|
||||
|
||||
```bash
|
||||
# Install dependencies and start dev server
|
||||
mise run dev
|
||||
```
|
||||
|
||||
This will:
|
||||
- Start Zola dev server on http://localhost:8000
|
||||
- Watch and compile Tailwind CSS changes
|
||||
- Show posts in progress
|
||||
|
||||
### Available Commands
|
||||
|
||||
```bash
|
||||
mise run dev # Start development server with live reload
|
||||
mise run ci:pr # Run CI build for pull requests
|
||||
mise run ci:main # Run full CI pipeline (build + deploy)
|
||||
```
|
||||
|
||||
## Deployment
|
||||
|
||||
The blog uses a fully automated CI/CD pipeline built with Dagger (Rust SDK).
|
||||
|
||||
### Manual Deployment
|
||||
|
||||
```bash
|
||||
# Build and deploy to production
|
||||
(cd ci && cargo build)
|
||||
./ci/target/debug/ci main
|
||||
```
|
||||
|
||||
This will:
|
||||
1. Compile Tailwind CSS styles
|
||||
2. Build static site with Zola
|
||||
3. Package with Caddy web server
|
||||
4. Push Docker image to registry
|
||||
5. Update deployment configuration in git
|
||||
|
||||
### Automated Deployment
|
||||
|
||||
Pushing to `main` branch triggers automatic deployment via CI.
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
├── content/posts/ # Blog posts (Markdown with YAML frontmatter)
|
||||
├── templates/ # Zola templates (Tera)
|
||||
├── static/ # Static assets
|
||||
├── styles/ # Tailwind CSS source
|
||||
├── ci/ # Dagger CI/CD pipeline (Rust)
|
||||
├── deployment/ # Caddy server configuration
|
||||
├── config.toml # Zola configuration
|
||||
└── mise.toml # Development tasks
|
||||
```
|
||||
|
||||
## Adding Content
|
||||
|
||||
Create a new post in `content/posts/`:
|
||||
|
||||
```markdown
|
||||
+++
|
||||
title = "Your Post Title"
|
||||
description = "Brief description"
|
||||
date = 2025-01-01
|
||||
draft = false
|
||||
[taxonomies]
|
||||
tags = ["rust", "development"]
|
||||
+++
|
||||
|
||||
Your content here...
|
||||
```
|
||||
@@ -8,7 +8,7 @@ edition = "2021"
|
||||
[dependencies]
|
||||
chrono = "0.4.26"
|
||||
color-eyre = "0.6.2"
|
||||
dagger-sdk = "0.18.3"
|
||||
dagger-sdk = "0.19.0"
|
||||
eyre = "0.6.8"
|
||||
tokio = { version = "1.31.0", features = ["full"] }
|
||||
tokio-scoped = "0.2.0"
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
use cuddle_ci::{Context, MainAction, PullRequestAction};
|
||||
use dagger_sdk::{
|
||||
Container, HostDirectoryOptsBuilder, PortForward, QueryContainerOptsBuilder,
|
||||
ServiceUpOptsBuilder,
|
||||
};
|
||||
use dagger_sdk::{Container, HostDirectoryOptsBuilder, PortForward, ServiceUpOptsBuilder};
|
||||
|
||||
const UPDATE_DEPLOYMENT_IMAGE: &str = "docker.io/kasperhermansen/update-deployment:1690401410";
|
||||
|
||||
const ZOLA_VERSION: &str = "0.17.2-1";
|
||||
const DEBIAN_EDITION: &str = "bullseye";
|
||||
const CADDY_IMAGE: &str = "caddy:2.10.0";
|
||||
|
||||
const ZOLA_PLATFORM: &str = "x86_64";
|
||||
const ZOLA_VERSION: &str = "v0.21.0";
|
||||
|
||||
const DEBIAN_EDITION: &str = "bookworm";
|
||||
const DEBIAN_PLATFORM: &str = "amd64";
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -24,7 +25,7 @@ impl BlogComponent {
|
||||
.up_opts(
|
||||
ServiceUpOptsBuilder::default()
|
||||
.ports(vec![PortForward {
|
||||
backend: 8000,
|
||||
backend: 80,
|
||||
frontend: 8000,
|
||||
protocol: dagger_sdk::NetworkProtocol::Tcp,
|
||||
}])
|
||||
@@ -64,7 +65,7 @@ async fn build(client: dagger_sdk::Query) -> eyre::Result<(i64, Container)> {
|
||||
.build()?,
|
||||
);
|
||||
|
||||
let github_zola_download: String = format!("https://github.com/barnumbirr/zola-debian/releases/download/v{ZOLA_VERSION}/zola_{ZOLA_VERSION}_{DEBIAN_PLATFORM}_{DEBIAN_EDITION}.deb");
|
||||
// let github_zola_download: String = format!("https://github.com/barnumbirr/zola-debian/releases/download/v{ZOLA_VERSION}/zola_{ZOLA_VERSION}_{DEBIAN_PLATFORM}_{DEBIAN_EDITION}.deb");
|
||||
let node_cache = client.cache_volume("node_cache");
|
||||
let debian_cache = client.cache_volume("debian_cache");
|
||||
|
||||
@@ -72,14 +73,16 @@ async fn build(client: dagger_sdk::Query) -> eyre::Result<(i64, Container)> {
|
||||
.container()
|
||||
.from("node:16")
|
||||
.with_workdir("/app")
|
||||
.with_directory(".", src.id().await?)
|
||||
.with_mounted_cache("node_modules", node_cache.id().await?)
|
||||
.with_directory(".", src.clone())
|
||||
.with_mounted_cache("node_modules", node_cache)
|
||||
.with_exec(vec!["yarn"])
|
||||
.with_exec(vec!["yarn", "compile"])
|
||||
.file("static/styles/styles.css");
|
||||
|
||||
let tag = chrono::Utc::now().timestamp();
|
||||
|
||||
let zola_download_link = format!("https://github.com/getzola/zola/releases/download/{ZOLA_VERSION}/zola-{ZOLA_VERSION}-{ZOLA_PLATFORM}-unknown-linux-gnu.tar.gz");
|
||||
|
||||
let dist_dir = client
|
||||
.container_opts(
|
||||
dagger_sdk::QueryContainerOptsBuilder::default()
|
||||
@@ -87,29 +90,32 @@ async fn build(client: dagger_sdk::Query) -> eyre::Result<(i64, Container)> {
|
||||
.build()?,
|
||||
)
|
||||
.from(format!("debian:{DEBIAN_EDITION}"))
|
||||
.with_exec(vec!["apt", "update"])
|
||||
.with_exec(vec!["apt", "install", "wget", "-y"])
|
||||
// .with_exec(vec!["apt", "update"])
|
||||
// .with_exec(vec!["apt", "install", "wget", "-y"])
|
||||
.with_workdir("/mnt")
|
||||
.with_mounted_cache("/mnt", debian_cache.id().await?)
|
||||
.with_exec(vec!["wget", &github_zola_download])
|
||||
.with_exec(vec![
|
||||
"dpkg",
|
||||
"-i",
|
||||
format!("zola_{ZOLA_VERSION}_{DEBIAN_PLATFORM}_{DEBIAN_EDITION}.deb").as_str(),
|
||||
])
|
||||
.with_file("/mnt/zola.tar.gz", client.http(zola_download_link))
|
||||
.with_exec(vec!["tar", "-xvf", "/mnt/zola.tar.gz"])
|
||||
.with_exec(vec!["mv", "/mnt/zola", "/usr/local/bin/zola"])
|
||||
// .with_mounted_cache("/mnt", debian_cache)
|
||||
// .with_exec(vec!["wget", &github_zola_download])
|
||||
// .with_exec(vec![
|
||||
// "dpkg",
|
||||
// "-i",
|
||||
// format!("zola_{ZOLA_VERSION}_{DEBIAN_PLATFORM}_{DEBIAN_EDITION}.deb").as_str(),
|
||||
// ])
|
||||
.with_workdir("/app")
|
||||
.with_directory(".", src.id().await?)
|
||||
.with_file("static/styles/styles.css", styles_file.id().await?)
|
||||
.with_directory(".", src)
|
||||
.with_file("static/styles/styles.css", styles_file)
|
||||
.with_exec(vec!["zola", "build"])
|
||||
.directory("public");
|
||||
|
||||
let caddy_file = client.host().directory("deployment").file("Caddyfile");
|
||||
|
||||
let dep_image = client
|
||||
.container_opts(QueryContainerOptsBuilder::default().build().unwrap())
|
||||
.from("caddy")
|
||||
.with_directory("/usr/share/caddy", dist_dir.id().await.unwrap())
|
||||
.with_file("/etc/caddy/Caddyfile", caddy_file.id().await.unwrap());
|
||||
.container()
|
||||
.from(CADDY_IMAGE)
|
||||
.with_directory("/srv", dist_dir)
|
||||
.with_file("/etc/caddy/Caddyfile", caddy_file);
|
||||
|
||||
Ok((tag, dep_image))
|
||||
}
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
{
|
||||
debug
|
||||
log default {
|
||||
output stdout
|
||||
level DEBUG
|
||||
}
|
||||
}
|
||||
|
||||
http://blog.kasperhermansen.com {
|
||||
root * /usr/share/caddy
|
||||
:80 {
|
||||
root * /srv
|
||||
file_server
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ dir = "./ci"
|
||||
run = "cargo build"
|
||||
|
||||
[tasks."ci:run"]
|
||||
run = "./ci/target/debug/ci"
|
||||
run = "./target/debug/ci"
|
||||
|
||||
[tasks."ci:main"]
|
||||
depends = ["ci:build"]
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
|
||||
{{ post_macros::styles() }}
|
||||
|
||||
<script defer data-domain="blog.kasperhermansen.com" src="https://plausible.front.kjuulh.io/js/script.js"></script>
|
||||
<script defer data-domain="blog.kasperhermansen.com" src="https://plausible.front.kjuulh.io/js/script.file-downloads.hash.outbound-links.pageview-props.tagged-events.js"></script>
|
||||
<script>window.plausible = window.plausible || function() { (window.plausible.q = window.plausible.q || []).push(arguments) }</script>
|
||||
|
||||
<meta name="description" content="Kasper Hermansen's blog is a hub of insights on platform engineering. ">
|
||||
<meta name="keywords" content="Kasper Hermansen, kjuulh, blog, technology, platform engineering, cloud native">
|
||||
|
||||
@@ -36,5 +36,5 @@
|
||||
{% endmacro list_posts %}
|
||||
|
||||
{% macro styles() %}
|
||||
<link rel="stylesheet" href="{{ get_url(path="styles/styles.css") | safe }}" />
|
||||
<link rel="stylesheet" href="{{ get_url(path="/styles/styles.css") | safe }}" />
|
||||
{% endmacro styles %}
|
||||
|
||||
24
yarn.lock
24
yarn.lock
@@ -67,13 +67,10 @@
|
||||
tailwindcss "^2.0.1"
|
||||
|
||||
"@tailwindcss/typography@^0.5.9":
|
||||
version "0.5.9"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/typography/-/typography-0.5.9.tgz#027e4b0674929daaf7c921c900beee80dbad93e8"
|
||||
integrity sha512-t8Sg3DyynFysV9f4JDOVISGsjazNb48AeIYQwcL+Bsq5uf4RYL75C1giZ43KISjeDGBaTN3Kxh7Xj/vRSMJUUg==
|
||||
version "0.5.19"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/typography/-/typography-0.5.19.tgz#ecb734af2569681eb40932f09f60c2848b909456"
|
||||
integrity sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg==
|
||||
dependencies:
|
||||
lodash.castarray "^4.4.0"
|
||||
lodash.isplainobject "^4.0.6"
|
||||
lodash.merge "^4.6.2"
|
||||
postcss-selector-parser "6.0.10"
|
||||
|
||||
"@types/parse-json@^4.0.0":
|
||||
@@ -600,21 +597,6 @@ lines-and-columns@^1.1.6:
|
||||
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632"
|
||||
integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
|
||||
|
||||
lodash.castarray@^4.4.0:
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.castarray/-/lodash.castarray-4.4.0.tgz#c02513515e309daddd4c24c60cfddcf5976d9115"
|
||||
integrity sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==
|
||||
|
||||
lodash.isplainobject@^4.0.6:
|
||||
version "4.0.6"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
|
||||
integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==
|
||||
|
||||
lodash.merge@^4.6.2:
|
||||
version "4.6.2"
|
||||
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
|
||||
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
|
||||
|
||||
lodash.topath@^4.5.2:
|
||||
version "4.5.2"
|
||||
resolved "https://registry.yarnpkg.com/lodash.topath/-/lodash.topath-4.5.2.tgz#3616351f3bba61994a0931989660bd03254fd009"
|
||||
|
||||
Reference in New Issue
Block a user