Skip to content

fix(labels): add and remove deploy labels without replacing the pr's label set - #280

Merged
vigneshrajsb merged 3 commits into
mainfrom
fix/additive-deploy-label
Nov 12, 2026
Merged

vigneshrajsb merged 3 commits into
mainfrom
fix/additive-deploy-label

Conversation

@vigneshrajsb

@vigneshrajsb vigneshrajsb commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

Description

  • With autoDeploy: true, Lifecycle could strip every other label from a pull request when it added the deploy label. The label job sent PUT /issues/{n}/labels with the label list from the opened webhook plus the deploy label, and that call replaces the whole set. Labels applied in the seconds after the PR opened were missing from that list and got removed. Bots such as Dependabot hit this every time, because they open the PR first and add labels in a separate call.
  • The label job now adds the deploy label with POST /issues/{n}/labels and removes deploy labels one at a time with DELETE /issues/{n}/labels/{name}, so Lifecycle only touches labels it owns. On close it reads the PR's current labels first, so a deploy label added after the webhook was sent is removed too.
  • Removing a label that is already gone (404) counts as success, so a user removing the deploy label at the same moment no longer fails the job and makes it retry. It is logged as a warning, because a missing issue or lost app access also returns 404.
  • TTL cleanup made the same replace-all call to swap the deploy label for the disabled label. It now adds the disabled label first and then removes the deploy label, so a failure partway through still leaves the disabled label on the PR. If removing the deploy label fails, the cleanup comment is still posted, and a PR left with both labels is no longer skipped by later scans.
  • updatePullRequestLabels (the replace-all helper) is removed; nothing calls it anymore.

Verifying Changes

  • Unit tests cover the add/remove request shapes, URL-encoding of the label name, 404 vs. other errors, enable sending only the deploy label, and disable removing each configured deploy label and nothing else. eslint and jest pass; tsc shows only errors that already exist on main.
  • End to end against a local install with real GitHub webhooks: labels added right after a PR opens survive, as do labels added while the job waits for the status comment. Close removes only the deploy label(s) and reopen re-adds the deploy label. autoDeploy: false and manual add/remove of the deploy label still behave as before. A close racing a manual removal finishes cleanly. With two deploy labels configured, close removes both. TTL cleanup keeps unrelated labels. A full deploy still succeeds.

…label set

The label job replaced every label on the pull request with a snapshot taken
from the webhook payload. When autoDeploy queued the deploy label right after a
PR opened, labels applied in the seconds after open (bots like Dependabot add
them in a separate call) were missing from that snapshot and got wiped.

Use GitHub's add-labels and remove-label endpoints instead, so Lifecycle only
touches the labels it owns. Removing a label that is already gone is treated as
success. TTL cleanup gets the same treatment when it swaps the deploy label for
the disabled label.
@vigneshrajsb
vigneshrajsb marked this pull request as ready for review November 12, 2026 19:25
@vigneshrajsb
vigneshrajsb requested a review from a team as a code owner November 12, 2026 19:25

@vmelikyan vmelikyan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

lgtm, the POST/DELETE split fixes the label wipe and the add-before-remove order in ttl is the right call.

one edge case, not blocking: in cleanupStaleEnvironment, if the disabled label is added and the deploy label removal fails, the next scan skips the PR (it already has the disabled label), so the deploy label stays and the ttl comment never posts. the env still tears down through the kill switch. could be a follow-up: either don't skip when both labels are present, or catch the remove failure and still post the comment.

nit: on close, label.ts only removes deploy labels that were in the webhook payload. removing all configured deploy labels would cover one added after the event, since 404 is already handled.

@vigneshrajsb

Copy link
Copy Markdown
Contributor Author

Thanks! Fixed both in d227c13:

  • TTL: if removing the deploy label fails after the disabled label is added, it logs a warning and still posts the cleanup comment, since the disabled label alone tears the env down and the namespace may be gone before the next scan. Later scans also no longer skip a PR that has both labels.
  • Close: it now reads the PR's current labels from GitHub and removes the configured deploy labels it actually has, so one added after the event is covered too.

@vigneshrajsb
vigneshrajsb merged commit 6c632c5 into main Nov 12, 2026
5 checks passed
@vigneshrajsb
vigneshrajsb deleted the fix/additive-deploy-label branch November 12, 2026 20:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants