From 60a81f66a88aa5635f439843e6a5c9af468d111e Mon Sep 17 00:00:00 2001 From: Lucien Cartier-Tilet Date: Sat, 25 Nov 2023 16:55:31 +0100 Subject: [PATCH] feat: Enable Docker deployment and CD Closes #8, partially addresses #6 --- .dockerignore | 3 ++ .env.example | 3 ++ .gitea/workflows/publish-docker.yaml | 43 ++++++++++++++++++++++++ Dockerfile | 46 ++++++++++++++++++++++++++ docker-compose.yml | 8 +++++ src/db/mod.rs | 14 ++++---- src/discord/mod.rs | 49 ++++++++++++++++------------ src/main.rs | 14 +++++--- 8 files changed, 147 insertions(+), 33 deletions(-) create mode 100644 .dockerignore create mode 100644 .gitea/workflows/publish-docker.yaml create mode 100644 Dockerfile create mode 100644 docker-compose.yml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..5c116c7 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +/assets +/.sqlx +/.env.example \ No newline at end of file diff --git a/.env.example b/.env.example index d6dd000..2a78501 100644 --- a/.env.example +++ b/.env.example @@ -1,2 +1,5 @@ DISCORD_TOKEN=changeme + +# Only useful when developing locally. Do not change it when deploying +# with Docker. DATABASE_URL=sqlite:p4bl0t.db diff --git a/.gitea/workflows/publish-docker.yaml b/.gitea/workflows/publish-docker.yaml new file mode 100644 index 0000000..6792d91 --- /dev/null +++ b/.gitea/workflows/publish-docker.yaml @@ -0,0 +1,43 @@ +name: Create and publish a Docker image + +on: + push: + branches: ['main', 'feature/docker'] + +env: + REGISTRY: labs.phundrak.com + IMAGE_NAME: ${{ gitea.repository }} + +jobs: + build-and-push-image: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install Docker + run: curl -fsSL https://get.docker.com | sh + + - name: Log in to the Container registry + uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1 + with: + registry: ${{ env.REGISTRY }} + username: ${{ gitea.actor }} + password: ${{ secrets.DOCKER_REGISTRY_TOKEN }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + - name: Build and push Docker image + uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..9b4bce4 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,46 @@ +ARG RUST_VERSION=1.73.0 +FROM rust:${RUST_VERSION}-slim-bullseye AS build + +RUN --mount=type=cache,target=/usr/local/cargo/registry \ + cargo install sqlx-cli --no-default-features --features rustls,sqlite && \ + cp /usr/local/cargo/bin/sqlx /bin/sqlx + +ENV DATABASE_URL=sqlite:/var/p4bl0t.db + +WORKDIR /app +RUN --mount=type=bind,source=src,target=src \ + --mount=type=bind,source=Cargo.toml,target=Cargo.toml \ + --mount=type=bind,source=Cargo.lock,target=Cargo.lock \ + --mount=type=bind,source=migrations,target=migrations \ + --mount=type=cache,target=/app/target/ \ + --mount=type=cache,target=/usr/local/cargo/registry \ + < = ::std::result::Result; @@ -24,14 +24,12 @@ impl Database { /// /// This function will return an error if the Sqlite pool fails to /// create. + // TODO: Create the database if it doesn’t exist already and run migrations pub async fn new() -> Result { - Ok(Self( - SqlitePool::connect( - &env::var("DATABASE_URL") - .expect("Missing enviroment variable DATABASE_URL"), - ) - .await?, - )) + let db_url = env::var("DATABASE_URL") + .expect("Missing enviroment variable DATABASE_URL"); + info!("Connecting to database located at {db_url}"); + Ok(Self(SqlitePool::connect(&db_url).await?)) } /// Return from database all channels registered as loggers for a diff --git a/src/discord/mod.rs b/src/discord/mod.rs index 3f8be4d..9e00799 100644 --- a/src/discord/mod.rs +++ b/src/discord/mod.rs @@ -1,9 +1,10 @@ mod commands; +pub mod error; mod events; pub mod utils; -pub mod error; use poise::FrameworkBuilder; +use tracing::info; use utils::serenity; use commands::logging; @@ -19,24 +20,30 @@ pub type Result = ::std::result::Result<(), Error>; /// /// Panics if the environment `DISCORD_TOKEN` is unavailable. pub fn make_bot() -> FrameworkBuilder { - poise::Framework::builder() - .options(poise::FrameworkOptions { - commands: vec![logging()], - event_handler: |ctx, event, framework, data| { - Box::pin(event_handler(ctx, event, framework, data)) - }, - ..Default::default() - }) - .token(std::env::var("DISCORD_TOKEN").expect("missing DISCORD_TOKEN")) - .intents(serenity::GatewayIntents::non_privileged()) - .setup(|ctx, _ready, framework| { - Box::pin(async move { - poise::builtins::register_globally( - ctx, - &framework.options().commands, - ) - .await?; - Ok(BotData::new().await?) - }) - }) + match std::env::var("DISCORD_TOKEN") { + Ok(token) => { + info!("Launching bot with token {token}"); + poise::Framework::builder() + .options(poise::FrameworkOptions { + commands: vec![logging()], + event_handler: |ctx, event, framework, data| { + Box::pin(event_handler(ctx, event, framework, data)) + }, + ..Default::default() + }) + .token(token) + .intents(serenity::GatewayIntents::non_privileged()) + .setup(|ctx, _ready, framework| { + Box::pin(async move { + poise::builtins::register_globally( + ctx, + &framework.options().commands, + ) + .await?; + Ok(BotData::new().await?) + }) + }) + } + Err(_) => panic!("DISCORD_TOKEN environment variable is missing."), + } } diff --git a/src/main.rs b/src/main.rs index 4d8c8eb..44892f6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,17 +1,23 @@ #![warn(clippy::style, clippy::pedantic)] -mod utils; mod db; mod discord; +mod utils; use std::error::Error; +use tracing::info; + #[tokio::main] async fn main() -> Result<(), Box> { - dotenvy::dotenv()?; - color_eyre::install()?; + println!("Setting logging up"); utils::setup_logging(); - + info!("Setting up color_eyre"); + color_eyre::install()?; + info!("Reading from dotenv"); + let _ = + dotenvy::dotenv().map_err(|_| info!("No .env file found, skipping")); + info!("Launching bot"); let bot = discord::make_bot(); bot.run().await?;