feat: dockerize project for hosts blocked from native Playwright install#625
feat: dockerize project for hosts blocked from native Playwright install#625monzim wants to merge 1 commit into
Conversation
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.
📝 WalkthroughWalkthroughThis 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. ChangesDocker Container & Orchestration
🎯 2 (Simple) | ⏱️ ~12 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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.
Built for teams:
One agent for your entire SDLC. Right inside Slack. 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. Comment |
|
Welcome to career-ops, @monzim! Thanks for your first PR. A few things to know:
We'll review your PR soon. Join our Discord if you have questions. |
There was a problem hiding this comment.
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
📒 Files selected for processing (6)
.dockerignoreDOCKER.mdDockerfilecopsdocker-compose.ymlpackage.json
| # 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"] |
There was a problem hiding this comment.
🧹 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
(IaC/Dockerfile)
[info] 1-1: No HEALTHCHECK defined
Add HEALTHCHECK instruction in your Dockerfile
Rule: DS-0026
(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.
|
|
||
| ENV DEBIAN_FRONTEND=noninteractive \ | ||
| NODE_ENV=development \ | ||
| PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=0 \ |
There was a problem hiding this comment.
🧩 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:
- 1: https://playwright.dev/docs/docker
- 2: https://github.com/microsoft/playwright/blob/main/utils/docker/Dockerfile.jammy
- 3: microsoft/playwright@89b2fe5
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.
| 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; \ |
There was a problem hiding this comment.
🧹 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.
| RUN npm install --no-audit --no-fund \ | ||
| && npm install --no-audit --no-fund --save-exact playwright@1.58.1 |
There was a problem hiding this comment.
🧹 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.
| 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.
Adds a Docker-based runtime so career-ops works on hosts where
npx playwright install chromiumfails (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../cops doctor,./cops pdf,./cops scan, ...).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
Checklist
node test-all.mjsand all tests passQuestions? Join the Discord for faster feedback.
Summary by CodeRabbit
New Features
Documentation
Chores