Implement CI/CD Pipeline with Production Environment (Fixes #4)#13
Implement CI/CD Pipeline with Production Environment (Fixes #4)#13Art4Man wants to merge 15 commits into
Conversation
- Add Dockerfile for containerizing the bot - Add GitHub Actions workflow for build, release, and deploy - Add docker-compose.yml for easy deployment - Add .env.example with all required environment variables - Support tag-based releases with format v*.*.*
available for testing
WalkthroughIntroduces Dockerization, environment configuration, and a CI/CD pipeline. Adds a Dockerfile, docker-compose, and .dockerignore; provides an .env.example; configures a GitHub Actions workflow for build/release/deploy with Docker Hub and remote server steps; adds IDE-specific ignore rules. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor Dev as Developer
participant GH as GitHub
participant CI as GitHub Actions (CI/CD)
participant DH as Docker Hub
participant RS as Remote Server
Dev->>GH: Push to main / Create tag vX.Y.Z / PR
GH-->>CI: Trigger workflow
rect rgba(200,230,255,0.3)
note over CI: Job: build
CI->>CI: Build image (linux/amd64) with Buildx --load
CI->>CI: Runtime check (python -c "import bot")
CI-->>GH: Build status
end
alt Tag push (refs/tags/v*)
rect rgba(220,255,220,0.3)
note over CI,DH: Job: release
CI->>DH: Login and push images (amd64, arm64)<br/>tags: latest, vX.Y.Z
CI->>RS: SCP docker-compose.yml
CI->>RS: SSH prepare (mkdir, docker pull)
end
rect rgba(255,240,200,0.3)
note over CI,RS: Job: deploy
CI->>RS: SSH: docker compose down && up -d
RS->>RS: docker system prune -f
end
else Non-tag events
CI-->>GH: Release/deploy skipped
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (10)
.idea/.gitignore (1)
1-5: Scope and placement: consider moving IDE ignores to the repo root and Docker contextAdding an IDE-specific .gitignore under .idea works, but it’s easier to keep things consistent by:
- Centralizing IDE ignores in the repository’s root .gitignore (or ignoring .idea/ entirely) to avoid accidental commits elsewhere.
- Ensuring .idea is also listed in .dockerignore so editor artifacts don’t bloat build contexts.
If you intend to keep selected .idea files tracked (e.g., run configs), retain this file and expand the ignore set with other common JetBrains artifacts.
Suggested additions within this file:
# Default ignored files /shelf/ /workspace.xml +# Additional IDE-specific files and directories +/tasks.xml +/usage.statistics.xml +/dictionaries/ +/inspectionProfiles/ +/libraries/ # Editor-based HTTP Client requests /httpRequests/ +# Database tools metadata +/dataSources/ +/dataSources.local.xmlAlso verify that .idea/ is excluded in:
- .gitignore at repo root (unless you intentionally track specific files under .idea)
- .dockerignore (to reduce build context size)
.dockerignore (2)
85-90: Strengthen secret hygiene: ignore more dotenv and key patternsGood that
.envis ignored. Add common variants to avoid accidental inclusion of secrets..env +*.env +.env.* +.secrets +.ssh/ +id_rsa +id_ed25519 +*.pem +*.key
1-16: Trim build context: ignore VCS and editor configsTo keep images smaller and builds faster, exclude VCS and CI metadata, editor settings, and Compose files that don’t need to be copied into the image.
.Python [Bb]in [Ii]nclude [Ll]ib [Ll]ib64 [Ll]ocal [Ss]cripts pyvenv.cfg .venv pip-selfcheck.json .idea +.vscode +.DS_Store +.git +.gitmodules +.github/ +docker-compose.yml.env.example (2)
2-5: Satisfy dotenv-linter ordering (minor) and clarify typesThe linter suggests ordering keys; also, make the intended numeric type obvious for
ADD_TRACK_TIME_WINDOW.- BOT_TOKEN=your_telegram_bot_token_here - DATABASE_NAME=playlist.db - ADD_TRACK_TIME_WINDOW=60 - LOG_LEVEL=INFO +ADD_TRACK_TIME_WINDOW=60 +BOT_TOKEN=your_telegram_bot_token_here +DATABASE_NAME=playlist.db +LOG_LEVEL=INFO
7-12: Avoid suggesting storing private key content in .envThe workflow expects an SSH key via GitHub Secrets, not committed files. Keep these commented hints, but be explicit that these must be set only as Actions secrets, not
.env.Consider rewording comments to: “Set in GitHub Actions Secrets only; do not store locally or commit.”
docker-compose.yml (3)
8-9: Use the declared named volume or remove the declarationYou declare a named volume
data:but mount a bind./data:/app/data. For predictable persistence across redeploys (especially on a server path like/opt/bilbomusicbot), prefer the named volume.- volumes: - - ./data:/app/data + volumes: + - data:/app/dataIf you want the bind mount instead, remove the bottom
volumes:section to avoid confusion.
6-7: Pass through additional runtime envs (optional)
.env.examplelistsDATABASE_NAME,ADD_TRACK_TIME_WINDOW, andLOG_LEVEL. If your bot reads these, surface them here for clarity.environment: - - BOT_TOKEN=${BOT_TOKEN} + - BOT_TOKEN=${BOT_TOKEN} + - DATABASE_NAME=${DATABASE_NAME:-playlist.db} + - ADD_TRACK_TIME_WINDOW=${ADD_TRACK_TIME_WINDOW:-60} + - LOG_LEVEL=${LOG_LEVEL:-INFO}
18-18: Add newline at EOF (lint)
[yamllint] flagged missing newline. Add a newline to satisfy linters and tooling.- data: + data: +.github/workflows/ci-cd.yml (2)
49-56: Minor: remove commented key line and add QEMU for multi-archClean up the commented “key” in the Docker login step (it’s unrelated), and add QEMU setup to ensure smooth multi-arch builds.
- name: Login to Docker Hub uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - # if your server support ssh key use this format - # key: ${{ secrets.SERVER_SSH_KEY }}Add after checkout in both build and release jobs:
+ - name: Set up QEMU + uses: docker/setup-qemu-action@v3
1-10: Hardening: add permissions and concurrency; fix lint nits
- Restrict default token permissions.
- Prevent overlapping deployments per ref.
- Address yamllint indentation/trailing-space warnings and ensure newline at EOF.
name: CI/CD Pipeline on: push: branches: [ main ] tags: - "v*.*.*" pull_request: branches: [ main ] +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: falseAlso run yamllint and fix reported indentation/trailing spaces, and add a newline at EOF.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (6)
.dockerignore(1 hunks).env.example(1 hunks).github/workflows/ci-cd.yml(1 hunks).idea/.gitignore(1 hunks)Dockerfile(1 hunks)docker-compose.yml(1 hunks)
🧰 Additional context used
🪛 dotenv-linter (3.3.0)
.env.example
[warning] 4-4: [UnorderedKey] The ADD_TRACK_TIME_WINDOW key should go before the BOT_TOKEN key
(UnorderedKey)
🪛 YAMLlint (1.37.1)
docker-compose.yml
[error] 18-18: no new line character at the end of file
(new-line-at-end-of-file)
.github/workflows/ci-cd.yml
[warning] 16-16: wrong indentation: expected 6 but found 4
(indentation)
[error] 18-18: trailing spaces
(trailing-spaces)
[error] 21-21: trailing spaces
(trailing-spaces)
[error] 25-25: trailing spaces
(trailing-spaces)
[warning] 43-43: wrong indentation: expected 6 but found 4
(indentation)
[error] 45-45: trailing spaces
(trailing-spaces)
[error] 48-48: trailing spaces
(trailing-spaces)
[error] 63-63: trailing spaces
(trailing-spaces)
[error] 72-72: trailing spaces
(trailing-spaces)
[error] 83-83: trailing spaces
(trailing-spaces)
[warning] 90-90: wrong indentation: expected 6 but found 4
(indentation)
[error] 102-102: no new line character at the end of file
(new-line-at-end-of-file)
🔇 Additional comments (2)
Dockerfile (2)
1-1: Pin base image more tightly for reproducibility
python:3.13-slimwill float to the latest patch, changing over time. Pin to a specific patch or digest to ensure reproducible builds.If you want, I can propose a pinned digest once you confirm the exact variant (bookworm vs. bullseye) you target.
7-7: Optional: verify 3.13 compatibility of dependenciesPython 3.13 is relatively new; some libs may not have wheels yet. If builds fail or pull in compilers, consider 3.12 as a fallback or install build deps.
I can check your
requirements.txtfor 3.13 compatibility if you share it.
| - name: Copy docker-compose.yml to server | ||
| uses: appleboy/scp-action@v0.1.7 | ||
| with: | ||
| host: ${{ secrets.SERVER_HOST }} | ||
| username: ${{ secrets.SERVER_USER }} | ||
| password: ${{ secrets.SERVER_SSH_KEY }} | ||
| source: "docker-compose.yml" | ||
| target: "/opt/bilbomusicbot/" | ||
|
|
There was a problem hiding this comment.
Incorrect credential field for SCP: use key, not password; ensure target dir exists first
You’re passing an SSH private key via the password field, which won’t work. Also, ensure /opt/bilbomusicbot exists before scp.
Apply:
- - name: Copy docker-compose.yml to server
- uses: appleboy/scp-action@v0.1.7
- with:
- host: ${{ secrets.SERVER_HOST }}
- username: ${{ secrets.SERVER_USER }}
- password: ${{ secrets.SERVER_SSH_KEY }}
- source: "docker-compose.yml"
- target: "/opt/bilbomusicbot/"
+ - name: Prepare server for deployment
+ uses: appleboy/ssh-action@v1.0.3
+ with:
+ host: ${{ secrets.SERVER_HOST }}
+ username: ${{ secrets.SERVER_USER }}
+ key: ${{ secrets.SERVER_SSH_KEY }}
+ script: |
+ mkdir -p /opt/bilbomusicbot
+ docker pull ${{ secrets.DOCKER_USERNAME }}/bilbomusicbot:latest
+
+ - name: Copy docker-compose.yml to server
+ uses: appleboy/scp-action@v0.1.7
+ with:
+ host: ${{ secrets.SERVER_HOST }}
+ username: ${{ secrets.SERVER_USER }}
+ key: ${{ secrets.SERVER_SSH_KEY }}
+ source: "docker-compose.yml"
+ target: "/opt/bilbomusicbot/"Note: This reorders steps so the directory exists before SCP and fixes the credential field.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - name: Copy docker-compose.yml to server | |
| uses: appleboy/scp-action@v0.1.7 | |
| with: | |
| host: ${{ secrets.SERVER_HOST }} | |
| username: ${{ secrets.SERVER_USER }} | |
| password: ${{ secrets.SERVER_SSH_KEY }} | |
| source: "docker-compose.yml" | |
| target: "/opt/bilbomusicbot/" | |
| - name: Prepare server for deployment | |
| uses: appleboy/ssh-action@v1.0.3 | |
| with: | |
| host: ${{ secrets.SERVER_HOST }} | |
| username: ${{ secrets.SERVER_USER }} | |
| key: ${{ secrets.SERVER_SSH_KEY }} | |
| script: | | |
| mkdir -p /opt/bilbomusicbot | |
| docker pull ${{ secrets.DOCKER_USERNAME }}/bilbomusicbot:latest | |
| - name: Copy docker-compose.yml to server | |
| uses: appleboy/scp-action@v0.1.7 | |
| with: | |
| host: ${{ secrets.SERVER_HOST }} | |
| username: ${{ secrets.SERVER_USER }} | |
| key: ${{ secrets.SERVER_SSH_KEY }} | |
| source: "docker-compose.yml" | |
| target: "/opt/bilbomusicbot/" |
🧰 Tools
🪛 YAMLlint (1.37.1)
[error] 72-72: trailing spaces
(trailing-spaces)
🤖 Prompt for AI Agents
In .github/workflows/ci-cd.yml around lines 64 to 72, the scp step incorrectly
supplies the SSH private key via the `password` field and runs before the remote
target directory is created; update the workflow to first ensure
/opt/bilbomusicbot exists on the server (e.g., an SSH or run step that creates
the directory with proper permissions) and then call appleboy/scp-action with
the private key passed in the `key` field (not `password`), keeping source as
"docker-compose.yml" and target "/opt/bilbomusicbot/".
There was a problem hiding this comment.
@Art4Man please use appleboy/scp-action@v1.0.0 and also use key instead of password. Please refer to this doc https://github.com/appleboy/scp-action
There was a problem hiding this comment.
Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!
| COPY requirements.txt . | ||
|
|
||
| RUN pip install --no-cache-dir -r requirements.txt | ||
|
|
||
| COPY . . | ||
|
|
||
| CMD ["python", "bot.py"] No newline at end of file |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Run as non-root and add Python runtime envs
Running as root is risky in production. Also consider standard Python envs to reduce noise and improve logging behavior.
FROM python:3.13-slim
WORKDIR /app
+ENV PYTHONDONTWRITEBYTECODE=1 \
+ PYTHONUNBUFFERED=1
+
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
-CMD ["python", "bot.py"]
+# Create non-root user and switch
+RUN addgroup --system app && adduser --system --ingroup app app && \
+ chown -R app:app /app
+USER app
+
+CMD ["python", "bot.py"]📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| COPY requirements.txt . | |
| RUN pip install --no-cache-dir -r requirements.txt | |
| COPY . . | |
| CMD ["python", "bot.py"] | |
| FROM python:3.13-slim | |
| WORKDIR /app | |
| ENV PYTHONDONTWRITEBYTECODE=1 \ | |
| PYTHONUNBUFFERED=1 | |
| COPY requirements.txt . | |
| RUN pip install --no-cache-dir -r requirements.txt | |
| COPY . . | |
| # Create non-root user and switch | |
| RUN addgroup --system app && adduser --system --ingroup app app && \ | |
| chown -R app:app /app | |
| USER app | |
| CMD ["python", "bot.py"] |
There was a problem hiding this comment.
Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!
There was a problem hiding this comment.
Thanks a lot for this great PR @Art4Man .
But we need to set all variables in .env.example as environment variables in the container. I think setting them as secret is a good option
| BOT_TOKEN=your_telegram_bot_token_here | ||
| DATABASE_NAME=playlist.db | ||
| ADD_TRACK_TIME_WINDOW=60 | ||
| LOG_LEVEL=INFO |
| environment: | ||
| - BOT_TOKEN=${BOT_TOKEN} | ||
| volumes: | ||
| - ./data:/app/data |
There was a problem hiding this comment.
There isn't data folder. It's better to mount log directory
Summary
This PR implements a GitHub Action CI/CD pipeline that builds, pushes to Docker Hub, and deploys the BilboMusicBot to a server, addressing
issue #4.
Changes Made
✅ GitHub Action workflow for automated CI/CD
✅ Docker build and push to Docker Hub with multi-platform support (linux/amd64, linux/arm64)
✅ Server deployment automation via SSH
✅ Docker image testing to ensure bot module imports successfully
Workflow Jobs
Triggers
v1.0.0): Triggers build, push to Docker Hub, and server deploymentRequired GitHub Secrets
DOCKER_USERNAME- Docker Hub usernameDOCKER_PASSWORD- Docker Hub passwordSERVER_HOST- Target server hostname/IPSERVER_USER- SSH username for serverSERVER_SSH_KEY- SSH password for server authenticationBOT_TOKEN- Bot token for the applicationUsage