feat: Enable Docker deployment and CD
All checks were successful
Create and publish a Docker image / build-and-push-image (push) Successful in 8m1s

Closes #8, partially addresses #6
This commit is contained in:
Lucien Cartier-Tilet 2023-11-25 16:55:31 +01:00
parent d789ea7e74
commit 60a81f66a8
Signed by: phundrak
GPG Key ID: BD7789E705CB8DCA
8 changed files with 147 additions and 33 deletions

3
.dockerignore Normal file
View File

@ -0,0 +1,3 @@
/assets
/.sqlx
/.env.example

View File

@ -1,2 +1,5 @@
DISCORD_TOKEN=changeme DISCORD_TOKEN=changeme
# Only useful when developing locally. Do not change it when deploying
# with Docker.
DATABASE_URL=sqlite:p4bl0t.db DATABASE_URL=sqlite:p4bl0t.db

View File

@ -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 }}

46
Dockerfile Normal file
View File

@ -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 \
<<EOF
set -e
sqlx database create
sqlx migrate run
cargo build --locked --release
cargo install --path .
EOF
FROM debian:bullseye-slim AS final
RUN apt-get update && apt-get install -qqy ca-certificates
ARG UID=10001
RUN adduser \
--disabled-password \
--gecos "" \
--home "/nonexistent" \
--shell "/sbin/nologin" \
--no-create-home \
--uid "${UID}" \
appuser
USER appuser
ENV DATABASE_URL=sqlite:/var/p4bl0t.db
ENV DISCORD_TOKEN=changeme
COPY --from=build /usr/local/cargo/bin/p4bl0t /bin
COPY --chown=appuser --from=build /var/p4bl0t.db /var/p4bl0t.db
CMD [ "p4bl0t" ]

8
docker-compose.yml Normal file
View File

@ -0,0 +1,8 @@
version: '3'
services:
p4bl0t:
env_file: .env
build:
context: .
target: final

View File

@ -4,7 +4,7 @@ use std::env;
use poise::serenity_prelude::{ChannelId, GuildId}; use poise::serenity_prelude::{ChannelId, GuildId};
use sqlx::SqlitePool; use sqlx::SqlitePool;
use tracing::error; use tracing::{error, info};
pub type Result<T> = ::std::result::Result<T, sqlx::Error>; pub type Result<T> = ::std::result::Result<T, sqlx::Error>;
@ -24,14 +24,12 @@ impl Database {
/// ///
/// This function will return an error if the Sqlite pool fails to /// This function will return an error if the Sqlite pool fails to
/// create. /// create.
// TODO: Create the database if it doesnt exist already and run migrations
pub async fn new() -> Result<Self> { pub async fn new() -> Result<Self> {
Ok(Self( let db_url = env::var("DATABASE_URL")
SqlitePool::connect( .expect("Missing enviroment variable DATABASE_URL");
&env::var("DATABASE_URL") info!("Connecting to database located at {db_url}");
.expect("Missing enviroment variable DATABASE_URL"), Ok(Self(SqlitePool::connect(&db_url).await?))
)
.await?,
))
} }
/// Return from database all channels registered as loggers for a /// Return from database all channels registered as loggers for a

View File

@ -1,9 +1,10 @@
mod commands; mod commands;
pub mod error;
mod events; mod events;
pub mod utils; pub mod utils;
pub mod error;
use poise::FrameworkBuilder; use poise::FrameworkBuilder;
use tracing::info;
use utils::serenity; use utils::serenity;
use commands::logging; use commands::logging;
@ -19,6 +20,9 @@ pub type Result = ::std::result::Result<(), Error>;
/// ///
/// Panics if the environment `DISCORD_TOKEN` is unavailable. /// Panics if the environment `DISCORD_TOKEN` is unavailable.
pub fn make_bot() -> FrameworkBuilder<BotData, Error> { pub fn make_bot() -> FrameworkBuilder<BotData, Error> {
match std::env::var("DISCORD_TOKEN") {
Ok(token) => {
info!("Launching bot with token {token}");
poise::Framework::builder() poise::Framework::builder()
.options(poise::FrameworkOptions { .options(poise::FrameworkOptions {
commands: vec![logging()], commands: vec![logging()],
@ -27,7 +31,7 @@ pub fn make_bot() -> FrameworkBuilder<BotData, Error> {
}, },
..Default::default() ..Default::default()
}) })
.token(std::env::var("DISCORD_TOKEN").expect("missing DISCORD_TOKEN")) .token(token)
.intents(serenity::GatewayIntents::non_privileged()) .intents(serenity::GatewayIntents::non_privileged())
.setup(|ctx, _ready, framework| { .setup(|ctx, _ready, framework| {
Box::pin(async move { Box::pin(async move {
@ -40,3 +44,6 @@ pub fn make_bot() -> FrameworkBuilder<BotData, Error> {
}) })
}) })
} }
Err(_) => panic!("DISCORD_TOKEN environment variable is missing."),
}
}

View File

@ -1,17 +1,23 @@
#![warn(clippy::style, clippy::pedantic)] #![warn(clippy::style, clippy::pedantic)]
mod utils;
mod db; mod db;
mod discord; mod discord;
mod utils;
use std::error::Error; use std::error::Error;
use tracing::info;
#[tokio::main] #[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> { async fn main() -> Result<(), Box<dyn Error>> {
dotenvy::dotenv()?; println!("Setting logging up");
color_eyre::install()?;
utils::setup_logging(); 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(); let bot = discord::make_bot();
bot.run().await?; bot.run().await?;