Build1 distinct publisher3 min readPublished
An Nx deploy target holds the whole build, push and deploy line, so CI only calls it and cannot keep a drifting copy. The tradeoff is a production shell string that nothing validates and a public-access flag one diff away.
The Engineer · Build desk

Compiled by The EngineerSomething wrong?How this is made
What CI does in this design is shell out. `nx affected -t deploy` runs each affected project's own deploy target, and that target's `dependsOn: ["build"]` puts the build ahead of it [6][5]. The verb itself sits in `apps/web/project.json` as one `nx:run-commands` string chaining a `docker build --platform linux/amd64`, a push to `gcr.io/$PROJECT/web:latest`, and `gcloud run deploy web --region us-central1 --allow-unauthenticated` [5].
Read that string for what it needs from outside itself. It needs `$PROJECT` set, a Docker daemon that can produce linux/amd64, and credentials `gcloud` will pick up [15]. Those three inputs vary by environment even when the command's text does not: in CI the identity is a keyless federated service account [10][11], on a laptop it is whatever you last logged into, and a stale `$PROJECT` in your shell sends the same image to the wrong project, so "same command, same flags, same memory limits" [6] describes the string, not the run it produces. One copy of the command does beat the two copies the post blames for drift, where a hand-tuned memory limit ships by hand and CI overwrites it for a month [2][3]. The local run and the CI run still differ in identity and environment even when the command string matches.
Nothing checks that string, either. Change `us-central1` to a region you have no quota in and the failure arrives at deploy time on main. `--allow-unauthenticated` rides in the same line, so whether the service answers the public internet is now a one-line diff in a project file [18]. If `apps/*/project.json` is not in a CODEOWNERS path, that is the gap to close before the pattern is worth adopting.
The identity side is the part I would copy without changes. It replaces a service-account JSON key pasted into a repo secret, which is a permanent deploy credential that nothing rotates and nobody revokes [9], with an exchange of GitHub's per-run OIDC token narrowed by provider condition to a single repository [10][13]. Standing it up is eight one-time gcloud calls: one service account, five role bindings, one pool, one provider [14]. Of the five roles the post explains one, `roles/iam.serviceAccountUser`, and the explanation earns its space: deploying a service that runs as another account is impersonation, and the permission error names the runtime account rather than the deployer, which the author prices at twenty minutes [12]. `roles/storage.admin` and `roles/secretmanager.viewer` arrive without a stated reason [17]. storage.admin is a broad grant for something whose described job is pushing images.
Two things would have to be true for the "deploy what changed" half to transfer. First, each app's deploy has to be one idempotent command with no ordering between services. Cloud Run fits that [1]; a schema migration that must land before the new revision does not. Second, `affected` has to be right about inputs, and that is the section the supplied text stops before [16]. If the graph does not count the Dockerfile or the lockfile as inputs to `web`, then "deploy what changed" quietly means "deploy what changed in application code".
Keep the registry note in view regardless of the rest. Container Registry stopped accepting writes on 18 March 2025, and `gcr.io` now proxies to Artifact Registry only for repositories that already exist, so a new project's first push returns 404 until one is created [7]. The post's own advice for new projects is to skip the mirror and push to `us-docker.pkg.dev/$PROJECT/gcr.io/web:latest` [8]. That is the line I would take, because a mirror you created by hand to satisfy a hostname is one more thing in the deploy path with no owner.
Ranked by verification strength, evidence, and original report placement.
The pipeline described uses an Nx monorepo with Python and Node services deployed to Cloud Run, authenticated with Workload Identity Federation, with zero secrets in the repo.
The named failure mode is someone tuning memory limits locally and shipping by hand, after which CI keeps deploying the old shape for a month before anyone notices.
The stated rule is that the deploy command lives in the repo next to the app it deploys, and CI does not know how to deploy anything, only how to ask.
In apps/web/project.json the deploy target uses executor nx:run-commands with dependsOn ["build"], and its single command chains docker build --platform linux/amd64 -f apps/web/Dockerfile, docker push to gcr.io/$PROJECT/web:latest, and gcloud run deploy web --image gcr.io/$PROJECT/web:latest --region us-central1 --allow-unauthenticated.
A human types nx deploy web and CI runs nx affected -t deploy, which the post describes as the same command, same flags, same memory limits, no drift.
Container Registry stopped accepting writes on March 18, 2025; gcr.io hostnames now proxy to Artifact Registry but only for repositories that already exist, so a brand-new project's first docker push fails with a 404.
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · August 31, 2026
Follow any of these and your For You feed starts watching them — no settings page required.
build
Partition, not consolidation: what a 43-minute Jenkins queue actually cost1 distinct publisher
build
The stability step is a branch, not a pipeline: inside one team's release-candidate discipline1 distinct publisher
build
Five rewrites later, the LLM is out of the test loop and into the selectors1 distinct publisher
build
A four-app workspace is where pnpm's symlink store starts paying for its migration cost1 distinct publisher
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
First-hand config, one witness
What is checkable here is checkable because the author pasted it: the whole Nx target, the five role bindings, the pool and provider flags including the attribute condition. That is stronger than a description of a pipeline and weaker than a demonstration of one — there is no run log, no failing-then-passing push, and the dated registry cutoff the migration advice rests on comes with no vendor notice beside it. The commands are specific enough that anyone can falsify them by typing them, which is what keeps this above the middle.
No uptake to measure
Nothing here counts as anyone adopting anything. The two dated events in this reporting are Google's own moves — closing Container Registry to writes and shipping a gcloud that refuses an unconditioned provider — and they say nothing about whether a second team runs this pipeline shape. One engineer's workspace, described by that engineer, is not a measurement.
Slightly ahead of its own proof
Two sentences do more work than the post can pay for: "most broken deploy workflows are broken the same way" and "no drift". The design genuinely removes the duplicate deploy commands from CI. It also parks a three-command shell string that nothing lints and a public-access flag in a JSON file reviewed by whoever reviews app config, and it grants two project-level roles it never explains — costs the piece collects without ever counting.
Practitioner byline, small traffic pull
No vendor money is visible, and the advice repeatedly costs Google surface rather than selling it: skip the gcr.io mirror, never mint a JSON key. This reads as an engineer documenting his own workspace under his own name. The one tug is attention — the interactive token-exchange diagram lives on the original post, and this version stops to send you there.
Run the commands, hold the conclusions
Confidence splits by layer. The setup instructions are worth acting on tonight and will tell you quickly if they are wrong. The framing around them — how common the drift failure is, whether this shape is safe in production — rests on a single author with no second outlet in sight, and the piece stops before the Nx affected section where "deploy only what changed" would have had to earn its title.