Skip to content

feat: deployment version target selector field to scope version to specific targets#907

Open
adityachoudhari26 wants to merge 4 commits intomainfrom
version-optional-selector
Open

feat: deployment version target selector field to scope version to specific targets#907
adityachoudhari26 wants to merge 4 commits intomainfrom
version-optional-selector

Conversation

@adityachoudhari26
Copy link
Copy Markdown
Member

@adityachoudhari26 adityachoudhari26 commented Apr 2, 2026

Summary by CodeRabbit

  • New Features

    • Deployment versions can include a CEL selector to scope versions to matching release targets; selector-aware filtering is applied when choosing deployable versions.
  • API Changes

    • Added optional ordering (asc/desc) to listing deployment versions.
    • createWorkflow now documents a 201 response for successful creation.
  • Database Migration

    • New nullable selector column added to deployment version records.
  • Tests

    • Added tests covering selector filtering behavior.

Copilot AI review requested due to automatic review settings April 2, 2026 18:36
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 2, 2026

📝 Walkthrough

Walkthrough

Adds an optional selector string (CEL expression) across deployment-version surfaces: OpenAPI types, DB schema/migration, SQLC models/queries, TypeScript/SDK schemas, and controller logic that filters candidate versions by evaluating the selector against a release target scope.

Changes

Cohort / File(s) Summary
OpenAPI & SDK schemas
apps/api/openapi/schemas/deploymentversions.jsonnet, apps/api/openapi/openapi.json, apps/api/src/types/openapi.ts, apps/web/app/api/openapi.ts, apps/workspace-engine/oapi/spec/schemas/deployments.jsonnet, apps/workspace-engine/oapi/openapi.json, packages/workspace-engine-sdk/src/schema.ts
Added optional selector?: string property (description: "CEL expression to scope this version to matching release targets") to DeploymentVersion and related request/response shapes. Also added order query param and adjusted one response code in apps/web/app/api/openapi.ts.
Database schema & migrations
apps/workspace-engine/pkg/db/queries/schema.sql, packages/db/drizzle/0182_right_bastion.sql, packages/db/drizzle/meta/_journal.json
Added nullable selector TEXT column to deployment_version and appended migration journal entry.
DB queries / SQLC models
apps/workspace-engine/pkg/db/queries/deployment_versions.sql, apps/workspace-engine/pkg/db/deployment_versions.sql.go, apps/workspace-engine/pkg/db/models.go
Updated UpsertDeploymentVersion to INSERT/UPDATE selector (adjusted placeholder indexes), and updated SQLC-generated code and model structs to include Selector/scan selector in select queries and upsert params.
TypeScript / ORM schema
packages/db/src/schema/deployment-version.ts
Added selector: text("selector") column to drizzle/ORM table definition for deploymentVersion.
Controller logic & tests
apps/workspace-engine/svc/controllers/desiredrelease/reconcile.go, apps/workspace-engine/svc/controllers/desiredrelease/reconcile_test.go, apps/workspace-engine/test/controllers/harness/pipeline_opts.go, apps/workspace-engine/test/controllers/version_test.go
Added filterByTargetSelector() step in reconciler to compile/evaluate CEL selectors per target and filter candidate versions; added unit and integration test coverage plus test helper to set VersionSelector.

Sequence Diagram(s)

sequenceDiagram
  participant Reconciler
  participant DB
  participant CEL as "CEL runtime"
  participant Versions as "Candidate Versions"

  Reconciler->>DB: fetch candidate versions (include selector)
  DB-->>Reconciler: versions list (selector nullable)
  Reconciler->>CEL: compile/eval selector for each non-nil
  CEL-->>Reconciler: boolean result / error
  Reconciler->>Versions: retain versions where selector==nil OR eval==true (errors -> fail-open)
  Reconciler->>Reconciler: continue policy evaluation with filtered versions
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 I nibble selectors in the night,
CEL words glinting soft and bright,
From schema roots to reconciler's sight,
I hop and filter, left and right.
Hooray — versions find their perfect light!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.77% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding a selector field to deployment versions for scoping to specific targets. It directly reflects the core functionality introduced across the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch version-optional-selector

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/workspace-engine/pkg/db/queries/deployment_versions.sql`:
- Around line 9-10: The generated sqlc artifacts are out of sync with the SQL
change that adds selector to the INSERT; regenerate and commit the updated
artifacts so the query constant and param types include selector: run sqlc
generate to update apps/workspace-engine/pkg/db/deployment_versions.sql.go,
verify the upsertDeploymentVersion SQL constant now has the selector
placeholder, ensure the UpsertDeploymentVersionParams struct includes a Selector
field and that the call sites pass selector in the argument list, then rebuild
and commit the regenerated file.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f819708f-62e9-4aff-8628-271af467f2eb

📥 Commits

Reviewing files that changed from the base of the PR and between c62e858 and 9feb45b.

📒 Files selected for processing (5)
  • apps/api/openapi/schemas/deploymentversions.jsonnet
  • apps/workspace-engine/oapi/spec/schemas/deployments.jsonnet
  • apps/workspace-engine/pkg/db/queries/deployment_versions.sql
  • apps/workspace-engine/pkg/db/queries/schema.sql
  • packages/db/src/schema/deployment-version.ts

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a selector field to deployment_version so a version can be scoped to specific release targets (via a CEL expression), and exposes that field through the OpenAPI schemas.

Changes:

  • Add selector column to the deployment_version DB schema (Drizzle + workspace-engine SQL schema).
  • Extend the workspace-engine upsert query to persist selector.
  • Add selector to OpenAPI DeploymentVersion schemas (workspace-engine + api).

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
packages/db/src/schema/deployment-version.ts Adds selector column to Drizzle table definition for deployment versions.
apps/workspace-engine/pkg/db/queries/schema.sql Adds selector column to workspace-engine’s Postgres schema for sqlc.
apps/workspace-engine/pkg/db/queries/deployment_versions.sql Updates upsert to insert/update selector.
apps/workspace-engine/oapi/spec/schemas/deployments.jsonnet Adds selector to workspace-engine OpenAPI schema for DeploymentVersion.
apps/api/openapi/schemas/deploymentversions.jsonnet Adds selector to API OpenAPI schemas for deployment version request/response models.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +50 to 52
selector: text("selector"),

workspaceId: uuid("workspace_id").references(() => workspace.id),
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

selector was added to the Drizzle table schema, but there’s no corresponding Drizzle migration in packages/db/drizzle/ to add this column in Postgres. Since packages/db/migrate.ts runs migrations from that folder, deployments will end up with a schema mismatch unless you add an ALTER TABLE deployment_version ADD COLUMN selector text migration (and consider a default if you need non-null semantics).

Copilot uses AI. Check for mistakes.
Comment on lines 8 to 13
-- name: UpsertDeploymentVersion :one
INSERT INTO deployment_version (id, name, tag, config, job_agent_config, deployment_id, metadata, status, message, workspace_id, created_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, COALESCE(sqlc.narg('created_at')::timestamptz, NOW()))
INSERT INTO deployment_version (id, name, tag, config, job_agent_config, deployment_id, metadata, status, message, selector, workspace_id, created_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, COALESCE(sqlc.narg('created_at')::timestamptz, NOW()))
ON CONFLICT (deployment_id, tag) DO UPDATE
SET name = EXCLUDED.name, config = EXCLUDED.config, job_agent_config = EXCLUDED.job_agent_config, metadata = EXCLUDED.metadata, status = EXCLUDED.status, message = EXCLUDED.message, workspace_id = EXCLUDED.workspace_id,
SET name = EXCLUDED.name, config = EXCLUDED.config, job_agent_config = EXCLUDED.job_agent_config, metadata = EXCLUDED.metadata, status = EXCLUDED.status, message = EXCLUDED.message, selector = EXCLUDED.selector, workspace_id = EXCLUDED.workspace_id,
created_at = CASE WHEN sqlc.narg('created_at')::timestamptz IS NOT NULL THEN EXCLUDED.created_at ELSE deployment_version.created_at END
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

This query change adds a new selector parameter/column, but the committed sqlc outputs are not regenerated: apps/workspace-engine/pkg/db/deployment_versions.sql.go and apps/workspace-engine/pkg/db/models.go currently have no Selector field/param and still generate SQL without the column. Run go generate ./pkg/db (sqlc) and commit the updated generated files so callers can set/read selector.

Copilot uses AI. Check for mistakes.
Comment on lines 109 to 113
status: openapi.schemaRef('DeploymentVersionStatus'),
message: { type: 'string' },
createdAt: { type: 'string', format: 'date-time' },
selector: { type: 'string', description: 'CEL expression to scope this version to matching release targets' },
},
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

The spec adds selector to DeploymentVersion, but the generated artifacts are not updated: apps/workspace-engine/oapi/openapi.json and apps/workspace-engine/pkg/oapi/oapi.gen.go currently omit this field. Re-run the oapi generation step and commit the regenerated outputs so the server/client models match the spec.

Copilot uses AI. Check for mistakes.
Comment on lines 19 to 23
name: { type: 'string' },
tag: { type: 'string' },
createdAt: { type: 'string', format: 'date-time' },
selector: { type: 'string', description: 'CEL expression to scope this version to matching release targets' },
metadata: {
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

selector was added to the jsonnet schemas, but apps/api/openapi/openapi.json is what the API server imports/uses for request validation and Swagger (apps/api/src/server.ts). That bundled spec currently has no selector on DeploymentVersion, so regenerate/rebuild the OpenAPI JSON output and commit it to keep runtime validation/docs in sync.

Copilot uses AI. Check for mistakes.
@adityachoudhari26 adityachoudhari26 marked this pull request as ready for review April 2, 2026 20:01
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
apps/workspace-engine/svc/controllers/desiredrelease/reconcile_test.go (1)

368-383: Add a runtime-eval error case for full branch coverage.

You cover compile-invalid fail-open, but not eval-time errors (e.g., selector compiles but returns non-boolean). Add one case like resource.metadata.region to exercise the EvalBool error path.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/workspace-engine/svc/controllers/desiredrelease/reconcile_test.go`
around lines 368 - 383, Add a new test that exercises the eval-time error branch
of reconciler.filterByTargetSelector by creating a reconciler with an
EvaluatorScope (e.g., evaluator.EvaluatorScope with Resource.Metadata containing
a "region" string) and a DeploymentVersion whose Selector is an expression that
compiles but evaluates to non-boolean (for example "resource.metadata.region");
call r.filterByTargetSelector() and assert that r.versions remains length 1
(fail-open). Reference the existing
TestFilterByTargetSelector_InvalidCEL_FailOpen test and the
reconciler.filterByTargetSelector, EvaluatorScope, and
DeploymentVersion.Selector symbols to place and implement the new case.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/workspace-engine/svc/controllers/desiredrelease/reconcile.go`:
- Around line 61-72: The current logic treats selector compile/eval errors as
"fail-open" by appending v to filtered; change this to "fail-closed" so versions
with selector errors are excluded: when celLang.CompileProgram(*v.Selector)
returns err or when celutil.EvalBool(program, celCtx) returns err, do not append
v to filtered, instead log the error (using log.Error or log.Warn with context
including v.Id and the error) and skip to the next version; update the branches
around celLang.CompileProgram, celutil.EvalBool, and the append(filtered, v)
usage to remove the append on errors and ensure only successful EvalBool true
results add v to filtered.

---

Nitpick comments:
In `@apps/workspace-engine/svc/controllers/desiredrelease/reconcile_test.go`:
- Around line 368-383: Add a new test that exercises the eval-time error branch
of reconciler.filterByTargetSelector by creating a reconciler with an
EvaluatorScope (e.g., evaluator.EvaluatorScope with Resource.Metadata containing
a "region" string) and a DeploymentVersion whose Selector is an expression that
compiles but evaluates to non-boolean (for example "resource.metadata.region");
call r.filterByTargetSelector() and assert that r.versions remains length 1
(fail-open). Reference the existing
TestFilterByTargetSelector_InvalidCEL_FailOpen test and the
reconciler.filterByTargetSelector, EvaluatorScope, and
DeploymentVersion.Selector symbols to place and implement the new case.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0368e75b-c0a4-4bcf-8e65-3ebad213d11b

📥 Commits

Reviewing files that changed from the base of the PR and between 9feb45b and 2f0dd4b.

📒 Files selected for processing (15)
  • apps/api/openapi/openapi.json
  • apps/api/src/types/openapi.ts
  • apps/web/app/api/openapi.ts
  • apps/workspace-engine/oapi/openapi.json
  • apps/workspace-engine/pkg/db/deployment_versions.sql.go
  • apps/workspace-engine/pkg/db/models.go
  • apps/workspace-engine/pkg/oapi/oapi.gen.go
  • apps/workspace-engine/svc/controllers/desiredrelease/reconcile.go
  • apps/workspace-engine/svc/controllers/desiredrelease/reconcile_test.go
  • apps/workspace-engine/test/controllers/harness/pipeline_opts.go
  • apps/workspace-engine/test/controllers/version_test.go
  • packages/db/drizzle/0182_right_bastion.sql
  • packages/db/drizzle/meta/0182_snapshot.json
  • packages/db/drizzle/meta/_journal.json
  • packages/workspace-engine-sdk/src/schema.ts
✅ Files skipped from review due to trivial changes (2)
  • packages/db/drizzle/0182_right_bastion.sql
  • packages/db/drizzle/meta/_journal.json

Comment on lines +61 to +72
program, err := celLang.CompileProgram(*v.Selector)
if err != nil {
log.Warn("selector compile failed, including version", "version", v.Id, "error", err)
filtered = append(filtered, v)
continue
}

matches, err := celutil.EvalBool(program, celCtx)
if err != nil {
log.Warn("selector eval failed, including version", "version", v.Id, "error", err)
filtered = append(filtered, v)
continue
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.

⚠️ Potential issue | 🟠 Major

Fail-open selector errors can bypass target scoping.

On compile/eval failure, the version is re-added to candidates, so invalid selectors effectively behave like “include”. That can deploy versions outside intended targets.

Suggested change (fail-closed on selector errors)
 		program, err := celLang.CompileProgram(*v.Selector)
 		if err != nil {
-			log.Warn("selector compile failed, including version", "version", v.Id, "error", err)
-			filtered = append(filtered, v)
+			log.Warn("selector compile failed, excluding version", "version", v.Id, "error", err)
 			continue
 		}
 
 		matches, err := celutil.EvalBool(program, celCtx)
 		if err != nil {
-			log.Warn("selector eval failed, including version", "version", v.Id, "error", err)
-			filtered = append(filtered, v)
+			log.Warn("selector eval failed, excluding version", "version", v.Id, "error", err)
 			continue
 		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/workspace-engine/svc/controllers/desiredrelease/reconcile.go` around
lines 61 - 72, The current logic treats selector compile/eval errors as
"fail-open" by appending v to filtered; change this to "fail-closed" so versions
with selector errors are excluded: when celLang.CompileProgram(*v.Selector)
returns err or when celutil.EvalBool(program, celCtx) returns err, do not append
v to filtered, instead log the error (using log.Error or log.Warn with context
including v.Id and the error) and skip to the next version; update the branches
around celLang.CompileProgram, celutil.EvalBool, and the append(filtered, v)
usage to remove the append on errors and ensure only successful EvalBool true
results add v to filtered.

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.

2 participants