Skip to content

Commit 5afe3a1

Browse files
committed
Add Docker multi-bot deployment flow
1 parent 285ac78 commit 5afe3a1

16 files changed

Lines changed: 336 additions & 25 deletions

File tree

.dockerignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
.git
2+
.gitignore
3+
.circleci
4+
.github
5+
target
6+
tmp
7+
docker-compose.generated.yml
8+
docker/instances
9+
scripts
10+
*.log
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
name: Container Image
2+
3+
on:
4+
push:
5+
branches: [master]
6+
tags:
7+
- "v*"
8+
- "*.*.*"
9+
pull_request:
10+
branches: [master]
11+
workflow_dispatch:
12+
13+
concurrency:
14+
group: container-image-${{ github.ref }}
15+
cancel-in-progress: true
16+
17+
env:
18+
REGISTRY: ghcr.io
19+
20+
jobs:
21+
docker:
22+
name: Build Container
23+
runs-on: ubuntu-latest
24+
permissions:
25+
contents: read
26+
packages: write
27+
28+
steps:
29+
- name: Checkout
30+
uses: actions/checkout@v4
31+
32+
- name: Compute image name
33+
id: prep
34+
shell: bash
35+
run: echo "image=${REGISTRY}/${GITHUB_REPOSITORY_OWNER,,}/discordmusicbot" >> "$GITHUB_OUTPUT"
36+
37+
- name: Set up Docker Buildx
38+
uses: docker/setup-buildx-action@v3
39+
40+
- name: Log in to GHCR
41+
if: github.event_name != 'pull_request'
42+
uses: docker/login-action@v3
43+
with:
44+
registry: ${{ env.REGISTRY }}
45+
username: ${{ github.actor }}
46+
password: ${{ secrets.GITHUB_TOKEN }}
47+
48+
- name: Extract Docker metadata
49+
id: meta
50+
uses: docker/metadata-action@v5
51+
with:
52+
images: ${{ steps.prep.outputs.image }}
53+
tags: |
54+
type=ref,event=branch
55+
type=ref,event=pr
56+
type=sha,prefix=sha-
57+
type=semver,pattern={{version}}
58+
type=semver,pattern={{major}}.{{minor}}
59+
type=raw,value=latest,enable={{is_default_branch}}
60+
61+
- name: Build and optionally push image
62+
uses: docker/build-push-action@v6
63+
with:
64+
context: .
65+
file: ./Dockerfile
66+
platforms: linux/amd64
67+
pull: true
68+
push: ${{ github.event_name != 'pull_request' }}
69+
provenance: false
70+
sbom: false
71+
cache-from: type=gha
72+
cache-to: type=gha,mode=max
73+
tags: ${{ steps.meta.outputs.tags }}
74+
labels: ${{ steps.meta.outputs.labels }}

.github/workflows/make-release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ jobs:
3939
- name: Find built jar
4040
run: |
4141
set -e
42-
JAR="$(ls target/*-All.jar 2>/dev/null || ls target/*.jar | head -n1)"
42+
JAR="$(find target -maxdepth 1 -type f \( -name '*-All.jar' -o -name '*.jar' \) | sort | head -n1)"
4343
echo "JAR=$JAR" >> "$GITHUB_ENV"
4444
4545
- name: Rename jar

.gitignore

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
1-
/nbproject/
2-
/target/
3-
/Playlists/
4-
/test/
5-
*.json
6-
*.txt
7-
nb*.xml
1+
/nbproject/
2+
/target/
3+
/Playlists/
4+
/test/
5+
*.json
6+
*.txt
7+
tmp/
8+
docker/instances/*
9+
!docker/instances/_template/
10+
!docker/instances/_template/**
11+
docker/instances/*/bot.env
12+
docker-compose.generated.yml
13+
nb*.xml

Dockerfile

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
FROM maven:3.9.9-eclipse-temurin-17 AS build
2+
WORKDIR /src
3+
4+
COPY pom.xml ./
5+
RUN mvn -q -DskipTests dependency:go-offline
6+
7+
COPY src ./src
8+
RUN mvn -q -DskipTests package && \
9+
mkdir -p /out && \
10+
cp target/*-All.jar /out/JMusicBot.jar
11+
12+
FROM eclipse-temurin:17-jre-jammy
13+
WORKDIR /app
14+
15+
COPY --from=build /out/JMusicBot.jar /app/JMusicBot.jar
16+
COPY docker/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
17+
18+
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
19+
20+
ENV JMUSICBOT_HOME=/data
21+
VOLUME ["/data"]
22+
23+
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]

README.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,47 @@ At that point the token is persisted and future restarts should not require the
119119
4. The device flow appears again after restart
120120
The refresh token was not copied into `config.txt` correctly or Google invalidated it.
121121

122+
## Local Docker Multi-Bot Test
123+
124+
This fork can be tested locally with multiple isolated bot containers.
125+
126+
Recommended local layout:
127+
1. One container per bot token.
128+
2. One data directory per bot under `docker/instances/<bot-name>`.
129+
3. One generated compose file for all active local test bots.
130+
131+
Quick start:
132+
1. Create a bot scaffold:
133+
`powershell -ExecutionPolicy Bypass -File scripts/add-bot.ps1 devshmusic-test1`
134+
2. Copy or edit `docker/instances/devshmusic-test1/bot.env`.
135+
3. Repeat for more bots.
136+
4. Regenerate compose:
137+
`powershell -ExecutionPolicy Bypass -File scripts/render-compose.ps1`
138+
5. Start all bots:
139+
`docker compose -f docker-compose.generated.yml up -d --build`
140+
141+
Container-specific behavior:
142+
1. `BOT_TOKEN` and `BOT_OWNER` can be provided from `bot.env`.
143+
2. Relative state paths are resolved from `JMUSICBOT_HOME`, which defaults to `/data` in the container.
144+
3. Each bot keeps its own `config.txt`, `serversettings.json`, and `Playlists/` under its own instance directory.
145+
146+
## GHCR Container Publishing
147+
148+
GitHub Actions now builds the Docker image from this repository and publishes it to GHCR.
149+
150+
Published image:
151+
1. `ghcr.io/devsh-graphics-programming/discordmusicbot:latest` from `master`
152+
2. `ghcr.io/devsh-graphics-programming/discordmusicbot:sha-<commit>` for immutable rollbacks
153+
3. `ghcr.io/devsh-graphics-programming/discordmusicbot:<version>` for matching Git tags
154+
155+
Recommended deployment flow:
156+
1. Build and verify locally first.
157+
2. Push to `master` when ready.
158+
3. On the target host run `docker compose pull`.
159+
4. Then run `docker compose up -d`.
160+
161+
That lets consumers update by pulling a new image instead of rebuilding the jar on the server.
162+
122163
A cross-platform Discord music bot with a clean interface, and that is easy to set up and run yourself!
123164

124165
[![Setup](http://i.imgur.com/VvXYp5j.png)](https://jmusicbot.com/setup)

docker/docker-entrypoint.sh

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#!/bin/sh
2+
set -eu
3+
4+
DATA_DIR="${JMUSICBOT_HOME:-/data}"
5+
CONFIG_FILE="${JMUSICBOT_CONFIG_FILE:-$DATA_DIR/config.txt}"
6+
CONFIG_TEMPLATE="$DATA_DIR/config.template.txt"
7+
SETTINGS_FILE="$DATA_DIR/serversettings.json"
8+
SETTINGS_TEMPLATE="$DATA_DIR/serversettings.template.json"
9+
10+
mkdir -p "$DATA_DIR" "$DATA_DIR/Playlists"
11+
12+
if [ ! -f "$CONFIG_FILE" ] && [ -f "$CONFIG_TEMPLATE" ]; then
13+
cp "$CONFIG_TEMPLATE" "$CONFIG_FILE"
14+
fi
15+
16+
if [ ! -f "$SETTINGS_FILE" ]; then
17+
if [ -f "$SETTINGS_TEMPLATE" ]; then
18+
cp "$SETTINGS_TEMPLATE" "$SETTINGS_FILE"
19+
else
20+
printf '{}\n' > "$SETTINGS_FILE"
21+
fi
22+
fi
23+
24+
exec java ${JAVA_OPTS:-} \
25+
-Dnogui=true \
26+
-Dconfig.file="$CONFIG_FILE" \
27+
-Djmusicbot.home="$DATA_DIR" \
28+
-jar /app/JMusicBot.jar
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
BOT_TOKEN=replace_with_discord_bot_token
2+
BOT_OWNER=497820759096295429
3+
# Optional:
4+
# YT_OAUTH=false
5+
# YT_OAUTH_REFRESH_TOKEN=
6+
# JAVA_OPTS=-Xms256m -Xmx768m
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/////////////////////////////////////////////////////////
2+
// Minimal per-instance config for local Docker tests //
3+
/////////////////////////////////////////////////////////
4+
5+
token = "BOT_TOKEN_HERE"
6+
owner = 0 // OWNER ID
7+
prefix = "@mention"
8+
playlistsfolder = "Playlists"
9+
stayinchannel = false
10+
npimages = false
11+
ytoauth = false
12+
updatealerts = false
13+
loglevel = INFO

0 commit comments

Comments
 (0)