Skip to content

feat: per-service tag filter for tag-triggered deployments - #4999

Open
elisiumm wants to merge 5 commits into
Dokploy:canaryfrom
elisiumm:feat/tag-filter-per-service
Open

feat: per-service tag filter for tag-triggered deployments#4999
elisiumm wants to merge 5 commits into
Dokploy:canaryfrom
elisiumm:feat/tag-filter-per-service

Conversation

@elisiumm

@elisiumm elisiumm commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

What

Adds an optional per-service Tag Filter for triggerType: tag, so a pushed tag only deploys the services whose filter matches the tag name. Mirrors the existing Watch Paths feature (which filters branch pushes by changed path) — this filters tag pushes by tag name.

Closes #4995

Why

With triggerType: tag, a single pushed tag currently triggers every application/compose in the repo configured to deploy on tag — the tag name is never compared. For a monorepo where each service has its own tag prefix (common with release-please + node-workspace, which emits per-package tags like web-v0.7.1, api-v0.5.1, shared-v0.5.1), a frontend-only release still rebuilds and redeploys the backend, doubling deploy time and build-node resource use for nothing.

Before this change, the tag path in github.ts finds all triggerType = "tag" apps and enqueues a deployment for each, with no hook to scope by tag — tagName was only used for the deployment title.

Changes (mirror of Watch Paths, opt-in, fully backward compatible)

  • Schema (application.ts, compose.ts): new nullable tagFilter text column + drizzle-zod wiring. Empty/null = deploy on any tag (today's behavior).
  • Helper (packages/server/src/utils/tag-filter/matches-tag.ts): matchesTag(tagFilter, tagName) using micromatch.isMatch — direct mirror of should-deploy.ts. Empty filter returns true.
  • Webhook (apps/dokploy/pages/api/deploy/github.ts): in the tag-event handler, continue past any app/compose whose tagFilter doesn't match tagName, in both the applications and compose loops. Push-branch path untouched.
  • Router (application.ts): saveGithubProvider now persists tagFilter (compose's update already forwards the full validated input).
  • UI (save-github-provider.tsx, save-github-provider-compose.tsx): a "Tag Filter" input shown only when triggerType === "tag", reusing the existing form components, with a short tooltip: only deploy on tags matching this glob (e.g. web-*); leave empty to deploy on any tag. A brace expansion ({web-*,shared-*}) covers a service that must react to more than one prefix.
  • Migration: 0180_shocking_thena.sql (+ snapshot/journal), generated via the repo's drizzle-kit generate (schema diff only, no live DB needed).

Example for a release-please monorepo:

Service Tag filter
front web-*
api api-*
a service reacting to the shared contract too {api-*,shared-*}

Testing

  • Unit tests added: apps/dokploy/__test__/tag-filter/matches-tag.test.ts — empty filter deploys any tag, simple prefix glob, brace-expansion multi-prefix, non-matching tag rejected (4/4 pass).
  • packages/server and apps/dokploy typecheck: clean.
  • biome check: clean on all touched files.
  • Two Application-typed test fixtures (drop.test.ts, traefik.test.ts) got tagFilter: null added next to their existing watchPaths: [], required by the fully-typed select type.

Honest disclosure per CONTRIBUTING: I verified via unit test (matchesTag), typecheck, Biome, and end-to-end code reading of the schema → router → webhook → UI path. I did not run the full local stack (Docker + Postgres + server:3000) for an interactive browser round-trip of the form persisting a value, nor a real GitHub tag-push payload against a seeded DB. Happy to add a screencast or further tests if maintainers prefer. One pre-existing repo-wide typecheck failure in apps/schedules (packages/server/src/templates/processors.ts) is unrelated to this change (zero diff on that file vs canary).

Greptile Summary

The PR adds optional glob-based tag filters to application and compose GitHub tag deployments while preserving deploy-on-any-tag behavior for empty filters.

  • Adds nullable tagFilter fields and the corresponding database migration.
  • Persists and exposes tag filters through the application and compose configuration forms.
  • Applies tag matching before enqueueing GitHub tag-triggered deployments.
  • Adds unit coverage for tag glob matching and updates typed fixtures.

Confidence Score: 4/5

The PR is not yet safe to merge because tag-filtered webhooks can report deployments that were never queued.

The previously reported response-count defect remains: filtering occurs inside both deployment loops, while the response count is still derived from the unfiltered application and compose query lengths.

Files Needing Attention: apps/dokploy/pages/api/deploy/github.ts

Reviews (4): Last reviewed commit: "test(webhooks): add matchesTag to @dokpl..." | Re-trigger Greptile

Context used (3)

Adds an optional tagFilter glob (micromatch) on application and compose,
mirroring the existing watchPaths mechanism but for the tag deployment
path. When set, a pushed tag only deploys services whose tagFilter
matches the tag name; empty filter deploys on any tag (backward
compatible). Solves the monorepo release-please case where a single
tag (web-v*, api-v*, shared-v*) should only rebuild its own service.
Wires tagFilter through the GitHub provider forms (application and
compose), shown only when triggerType is "tag" (mirrors the existing
Watch Paths field shown for "push"), and through the
saveGithubProvider router mutation. Compose picks it up automatically
via its existing partial update schema. Adds a unit test suite for
matchesTag and updates two existing test fixtures that construct a
full Application object literal to account for the new column.
@elisiumm
elisiumm requested a review from Siumauricio as a code owner August 6, 2026 22:20
@dosubot dosubot Bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Aug 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] Per-service tag filter for triggerType: tag (monorepo / release-please)

1 participant