All jobs

Dockerize an Angular App

Yes. An agent builds your Angular app in a Bun stage, serves the output from nginx with a single-page fallback, checks deep links in a sandbox and opens a PR.

Run this on RushIn a Project that has your repository.

What the agent does

The agent starts with how the app is built and where the output lands, because an Angular image is a static-file image and every mistake is in those two facts. In an app like realworld-apps/angular-realworld-example-app, package.json pins Angular 21.2.12 with the @angular/build application builder, engines.node is >=20.11.1, and the package manager is Bun: bun.lock is checked in, the setup script runs git submodule init && git submodule update && bun install, and every workflow under .github/workflows/ installs with bun install. angular.json writes the build to dist/angular-conduit, and deploy.yml zips dist/angular-conduit/browser, which is the directory the runtime image has to serve. There is no Dockerfile, no .dockerignore and no nginx configuration.

Two details in angular.json decide whether the Docker build works at all. The styles entry is realworld/assets/theme/styles.css and an assets glob copies realworld/assets/media/*.svg, and realworld is a git submodule declared in .gitmodules, so a build context cloned without --recurse-submodules fails on a missing stylesheet. The agent documents git submodule update --init before docker build and keeps submodules: true in any workflow it adds, matching the existing ones. The other detail is src/_redirects, copied to the output root with /* /index.html 200: it is a Netlify-style rule that nginx ignores, so the agent writes the equivalent try_files $uri $uri/ /index.html into nginx.conf so /article/some-slug and /profile/someone load on refresh.

The Dockerfile has a build stage on the Bun image that copies package.json and bun.lock first so bun install --frozen-lockfile caches as its own layer, then copies the source and the submodule and runs bun run build, which uses the production configuration with outputHashing: all and the bundle budgets of 500 kB warning and 1 MB error from angular.json. The runtime stage is nginx:alpine with dist/angular-conduit/browser copied to the web root and an nginx.conf that adds the fallback, gzip for text types, a long cache lifetime for the hashed files and no cache for index.html. No backend container is needed: src/app/core/interceptors/api.interceptor.ts prefixes every request with https://api.realworld.show/api, so the pull request says where to change that for your own API. A .dockerignore keeps node_modules, dist, .angular/cache, playwright-report and test-results out of the context.

Then it proves the image serves the app. In the sandbox it builds and starts the container, requests / and a deep link such as /article/does-not-exist and checks both return the index.html shell with status 200, and checks a hashed script file comes back with the long cache header. It then runs the repository's own end-to-end suite against the container: playwright.config.ts uses baseURL: http://localhost:4200 with reuseExistingServer on outside CI, so publishing the container on port 4200 lets bun run test:e2e run unchanged against nginx rather than the dev server. It records docker image ls for the image size and fixes and rebuilds until the requests and the suite pass.

Start it with one API call

POST https://api.prix.dev/o/{org}/sessions with this body:

{
  "agent": "claude",
  "input": "Containerize this Angular app. Read package.json, bun.lock, angular.json, .gitmodules, src/_redirects, src/index.html, src/app/core/interceptors/api.interceptor.ts, playwright.config.ts, .gitignore and the workflows under .github/workflows/ first, and list the Angular version, the package manager, the build command, the output directory, any git submodule the build reads, the API base URL and the port the Playwright config expects. Write a multi-stage Dockerfile: a build stage that caches the dependency install in its own layer and runs the production build, and an nginx runtime stage that serves the browser output with a single-page fallback to index.html, gzip, long cache headers for hashed files and no cache for index.html. Add a .dockerignore. Build it in the sandbox, start it on the Playwright port, request the home page and a deep link and confirm both return the app shell, run the existing Playwright suite against the container, fix anything until it passes, and open a pull request with the image size and the commands to run it locally.",
  "resources": [
    {
      "url": "https://github.com/your-org/your-app"
    }
  ]
}
  1. Get an API key. In the console, open Settings, then API keys, and create a key for your org. Send it in the Authorization header as a Bearer token.
  2. Send the request. POST the body in request.json to https://api.prix.dev/o/{org}/sessions, with your repository as the resources URL. The run starts in its own cloud sandbox.
  3. Read what came back. Follow the run on its live stream or wait for the run.completed webhook, then read the result and its trajectory.

What comes back

What comes back is a pull request on its own branch with Dockerfile, nginx.conf, .dockerignore and a README section with the build and run commands, including the submodule step. The description lists each choice and the file it came from: Bun from bun.lock and the workflows, the output directory from angular.json and deploy.yml, the fallback rule from src/_redirects, the submodule from .gitmodules, and the external API from the interceptor.

The transcript on the session keeps every command with its output: each docker build, the nginx access log lines for / and the deep link, the header check on a hashed file, the Playwright run against the container, and any build that failed before the fix. The repository's own Format Check, Playwright Tests and Security Tests workflows run on the pull request too, so Prettier and both Playwright suites report on a change that touches no TypeScript file.

Before merging, a reviewer checks that the runtime image contains no Bun or node_modules, that index.html is served with no cache so a deploy is picked up, that the fallback does not swallow requests for missing hashed assets with a 200, and that the API base URL is the one production should use. If the team deploys behind a path prefix, one follow-up message to the same session changes base href and the nginx location and rebuilds.

Run it on every new Angular project without a Dockerfile

A team containerizing several front ends starts one run per repository: a POST /o/{org}/sessions with that repository in resources and this prompt. Send an Idempotency-Key per repository so a retried request returns the run it already started.

Each run has its own sandbox with its own Docker daemon and its own browser for Playwright, so several builds proceed at once without sharing a port or a cache. Up to the key's max_concurrent runs proceed in parallel, and a webhook on the key receives run.completed or run.failed for each turn with the session_url that holds the pull request.

The use case this job follows, with its automation and trigger: Hand off from your terminal.

Why run it on Rush?

The mistakes in this job show up only when the container runs: a refresh on a deep link returns the nginx 404 page, a stylesheet from a submodule is missing, or a stale index.html keeps pointing at hashed files that no longer exist. On a laptop that means building, clicking around, and building again while the dev server holds port 4200. In a Rush sandbox the agent has its own Docker daemon, its own browser and its own ports, and the sandbox parks when the turn ends.

The pull request carries its own proof. The deep-link requests, the cache header check, the Playwright run against the container and the image size sit in the transcript beside the commands that produced them, so a reviewer can see the app served from nginx rather than from ng serve on a machine that already had Bun and the submodule checked out.

Frequently asked questions

Why does the build need the git submodule?

Because angular.json reads the theme stylesheet and the media SVGs from realworld/assets/, which .gitmodules declares as a submodule. A clone without it has an empty realworld/ directory and the build fails on the missing stylesheet. The pull request documents git submodule update --init before docker build, the same step the repository's setup script and its workflows already perform.

Does it containerize a backend too?

No. The app calls https://api.realworld.show/api from api.interceptor.ts, so the image is a static site with nothing else to run. The pull request names that file as the place to point the app at your own API, and if you want nginx to proxy /api to a backend container, one follow-up message adds that location block.

Will refreshing a deep link work?

Yes. src/_redirects only works on hosts that read that file, so the nginx.conf adds try_files $uri $uri/ /index.html, which serves the app shell for any path that is not a real file. The agent checks this in the sandbox by requesting an article URL directly and confirming the shell comes back with status 200.

Does it run the Playwright tests against the container?

Yes. playwright.config.ts targets http://localhost:4200 and reuses an existing server when it is not running in CI, so the agent publishes the container on port 4200 and runs bun run test:e2e unchanged. The run and its report are in the transcript.