Skip to content

Docker#38

Merged
saada merged 7 commits intomainfrom
jlee-docker
Mar 16, 2026
Merged

Docker#38
saada merged 7 commits intomainfrom
jlee-docker

Conversation

@saada
Copy link
Copy Markdown

@saada saada commented Oct 29, 2025

Summary by CodeRabbit

  • Chores
    • CI Docker image build updated: workflow simplified and build caching added for faster repeated builds; branch-based image tagging retained.
    • Development container added: Python-based dev environment with Jupyter server access, scientific tools, editable local package install, and common developer utilities.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 25, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 8258a1a8-626c-46e4-9759-fa62082509f7

📥 Commits

Reviewing files that changed from the base of the PR and between 26edb40 and 1c8b32d.

📒 Files selected for processing (2)
  • .github/workflows/docker.yml
  • dockerfile
🚧 Files skipped from review as they are similar to previous changes (2)
  • dockerfile
  • .github/workflows/docker.yml

Walkthrough

Modifies the GitHub Actions workflow for building/pushing Docker images (removes QEMU setup, changes Dockerfile path, adds build cache options) and adds a multi-stage dockerfile that builds a conda-based Python/Jupyter development image with JupyterHub support and an editable local package install.

Changes

Cohort / File(s) Summary
CI/CD Workflow
.github/workflows/docker.yml
Updated workflow: removed the "Set up QEMU" step, changed the Dockerfile path used by Buildx from .github/Dockerfile to dockerfile, and added cache-from / cache-to options to the docker/build-push-action@v6 build-push step. Tagging logic (latest for main, test otherwise) and remaining steps unchanged.
Docker Build Configuration
dockerfile
New multi-stage dockerfile creating a conda/micromamba-based Python/Jupyter development image: sets up micromamba, creates mdx2-dev from env.yaml, installs conda-forge packages and editable local package via pip, adds JupyterHub and jupyter-vscode-proxy, exposes port 8888, and uses cached conda/pip layers during build.

Sequence Diagram(s)

sequenceDiagram
  participant Dev as Developer
  participant GH as GitHub Actions (Runner)
  participant Buildx as Docker Buildx
  participant DH as Docker Hub

  Dev->>GH: push to branch / open PR
  GH->>GH: checkout repo, read workflow, load secrets
  GH->>Buildx: create builder and run build using `dockerfile` with cache-from/cache-to
  Buildx-->>GH: build artifacts
  GH->>DH: push image with computed tag (latest/test)
  DH-->>GH: push confirmation
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 I nibble layers, tidy and neat,
Cache crumbs saved make builds quick and sweet,
No QEMU hops — just lean, clean flow,
Images bloom where condas grow,
A tiny hop, CI's on repeat! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'Docker' is too generic and doesn't convey meaningful information about the specific changes made to the Docker configuration. Use a more descriptive title that specifies the key change, such as 'Add multi-stage Dockerfile with micromamba environment' or 'Refactor Docker build with GitHub Actions caching'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch jlee-docker
📝 Coding Plan
  • Generate coding plan for human review comments

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.

❤️ Share

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

Tip

Flake8 can be used to improve the quality of Python code reviews.

Flake8 is a Python linter that wraps PyFlakes, pycodestyle and Ned Batchelder's McCabe script.

To configure Flake8, add a '.flake8' or 'setup.cfg' file to your project root.

See Flake8 Documentation for more details.

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: 9

🧹 Nitpick comments (3)
.github/workflows/docker.yml (1)

19-23: QEMU and Buildx are configured for multi-platform builds but no platforms: target is specified.

Setting up QEMU and Buildx implies multi-arch intent, but without a platforms: input in the build-push step the build only targets the runner's native architecture (linux/amd64). Either add the intended platform list or remove the QEMU/Buildx setup to simplify the workflow.

🔧 Suggested addition (if multi-arch is intended)
       - name: Build and push
         uses: docker/build-push-action@v6
         with:
           file: dockerfile
+          platforms: linux/amd64,linux/arm64
           push: true
           tags: ${{ steps.tags.outputs.tags }}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/docker.yml around lines 19 - 23, The workflow sets up QEMU
(docker/setup-qemu-action@v3) and Buildx (docker/setup-buildx-action@v3) but
never specifies a platforms: target in the build-push step, so builds default to
the runner architecture; either add a platforms: input (e.g., platforms:
linux/amd64,linux/arm64) to the docker/build-push-action step to enable
multi-arch outputs or remove the Set up QEMU and Set up Docker Buildx steps to
keep single-arch builds—update the build-push action block accordingly
(reference the docker/build-push-action step) to reflect the chosen approach.
dockerfile (2)

22-24: Consider pinning the conda-forge channel packages to ensure reproducible builds.

nexpy jupyterlab jupyterlab-h5web dials xia2 are installed without version pins. A future run will silently pick up incompatible versions of any of these packages, causing hard-to-diagnose breakage. Pin at least the major/minor versions here or use conda-lock to generate a lock file.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dockerfile` around lines 22 - 24, The Dockerfile installs unpinned packages
via the micromamba install command (see "/usr/local/bin/micromamba install -y -n
mdx2-dev nexpy jupyterlab jupyterlab-h5web dials xia2 ..."), which risks
non-reproducible builds; fix by pinning versions (at least major.minor) for
those packages in the env.yaml or directly in the micromamba install args (e.g.,
nexpy=MAJOR.MINOR, jupyterlab=MAJOR.MINOR, jupyterlab-h5web=MAJOR.MINOR,
dials=MAJOR.MINOR, xia2=MAJOR.MINOR), or generate and use a conda-lock-based
lock file and install from that to guarantee reproducibility while keeping the
env name mdx2-dev and the existing micromamba create/clean steps.

29-30: Merge the two pip install RUN commands to avoid an extra layer, and run them before COPY . . where possible.

Two separate RUN layers for pip installs add unnecessary image weight. More importantly, jupyterhub and jupyter-vscode-proxy don't depend on the local source, so they can be installed in the same layer as the micromamba env setup (before COPY . .), improving cache reuse on subsequent local-package-only changes.

🔧 Suggested fix
 RUN /usr/local/bin/micromamba create -f env.yaml -n mdx2-dev && \
     /usr/local/bin/micromamba install -y -n mdx2-dev nexpy jupyterlab jupyterlab-h5web dials xia2 wget tar -c conda-forge && \
+    /usr/local/bin/micromamba run -n mdx2-dev pip install --no-cache-dir jupyterhub jupyter-vscode-proxy && \
     /usr/local/bin/micromamba clean --all --yes

 COPY . .

 # Install the local package in editable mode within the environment
-RUN /usr/local/bin/micromamba run -n mdx2-dev pip install -e .
-RUN /usr/local/bin/micromamba run -n mdx2-dev pip install --no-cache-dir jupyterhub jupyter-vscode-proxy
+RUN /usr/local/bin/micromamba run -n mdx2-dev pip install -e .
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dockerfile` around lines 29 - 30, Combine the two separate RUN lines that
call "/usr/local/bin/micromamba run -n mdx2-dev pip install -e ." and
"/usr/local/bin/micromamba run -n mdx2-dev pip install --no-cache-dir jupyterhub
jupyter-vscode-proxy" into a single RUN that installs both the editable local
package and the independent packages in one layer, and move that RUN to occur
before the COPY . . step so jupyterhub and jupyter-vscode-proxy caching is
preserved; ensure you preserve flags like --no-cache-dir and use the same
micromamba invocation ("/usr/local/bin/micromamba run -n mdx2-dev pip install
...") so the environment name mdx2-dev remains correct.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/docker.yml:
- Around line 28-32: The workflow is directly interpolating the GitHub context
expression `${{ github.ref }}` into the shell, which is a script-injection
anti-pattern; instead set a separate environment variable (e.g., REF or GIT_REF)
via the job/step `env:` using `github.ref` and reference that env var inside the
`run:` block, then branch on that env var to write the tags to GITHUB_OUTPUT
(update the conditional that currently checks `${{ github.ref }}` to check
`$REF` or similar and keep writing to `$GITHUB_OUTPUT` as before).
- Around line 13-17: The Docker Hub login step ("Login to Docker Hub" using
docker/login-action@v3) and the build-push step currently run unconditionally
and will fail on forked PRs because secrets are unavailable; update the workflow
to guard the login step and the build-push "push: true" behavior with a
condition such as if: github.event_name != 'pull_request' or if:
github.event.pull_request.head.repo.full_name == github.repository, or
alternatively set push: false for PR builds so fork PRs only perform a build;
ensure the conditional is applied to the step named "Login to Docker Hub" and to
the job/step that contains push: true (the build-push step).
- Around line 34-38: The GitHub Actions "Build and push" step using
docker/build-push-action@v6 is failing on case-sensitive hosts because the repo
Dockerfile is named "dockerfile" (lowercase); update the step (the "Build and
push" job that uses docker/build-push-action@v6) to explicitly set the file
input to "dockerfile" and (optionally) set context to the repository root (e.g.,
context: .) so BuildKit can locate the lowercase Dockerfile.

In `@dockerfile`:
- Line 26: The Dockerfile uses COPY . . which will pull your entire repo
(including .git, test artifacts, caches) into the image; add a .dockerignore
file at the repository root that excludes at minimum .git, __pycache__, *.pyc,
dist, *.egg-info and CI/test directories (e.g., .github, .gitlab, tests) so that
the COPY . . in the Dockerfile doesn't add unwanted files to the image and
rebuilds are faster.
- Around line 1-5: The Dockerfile uses the mutable tag "debian:stable-slim" for
the final stage (the line with FROM debian:stable-slim AS final); replace that
mutable tag with a pinned, reproducible reference (either a specific Debian
version tag such as debian:12-slim or a full image digest) so builds remain
deterministic—update the FROM debian:stable-slim AS final line accordingly to
the chosen pinned tag or digest.
- Line 7: Update the RUN instruction that currently executes "apt-get update &&
apt-get install -y ca-certificates" to use "--no-install-recommends" and remove
apt caches in the same layer; change the RUN line (the Dockerfile RUN that calls
apt-get update/install ca-certificates) to run apt-get install with
--no-install-recommends and then delete /var/lib/apt/lists/* (and optionally run
apt-get clean) in the same command so index files are not baked into the image.
- Around line 12-15: Set MAMBA_ROOT_PREFIX so micromamba uses /opt/conda: add an
ENV MAMBA_ROOT_PREFIX=/opt/conda (alongside the existing ENV
PATH="/usr/local/bin:/opt/conda/bin:$PATH") and keep the RUN mkdir -p
/opt/conda; this ensures environments created/used by micromamba (e.g.,
micromamba run -n mdx2-dev) are located at /opt/conda rather than the default
~/micromamba.
- Line 9: Remove the unused build stage and the COPY that imports its /usr/local
into the final image: delete the "stage1" FROM (python:3.10-slim) stage and
remove the line `COPY --from=stage1 /usr/local /usr/local`; then verify there
are no remaining references to "stage1" elsewhere in the Dockerfile and that the
final image relies solely on the micromamba-managed mdx2-dev environment for
Python/pip usage.
- Line 34: The Dockerfile currently runs CMD [".../micromamba", "run", "-n",
"mdx2-dev", "jupyterhub-singleuser"] as root and via a wrapper that won't
receive/forward signals; change the image to create/use a non-root user (add a
USER directive and ensure filesystem perms for that user) so
jupyterhub-singleuser does not run as root, and install/use a proper init as PID
1 (e.g., install tini and set ENTRYPOINT to tini -- so tini is PID 1 and
forwards signals) while leaving the micromamba run command as CMD so the
jupyterhub-singleuser process is started under the non-root user and receives
container signals correctly.

---

Nitpick comments:
In @.github/workflows/docker.yml:
- Around line 19-23: The workflow sets up QEMU (docker/setup-qemu-action@v3) and
Buildx (docker/setup-buildx-action@v3) but never specifies a platforms: target
in the build-push step, so builds default to the runner architecture; either add
a platforms: input (e.g., platforms: linux/amd64,linux/arm64) to the
docker/build-push-action step to enable multi-arch outputs or remove the Set up
QEMU and Set up Docker Buildx steps to keep single-arch builds—update the
build-push action block accordingly (reference the docker/build-push-action
step) to reflect the chosen approach.

In `@dockerfile`:
- Around line 22-24: The Dockerfile installs unpinned packages via the
micromamba install command (see "/usr/local/bin/micromamba install -y -n
mdx2-dev nexpy jupyterlab jupyterlab-h5web dials xia2 ..."), which risks
non-reproducible builds; fix by pinning versions (at least major.minor) for
those packages in the env.yaml or directly in the micromamba install args (e.g.,
nexpy=MAJOR.MINOR, jupyterlab=MAJOR.MINOR, jupyterlab-h5web=MAJOR.MINOR,
dials=MAJOR.MINOR, xia2=MAJOR.MINOR), or generate and use a conda-lock-based
lock file and install from that to guarantee reproducibility while keeping the
env name mdx2-dev and the existing micromamba create/clean steps.
- Around line 29-30: Combine the two separate RUN lines that call
"/usr/local/bin/micromamba run -n mdx2-dev pip install -e ." and
"/usr/local/bin/micromamba run -n mdx2-dev pip install --no-cache-dir jupyterhub
jupyter-vscode-proxy" into a single RUN that installs both the editable local
package and the independent packages in one layer, and move that RUN to occur
before the COPY . . step so jupyterhub and jupyter-vscode-proxy caching is
preserved; ensure you preserve flags like --no-cache-dir and use the same
micromamba invocation ("/usr/local/bin/micromamba run -n mdx2-dev pip install
...") so the environment name mdx2-dev remains correct.

ℹ️ Review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0f9aae6 and f9cec2c.

📒 Files selected for processing (2)
  • .github/workflows/docker.yml
  • dockerfile

Comment on lines +13 to +17
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Docker Hub login will fail on fork PRs — consider gating the push on non-fork events.

Secrets are unavailable on workflows triggered by fork pull requests, so the login step will error. Additionally, push: true is unconditional, meaning every same-repo PR pushes a :test image to Docker Hub. Consider wrapping the login and push behind a condition (e.g., if: github.event_name != 'pull_request' or if: github.event.pull_request.head.repo.full_name == github.repository), or switch PR builds to push: false to perform a build-only validation.

🔧 Suggested fix
       - name: Login to Docker Hub
         uses: docker/login-action@v3
+        if: github.event_name != 'pull_request'
         with:
           username: ${{ vars.DOCKERHUB_USERNAME }}
           password: ${{ secrets.DOCKERHUB_TOKEN }}

And in the build-push step:

       - name: Build and push
         uses: docker/build-push-action@v6
         with:
-          push: true
+          push: ${{ github.event_name != 'pull_request' }}
           tags: ${{ steps.tags.outputs.tags }}
📝 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.

Suggested change
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to Docker Hub
uses: docker/login-action@v3
if: github.event_name != 'pull_request'
with:
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/docker.yml around lines 13 - 17, The Docker Hub login step
("Login to Docker Hub" using docker/login-action@v3) and the build-push step
currently run unconditionally and will fail on forked PRs because secrets are
unavailable; update the workflow to guard the login step and the build-push
"push: true" behavior with a condition such as if: github.event_name !=
'pull_request' or if: github.event.pull_request.head.repo.full_name ==
github.repository, or alternatively set push: false for PR builds so fork PRs
only perform a build; ensure the conditional is applied to the step named "Login
to Docker Hub" and to the job/step that contains push: true (the build-push
step).

Comment on lines +28 to +32
if [ "${{ github.ref }}" = "refs/heads/main" ]; then
echo "tags=${{ vars.DOCKERHUB_USERNAME }}/mdx2:latest" >> $GITHUB_OUTPUT
else
echo "tags=${{ vars.DOCKERHUB_USERNAME }}/mdx2:test" >> $GITHUB_OUTPUT
fi
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Script injection risk: ${{ github.ref }} is interpolated directly into the shell script.

While github.ref is generally safe (it comes from Git refs), directly embedding context expressions into run: scripts is flagged as a security anti-pattern by GitHub's own hardening guide. Use an intermediate environment variable instead.

🔧 Suggested fix
       - name: Determine Docker tags
         id: tags
+        env:
+          GIT_REF: ${{ github.ref }}
         run: |
-          if [ "${{ github.ref }}" = "refs/heads/main" ]; then
-            echo "tags=${{ vars.DOCKERHUB_USERNAME }}/mdx2:latest" >> $GITHUB_OUTPUT
+          if [ "$GIT_REF" = "refs/heads/main" ]; then
+            echo "tags=${{ vars.DOCKERHUB_USERNAME }}/mdx2:latest" >> $GITHUB_OUTPUT
           else
             echo "tags=${{ vars.DOCKERHUB_USERNAME }}/mdx2:test" >> $GITHUB_OUTPUT
           fi
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/docker.yml around lines 28 - 32, The workflow is directly
interpolating the GitHub context expression `${{ github.ref }}` into the shell,
which is a script-injection anti-pattern; instead set a separate environment
variable (e.g., REF or GIT_REF) via the job/step `env:` using `github.ref` and
reference that env var inside the `run:` block, then branch on that env var to
write the tags to GITHUB_OUTPUT (update the conditional that currently checks
`${{ github.ref }}` to check `$REF` or similar and keep writing to
`$GITHUB_OUTPUT` as before).

Comment thread .github/workflows/docker.yml
Comment thread dockerfile
Comment on lines +1 to +5
FROM python:3.10-slim AS stage1

FROM mambaorg/micromamba:1.5.5 AS stage2

FROM debian:stable-slim AS final
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

debian:stable-slim is a mutable/floating tag — pin to a specific version for reproducibility.

stable currently resolves to Debian 12 (bookworm) but will silently change with the next Debian stable release, breaking reproducible builds.

🔧 Suggested fix
-FROM debian:stable-slim AS final
+FROM debian:12-slim AS final
📝 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.

Suggested change
FROM python:3.10-slim AS stage1
FROM mambaorg/micromamba:1.5.5 AS stage2
FROM debian:stable-slim AS final
FROM python:3.10-slim AS stage1
FROM mambaorg/micromamba:1.5.5 AS stage2
FROM debian:12-slim AS final
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dockerfile` around lines 1 - 5, The Dockerfile uses the mutable tag
"debian:stable-slim" for the final stage (the line with FROM debian:stable-slim
AS final); replace that mutable tag with a pinned, reproducible reference
(either a specific Debian version tag such as debian:12-slim or a full image
digest) so builds remain deterministic—update the FROM debian:stable-slim AS
final line accordingly to the chosen pinned tag or digest.

Comment thread dockerfile

FROM debian:stable-slim AS final

RUN apt-get update && apt-get install -y ca-certificates
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

apt-get layer is missing --no-install-recommends and cache cleanup, bloating the image.

Without --no-install-recommends, extra recommended packages are pulled in. Without removing the apt lists, the package index is permanently baked into the layer.

🔧 Suggested fix
-RUN apt-get update && apt-get install -y ca-certificates
+RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates \
+    && rm -rf /var/lib/apt/lists/*
📝 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.

Suggested change
RUN apt-get update && apt-get install -y ca-certificates
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates \
&& rm -rf /var/lib/apt/lists/*
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dockerfile` at line 7, Update the RUN instruction that currently executes
"apt-get update && apt-get install -y ca-certificates" to use
"--no-install-recommends" and remove apt caches in the same layer; change the
RUN line (the Dockerfile RUN that calls apt-get update/install ca-certificates)
to run apt-get install with --no-install-recommends and then delete
/var/lib/apt/lists/* (and optionally run apt-get clean) in the same command so
index files are not baked into the image.

Comment thread dockerfile

RUN apt-get update && apt-get install -y ca-certificates

COPY --from=stage1 /usr/local /usr/local
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

COPY --from=stage1 /usr/local /usr/local is redundant and bloats the image.

stage1 (python:3.10-slim) is brought in solely to copy its entire /usr/local tree (~100 MB+) into the final image. All subsequent Python/pip usage goes through the micromamba-managed mdx2-dev environment at its own prefix — the stage1 Python installation is never used. Remove stage1 entirely and drop this COPY.

🔧 Suggested fix
-FROM python:3.10-slim AS stage1
-
 FROM mambaorg/micromamba:1.5.5 AS stage2

 FROM debian:12-slim AS final
 ...
-COPY --from=stage1 /usr/local /usr/local
 COPY --from=stage2 /bin/micromamba /usr/local/bin/micromamba
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dockerfile` at line 9, Remove the unused build stage and the COPY that
imports its /usr/local into the final image: delete the "stage1" FROM
(python:3.10-slim) stage and remove the line `COPY --from=stage1 /usr/local
/usr/local`; then verify there are no remaining references to "stage1" elsewhere
in the Dockerfile and that the final image relies solely on the
micromamba-managed mdx2-dev environment for Python/pip usage.

Comment thread dockerfile Outdated
Comment on lines +12 to +15
ENV PATH="/usr/local/bin:/opt/conda/bin:$PATH"

# Ensure /opt/conda exists for micromamba to use
RUN mkdir -p /opt/conda
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

MAMBA_ROOT_PREFIX is not set — micromamba will not use /opt/conda as its root prefix.

MAMBA_ROOT_PREFIX "defaults to ~/micromamba" when unset, so the manually created /opt/conda directory serves no purpose and all environments land in /root/micromamba/envs/ instead. The micromamba run -n mdx2-dev calls at runtime will also need to find the environment at the correct location.

🔧 Suggested fix
 ENV PATH="/usr/local/bin:/opt/conda/bin:$PATH"
+ENV MAMBA_ROOT_PREFIX="/opt/conda"

 # Ensure /opt/conda exists for micromamba to use
 RUN mkdir -p /opt/conda
📝 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.

Suggested change
ENV PATH="/usr/local/bin:/opt/conda/bin:$PATH"
# Ensure /opt/conda exists for micromamba to use
RUN mkdir -p /opt/conda
ENV PATH="/usr/local/bin:/opt/conda/bin:$PATH"
ENV MAMBA_ROOT_PREFIX="/opt/conda"
# Ensure /opt/conda exists for micromamba to use
RUN mkdir -p /opt/conda
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dockerfile` around lines 12 - 15, Set MAMBA_ROOT_PREFIX so micromamba uses
/opt/conda: add an ENV MAMBA_ROOT_PREFIX=/opt/conda (alongside the existing ENV
PATH="/usr/local/bin:/opt/conda/bin:$PATH") and keep the RUN mkdir -p
/opt/conda; this ensures environments created/used by micromamba (e.g.,
micromamba run -n mdx2-dev) are located at /opt/conda rather than the default
~/micromamba.

Comment thread dockerfile
Comment thread dockerfile

EXPOSE 8888

CMD ["/usr/local/bin/micromamba", "run", "-n", "mdx2-dev", "jupyterhub-singleuser"]
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Container runs as root and micromamba run is not a proper PID 1 — signals won't propagate correctly.

Two concerns:

  1. No USER directive — the jupyterhub-singleuser process runs as root, which is a security risk in a multi-user JupyterHub deployment.
  2. micromamba run is a wrapper process; it doesn't forward SIGTERM/SIGINT to its child. Use exec or a proper init (e.g., tini) so the Jupyter process receives container lifecycle signals correctly.
🔧 Suggested fix
+RUN useradd -m dev
+USER dev

 EXPOSE 8888

-CMD ["/usr/local/bin/micromamba", "run", "-n", "mdx2-dev", "jupyterhub-singleuser"]
+CMD ["/usr/local/bin/micromamba", "run", "-n", "mdx2-dev", "--", \
+     "python", "-m", "jupyterhub_singleuser"]

Alternatively, install tini and use it as the entrypoint:

RUN apt-get install -y --no-install-recommends tini
ENTRYPOINT ["/usr/bin/tini", "--"]
CMD ["/usr/local/bin/micromamba", "run", "-n", "mdx2-dev", "jupyterhub-singleuser"]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dockerfile` at line 34, The Dockerfile currently runs CMD [".../micromamba",
"run", "-n", "mdx2-dev", "jupyterhub-singleuser"] as root and via a wrapper that
won't receive/forward signals; change the image to create/use a non-root user
(add a USER directive and ensure filesystem perms for that user) so
jupyterhub-singleuser does not run as root, and install/use a proper init as PID
1 (e.g., install tini and set ENTRYPOINT to tini -- so tini is PID 1 and
forwards signals) while leaving the micromamba run command as CMD so the
jupyterhub-singleuser process is started under the non-root user and receives
container signals correctly.

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.

♻️ Duplicate comments (4)
dockerfile (4)

12-15: ⚠️ Potential issue | 🟠 Major

Set MAMBA_ROOT_PREFIX explicitly to match /opt/conda.

Line 12 updates PATH for /opt/conda, and Line 15 creates /opt/conda, but MAMBA_ROOT_PREFIX is not set, so micromamba may place envs elsewhere.

🔧 Proposed fix
 ENV PATH="/opt/conda/envs/mdx2-dev/bin:/usr/local/bin:/opt/conda/bin:$PATH"
+ENV MAMBA_ROOT_PREFIX="/opt/conda"
 
 # Ensure /opt/conda exists for micromamba to use
 RUN mkdir -p /opt/conda
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dockerfile` around lines 12 - 15, Set the micromamba root prefix to
/opt/conda by adding MAMBA_ROOT_PREFIX environment variable so micromamba
creates environments under /opt/conda (update the ENV block that currently sets
PATH); specifically add an ENV MAMBA_ROOT_PREFIX=/opt/conda alongside the
existing ENV PATH and ensure the existing mkdir -p /opt/conda remains so the
directory exists before micromamba runs.

17-17: ⚠️ Potential issue | 🟠 Major

Run Jupyter as a non-root user.

Line 34 runs as root because no USER is set, which is risky for multi-user Jupyter deployments.

🔧 Proposed fix
 WORKDIR /home/dev
+RUN useradd -m -u 1000 dev && chown -R dev:dev /home/dev /opt/conda
+USER dev
 
@@
 CMD ["/usr/local/bin/micromamba", "run", "-n", "mdx2-dev", "jupyterhub-singleuser"]

Also applies to: 34-34

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dockerfile` at line 17, The Dockerfile currently leaves the container running
as root (WORKDIR /home/dev) which is unsafe for Jupyter; create a non-root user
(e.g., "jupyter" or "devuser"), chown the working directory and any runtime
directories to that user, and add a USER instruction to switch to that non-root
account before starting Jupyter. Update relevant Dockerfile steps that install
packages or create files so they run as root where needed, then switch ownership
(chown) of /home/dev (and any notebook/data folders) and set USER jupyter (or
chosen username) before the CMD/ENTRYPOINT that launches Jupyter.

7-7: ⚠️ Potential issue | 🟠 Major

Harden the apt-get layer to avoid image bloat.

Line 7 should use --no-install-recommends and remove apt lists in the same layer.

🔧 Proposed fix
-RUN apt-get update && apt-get install -y ca-certificates
+RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates \
+    && rm -rf /var/lib/apt/lists/*
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dockerfile` at line 7, Replace the current RUN layer that reads "RUN apt-get
update && apt-get install -y ca-certificates" with a single hardened apt
invocation: run apt-get update, install with --no-install-recommends, and remove
apt lists in the same layer (e.g., append "&& rm -rf /var/lib/apt/lists/*") so
the RUN instruction both avoids installing recommended packages and deletes apt
cache to prevent image bloat.

5-5: ⚠️ Potential issue | 🟡 Minor

Pin the Debian base image to an immutable reference.

Line 5 uses debian:stable-slim, which can silently drift and break reproducibility.

🔧 Proposed fix
-FROM debian:stable-slim AS final
+FROM debian:12-slim AS final
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dockerfile` at line 5, Replace the mutable FROM reference
"debian:stable-slim" in the Dockerfile with an immutable base (either a specific
Debian release tag like a numbered minor version or a content-addressable
digest) so the image cannot drift; update the FROM line in the Dockerfile to use
that pinned tag or sha256 digest to ensure reproducible builds.
🧹 Nitpick comments (1)
dockerfile (1)

30-30: Pin pip-installed runtime packages for deterministic builds.

Line 30 installs jupyterhub and jupyter-vscode-proxy without version pins, which can cause unpredictable image rebuilds.

🔧 Proposed refactor
-RUN /usr/local/bin/micromamba run -n mdx2-dev pip install --no-cache-dir jupyterhub jupyter-vscode-proxy
+RUN /usr/local/bin/micromamba run -n mdx2-dev pip install --no-cache-dir \
+    jupyterhub==<pin> jupyter-vscode-proxy==<pin>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dockerfile` at line 30, The Dockerfile RUN step that uses
/usr/local/bin/micromamba run -n mdx2-dev pip install --no-cache-dir jupyterhub
jupyter-vscode-proxy must pin the pip-installed runtime packages for
deterministic builds: update that RUN command to install jupyterhub and
jupyter-vscode-proxy with explicit versions (or reference a
requirements/constraints file) instead of unpinned packages, e.g., specify
package==<version> or --requirement <file>, and include a short comment
indicating pinned versions for reproducibility; ensure the change targets the
micromamba env name mdx2-dev and that the pin strategy is consistent with other
Dockerfile/package management in the repo.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@dockerfile`:
- Around line 12-15: Set the micromamba root prefix to /opt/conda by adding
MAMBA_ROOT_PREFIX environment variable so micromamba creates environments under
/opt/conda (update the ENV block that currently sets PATH); specifically add an
ENV MAMBA_ROOT_PREFIX=/opt/conda alongside the existing ENV PATH and ensure the
existing mkdir -p /opt/conda remains so the directory exists before micromamba
runs.
- Line 17: The Dockerfile currently leaves the container running as root
(WORKDIR /home/dev) which is unsafe for Jupyter; create a non-root user (e.g.,
"jupyter" or "devuser"), chown the working directory and any runtime directories
to that user, and add a USER instruction to switch to that non-root account
before starting Jupyter. Update relevant Dockerfile steps that install packages
or create files so they run as root where needed, then switch ownership (chown)
of /home/dev (and any notebook/data folders) and set USER jupyter (or chosen
username) before the CMD/ENTRYPOINT that launches Jupyter.
- Line 7: Replace the current RUN layer that reads "RUN apt-get update &&
apt-get install -y ca-certificates" with a single hardened apt invocation: run
apt-get update, install with --no-install-recommends, and remove apt lists in
the same layer (e.g., append "&& rm -rf /var/lib/apt/lists/*") so the RUN
instruction both avoids installing recommended packages and deletes apt cache to
prevent image bloat.
- Line 5: Replace the mutable FROM reference "debian:stable-slim" in the
Dockerfile with an immutable base (either a specific Debian release tag like a
numbered minor version or a content-addressable digest) so the image cannot
drift; update the FROM line in the Dockerfile to use that pinned tag or sha256
digest to ensure reproducible builds.

---

Nitpick comments:
In `@dockerfile`:
- Line 30: The Dockerfile RUN step that uses /usr/local/bin/micromamba run -n
mdx2-dev pip install --no-cache-dir jupyterhub jupyter-vscode-proxy must pin the
pip-installed runtime packages for deterministic builds: update that RUN command
to install jupyterhub and jupyter-vscode-proxy with explicit versions (or
reference a requirements/constraints file) instead of unpinned packages, e.g.,
specify package==<version> or --requirement <file>, and include a short comment
indicating pinned versions for reproducibility; ensure the change targets the
micromamba env name mdx2-dev and that the pin strategy is consistent with other
Dockerfile/package management in the repo.

ℹ️ Review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f9cec2c and e534b6e.

📒 Files selected for processing (1)
  • dockerfile

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.

♻️ Duplicate comments (1)
.github/workflows/docker.yml (1)

31-35: ⚠️ Potential issue | 🔴 Critical

Set explicit Dockerfile path for build-push-action.

Line 32 uses docker/build-push-action@v6 without a file: input, and your image file is named dockerfile (lowercase). On case-sensitive runners, default resolution expects Dockerfile, so this can fail the build.

🔧 Proposed fix
       - name: Build and push
         uses: docker/build-push-action@v6
         with:
+          file: dockerfile
           push: true
           tags: ${{ steps.tags.outputs.tags }}
           cache-from: type=gha
           cache-to: type=gha,mode=max
#!/bin/bash
set -euo pipefail

echo "Checking Dockerfile naming in repo:"
fd -HI -a '^dockerfile$|^Dockerfile$'

echo
echo "Inspecting build-push inputs in workflow:"
rg -n -C2 'name: Build and push|uses: docker/build-push-action@v6|^[[:space:]]+file:|^[[:space:]]+context:' .github/workflows/docker.yml

Expected verification: repo shows lowercase dockerfile, and workflow currently lacks file: under the build-push step.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/docker.yml around lines 31 - 35, The "Build and push" step
using docker/build-push-action@v6 omits the file input and will fail on
case-sensitive runners because your image file is named "dockerfile"
(lowercase); update the step to include the explicit file parameter (file:
dockerfile) and ensure context is correct (e.g., context: .) so the action picks
the lowercase Dockerfile reliably.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In @.github/workflows/docker.yml:
- Around line 31-35: The "Build and push" step using docker/build-push-action@v6
omits the file input and will fail on case-sensitive runners because your image
file is named "dockerfile" (lowercase); update the step to include the explicit
file parameter (file: dockerfile) and ensure context is correct (e.g., context:
.) so the action picks the lowercase Dockerfile reliably.

ℹ️ Review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e534b6e and 26edb40.

📒 Files selected for processing (2)
  • .github/workflows/docker.yml
  • dockerfile
🚧 Files skipped from review as they are similar to previous changes (1)
  • dockerfile

@saada saada merged commit a71f2f3 into main Mar 16, 2026
2 checks passed
@saada saada deleted the jlee-docker branch March 16, 2026 22:51
@jlee733 jlee733 mentioned this pull request Mar 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant