3 Commits

Author SHA1 Message Date
58bd07d556 chore: remove unnecessary step
All checks were successful
Create and publish a Docker image / build-and-push-image (push) Successful in 1m29s
Default docker image in Gitea runner now ships with Docker
2023-12-19 20:09:05 +01:00
ad014dd1ae chore: simplify Dockerfile 2023-12-19 20:09:05 +01:00
60a81f66a8 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
2023-11-26 04:22:56 +01:00
17 changed files with 319 additions and 479 deletions

View File

@@ -1,32 +1,3 @@
# 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
/assets
/.sqlx
/.env.example

View File

@@ -1 +1,5 @@
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/cd']
branches: ['main', 'feature/docker']
env:
REGISTRY: labs.phundrak.com

1
.gitignore vendored
View File

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

View File

@@ -1,12 +0,0 @@
{
"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

@@ -1,20 +0,0 @@
{
"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

@@ -1,12 +0,0 @@
{
"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.3", features = ["sqlite", "tls-rustls", "runtime-tokio-rustls"] }
sqlx = { version = "0.7.2", 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,74 +1,30 @@
# 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
ARG APP_NAME=p4bl0t
FROM rust:${RUST_VERSION}-slim-bullseye AS build
################################################################################
# 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
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
################################################################################
# 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/,id=rust-cache-${APP_NAME}-${TARGETPLATFORM} \
--mount=type=cache,target=/usr/local/cargo/git/db \
--mount=type=cache,target=/usr/local/cargo/registry/ \
--mount=type=cache,target=/app/target/ \
--mount=type=cache,target=/usr/local/cargo/registry \
<<EOF
set -e
xx-cargo build --locked --release --target-dir ./target
cp ./target/$(xx-cargo --print-target-triple)/debug/$APP_NAME /bin/server
xx-verify /bin/server
sqlx database create
sqlx migrate run
cargo install --locked --path .
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
# Create a non-privileged user that the app will run under.
# See https://docs.docker.com/go/dockerfile-user-best-practices/
FROM debian:bullseye-slim AS final
RUN apt-get update && apt-get install -qqy ca-certificates
ARG UID=10001
RUN adduser \
--disabled-password \
@@ -78,15 +34,12 @@ RUN adduser \
--no-create-home \
--uid "${UID}" \
appuser
WORKDIR /app
RUN chown -R appuser /app
USER appuser
# Copy the executable from the "build" stage.
COPY --from=build /bin/server /bin/
ENV DATABASE_URL=sqlite:/var/p4bl0t.db
ENV DISCORD_TOKEN=changeme
# Expose the port that the application listens on.
# EXPOSE 8080
COPY --from=build /usr/local/cargo/bin/p4bl0t /bin
COPY --chown=appuser --from=build /var/p4bl0t.db /var/p4bl0t.db
# What the container should run when it is started.
CMD ["/bin/server"]
CMD [ "p4bl0t" ]

View File

@@ -3,24 +3,11 @@
p4bl0t is a simple logging bot for Discord written in Rust.
## Usage
### Preparation
In order to run p4bl0t, you will need a Discord token with which your
bot will authenticate. Head over to your [developer
In order to run p4bl0t, head over to your [developer
portal](https://discord.com/developers) on Discords website, and
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.
create a bot there. Then, copy the `.env.example` file to a `.env`
file and fill in the details.
```sh
cp .env.example .env
emacs .env
@@ -40,6 +27,7 @@ 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
```

View File

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

View File

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

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

@@ -1,8 +1,10 @@
#![allow(clippy::cast_possible_wrap, clippy::cast_sign_loss)]
use std::env;
use poise::serenity_prelude::{ChannelId, GuildId};
use sqlx::{migrate::MigrateDatabase, Sqlite, SqlitePool};
use tracing::{error, info, debug};
use sqlx::SqlitePool;
use tracing::{error, info};
pub type Result<T> = ::std::result::Result<T, sqlx::Error>;
@@ -14,23 +16,20 @@ 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 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))
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

View File

@@ -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,6 +20,9 @@ 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()],
@@ -27,7 +31,7 @@ pub fn make_bot() -> FrameworkBuilder<BotData, Error> {
},
..Default::default()
})
.token(std::env::var("DISCORD_TOKEN").expect("missing DISCORD_TOKEN"))
.token(token)
.intents(serenity::GatewayIntents::non_privileged())
.setup(|ctx, _ready, framework| {
Box::pin(async move {
@@ -39,4 +43,7 @@ pub fn make_bot() -> FrameworkBuilder<BotData, Error> {
Ok(BotData::new().await?)
})
})
}
Err(_) => panic!("DISCORD_TOKEN environment variable is missing."),
}
}

View File

@@ -6,11 +6,18 @@ 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?;