Skip to content

feat: dockerize project for hosts blocked from native Playwright install#625

Open
monzim wants to merge 1 commit into
santifer:mainfrom
monzim:main
Open

feat: dockerize project for hosts blocked from native Playwright install#625
monzim wants to merge 1 commit into
santifer:mainfrom
monzim:main

Conversation

@monzim
Copy link
Copy Markdown

@monzim monzim commented May 11, 2026

Adds a Docker-based runtime so career-ops works on hosts where npx playwright install chromium fails (e.g. Ubuntu 26.04). The image is based on the official Playwright image with Chromium preinstalled, and bundles Node, Go (for the dashboard TUI), and LaTeX (for the LaTeX CV path), preserving every feature of the native setup.

  • Dockerfile: mcr.microsoft.com/playwright:v1.58.1-jammy base, pins playwright npm to 1.58.1 to match bundled chromium-1208, adds Go 1.23 and a small TeX Live footprint.
  • docker-compose.yml: bind-mounts the project at /app, named volume for node_modules to avoid host/container ABI mismatch, forwards API keys.
  • .dockerignore: trims build context.
  • cops: thin wrapper that runs any npm script or raw command inside the container (./cops doctor, ./cops pdf, ./cops scan, ...).
  • DOCKER.md: end-user docs.

Pins playwright dep to 1.58.1 exact so the npm version stays aligned with the chromium build shipped in the base image.

What does this PR do?

Related issue

Type of change

  • Bug fix
  • New feature
  • Documentation / translation
  • Refactor (no behavior change)

Checklist

  • I have read CONTRIBUTING.md
  • I linked a related issue above (required for features and architecture changes)
  • My PR does not include personal data (CV, email, real names)
  • I ran node test-all.mjs and all tests pass
  • My changes respect the Data Contract (no modifications to user-layer files)
  • My changes align with the project roadmap

Questions? Join the Discord for faster feedback.

Summary by CodeRabbit

  • New Features

    • Added Docker containerization support for environments where Playwright/Chromium cannot be installed directly.
    • Introduced a simplified wrapper command to streamline Docker container operations and daily workflows.
  • Documentation

    • Added comprehensive guide covering Docker setup, usage, troubleshooting, and data persistence.
  • Chores

    • Updated Docker build configuration with optimized layer caching and dependency management.

Review Change Stack

Adds a Docker-based runtime so career-ops works on hosts where
`npx playwright install chromium` fails (e.g. Ubuntu 26.04). The image
is based on the official Playwright image with Chromium preinstalled,
and bundles Node, Go (for the dashboard TUI), and LaTeX (for the
LaTeX CV path), preserving every feature of the native setup.

- Dockerfile: mcr.microsoft.com/playwright:v1.58.1-jammy base, pins
  playwright npm to 1.58.1 to match bundled chromium-1208, adds Go 1.23
  and a small TeX Live footprint.
- docker-compose.yml: bind-mounts the project at /app, named volume for
  node_modules to avoid host/container ABI mismatch, forwards API keys.
- .dockerignore: trims build context.
- cops: thin wrapper that runs any npm script or raw command inside the
  container (`./cops doctor`, `./cops pdf`, `./cops scan`, ...).
- DOCKER.md: end-user docs.

Pins playwright dep to 1.58.1 exact so the npm version stays aligned
with the chromium build shipped in the base image.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 11, 2026

📝 Walkthrough

Walkthrough

This PR adds Docker containerization to career-ops, enabling development in environments where Playwright/Chromium cannot be installed on the host. It includes a Playwright-based Dockerfile with Go toolchain, docker-compose.yml for service orchestration with volume isolation, a cops wrapper script for CLI delegation, build context filtering, Playwright version pinning, and complete setup/usage documentation.

Changes

Docker Container & Orchestration

Layer / File(s) Summary
Container Image Definition
Dockerfile, package.json
Dockerfile builds from mcr.microsoft.com/playwright:v1.58.1-jammy, installs OS packages and pinned Go toolchain, copies and installs npm dependencies with exact playwright@1.58.1 pinning, sets /app working directory, and uses tini entrypoint with bash default.
Docker Compose & Build Context
docker-compose.yml, .dockerignore
docker-compose.yml defines career-ops service with local build, project bind mount, named node_modules volume, Playwright/Node environment variables, optional API keys, 1gb shared memory, and unless-stopped restart. .dockerignore excludes .git, node_modules, batch/jds state files, reports, logs, .env, and metadata from build context.
CLI Wrapper & Command Orchestration
cops
cops Bash wrapper ensures Docker Compose service is running and delegates commands: lifecycle actions (up/down/rebuild/logs), interactive shells, tool commands (node/npm/npx/go), recognized npm scripts, and unknown commands to docker compose exec.
Docker Usage & Architecture Guide
DOCKER.md
Comprehensive documentation covering purpose/prerequisites, first-time setup, daily usage patterns, lifecycle commands, architecture explanation, API key setup, data persistence boundaries, update workflows, and troubleshooting for Docker, Playwright, permissions, and build performance.

🎯 2 (Simple) | ⏱️ ~12 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the primary objective of the PR: adding Docker support to enable the project to run on hosts where native Playwright installation fails.
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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.

@github-actions
Copy link
Copy Markdown
Contributor

Welcome to career-ops, @monzim! Thanks for your first PR.

A few things to know:

  • Tests will run automatically — check the status below
  • Make sure you've linked a related issue (required for features)
  • Read CONTRIBUTING.md if you haven't

We'll review your PR soon. Join our Discord if you have questions.

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

🤖 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 `@Dockerfile`:
- Around line 1-45: Add a non-root user and switch to it before runtime to avoid
running the container as root: create a user (e.g., "appuser") and group, set
HOME and update ownership of runtime dirs used in this Dockerfile (WORKDIR /app,
PLAYWRIGHT_BROWSERS_PATH /ms-playwright and any npm cache/lock locations) so the
new user can write there, then set USER to that user after the setup steps but
before ENTRYPOINT/CMD; reference the Dockerfile symbols WORKDIR,
PLAYWRIGHT_BROWSERS_PATH, npm install layer, and ENTRYPOINT/CMD to locate where
to add user creation, chown, ENV HOME, and the USER directive.
- Line 19: The apt-get install line in the Dockerfile currently installs
packages without version pins; update the Dockerfile's apt install command to
pin critical packages (e.g., ca-certificates, curl, git, tini) to specific
versions using the apt package=version syntax and/or use Debian/Ubuntu package
snapshots (or a known-OS-release mirror) to ensure reproducible installs, and
optionally mark them held (apt-mark hold) after installation; locate the apt-get
install invocation in the Dockerfile (the line installing ca-certificates curl
git tini latexmk ...) and replace it with pinned-version installs or reference
to a snapshot repository so builds are deterministic while keeping less-critical
LaTeX packages unpinned if you prefer.
- Around line 37-38: The Dockerfile runs a full npm install (the RUN line
invoking "npm install --no-audit --no-fund") and then immediately re-runs "npm
install ... playwright@1.58.1", which is redundant because the package is
already pinned in package.json; remove the second install step (the separate
"npm install --no-audit --no-fund --save-exact playwright@1.58.1" command) from
the RUN chain, or if there is a deliberate reason to reinstall Playwright,
consolidate into a single npm install invocation and preserve necessary flags
(--no-audit, --no-fund, --save-exact) so the build only installs dependencies
once.
- Line 10: The Dockerfile currently sets the environment variable
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=0 which is redundant because the base image
mcr.microsoft.com/playwright:v1.58.1-jammy already includes browsers; remove the
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD line entirely or change it to
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 only when you have an explicit
browser-download step to suppress, ensuring any CI/compose logic that expects
browser downloads is updated accordingly.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: a48d1a44-d1f0-4c43-9044-ef74dda204b9

📥 Commits

Reviewing files that changed from the base of the PR and between 8e554cc and 68947fa.

📒 Files selected for processing (6)
  • .dockerignore
  • DOCKER.md
  • Dockerfile
  • cops
  • docker-compose.yml
  • package.json

Comment thread Dockerfile
Comment on lines +1 to +45
# career-ops container
# Base: Playwright image with Chromium preinstalled (matches playwright@1.58.1 in package.json).
# Host kernels that block Playwright's chromium installer (e.g. Ubuntu 26.04) work fine here
# because the browser ships in the image and runs under the image's userland.

FROM mcr.microsoft.com/playwright:v1.58.1-jammy

ENV DEBIAN_FRONTEND=noninteractive \
NODE_ENV=development \
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=0 \
PLAYWRIGHT_BROWSERS_PATH=/ms-playwright \
PATH=/usr/local/go/bin:$PATH

# Optional: Go toolchain for the dashboard TUI (./dashboard).
# Small footprint, keeps full feature parity with the README setup.
ARG GO_VERSION=1.23.4
RUN set -eux; \
apt-get update; \
apt-get install -y --no-install-recommends ca-certificates curl git tini latexmk texlive-latex-recommended texlive-latex-extra texlive-fonts-recommended texlive-xetex; \
arch="$(dpkg --print-architecture)"; \
case "$arch" in \
amd64) go_arch=amd64 ;; \
arm64) go_arch=arm64 ;; \
*) echo "unsupported arch: $arch" >&2; exit 1 ;; \
esac; \
curl -fsSL "https://go.dev/dl/go${GO_VERSION}.linux-${go_arch}.tar.gz" -o /tmp/go.tgz; \
tar -C /usr/local -xzf /tmp/go.tgz; \
rm /tmp/go.tgz; \
apt-get clean; \
rm -rf /var/lib/apt/lists/*

WORKDIR /app

# Prime npm deps in a layer so rebuilds stay fast.
# Pin playwright to the version that matches the base image's bundled chromium.
COPY package.json package-lock.json* ./
RUN npm install --no-audit --no-fund \
&& npm install --no-audit --no-fund --save-exact playwright@1.58.1

# The rest of the project is bind-mounted at runtime via docker compose,
# so we don't COPY sources here — keeps the image generic and lets local
# edits show up instantly inside the container.

ENTRYPOINT ["/usr/bin/tini", "--"]
CMD ["bash"]
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial | ⚖️ Poor tradeoff

Container runs as root—consider adding non-root USER.

Running as root can cause permission issues with bind-mounted files on the host (already documented in DOCKER.md troubleshooting section). For better security posture and to avoid chown workarounds, consider adding a non-root user or documenting the user: "${UID}:${GID}" compose override more prominently.

This is acceptable for a local development container with documented workarounds, but worth noting for production use cases.

🧰 Tools
🪛 Checkov (3.2.526)

[low] 1-45: Ensure that HEALTHCHECK instructions have been added to container images

(CKV_DOCKER_2)


[low] 1-45: Ensure that a user for the container has been created

(CKV_DOCKER_3)

🪛 Hadolint (2.14.0)

[warning] 17-17: Pin versions in apt get install. Instead of apt-get install <package> use apt-get install <package>=<version>

(DL3008)

🪛 Trivy (0.69.3)

[error] 1-1: Image user should not be 'root'

Specify at least 1 USER command in Dockerfile with non-root user as argument

Rule: DS-0002

Learn more

(IaC/Dockerfile)


[info] 1-1: No HEALTHCHECK defined

Add HEALTHCHECK instruction in your Dockerfile

Rule: DS-0026

Learn more

(IaC/Dockerfile)

🤖 Prompt for 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.

In `@Dockerfile` around lines 1 - 45, Add a non-root user and switch to it before
runtime to avoid running the container as root: create a user (e.g., "appuser")
and group, set HOME and update ownership of runtime dirs used in this Dockerfile
(WORKDIR /app, PLAYWRIGHT_BROWSERS_PATH /ms-playwright and any npm cache/lock
locations) so the new user can write there, then set USER to that user after the
setup steps but before ENTRYPOINT/CMD; reference the Dockerfile symbols WORKDIR,
PLAYWRIGHT_BROWSERS_PATH, npm install layer, and ENTRYPOINT/CMD to locate where
to add user creation, chown, ENV HOME, and the USER directive.

Comment thread Dockerfile

ENV DEBIAN_FRONTEND=noninteractive \
NODE_ENV=development \
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=0 \
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 | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

Does the Microsoft Playwright Docker image mcr.microsoft.com/playwright v1.58.1-jammy include pre-installed browsers, and should PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD be set to 1 when using it?

💡 Result:

Yes. The Microsoft Playwright Docker images (including the mcr.microsoft.com/playwright:v1.58.1-jammy tag you mentioned) are published with the Playwright browsers baked into the image. The official Playwright “Docker” docs state that this Docker image “includes the Playwright browsers and browser system dependencies.” [1] The official Dockerfile used to build the Ubuntu Jammy variant explicitly sets PLAYWRIGHT_BROWSERS_PATH=/ms-playwright and runs playwright-core install --with-deps during image build to bake browsers into the image. [2] Should PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD be set to 1? No—when you run code against the official mcr.microsoft.com/playwright image, you generally do not need to set PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1, because the browsers are already present in the container image. [1][2] PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD is meant for installs where you want to avoid downloading browser binaries (e.g., when browsers are managed separately). The Playwright source/feature indicates it “skip[s] browser downloads altogether” when the env var is set. [3] Therefore, setting it to 1 is only useful if your setup would otherwise try to download browsers (e.g., you are doing a Playwright install step that downloads browsers, or you’re building your own image and don’t want downloads). [3] In contrast, for the official Microsoft Playwright Docker image, you should rely on the pre-baked browsers rather than skipping downloads. [1][2] Practical guidance - Use the image as-is; do not set PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 just to run tests. - Only consider setting PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 if your workflow still triggers a browser download step that you want to suppress. [3]

Citations:


Remove or conditionally set PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD.

The setting contradicts the documented fact that mcr.microsoft.com/playwright:v1.58.1-jammy includes pre-installed browsers. Setting it to 0 (download browsers) is redundant. Either remove this variable to rely on the pre-installed browsers, or set it to 1 only if your workflow explicitly triggers a browser download step that needs suppression.

🤖 Prompt for 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.

In `@Dockerfile` at line 10, The Dockerfile currently sets the environment
variable PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=0 which is redundant because the base
image mcr.microsoft.com/playwright:v1.58.1-jammy already includes browsers;
remove the PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD line entirely or change it to
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 only when you have an explicit
browser-download step to suppress, ensuring any CI/compose logic that expects
browser downloads is updated accordingly.

Comment thread Dockerfile
ARG GO_VERSION=1.23.4
RUN set -eux; \
apt-get update; \
apt-get install -y --no-install-recommends ca-certificates curl git tini latexmk texlive-latex-recommended texlive-latex-extra texlive-fonts-recommended texlive-xetex; \
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial | ⚖️ Poor tradeoff

Consider pinning apt package versions for reproducibility.

Installing packages without version pins can lead to non-reproducible builds and potential security issues if package versions change. For production-grade containers, consider pinning critical packages.

However, for a development container where the base image already provides the core tooling, this is less critical and pinning all packages would add significant maintenance overhead.

🤖 Prompt for 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.

In `@Dockerfile` at line 19, The apt-get install line in the Dockerfile currently
installs packages without version pins; update the Dockerfile's apt install
command to pin critical packages (e.g., ca-certificates, curl, git, tini) to
specific versions using the apt package=version syntax and/or use Debian/Ubuntu
package snapshots (or a known-OS-release mirror) to ensure reproducible
installs, and optionally mark them held (apt-mark hold) after installation;
locate the apt-get install invocation in the Dockerfile (the line installing
ca-certificates curl git tini latexmk ...) and replace it with pinned-version
installs or reference to a snapshot repository so builds are deterministic while
keeping less-critical LaTeX packages unpinned if you prefer.

Comment thread Dockerfile
Comment on lines +37 to +38
RUN npm install --no-audit --no-fund \
&& npm install --no-audit --no-fund --save-exact playwright@1.58.1
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Redundant playwright@1.58.1 installation.

Line 37 runs npm install, which already installs playwright@1.58.1 from package.json (now pinned). Line 38's separate npm install playwright@1.58.1 appears redundant. Consider removing line 38 unless there's a specific reason to reinstall the same version.

♻️ Proposed fix to remove redundant install
-RUN npm install --no-audit --no-fund \
- && npm install --no-audit --no-fund --save-exact playwright@1.58.1
+RUN npm install --no-audit --no-fund
📝 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 npm install --no-audit --no-fund \
&& npm install --no-audit --no-fund --save-exact playwright@1.58.1
RUN npm install --no-audit --no-fund
🤖 Prompt for 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.

In `@Dockerfile` around lines 37 - 38, The Dockerfile runs a full npm install (the
RUN line invoking "npm install --no-audit --no-fund") and then immediately
re-runs "npm install ... playwright@1.58.1", which is redundant because the
package is already pinned in package.json; remove the second install step (the
separate "npm install --no-audit --no-fund --save-exact playwright@1.58.1"
command) from the RUN chain, or if there is a deliberate reason to reinstall
Playwright, consolidate into a single npm install invocation and preserve
necessary flags (--no-audit, --no-fund, --save-exact) so the build only installs
dependencies once.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant