Skip to content

Conversation

@Alw3ys
Copy link
Member

@Alw3ys Alw3ys commented Feb 11, 2026

No description provided.

@coderabbitai
Copy link

coderabbitai bot commented Feb 11, 2026

Walkthrough

Adds canary-by-device-ids support and client UI for three canary modes (automatic, labels, devices). Backend validates mutual exclusivity, selects devices via IDs, labels, or default logic, and inserts deployment associations. Frontend provides a two-step deploy modal to preview and submit canary selections.

Changes

Cohort / File(s) Summary
Backend Deployment Logic
api/src/deployment/mod.rs, models/src/deployment.rs
Adds canary_device_ids: Option<Vec<i32>> and changes deployment flow to branch on IDs, labels, or automatic selection. Validates empty inputs and mutual exclusivity; inserts selected devices and updates target_release_id.
Frontend Deployment UI
dashboard/app/(private)/releases/[id]/page.tsx
Implements a multi-step deploy modal with modes: automatic, by labels, and select devices. Adds state for canaryMode, canaryLabels, canaryDeviceIds, device search/preview, and passes a DeploymentRequest payload.
API Client / Types
dashboard/app/api-client.ts
Adds labels field to DeploymentDeviceWithStatus type and exports related types used by the UI.
CLI Deployment Request
cli/src/commands/releases.rs
Initializes canary_device_ids: None when constructing DeploymentRequest for deployments.
SQL Query Artifacts
api/.sqlx/query-3ac7cd412dedca0b8055d9d0f1d2c5a4d44e2534aa39292572ad6abd92e7a30d.json, api/.sqlx/query-a8dbf988075130d7ee1f97ab1ae0361e5d8f2ddde97281437550ddff28339e29.json
Adds new query descriptor for inserting deployment-device associations; updates hash in an existing descriptor. Review SQL parameters and expected affected-rows behavior.
Infrastructure & Build
compose.yaml, Makefile
Adds ./cli:/app/cli volume to api service in compose.yaml; fixes tab indentation in Makefile debug.smithd target.

Sequence Diagram

sequenceDiagram
    actor User as User
    participant UI as Deployment Modal
    participant API as Backend API
    participant DB as Database

    User->>UI: Open deploy modal → choose mode
    alt Select by Device IDs
        UI->>API: POST /releases/{id}/deployment (canary_device_ids=[...])
    else Select by Labels
        UI->>API: POST /releases/{id}/deployment (canary_device_labels=[...])
    else Automatic
        UI->>API: POST /releases/{id}/deployment ()
    end

    Note over API: Validate not both IDs and labels
    alt IDs provided
        API->>DB: SELECT devices WHERE id IN ($3) AND release/target match AND recent_ping
    else Labels provided
        API->>DB: SELECT DISTINCT devices WHERE labels match AND release alignment
    else Automatic
        API->>DB: SELECT top N devices by recency & score
    end

    DB-->>API: device list
    API->>DB: INSERT INTO deployment_devices (deployment_id, device_id) SELECT ...
    DB-->>API: insert result (rows affected)
    API-->>UI: 200 OK / Deployment started or 400 if no devices
Loading
🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive No description was provided by the author; the pull request lacks any explanatory text about the changes. Add a description explaining the canary release selection feature, its purpose, and how it works to help reviewers understand the implementation.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: canary release select' directly summarizes the main change: adding canary release selection functionality across the codebase.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/select-release

No actionable comments were generated in the recent review. 🎉


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

Copy link

@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: 2

🤖 Fix all issues with AI agents
In `@api/src/deployment/mod.rs`:
- Around line 75-90: The current logic treats canary_device_labels: [] or
canary_device_ids: [] as None (falling back to auto-selection); change
validation to explicitly reject empty selectors: add checks that detect when
request.as_ref().and_then(|r| r.canary_device_labels.as_ref()) is Some and empty
(and similarly for canary_device_ids) and if either is explicitly empty call
tx.rollback().await? and return Err(ApiError::bad_request("canary_device_labels
cannot be empty") or similar); keep the existing has_labels/has_ids behavior for
None so only explicit Some([]) is rejected and existing mutual-exclusion check
between canary_device_labels and canary_device_ids (using has_labels/has_ids)
remains unchanged.

In `@dashboard/app/api-client.ts`:
- Around line 89-92: The DeploymentRequest currently allows both
canary_device_labels and canary_device_ids simultaneously; update the Rust model
in models/src/deployment.rs to make these mutually exclusive by replacing the
flat struct with a discriminated enum (use #[serde(untagged)] and add
#[schema(discriminator = "kind")] on the enum) so utoipa emits a oneOf schema;
define two variants (e.g., Labels { kind: "labels", canary_device_labels:
Vec<String> } and Ids { kind: "ids", canary_device_ids: Vec<i32> })
corresponding to the existing fields and keep any shared fields in a common
variant or wrapper as needed so Orval will generate a TypeScript union instead
of allowing both fields at once.
🧹 Nitpick comments (3)
dashboard/app/(private)/releases/[id]/page.tsx (3)

145-162: Verify: labelMatchDevices includes offline devices while eligibleDevices filters them out.

The eligibleDevices query (line 132) uses online: true, but labelMatchDevices does not include this filter. This means the labels preview may show devices that are offline and won't actually receive the canary update, potentially misleading users about which devices will be targeted.

Consider adding online: true to maintain consistency:

 const { data: labelMatchDevices = [], isLoading: labelDevicesLoading } =
   useGetDevices(
     {
       distribution_id: release?.distribution_id,
+      online: true,
       outdated: false,
       labels: canaryLabels,
     },

Alternatively, if showing offline devices is intentional for preview purposes, consider adding a visual indicator for offline devices in the preview list.


823-840: Accessibility: Device selection lacks keyboard support.

The device selection items use onClick on div elements without keyboard accessibility. Users cannot navigate or select devices using keyboard.

Consider using semantic elements or adding proper ARIA attributes:

♿ Suggested accessibility improvements
-                                        <div
-                                          key={device.id}
-                                          onClick={() =>
-                                            setCanaryDeviceIds((prev) => {
-                                              const next = new Set(prev);
-                                              if (next.has(device.id)) {
-                                                next.delete(device.id);
-                                              } else {
-                                                next.add(device.id);
-                                              }
-                                              return next;
-                                            })
-                                          }
-                                          className={`flex items-center gap-3 px-3 py-2.5 border-b border-gray-100 last:border-b-0 transition-colors cursor-pointer ${
-                                            isSelected
-                                              ? "bg-blue-50"
-                                              : "hover:bg-gray-50"
-                                          }`}
-                                        >
+                                        <div
+                                          key={device.id}
+                                          role="checkbox"
+                                          aria-checked={isSelected}
+                                          tabIndex={0}
+                                          onClick={() =>
+                                            setCanaryDeviceIds((prev) => {
+                                              const next = new Set(prev);
+                                              if (next.has(device.id)) {
+                                                next.delete(device.id);
+                                              } else {
+                                                next.add(device.id);
+                                              }
+                                              return next;
+                                            })
+                                          }
+                                          onKeyDown={(e) => {
+                                            if (e.key === "Enter" || e.key === " ") {
+                                              e.preventDefault();
+                                              setCanaryDeviceIds((prev) => {
+                                                const next = new Set(prev);
+                                                if (next.has(device.id)) {
+                                                  next.delete(device.id);
+                                                } else {
+                                                  next.add(device.id);
+                                                }
+                                                return next;
+                                              });
+                                            }
+                                          }}
+                                          className={`flex items-center gap-3 px-3 py-2.5 border-b border-gray-100 last:border-b-0 transition-colors cursor-pointer ${
+                                            isSelected
+                                              ? "bg-blue-50"
+                                              : "hover:bg-gray-50"
+                                          }`}
+                                        >

You could also extract the toggle logic into a handler to avoid duplication.


979-1020: Selected devices may disappear from preview if they go offline.

The preview filters eligibleDevices (which only includes online devices) by canaryDeviceIds. If a device goes offline after selection, it will disappear from the preview while its ID remains in canaryDeviceIds. The deployment would still attempt to target that device ID.

Consider either:

  1. Filtering out stale selections when eligibleDevices changes
  2. Storing selected device objects (not just IDs) to preserve preview state
  3. Adding a visual indicator or message when selected devices are no longer eligible

volumes:
# Mount source code for hot reloading
- ./api:/app/api
- ./cli:/app/cli
Copy link
Member Author

Choose a reason for hiding this comment

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

this volumen mount for local dev on cli

@Alw3ys Alw3ys merged commit f1be624 into main Feb 11, 2026
3 checks passed
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