Skip to content

Phase 3: containerize for Linux with cross-platform voice libs#11

Merged
JacobHaig merged 2 commits into
mainfrom
feat/dockerize
Jun 1, 2026
Merged

Phase 3: containerize for Linux with cross-platform voice libs#11
JacobHaig merged 2 commits into
mainfrom
feat/dockerize

Conversation

@JacobHaig
Copy link
Copy Markdown
Member

Phase 3 of agent-cloud deployment alignment

Produces a Linux Docker image so agent-cloud can pull and run WisBot.

Voice-on-Linux spike result

  • libsodium + SQLite natives ship cross-platform via NuGet — verified they land in a dotnet publish -r linux-x64 output (libsodium.so, libe_sqlite3.so).
  • ⚠️ opus was the only Windows-locked dependency (OpusDotNet.opus.win-x64 ships win-x64 only). Fix: install libopus0 via apt in the image and symlink the unversioned name DllImport("opus") probes for (libopus.solibopus.so.0).
  • dotnet publish -c Release -r linux-x64 succeeds.

Changes

  • Dockerfile — multi-stage (dotnet/sdk:10.0 build → dotnet/runtime:10.0); non-root user; apt installs libopus0 libsodium23 curl + opus symlink; container env defaults (WISBOT_HEALTH_HOST=+, DB/recordings under /app/data + /app/recordings); HEALTHCHECK curls /health.
  • Wisbot.csprojOpusDotNet.opus.win-x64 scoped to Windows builds (Condition="$([MSBuild]::IsOSPlatform('Windows'))").
  • .dockerignore — keeps secrets/bin/obj/docs out of the image.
  • .github/workflows/docker-build.yml — builds the image on every PR (build only, no push) — this PR's run is the Dockerfile's first real build.
  • CLAUDE.md — Deployment + voice-deps sections updated.

Verification

  • dotnet build green locally (with opus package excluded on non-Windows).
  • Dockerfile build validated by this PR's Docker Build CI job.
  • ⚠️ Final voice capture still needs a live test against Discord on the running container (no Docker/live token available locally) — will confirm at deploy time. Everything else (LLM chat, reminders, welcome, stats, /health) is unaffected regardless.

Depends on: Phase 1 (#9), Phase 2 (#10) — both merged. Next: Phase 4 — publish to GHCR + retire self-hosted deploy workflows.

Phase 3 of agent-cloud deployment alignment.

- Multi-stage Dockerfile (dotnet/sdk build -> dotnet/runtime), publishes
  linux-x64, runs as non-root, exposes /health on 8080, points DB/recordings
  at /app/data and /app/recordings for volume mounts.
- Voice on Linux: libsodium + SQLite natives ship via NuGet (verified they land
  in a linux-x64 publish); opus installed via apt (libopus0) and symlinked to the
  unversioned name DllImport("opus") probes for. OpusDotNet.opus.win-x64 is now
  scoped to Windows builds in the csproj.
- .dockerignore keeps secrets/bin/obj/docs out of the image.
- docker-build.yml validates the image builds on every PR (build only, no push).
- CLAUDE.md updated (Deployment + voice deps).

Note: image build is validated in CI; final voice *capture* still needs a live
test against Discord on the deployed container.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 1, 2026

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 98ab6b19-288a-45c8-bb64-c718a06b64c8

📥 Commits

Reviewing files that changed from the base of the PR and between e7d2d5b and cd838be.

📒 Files selected for processing (2)
  • .github/workflows/docker-build.yml
  • Dockerfile
📜 Recent review details
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: uhstray-io/WisBot

Timestamp: 2026-06-01T16:47:36.645Z
Learning: All config settings resolve via `Config.Load()` in order: process environment variable → local `.env` file → default; Discord token may come from `discord.key` file (gitignored)
Learnt from: CR
Repo: uhstray-io/WisBot

Timestamp: 2026-06-01T16:47:36.645Z
Learning: `WISBOT_GUILD_ID` is required (fail-fast in `InitBot`); all config is never baked into Docker images
Learnt from: CR
Repo: uhstray-io/WisBot

Timestamp: 2026-06-01T16:47:36.645Z
Learning: After making changes, validate using `dotnet build` and consider updating CLAUDE.md or README.md to reflect the changes
Learnt from: CR
Repo: uhstray-io/WisBot

Timestamp: 2026-06-01T16:47:36.645Z
Learning: Read `.claude/memory/MEMORY.md` at the start of every session to load project context and architectural decisions
🔇 Additional comments (2)
Dockerfile (1)

17-23: LGTM!

.github/workflows/docker-build.yml (1)

19-20: LGTM!

Also applies to: 23-25, 28-28, 31-31


📝 Walkthrough

Summary by CodeRabbit

  • Documentation

    • Updated deployment guidance for containerized deployment, runtime config, and platform-native library handling.
  • Chores

    • Added Docker-based containerization for build and runtime with improved runtime behavior and health checks.
    • Added automated Docker image validation on pull requests.
    • Scoped platform-specific native library handling for Windows and Linux.

Walkthrough

Adds Docker containerization for Wisbot: a multi-stage Dockerfile for linux-x64, a .dockerignore, a GitHub Actions workflow to validate Docker builds, an OS-conditioned Opus package reference in Wisbot.csproj, and updated deployment docs describing the Docker-based flow.

Changes

Docker Containerization

Layer / File(s) Summary
Cross-platform build configuration
Wisbot.csproj, CLAUDE.md
OpusDotNet.opus.win-x64 is now Windows-only via MSBuild condition; CLAUDE.md clarifies native library handling (libsodium cross-platform via NuGet, Opus Windows-only package, Linux uses Docker-installed libopus0).
Docker containerization and runtime setup
Dockerfile
Multi-stage build publishes for linux-x64; runtime installs libopus0 and curl, creates libopus.so symlink, non-root wisbot user (UID 10001), /app/data & /app/recordings dirs with ownership, env vars for health/db/recordings, exposes port 8080, HEALTHCHECK /health, and dotnet Wisbot.dll ENTRYPOINT.
Build context optimization
.dockerignore
Excludes bin/, obj/, VCS/IDE folders (.git/, .github/, .vs/, .vscode/), local secrets/config (.env, *.key, .claude/), outputs (recordings/, *.db*), docs (docs/, architecture/), and copy-native-libs.bat from Docker build context.
GitHub Actions Docker validation
.github/workflows/docker-build.yml
Workflow triggers on PRs to main and manual runs; path filters include Dockerfile, .dockerignore, C# sources, Wisbot.csproj, and the workflow file; job sets up Buildx and builds wisbot:ci with caching and push: false.
Updated deployment guidance
CLAUDE.md
Deployment section replaced legacy self-hosted runner docs with Docker multi-stage build overview, runtime env injection guidance, PR validation via the new workflow, and migration notes to agent-cloud (Ansible + Semaphore).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • uhstray-io/WisBot#9: Dockerfile and containerization paths reference persistence/config keys introduced in PR #9 (Config.DbPath and Config.RecordingsDir).

Poem

🐇 A tiny Dockerfile hops into place,
With libopus linked and a non-root embrace,
CI checks the image, contexts kept lean,
Wisbot sails in containers, neat and clean —
Snack on carrots, watch the build routine.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and specifically describes the main change: containerizing the application for Linux while addressing cross-platform voice library dependencies.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, detailing the Docker containerization work, voice library handling, file changes, and verification status.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/dockerize

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/docker-build.yml:
- Around line 16-33: The workflow uses floating action tags and leaves
credentials/powers too broad; update the "Checkout", "Set up Docker Buildx", and
"Build image (no push)" steps to use pinned action references (replace
actions/checkout@v4, docker/setup-buildx-action@v3, docker/build-push-action@v6
with specific commit SHAs), add a minimal permissions block (e.g., permissions:
contents: read) at the job or workflow level, and disable credential persistence
by adding persist-credentials: false to the Checkout step so the build (Build
image (no push)) runs without writable tokens.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b6a7d9b7-2325-4aab-9f0f-d631d262f217

📥 Commits

Reviewing files that changed from the base of the PR and between ebfc75b and e7d2d5b.

📒 Files selected for processing (5)
  • .dockerignore
  • .github/workflows/docker-build.yml
  • CLAUDE.md
  • Dockerfile
  • Wisbot.csproj
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build
  • GitHub Check: Claude Code Review
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: uhstray-io/WisBot

Timestamp: 2026-06-01T16:16:44.122Z
Learning: Read `.claude/memory/MEMORY.md` at the start of every session to load project context
Learnt from: CR
Repo: uhstray-io/WisBot

Timestamp: 2026-06-01T16:16:44.122Z
Learning: Use `dotnet build` to validate changes after completing code modifications
Learnt from: CR
Repo: uhstray-io/WisBot

Timestamp: 2026-06-01T16:16:44.122Z
Learning: Ensure code is well-structured, follows best practices, includes appropriate error handling, and document changes in CLAUDE.md or README.md
🪛 zizmor (1.25.2)
.github/workflows/docker-build.yml

[warning] 20-21: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[error] 21-21: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[error] 24-24: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[error] 27-27: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🔇 Additional comments (4)
CLAUDE.md (1)

18-20: LGTM!

Also applies to: 81-82

Dockerfile (2)

26-48: LGTM!


17-24: 💤 Low value

libsodium23 may be redundant given the NuGet-provided libsodium.so

Wisbot.csproj pulls the libsodium NuGet package (linux-x64 native is runtimes/linux-x64/native/libsodium.so), and the Dockerfile comment says it lands in /app. If Discord.Net loads that libsodium.so, installing libsodium23 may be unnecessary; if Discord.Net probes for a versioned/system SONAME (e.g., libsodium.so.23) or relies on system loader paths, it’s required. Check the running container which library is actually resolved (e.g., list /app vs /usr/lib/x86_64-linux-gnu libsodium*.so*, and optionally use LD_DEBUG=libs during startup) and then remove or update the Dockerfile comment accordingly.

.dockerignore (1)

1-28: LGTM!

Comment thread .github/workflows/docker-build.yml
Address CodeRabbit review on PR #11.

- docker-build.yml: add 'permissions: contents: read', set
  persist-credentials: false on checkout, and pin all three actions to commit
  SHAs (verified against the live tags).
- Dockerfile: remove libsodium23 from apt — libsodium.so ships version-matched
  via NuGet and resolves from the app dir; the apt copy was redundant. Keep
  libopus0 (opus is not NuGet-provided for Linux) + curl.
@JacobHaig JacobHaig merged commit 91e947d into main Jun 1, 2026
2 checks passed
@JacobHaig JacobHaig deleted the feat/dockerize branch June 1, 2026 16:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant