diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 000000000..759818b0d --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,73 @@ +name: Docker + +on: + push: + branches: + - "master" + - "feature/docker" + - "translation/chinese" + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Get repo name + id: repo_name + run: | + echo ::set-output name=name::"ghcr.io/${{ github.repository }}" | tr '[:upper:]' '[:lower:]' + + - name: Docker Setup Qemu + uses: docker/setup-qemu-action@v3 + + - name: Docker Setup Buildx + uses: docker/setup-buildx-action@v3 + + - name: Docker Login + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.CR_PAT }} + + - name: Docker Cache + uses: actions/cache@v4 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx- + + - name: Docker Metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ steps.repo_name.outputs.name }} + tags: | + type=raw,value=latest,enable=${{ github.ref == 'refs/heads/master' }} + type=raw,value=${ + startsWith(github.ref, 'refs/heads/') && contains(github.ref, '/') + && format('{0}', github.ref.split('/')[-1]) || github.ref + },enable=${{ startsWith(github.ref, 'refs/heads/') }} + type=sha + + - name: Docker Build and Push + uses: docker/build-push-action@v6 + with: + context: . + file: ./Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + platforms: | + linux/amd64, + linux/arm64 + + cache-from: | + type=local,src=/tmp/.buildx-cache + + cache-to: | + type=local,dest=/tmp/.buildx-cache diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..b3bbb57d3 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,43 @@ +# --------------------------------------------- +# Build JDA-Utilities +FROM maven:3.8.6-openjdk-11 AS jda-builder + +WORKDIR /app + +RUN git clone https://github.com/JDA-Applications/JDA-Utilities ./ \ + && git checkout c16a4b264b7dc492b35e65cb295aec9980d186b2 \ + && chmod +x ./gradlew \ + && ./gradlew clean \ + && ./gradlew build \ + && ./gradlew publishToMavenLocal + +# --------------------------------------------- +# Build JMusicBot +FROM maven:3.8.5-openjdk-17 AS builder + +WORKDIR /app +ADD . . + +COPY --from=jda-builder /root/.m2/repository /root/.m2/repository +COPY --from=jda-builder /app/build/libs /root/.m2/repository/com/jagrosh/jda-utilities/3.1.0 + +RUN mvn clean +RUN mvn install + +# --------------------------------------------- +# Create final image with only runtime dependencies +FROM debian:12.7-slim + +WORKDIR /app + +COPY --from=builder /app/target/JMusicBot-Snapshot-All.jar /app/JMusicBot.jar + +RUN apt-get update \ + && apt-get install -y openjdk-17-jre-headless \ + && apt-get install -y locales \ + && rm -rf /var/lib/apt/lists/* \ + && localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8 + +ENV LANG en_US.utf8 + +CMD [ "/usr/bin/java", "-Dnogui=true", "-Dconfig.override_with_env_vars=true", "-jar", "/app/JMusicBot.jar" ] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..4046b7de2 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,12 @@ +services: + jagrosh-musicbot: + image: ghcr.io/lynbean/jagrosh-musicbot:latest + container_name: jmusicbot + restart: always + volumes: + - ./data:/app/data + environment: + # https://github.com/jagrosh/MusicBot/wiki/Getting-a-Bot-Token + CONFIG_FORCE_token: BOT_TOKEN_HERE + # https://github.com/jagrosh/MusicBot/wiki/Finding-Your-User-ID + CONFIG_FORCE_owner: 0 diff --git a/pom.xml b/pom.xml index b8b350654..d447c1266 100644 --- a/pom.xml +++ b/pom.xml @@ -47,8 +47,7 @@ com.jagrosh jda-utilities - 3.0.5 - pom + 3.1.0 @@ -60,7 +59,7 @@ dev.lavalink.youtube common - 1.5.2 + 1.7.2 com.github.jagrosh @@ -82,7 +81,7 @@ com.typesafe config - 1.3.2 + 1.4.2 org.jsoup @@ -149,7 +148,7 @@ UTF-8 - 11 - 11 + 17 + 17 diff --git a/src/main/java/com/jagrosh/jmusicbot/BotConfig.java b/src/main/java/com/jagrosh/jmusicbot/BotConfig.java index f6a8757ac..24409c015 100644 --- a/src/main/java/com/jagrosh/jmusicbot/BotConfig.java +++ b/src/main/java/com/jagrosh/jmusicbot/BotConfig.java @@ -106,6 +106,13 @@ public void load() // validate bot token if(token==null || token.isEmpty() || token.equalsIgnoreCase("BOT_TOKEN_HERE")) { + if(OtherUtil.inDockerContainer()) + { + prompt.alert(Prompt.Level.WARNING, CONTEXT, + "No token provided! Exiting.\n\nPlease set the token in an environment variable named `CONFIG_FORCE_token`" + ); + return; + } token = prompt.prompt("Please provide a bot token." + "\nInstructions for obtaining a token can be found here:" + "\nhttps://github.com/jagrosh/MusicBot/wiki/Getting-a-Bot-Token." @@ -126,6 +133,13 @@ public void load() { try { + if(OtherUtil.inDockerContainer()) + { + prompt.alert(Prompt.Level.ERROR, CONTEXT, + "Invalid User ID! Exiting.\n\nPlease set the Owner ID in an environment variable named `CONFIG_FORCE_owner`" + ); + return; + } owner = Long.parseLong(prompt.prompt("Owner ID was missing, or the provided owner ID is not valid." + "\nPlease provide the User ID of the bot's owner." + "\nInstructions for obtaining your User ID can be found here:" @@ -146,8 +160,8 @@ public void load() write = true; } } - - if(write) + + if(write || !path.toFile().exists()) writeToFile(); // if we get through the whole config, it's good to go diff --git a/src/main/java/com/jagrosh/jmusicbot/utils/OtherUtil.java b/src/main/java/com/jagrosh/jmusicbot/utils/OtherUtil.java index c0dd5b357..1c53dba1f 100644 --- a/src/main/java/com/jagrosh/jmusicbot/utils/OtherUtil.java +++ b/src/main/java/com/jagrosh/jmusicbot/utils/OtherUtil.java @@ -57,6 +57,11 @@ public class OtherUtil public static Path getPath(String path) { Path result = Paths.get(path); + if(inDockerContainer()) + { + // for docker mounting, the saved folder has to be separated from the jar file + result = Paths.get("data", path); + } // special logic to prevent trying to access system32 if(result.toAbsolutePath().toString().toLowerCase().startsWith(WINDOWS_INVALID_PATH)) { @@ -230,4 +235,14 @@ public static String getUnsupportedBotReason(JDA jda) return null; } + + /** + * Check if the program is running inside a docker container + * @return Boolean + */ + public static Boolean inDockerContainer() + { + File dockerenv = new File("/.dockerenv"); + return dockerenv.exists(); + } }