Skip to content
| Marketplace
Sign in
Visual Studio Code>Programming Languages>DeployRace: GitHub Actions Deploy Concurrency CheckerNew to Visual Studio Code? Get it now.
DeployRace: GitHub Actions Deploy Concurrency Checker

DeployRace: GitHub Actions Deploy Concurrency Checker

jaytank_dev

| (0) | Free
Finds deploy workflows that can run at the same time: GitHub Actions deploy jobs without concurrency, cancel-in-progress: true on deploys, two workflows deploying one environment with different groups, unscoped groups, per-PR previews, GitLab CI without resource_group. Quick Fixes, fully offline.
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

DeployRace

Two pushes, two deploys, the wrong version in production. DeployRace finds the deploy workflows that can race.
GitHub Actions deploy jobs without concurrency, cancel-in-progress: true on a deploy, two workflows that deploy the same environment under different groups, groups that name no environment, pull request previews without a per-PR group, and GitLab CI deploys without resource_group.

The problem

GitHub Actions runs workflows in parallel by default. Merge two pull requests a minute apart and two deploy runs start side by side:

  • The older commit wins. The first run hits a cold cache, the second one deploys the newer commit first, then the slow run finishes and puts the older build back. Both runs are green and production has quietly moved backwards.
  • Terraform applies collide. Two terraform apply runs fight over the state lock, or one applies a plan the other has already changed.
  • Migrations run twice, at the same time, against the same database.

The fix is a concurrency: group per environment with cancel-in-progress: false. Cancelling a deploy halfway (a half-applied Terraform state, a migration stopped in the middle, a half-rolled-out release) is worse than waiting, so the newer run should queue, not kill the running one.

concurrency:
  group: deploy-${{ github.ref }}-production
  cancel-in-progress: false

It is easy to miss: the GitHub default is "no limit", the popular CI snippet uses cancel-in-progress: true (right for tests, wrong for deploys), and a second workflow that deploys the same environment under a different group name still races.

Real reports:

  • GitHub Community: Older workflow runs overriding newer deployments when pushing multiple commits quickly
  • Concurrent deploys can publish an older commit over a newer one - two PRs merged 20 seconds apart, the older one published last
  • GitHub Docs: Control the concurrency of workflows and jobs

What you see

  • DeployRace in the activity bar (two arrows racing to a finish post). Its Deploy concurrency view lists every finding grouped by workflow file, and each row names the exact job: DR001 deploy.yml / job deploy-prod, DR002 migrate.yml / workflow (job migrate), DR001 .gitlab-ci.yml / job deploy_production. The description is the rule title; the tooltip has the full message (what makes the job a deploy, which other workflow deploys the same environment and with which group), the file and line, and the Quick Fix. Click a row to jump to the job. The view shows a badge with the number of findings.
  • Inline Quick Fix on each row that has one, and the same fix from the lightbulb in the editor. Every fix is a normal, undoable edit that keeps your comments and indentation.
  • Problems: every finding is also a diagnostic on the job, group or cancel-in-progress line.
  • Status bar: DeployRace: N. Click it to open the view.
  • With nothing found, the view says which files it read and what it looks for.

What counts as a deploy

A job is a deploy when it has environment:, or a step that runs a deploy command, or uses a known deploy action:

  • terraform apply / tofu apply / terragrunt apply, kubectl apply / rollout restart / set image, helm upgrade / install, helmfile apply, argocd app sync
  • serverless deploy, sam deploy, cdk deploy, pulumi up, flyctl deploy, vercel --prod, netlify deploy --prod, aws ecs update-service, aws lambda update-function-code, aws cloudformation deploy, gcloud run|app|functions deploy, az webapp deploy, wrangler deploy, firebase deploy, eb deploy, docker push to a :prod / :production / :live / :stable tag
  • migrations: alembic upgrade, prisma migrate deploy, rails db:migrate, manage.py migrate, flyway migrate, liquibase update, knex migrate:latest, sequelize db:migrate, artisan migrate, typeorm migration:run, dbmate up, ecto.migrate
  • actions such as aws-actions/amazon-ecs-deploy-task-definition, azure/webapps-deploy, google-github-actions/deploy-cloudrun, actions/deploy-pages, cloudflare/wrangler-action, dflook/terraform-apply, appleboy/ssh-action, and pulumi/actions with command: up
  • your own patterns from deployRace.deployPatterns

Not a deploy: terraform plan, helm diff / template, --dry-run, kubectl / helm in a job that starts a local cluster (kind, minikube, k3d), and migrations in a job that runs tests, has services: or points at a test database.

Rules

Rule Default What it finds
DR001 warning A deploy job with no concurrency on the job or the workflow, in a workflow that can run twice at once (push, workflow_dispatch, schedule, release, workflow_run and any other non pull request trigger). Also a group that is unique per run outside pull requests (${{ github.head_ref \|\| github.run_id }}, github.sha), which protects nothing on push. A reusable workflow (on: workflow_call) is checked through the jobs that call it: concurrency on the caller job, the caller workflow or inside the callee all count. GitLab CI: a deploy job without resource_group. Quick Fix: insert a block-style concurrency: with group: deploy-${{ github.ref }}-<environment> and cancel-in-progress: false (on the workflow when it deploys one environment, so queued runs keep push order; on the job otherwise), or resource_group: <environment> for GitLab. When another workflow already deploys the same environment, the fix uses its group.
DR002 error cancel-in-progress: true on a deploy job, or on a workflow that deploys. Pure CI and test jobs are never reported, and neither are pull request preview deploys unless they run Terraform, a stack update or a migration. An expression (${{ ... }}) is not judged. GitLab CI: interruptible: true on a deploy job (own, from a template, extends: or default:), unless workflow:auto_cancel:on_new_commit: none. Quick Fix: false.
DR003 warning Two different workflows deploy the same target with different concurrency groups (or one without): the same environment: name, the same Terraform directory (working-directory, -chdir=, cd dir &&) or the same Kubernetes namespace (-n / --namespace). Groups only serialize runs inside one group, so the workflows still race. Quick Fix: use the shared group (the one most of them already use, or deploy-<target>).
DR004 warning A concurrency group that names neither the environment nor the ref (for example a global group: deploy). A warning when it is shared by different environments (staging then blocks production), information when it serves one target today. Quick Fix: append the environment.
DR005 information A deploy in a pull_request workflow without a per-PR group (github.event.pull_request.number, github.head_ref or github.ref), so preview environments step on each other. Quick Fix: add a per-PR group.

Settings

Setting Default Description
deployRace.enabled true Turn DeployRace on or off.
deployRace.rules {} Severity per rule (error, warning, information, hint) or off, e.g. { "DR002": "warning", "DR005": "off" }.
deployRace.deployPatterns [] Extra regular expressions (case-insensitive) that mark a run: / script: line or a uses: action as a deploy, e.g. ["scripts/ship\\.sh"]. Invalid patterns are ignored.
deployRace.exclude ["**/vendor/**"] Glob patterns (workspace folder relative) that are never checked. node_modules and .git are always excluded.

Commands

  • DeployRace: Rescan Workspace
  • DeployRace: Show Deploy Concurrency
  • DeployRace: Open Settings

Files it reads

.github/workflows/*.yml and *.yaml at the root of each workspace folder (where GitHub reads them), .gitlab-ci.yml, *.gitlab-ci.yml and .gitlab/ci/**/*.yml. Workflows of one workspace folder are compared with each other for DR003, DR004 and reusable workflows.

Privacy and safety

  • No network, no telemetry. DeployRace runs no programs at all, so it works fully in Restricted Mode (untrusted workspaces).
  • Files are read only inside the workspace folders, with a 1 MB size cap. Symlinks are never followed and every path is checked against its real location.
  • Fixes are normal editor edits you can undo; they only touch the workflow file of the finding.

Limitations

  • The YAML reading is line based. It handles block and flow style, block scalars, anchors, aliases and merge keys, which covers real workflow files, but not every corner of YAML.
  • Deploy detection is pattern based. A deploy hidden in a script file (./deploy.sh) is found only with environment: on the job or a deployRace.deployPatterns entry. A job with environment: is always treated as a deploy.
  • DR003 compares targets by name: two workflows that use namespace payments in two different clusters are reported too.
  • Remote reusable workflows (uses: org/repo/.github/workflows/x.yml@v1) are not read; a calling job counts as a deploy when the file name contains deploy. GitLab include: files from other projects are not read; local ones are checked when they match the file names above, and extends: and anchors are followed inside one file.
  • Concurrency stops runs from overlapping. It does not stop a deploy started outside the CI system, and a job-level group alone can still let a later run's deploy start first when an earlier build is slower: a workflow-level group keeps push order.
  • File changes on disk are picked up by the file watcher; where watchers are unavailable, DeployRace rechecks when the window regains focus and every 15 seconds while it is focused.

License

MIT - included with the extension.

  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
  • Your Privacy Choices
  • Consumer Health Privacy
© 2026 Microsoft