3 Commits

Author SHA1 Message Date
c85551e2cf feat: add CD workflow
All checks were successful
Create and publish a Docker image / build-and-push-image (push) Successful in 6m25s
2024-01-18 03:22:09 +01:00
d17252b338 Merge pull request 'feat: dockerize p4bl0t' (#17) from feature/dockerized into develop
Reviewed-on: #17
2024-01-18 01:51:55 +00:00
4789ffd34d feat: dockerize p4bl0t
This commit removes DATABASE_URL variable in favour of a fixed name.
The project won’t panic anymore if this variable isn’t set. This
removes the need for dotenvy.

It also adds the necessary files to dockerize the application.

Update instructions in README on how to run the project.

Add possibility to compile the project without a database available.

Closes #8
2024-01-18 02:50:40 +01:00
17 changed files with 479 additions and 319 deletions

View File

@@ -1,3 +1,32 @@
/assets
/.sqlx
/.env.example
# Include any files or directories that you don't want to be copied to your
# container here (e.g., local build artifacts, temporary files, etc.).
#
# For more help, visit the .dockerignore file reference guide at
# https://docs.docker.com/go/build-context-dockerignore/
**/.DS_Store
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/charts
**/docker-compose*
**/compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/secrets.dev.yaml
**/values.dev.yaml
/bin
/target
LICENSE
README.md

View File

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

View File

@@ -2,7 +2,7 @@ name: Create and publish a Docker image
on:
push:
branches: ['main', 'feature/docker']
branches: ['main', 'feature/cd']
env:
REGISTRY: labs.phundrak.com

1
.gitignore vendored
View File

@@ -1,4 +1,3 @@
/target
/.env
*.db
/.sqlx/

View File

@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "\nINSERT INTO guild_log_channels (guild_id, channel_id)\nVALUES ( ?1, ?2 )",
"describe": {
"columns": [],
"parameters": {
"Right": 2
},
"nullable": []
},
"hash": "5b44991d1514160fa00572e398f0577ad44f839a0470f9eeb89da8b5e77f0e03"
}

View File

@@ -0,0 +1,20 @@
{
"db_name": "SQLite",
"query": "\nSELECT channel_id\nFROM guild_log_channels\nWHERE guild_id = ?1",
"describe": {
"columns": [
{
"name": "channel_id",
"ordinal": 0,
"type_info": "Int64"
}
],
"parameters": {
"Right": 1
},
"nullable": [
false
]
},
"hash": "8444f7b7452a5ace6352aef943274f8a345a958257d896c7658b7700557959ab"
}

View File

@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "\nDELETE FROM guild_log_channels\nWHERE guild_id = ?1 AND channel_id = ?2",
"describe": {
"columns": [],
"parameters": {
"Right": 2
},
"nullable": []
},
"hash": "d6e9f422d6ae29a00658f55165018119d1e13d407266440415dfcc17a97ba00e"
}

494
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -10,12 +10,12 @@ homepage = "https://github.com/phundrak/p4bl0t"
repository = "https://github.com/phundrak/p4bl0t"
keywords = ["discord", "bot", "logging"]
publish = false
build = "build.rs"
[dependencies]
color-eyre = "0.6.2"
dotenvy = "0.15.7"
poise = { version = "0.5.7" }
sqlx = { version = "0.7.2", features = ["sqlite", "tls-rustls", "runtime-tokio-rustls"] }
sqlx = { version = "0.7.3", features = ["sqlite", "tls-rustls", "runtime-tokio-rustls"] }
tokio = { version = "1.34.0", features = ["macros", "rt-multi-thread"] }
tracing = "0.1.40"
tracing-subscriber = "0.3.18"

View File

@@ -1,30 +1,74 @@
# syntax=docker/dockerfile:1
# Comments are provided throughout this file to help you get started.
# If you need more help, visit the Dockerfile reference guide at
# https://docs.docker.com/go/dockerfile-reference/
ARG RUST_VERSION=1.73.0
FROM rust:${RUST_VERSION}-slim-bullseye AS build
ARG APP_NAME=p4bl0t
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
################################################################################
# xx is a helper for cross-compilation.
# See https://github.com/tonistiigi/xx/ for more information.
FROM --platform=$BUILDPLATFORM tonistiigi/xx:1.3.0 AS xx
################################################################################
# Create a stage for building the application.
FROM --platform=$BUILDPLATFORM rust:${RUST_VERSION}-alpine AS build
ARG APP_NAME
WORKDIR /app
# Copy cross compilation utilities from the xx stage.
COPY --from=xx / /
# Install host build dependencies.
RUN apk add --no-cache clang lld musl-dev git file
# This is the architecture youre building for, which is passed in by the builder.
# Placing it here allows the previous steps to be cached across architectures.
ARG TARGETPLATFORM
# Install cross compilation build dependencies.
RUN xx-apk add --no-cache musl-dev gcc
# Build the application.
# Leverage a cache mount to /usr/local/cargo/registry/
# for downloaded dependencies, a cache mount to /usr/local/cargo/git/db
# for git repository dependencies, and a cache mount to /app/target/ for
# compiled dependencies which will speed up subsequent builds.
# Leverage a bind mount to the src directory to avoid having to copy the
# source code into the container. Once built, copy the executable to an
# output directory before the cache mounted /app/target is unmounted.
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=build.rs,target=build.rs \
--mount=type=bind,source=.sqlx,target=.sqlx \
--mount=type=bind,source=migrations,target=migrations \
--mount=type=cache,target=/app/target/ \
--mount=type=cache,target=/usr/local/cargo/registry \
--mount=type=cache,target=/app/target/,id=rust-cache-${APP_NAME}-${TARGETPLATFORM} \
--mount=type=cache,target=/usr/local/cargo/git/db \
--mount=type=cache,target=/usr/local/cargo/registry/ \
<<EOF
set -e
sqlx database create
sqlx migrate run
cargo install --locked --path .
xx-cargo build --locked --release --target-dir ./target
cp ./target/$(xx-cargo --print-target-triple)/debug/$APP_NAME /bin/server
xx-verify /bin/server
EOF
################################################################################
# Create a new stage for running the application that contains the minimal
# runtime dependencies for the application. This often uses a different base
# image from the build stage where the necessary files are copied from the build
# stage.
#
# The example below uses the alpine image as the foundation for running the app.
# By specifying the "3.18" tag, it will use version 3.18 of alpine. If
# reproducability is important, consider using a digest
# (e.g., alpine@sha256:664888ac9cfd28068e062c991ebcff4b4c7307dc8dd4df9e728bedde5c449d91).
FROM alpine:3.19 AS final
FROM debian:bullseye-slim AS final
RUN apt-get update && apt-get install -qqy ca-certificates
# Create a non-privileged user that the app will run under.
# See https://docs.docker.com/go/dockerfile-user-best-practices/
ARG UID=10001
RUN adduser \
--disabled-password \
@@ -34,12 +78,15 @@ RUN adduser \
--no-create-home \
--uid "${UID}" \
appuser
WORKDIR /app
RUN chown -R appuser /app
USER appuser
ENV DATABASE_URL=sqlite:/var/p4bl0t.db
ENV DISCORD_TOKEN=changeme
# Copy the executable from the "build" stage.
COPY --from=build /bin/server /bin/
COPY --from=build /usr/local/cargo/bin/p4bl0t /bin
COPY --chown=appuser --from=build /var/p4bl0t.db /var/p4bl0t.db
# Expose the port that the application listens on.
# EXPOSE 8080
CMD [ "p4bl0t" ]
# What the container should run when it is started.
CMD ["/bin/server"]

View File

@@ -3,11 +3,24 @@
p4bl0t is a simple logging bot for Discord written in Rust.
## Usage
In order to run p4bl0t, head over to your [developer
### Preparation
In order to run p4bl0t, you will need a Discord token with which your
bot will authenticate. Head over to your [developer
portal](https://discord.com/developers) on Discords website, and
create a bot there. Then, copy the `.env.example` file to a `.env`
file and fill in the details.
create a bot there. You will be able to get the bots token there.
### Docker
The easiest way to run p4bl0t is using Docker. Copy
`docker-compose.example.yml` to `docker-compose.yml` and modify the
`DISCORD_TOKEN` variable.
Then, you can simply run
```sh
docker compose up # or docker-compose on some machines
```
### Building and running it yourself
Copy the `.env.example` file to a `.env` file and fill in the details.
```sh
cp .env.example .env
emacs .env
@@ -27,7 +40,6 @@ cargo install sqlx-cli
Setup your SQLite database.
```sh
export DATABASE_URL=<your-database-url> # should be the same as in the .env file
sqlx database create
sqlx migrate run
```

5
build.rs Normal file
View File

@@ -0,0 +1,5 @@
// generated by `sqlx migrate build-script`
fn main() {
// trigger recompilation when a new migration is added
println!("cargo:rerun-if-changed=migrations");
}

View File

@@ -0,0 +1,9 @@
services:
p4bl0t:
build:
context: .
target: final
environment:
DISCORD_TOKEN: changeme
volumes:
- ./p4bl0t.db:/app/p4bl0t.db

View File

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

View File

@@ -1,10 +1,8 @@
#![allow(clippy::cast_possible_wrap, clippy::cast_sign_loss)]
use std::env;
use poise::serenity_prelude::{ChannelId, GuildId};
use sqlx::SqlitePool;
use tracing::{error, info};
use sqlx::{migrate::MigrateDatabase, Sqlite, SqlitePool};
use tracing::{error, info, debug};
pub type Result<T> = ::std::result::Result<T, sqlx::Error>;
@@ -16,20 +14,23 @@ impl Database {
/// The Sqlite database should already exist and have its
/// migrations already executed.
///
/// # Panics
///
/// Panics if the environment variable `DATABASE_URL` is not set.
///
/// # Errors
///
/// This function will return an error if the Sqlite pool fails to
/// create.
// TODO: Create the database if it doesnt exist already and run migrations
pub async fn new() -> Result<Self> {
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?))
let url = "sqlite:p4bl0t.db";
if !Sqlite::database_exists(url).await? {
info!("Creating database");
Sqlite::create_database(url).await?;
info!("Database created");
}
debug!("Getting pool connection");
let pool = SqlitePool::connect(url).await?;
info!("Running migrations");
sqlx::migrate!().run(&pool).await?;
debug!("Database initialized");
Ok(Self(pool))
}
/// Return from database all channels registered as loggers for a

View File

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

View File

@@ -6,18 +6,11 @@ mod utils;
use std::error::Error;
use tracing::info;
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
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?;