Skip to content

feat: add getDeployment endpoint#4604

Merged
ogzhanolguncu merged 19 commits intomainfrom
get-deployment-api
Jan 15, 2026
Merged

feat: add getDeployment endpoint#4604
ogzhanolguncu merged 19 commits intomainfrom
get-deployment-api

Conversation

@ogzhanolguncu
Copy link
Contributor

@ogzhanolguncu ogzhanolguncu commented Jan 13, 2026

What does this PR do?

This PR creates an endpoint for getDeployment process to decouple CLI and CTRL. That way CLI can talk to API instead of CTRL directly.

Note:

  • Currently /v2/deploy.getDeployment doesn't do any permission check for deployment related stuff. Will be added soon.

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • Chore (refactoring code, technical debt, workflow improvements)
  • Enhancement (small improvements)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How should this be tested?

  • Go to dashboard
  • Open, http://localhost:3000/[workspace]/settings/root-keys
  • Create a root key and copy our rootKey
  • Update your DB to see projects tab
  • Open, http://localhost:3000/[workspace]/projects
  • Create a project and copy your project_id
  • Use your root key for Authorization: Bearer xxx
curl -X POST http://localhost:7070/v2/deploy.getDeployment \
    -H "Authorization: Bearer xxx" \
    -H "Content-Type: application/json" \
    -d '{"deploymentId": "d_5TV5hCUQ3atBZNz3"}'

Expected: 200 OK with {"data":{"id":"d_5TV5hCUQ3atBZNz3","status":"DEPLOYING"},"meta":{"requestId":"req_xxxx"}}

Checklist

Required

  • Filled out the "How to test" section in this PR
  • Read Contributing Guide
  • Self-reviewed my own code
  • Commented on my code in hard-to-understand areas
  • Ran pnpm build
  • Ran pnpm fmt
  • Ran make fmt on /go directory
  • Checked for warnings, there are none
  • Removed all console.logs
  • Merged the latest changes from main onto my branch with git pull origin main
  • My changes don't cause any responsiveness issues

Appreciated

  • If a UI change was made: Added a screen recording or screenshots to this PR
  • Updated the Unkey Docs if changes were necessary

@vercel
Copy link

vercel bot commented Jan 13, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

2 Skipped Deployments
Project Deployment Review Updated (UTC)
dashboard Ignored Ignored Preview Jan 15, 2026 11:34am
engineering Ignored Ignored Preview Jan 15, 2026 11:34am

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 13, 2026

📝 Walkthrough

Walkthrough

Adds an internal POST endpoint /v2/deploy.getDeployment with OpenAPI schemas and generated Go models, a new route handler wired into registration and build targets, tests for success and error cases, and test-harness enhancements to provide controller clients and deployment setup helpers.

Changes

Cohort / File(s) Summary
OpenAPI request/response schemas
svc/api/openapi/spec/paths/v2/deploy/getDeployment/V2DeployGetDeploymentRequestBody.yaml, .../V2DeployGetDeploymentResponseBody.yaml, .../V2DeployGetDeploymentResponseData.yaml, .../V2DeployDeploymentStep.yaml
New schemas: request requires deploymentId; response data includes id, status (enum), optional errorMessage, hostnames, and steps (each with createdAt, status, message, errorMessage).
OpenAPI path & generation
svc/api/openapi/spec/paths/v2/deploy/getDeployment/index.yaml, svc/api/openapi/openapi-split.yaml, svc/api/openapi/openapi-generated.yaml
New POST operation deploy.getDeployment added (marked x-speakeasy-group: internal) and included in generated OpenAPI.
Generated Go models
svc/api/openapi/gen.go
Added types/constants: V2DeployGetDeploymentRequestBody, V2DeployGetDeploymentResponseBody, V2DeployGetDeploymentResponseData, V2DeployDeploymentStep, V2DeployGetDeploymentResponseDataStatus and status constants (BUILDING, DEPLOYING, FAILED, NETWORK, PENDING, READY, UNSPECIFIED). Duplicate V2DeployDeploymentStep declaration observed.
Route handler & registration
svc/api/routes/v2_deploy_get_deployment/handler.go, svc/api/routes/register.go
New Handler implementing Path/Method/Handle; resolves root key, calls controller GetDeployment, maps status and steps to API schema; route registered in register.go.
Route build & tests
svc/api/routes/v2_deploy_get_deployment/BUILD.bazel, svc/api/routes/BUILD.bazel, svc/api/routes/v2_deploy_get_deployment/*_test.go
New go_library and go_test targets; tests added for 200, 400, 401, 404 covering success and error scenarios.
Test harness & utilities
pkg/testutil/http.go, pkg/testutil/BUILD.bazel
Harness extended with CtrlDeploymentClient and CtrlBuildClient; added CreateTestDeploymentSetup helper and related types; Bazel deps updated to include ctrl connect and interceptors.
Existing deploy endpoints marked internal & validation
svc/api/openapi/spec/paths/v2/deploy/createDeployment/index.yaml, .../generateUploadUrl/index.yaml, .../createDeployment/V2DeployCreateDeploymentRequestBody.yaml, .../generateUploadUrl/V2DeployGenerateUploadUrlRequestBody.yaml
Added x-speakeasy-group: internal and INTERNAL notices; added projectId pattern validation.
Tests refactoring to harness clients
svc/api/routes/v2_deploy_create_deployment/*, .../v2_deploy_generate_upload_url/*
Tests updated to use harness-provided CtrlDeploymentClient / CtrlBuildClient and new CreateTestDeploymentSetup helper, removing per-test CTRL client construction and related deps.

Sequence Diagram(s)

sequenceDiagram
  participant Client as API Client
  participant API as v2 deploy.getDeployment Handler
  participant Keys as Key Service
  participant Ctrl as Controller DeploymentService
  participant DB as Database

  Client->>API: POST /v2/deploy.getDeployment { deploymentId } (Authorization)
  API->>Keys: Resolve root key from session
  Keys-->>API: rootKey / error
  API->>Ctrl: GetDeployment(deploymentId, rootKey)
  Ctrl-->>API: Deployment data (id, status, hostnames, steps) / error
  API->>DB: (optional) record/request metadata
  DB-->>API: ack
  API-->>Client: 200 { data: { id, status, hostnames, steps }, meta } / error response
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.25% 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
Title check ✅ Passed The title 'feat: add getDeployment endpoint' clearly and concisely describes the main change—adding a new API endpoint for getDeployment functionality.
Description check ✅ Passed The PR description is comprehensive and follows the template with all required sections filled: what the PR does, type of change marked, detailed testing instructions with example curl commands, and all checklist items completed.

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

✨ Finishing touches
  • 📝 Generate docstrings

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.

@ogzhanolguncu ogzhanolguncu marked this pull request as ready for review January 14, 2026 11:39
Copy link
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: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
svc/api/openapi/openapi-generated.yaml (1)

4271-4429: Doc/contract mismatch: endpoints claim permission enforcement that (per PR) doesn’t exist yet

Line 4271-4281, 4325-4332, 4379-4385: the descriptions say “Requires a valid root key with appropriate permissions”, but the PR objectives explicitly say there are currently no permission checks for deployment-related operations (to be added later). This is likely to confuse internal consumers and is a spec-contract mismatch.

Suggested adjustment: state that authentication is required (root key), and explicitly note that authorization/permission checks are not enforced yet (or remove the “appropriate permissions” phrasing until 403 is real).

Proposed diff (apply similarly across deploy internal endpoints)
 description: |
     **INTERNAL** - This endpoint is internal and may change without notice.
     Not recommended for production use.

     Retrieves deployment information including status, error messages, and steps.

-    **Authentication**: Requires a valid root key with appropriate permissions.
+    **Authentication**: Requires a valid root key.
+    **Authorization**: Deployment permission checks are not enforced yet (will be added later).
🤖 Fix all issues with AI agents
In
`@svc/api/openapi/spec/paths/v2/deploy/getDeployment/V2DeployGetDeploymentRequestBody.yaml`:
- Around line 8-9: The example deployment ID in
V2DeployGetDeploymentRequestBody.yaml is inconsistent with real IDs; update the
example value (the example field currently set to "dep_abc123xyz") to use the
actual deployment ID format that starts with "d_" (for example
"d_5TV5hCUQ3atBZNz3") so the example matches real-world IDs and testing
instructions.

In `@svc/api/routes/v2_deploy_get_deployment/200_test.go`:
- Around line 90-113: The helper createTestDeployment currently overrides the
Authorization header with rootKey which is inconsistent with the other test that
relies on the interceptor's ctrlToken; decide which token should be used and
make tests consistent: either (A) remove the explicit Header().Set call in
createTestDeployment so it uses the interceptor-provided ctrlToken (and update
callers if they need a different token), or (B) make the helper accept an auth
token parameter (e.g., token string) and set Header().Set("Authorization",
fmt.Sprintf("Bearer %s", token)) so callers can explicitly pass rootKey or
ctrlToken; update the v2_deploy_create_deployment test to match the chosen
pattern and add a short comment in createTestDeployment explaining why that
token is required.

In `@svc/api/routes/v2_deploy_get_deployment/404_test.go`:
- Around line 53-68: Test filename and expectations mismatch: rename the test
file svc/api/routes/v2_deploy_get_deployment/404_test.go to 500_test.go (and
update the t.Run name or comment to "deployment not found - expects 500") so the
filename reflects the actual assertion (require.Equal expecting
http.StatusInternalServerError) for handler.Request / handler.Response in this
route test; also open a tracker issue to update the CTRL service to return 404
for missing deployments and, when that is fixed, change the assertion back to
http.StatusNotFound and rename the test accordingly.
🧹 Nitpick comments (6)
svc/api/routes/v2_deploy_get_deployment/401_test.go (1)

3-15: Consider organizing imports into separate groups.

Per coding guidelines, Go imports should be organized with blank lines separating: (1) Standard library, (2) External/third-party packages, (3) Internal packages.

🔧 Suggested import organization
 import (
 	"fmt"
 	"net/http"
 	"testing"
 
 	"connectrpc.com/connect"
 	"github.com/stretchr/testify/require"
+
 	"github.com/unkeyed/unkey/gen/proto/ctrl/v1/ctrlv1connect"
 	"github.com/unkeyed/unkey/pkg/rpc/interceptor"
 	"github.com/unkeyed/unkey/pkg/testutil"
 	"github.com/unkeyed/unkey/pkg/testutil/containers"
 	handler "github.com/unkeyed/unkey/svc/api/routes/v2_deploy_get_deployment"
 )
svc/api/routes/v2_deploy_get_deployment/404_test.go (1)

3-17: Consider organizing imports into separate groups.

Per coding guidelines, add a blank line between external/third-party packages and internal packages.

🔧 Suggested import organization
 import (
 	"fmt"
 	"net/http"
 	"testing"
 
 	"connectrpc.com/connect"
 	"github.com/stretchr/testify/require"
+
 	"github.com/unkeyed/unkey/gen/proto/ctrl/v1/ctrlv1connect"
 	"github.com/unkeyed/unkey/pkg/rpc/interceptor"
 	"github.com/unkeyed/unkey/pkg/testutil"
 	"github.com/unkeyed/unkey/pkg/testutil/containers"
 	"github.com/unkeyed/unkey/pkg/testutil/seed"
 	"github.com/unkeyed/unkey/pkg/uid"
 	handler "github.com/unkeyed/unkey/svc/api/routes/v2_deploy_get_deployment"
 )
svc/api/routes/v2_deploy_get_deployment/400_test.go (1)

45-52: Unused project creation in test setup.

The project is created but appears unused after setup. The workspace and rootKey are used but projectID and the created project are not referenced in any of the test cases. Consider removing if not needed, or verify if this is intentional setup for the test harness.

🧹 Suggested cleanup
 	workspace := h.CreateWorkspace()
 	rootKey := h.CreateRootKey(workspace.ID)
-
-	projectID := uid.New(uid.ProjectPrefix)
-
-	h.CreateProject(seed.CreateProjectRequest{
-		WorkspaceID: workspace.ID,
-		Name:        "test-project",
-		ID:          projectID,
-		Slug:        "production",
-	})
svc/api/routes/v2_deploy_get_deployment/handler.go (1)

66-96: Consider simplifying step transformation.

The explicit nil initialization at lines 71-74 is redundant since Go zero-values struct fields to nil for pointer types. The conditional assignments that follow are correct.

🧹 Simplified step initialization
 		for i, protoStep := range deployment.GetSteps() {
-			step := openapi.V2DeployDeploymentStep{
-				ErrorMessage: nil,
-				CreatedAt:    nil,
-				Message:      nil,
-				Status:       nil,
-			}
+			step := openapi.V2DeployDeploymentStep{}

 			if protoStep.GetStatus() != "" {
svc/api/openapi/openapi-generated.yaml (2)

452-472: Tighten request/response envelopes (additionalProperties: false) and align ID examples

Line 452-462: V2DeployGetDeploymentRequestBody should likely set additionalProperties: false (most request bodies do), to avoid silently accepting/ignoring typos in internal clients. Also, the example uses dep_... but the PR test example uses d_...; please align examples to the canonical ID format used by the system.

Line 463-471: consider additionalProperties: false on V2DeployGetDeploymentResponseBody as well for consistency.

Proposed diff
 V2DeployGetDeploymentRequestBody:
     type: object
     required:
         - deploymentId
     properties:
         deploymentId:
             type: string
             minLength: 1
             description: Unique deployment identifier to retrieve
-            example: "dep_abc123xyz"
+            example: "d_5TV5hCUQ3atBZNz3"
+    additionalProperties: false

 V2DeployGetDeploymentResponseBody:
     type: object
     required:
         - meta
         - data
     properties:
         meta:
             $ref: "#/components/schemas/Meta"
         data:
             $ref: "#/components/schemas/V2DeployGetDeploymentResponseData"
+    additionalProperties: false

2668-2724: Clarify id vs deploymentId, and lock down V2DeployDeploymentStep shape

Line 2668-2705: V2DeployGetDeploymentResponseData returns id, while other deploy responses use deploymentId (e.g., create). Either is fine, but mixing them increases client foot-guns; at minimum, make the description explicit that id is the deployment id (it is currently, but I’d consider renaming for consistency if feasible).

Line 2706-2724: V2DeployDeploymentStep.status is a free-form string with a lower-case example (“completed”), while the top-level status is a strict enum with uppercase values. If step statuses are also finite, prefer an enum (or document allowed values). Also recommend additionalProperties: false (and possibly required) so internal clients can rely on stable shapes.

Proposed diff
 V2DeployGetDeploymentResponseData:
     type: object
     required:
         - id
         - status
     properties:
         id:
             type: string
             description: Unique deployment identifier
-            example: "dep_abc123xyz"
+            example: "d_5TV5hCUQ3atBZNz3"
         status:
             type: string
             description: Current deployment status
             enum:
                 - UNSPECIFIED
                 - PENDING
                 - BUILDING
                 - DEPLOYING
                 - NETWORK
                 - READY
                 - FAILED
             example: "READY"
@@
         steps:
             type: array
             items:
                 $ref: "#/components/schemas/V2DeployDeploymentStep"
             description: Deployment steps with status and messages
+    additionalProperties: false

 V2DeployDeploymentStep:
     type: object
+    additionalProperties: false
     properties:
         status:
             type: string
             description: Step status
             example: "completed"
         message:
             type: string
             description: Step message
             example: "Image pulled successfully"
         errorMessage:
             type: string
             description: Error message if step failed
             example: "Connection timeout"
         createdAt:
             type: integer
             format: int64
             description: Unix timestamp in milliseconds
             example: 1704067200000
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 66a7cac and 738f0b6.

📒 Files selected for processing (18)
  • svc/api/openapi/gen.go
  • svc/api/openapi/openapi-generated.yaml
  • svc/api/openapi/openapi-split.yaml
  • svc/api/openapi/spec/paths/v2/deploy/createDeployment/index.yaml
  • svc/api/openapi/spec/paths/v2/deploy/generateUploadUrl/index.yaml
  • svc/api/openapi/spec/paths/v2/deploy/getDeployment/V2DeployDeploymentStep.yaml
  • svc/api/openapi/spec/paths/v2/deploy/getDeployment/V2DeployGetDeploymentRequestBody.yaml
  • svc/api/openapi/spec/paths/v2/deploy/getDeployment/V2DeployGetDeploymentResponseBody.yaml
  • svc/api/openapi/spec/paths/v2/deploy/getDeployment/V2DeployGetDeploymentResponseData.yaml
  • svc/api/openapi/spec/paths/v2/deploy/getDeployment/index.yaml
  • svc/api/routes/BUILD.bazel
  • svc/api/routes/register.go
  • svc/api/routes/v2_deploy_get_deployment/200_test.go
  • svc/api/routes/v2_deploy_get_deployment/400_test.go
  • svc/api/routes/v2_deploy_get_deployment/401_test.go
  • svc/api/routes/v2_deploy_get_deployment/404_test.go
  • svc/api/routes/v2_deploy_get_deployment/BUILD.bazel
  • svc/api/routes/v2_deploy_get_deployment/handler.go
🧰 Additional context used
📓 Path-based instructions (3)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Organize Go imports in groups separated by blank lines: (1) Standard library, (2) External/third-party packages, (3) Internal packages (github.com/unkeyed/unkey/internal/...), (4) Package-level (github.com/unkeyed/unkey/pkg/...), (5) Service-level (github.com/unkeyed/unkey/svc/...), (6) Generated code (github.com/unkeyed/unkey/gen/...)
Use the fault package for structured error handling with fault.Wrap(), fault.Code(), fault.Internal(), and fault.Public() methods
Use snake_case.go for Go filenames and *_test.go for test files
Use PascalCase for exported Go functions and types, camelCase for unexported functions and types
Use short receiver names like (s *Service) or (h *Handler) in Go methods
Use SCREAMING_SNAKE_CASE for Go constants
Run make bazel after adding new Go files to sync BUILD.bazel files
Run make fmt before committing Go changes to format and lint code

Files:

  • svc/api/routes/v2_deploy_get_deployment/401_test.go
  • svc/api/routes/v2_deploy_get_deployment/200_test.go
  • svc/api/routes/v2_deploy_get_deployment/404_test.go
  • svc/api/routes/register.go
  • svc/api/routes/v2_deploy_get_deployment/400_test.go
  • svc/api/routes/v2_deploy_get_deployment/handler.go
  • svc/api/openapi/gen.go
**/*_test.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*_test.go: Use testify/require for assertions in Go tests instead of manual assertions
Use nested test scenarios with t.Run() in Go tests for better organization and clarity

Files:

  • svc/api/routes/v2_deploy_get_deployment/401_test.go
  • svc/api/routes/v2_deploy_get_deployment/200_test.go
  • svc/api/routes/v2_deploy_get_deployment/404_test.go
  • svc/api/routes/v2_deploy_get_deployment/400_test.go
svc/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Use the pkg/zen custom HTTP framework for Go HTTP handlers with the pattern of binding request bodies, handling business logic, and returning JSON responses

Files:

  • svc/api/routes/v2_deploy_get_deployment/401_test.go
  • svc/api/routes/v2_deploy_get_deployment/200_test.go
  • svc/api/routes/v2_deploy_get_deployment/404_test.go
  • svc/api/routes/register.go
  • svc/api/routes/v2_deploy_get_deployment/400_test.go
  • svc/api/routes/v2_deploy_get_deployment/handler.go
  • svc/api/openapi/gen.go
🧠 Learnings (7)
📓 Common learnings
Learnt from: imeyer
Repo: unkeyed/unkey PR: 3899
File: go/proto/metald/v1/deployment.proto:7-15
Timestamp: 2025-09-01T15:10:44.959Z
Learning: In the unkey/unkey repository, the metald service receives deployment_id values from upstream services rather than generating them internally. The deployment_id field in DeploymentRequest is part of a service-to-service communication pattern.
Learnt from: ogzhanolguncu
Repo: unkeyed/unkey PR: 3297
File: apps/dashboard/lib/trpc/routers/authorization/roles/query.ts:210-323
Timestamp: 2025-06-04T20:13:12.060Z
Learning: The user ogzhanolguncu prefers explicit, duplicated code over abstracted helper functions when it improves readability, even if it means some duplication in filter building functions in the authorization roles query module.
Learnt from: ogzhanolguncu
Repo: unkeyed/unkey PR: 2825
File: apps/dashboard/app/(app)/logs-v2/hooks/use-bookmarked-filters.ts:0-0
Timestamp: 2025-01-30T20:51:44.359Z
Learning: The user (ogzhanolguncu) prefers to handle refactoring suggestions in separate PRs to maintain focus in the current PR.
📚 Learning: 2026-01-13T17:46:51.335Z
Learnt from: CR
Repo: unkeyed/unkey PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T17:46:51.335Z
Learning: Applies to **/*.go : Run `make bazel` after adding new Go files to sync BUILD.bazel files

Applied to files:

  • svc/api/routes/BUILD.bazel
  • svc/api/routes/v2_deploy_get_deployment/BUILD.bazel
📚 Learning: 2025-08-14T16:25:48.167Z
Learnt from: Flo4604
Repo: unkeyed/unkey PR: 3785
File: go/apps/api/routes/v2_keys_reroll_key/401_test.go:52-61
Timestamp: 2025-08-14T16:25:48.167Z
Learning: User Flo4604 requested creation of a GitHub issue to track converting all test files to use table-driven test patterns as a broader codebase improvement, following the suggestion made during review of go/apps/api/routes/v2_keys_reroll_key/401_test.go.

Applied to files:

  • svc/api/routes/v2_deploy_get_deployment/401_test.go
  • svc/api/routes/v2_deploy_get_deployment/200_test.go
  • svc/api/routes/v2_deploy_get_deployment/404_test.go
  • svc/api/routes/v2_deploy_get_deployment/400_test.go
📚 Learning: 2025-09-25T18:49:28.948Z
Learnt from: mcstepp
Repo: unkeyed/unkey PR: 4010
File: go/apps/ctrl/run.go:224-233
Timestamp: 2025-09-25T18:49:28.948Z
Learning: In go/apps/ctrl/run.go, mcstepp prefers to keep ACME service handlers wrapped with auth interceptor during the demo phase for simplicity, even though typically ACME routes would be exempt from authentication. This is part of the temporary demo authentication system.

Applied to files:

  • svc/api/routes/v2_deploy_get_deployment/401_test.go
📚 Learning: 2025-09-01T15:10:44.959Z
Learnt from: imeyer
Repo: unkeyed/unkey PR: 3899
File: go/proto/metald/v1/deployment.proto:7-15
Timestamp: 2025-09-01T15:10:44.959Z
Learning: In the unkey/unkey repository, the metald service receives deployment_id values from upstream services rather than generating them internally. The deployment_id field in DeploymentRequest is part of a service-to-service communication pattern.

Applied to files:

  • svc/api/openapi/spec/paths/v2/deploy/getDeployment/V2DeployGetDeploymentRequestBody.yaml
  • svc/api/routes/register.go
📚 Learning: 2025-09-15T18:12:01.503Z
Learnt from: mcstepp
Repo: unkeyed/unkey PR: 3952
File: go/apps/ctrl/run.go:212-213
Timestamp: 2025-09-15T18:12:01.503Z
Learning: In Go connect-go generated handlers, mux.Handle can accept New*ServiceHandler functions directly even though they return (string, http.Handler) tuples. The pattern mux.Handle(ctrlv1connect.New*ServiceHandler(...)) is valid and compiles successfully in unkey codebase.

Applied to files:

  • svc/api/routes/v2_deploy_get_deployment/handler.go
📚 Learning: 2026-01-13T17:46:51.335Z
Learnt from: CR
Repo: unkeyed/unkey PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T17:46:51.335Z
Learning: Applies to svc/**/*.go : Use the `pkg/zen` custom HTTP framework for Go HTTP handlers with the pattern of binding request bodies, handling business logic, and returning JSON responses

Applied to files:

  • svc/api/routes/v2_deploy_get_deployment/handler.go
🧬 Code graph analysis (4)
svc/api/routes/v2_deploy_get_deployment/401_test.go (3)
pkg/testutil/http.go (1)
  • CallRoute (413-447)
pkg/rpc/interceptor/header.go (1)
  • NewHeaderInjector (22-24)
svc/api/routes/v2_deploy_get_deployment/handler.go (3)
  • Handler (23-28)
  • Request (19-19)
  • Response (20-20)
svc/api/routes/v2_deploy_get_deployment/200_test.go (5)
pkg/testutil/http.go (2)
  • NewHarness (60-209)
  • CallRoute (413-447)
pkg/rpc/interceptor/header.go (1)
  • NewHeaderInjector (22-24)
svc/api/routes/v2_deploy_get_deployment/handler.go (3)
  • Handler (23-28)
  • Request (19-19)
  • Response (20-20)
pkg/testutil/seed/seed.go (1)
  • CreateProjectRequest (124-132)
svc/api/openapi/gen.go (2)
  • Meta (293-296)
  • Set (45-45)
svc/api/routes/v2_deploy_get_deployment/404_test.go (4)
pkg/testutil/http.go (1)
  • CallRoute (413-447)
pkg/rpc/interceptor/header.go (1)
  • NewHeaderInjector (22-24)
svc/api/routes/v2_deploy_get_deployment/handler.go (3)
  • Handler (23-28)
  • Request (19-19)
  • Response (20-20)
pkg/testutil/seed/seed.go (1)
  • CreateProjectRequest (124-132)
svc/api/routes/v2_deploy_get_deployment/400_test.go (5)
pkg/testutil/http.go (1)
  • CallRoute (413-447)
pkg/rpc/interceptor/header.go (1)
  • NewHeaderInjector (22-24)
svc/api/routes/v2_deploy_get_deployment/handler.go (3)
  • Handler (23-28)
  • Request (19-19)
  • Response (20-20)
pkg/testutil/seed/seed.go (1)
  • CreateProjectRequest (124-132)
svc/api/openapi/gen.go (2)
  • BadRequestErrorResponse (79-85)
  • Meta (293-296)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Lint Go Code / Lint
  • GitHub Check: Bazel / Build
  • GitHub Check: Bazel / Test
🔇 Additional comments (25)
svc/api/openapi/spec/paths/v2/deploy/generateUploadUrl/index.yaml (1)

6-14: LGTM!

The internal designation is appropriate. The x-speakeasy-group: internal extension and the description notice clearly communicate that this endpoint is not intended for external consumption and may change without notice.

svc/api/openapi/spec/paths/v2/deploy/createDeployment/index.yaml (1)

6-14: LGTM!

Consistent internal designation pattern with the other deploy endpoints. The changes appropriately mark this endpoint as internal.

svc/api/routes/BUILD.bazel (1)

39-39: LGTM!

The new route dependency is correctly added and maintains alphabetical ordering with the existing dependencies.

svc/api/openapi/spec/paths/v2/deploy/getDeployment/V2DeployGetDeploymentResponseBody.yaml (1)

1-9: LGTM!

The response body schema follows the standard pattern with meta and data fields. The $ref paths are correctly structured.

svc/api/routes/register.go (1)

31-31: LGTM!

The import and route registration follow the established patterns for v2/deploy endpoints. The handler correctly uses CtrlDeploymentClient consistent with v2DeployCreateDeployment.

Also applies to: 341-350

svc/api/routes/v2_deploy_get_deployment/401_test.go (1)

39-53: LGTM!

The test correctly validates that requests with invalid authorization tokens receive a 401 Unauthorized response. Good use of t.Run() for subtest organization and require for assertions as per coding guidelines.

svc/api/openapi/openapi-split.yaml (1)

140-141: LGTM!

The new endpoint path is correctly added following the established pattern and alphabetical ordering within the Deploy Endpoints section.

svc/api/openapi/spec/paths/v2/deploy/getDeployment/V2DeployDeploymentStep.yaml (1)

1-19: Consider specifying required fields for the deployment step schema.

The schema defines all properties as optional. For a deployment step, status and createdAt would typically be essential fields that should always be present. Consider adding a required array if this is the intended contract.

type: object
required:
  - status
  - createdAt
properties:
  # ... existing properties
svc/api/routes/v2_deploy_get_deployment/BUILD.bazel (1)

1-42: LGTM!

The Bazel build configuration is well-structured with appropriate dependencies for both the library and test targets. The test target correctly includes all four test files (200, 400, 401, 404) and necessary test dependencies including testify/require.

svc/api/routes/v2_deploy_get_deployment/400_test.go (3)

1-18: LGTM!

Import organization follows the coding guidelines correctly: standard library first, then external packages, then internal packages.


59-82: LGTM!

Well-structured subtests for deployment ID validation. Good use of t.Run() for organization and testify/require for assertions. The tests properly verify both the HTTP status code and the error response structure.


84-112: LGTM!

Authorization header validation tests are correctly structured. Testing both missing and malformed authorization headers with appropriate 400 status expectations.

svc/api/openapi/spec/paths/v2/deploy/getDeployment/V2DeployGetDeploymentResponseData.yaml (1)

1-36: LGTM!

Well-structured OpenAPI schema with appropriate required fields (id, status), a comprehensive status enum matching the handler implementation, and properly typed optional fields for error messages, hostnames, and deployment steps. Examples are helpful for API consumers.

svc/api/routes/v2_deploy_get_deployment/handler.go (4)

1-16: LGTM!

Import organization follows the coding guidelines correctly: standard library, external packages, generated code, internal services, pkg utilities, and service-level packages.


98-125: LGTM!

Response construction is well-structured with proper nil checks for optional fields. The JSON response follows the zen framework pattern correctly.


128-147: LGTM!

The status enum mapping is comprehensive and includes a sensible default fallback to "UNSPECIFIED" for unknown status values. The enum values match the OpenAPI spec.


38-48: No action needed. The pattern of deferring emit() before error checking is intentional and safe—GetRootKey always returns a safe no-op emit function even when the main operation fails, allowing it to be deferred immediately. This pattern is consistently used across all handlers in the codebase.

svc/api/routes/v2_deploy_get_deployment/200_test.go (3)

1-19: LGTM!

Import organization follows the coding guidelines correctly: standard library, external packages, generated proto code, and internal packages properly grouped.


21-41: LGTM!

Test setup correctly initializes the harness, CTRL client with authorization interceptor, and registers the handler route.


43-87: LGTM!

Well-structured integration test that exercises the full flow: creating workspace, project, environment, deployment, and then verifying the getDeployment endpoint returns the expected data. Good use of t.Run() for organization and comprehensive assertions.

svc/api/openapi/spec/paths/v2/deploy/getDeployment/index.yaml (1)

1-53: LGTM - OpenAPI spec is well-structured.

The endpoint specification follows the existing patterns for deploy endpoints. The internal designation and warning are appropriate given that permission checks are noted for future implementation.

Consider adding a 403 Forbidden response when permission checks are implemented, similar to other secured endpoints.

svc/api/openapi/gen.go (4)

757-770: LGTM - Generated deployment step type.

The V2DeployDeploymentStep struct correctly models deployment steps with optional fields for timestamps, messages, and status.


795-828: LGTM - Generated request/response types are consistent.

The generated types for the getDeployment endpoint follow the established patterns in this file. The response data structure appropriately captures deployment status, error information, hostnames, and execution steps.


2428-2429: LGTM - Request body alias follows established pattern.


30-39: No changes needed. The status constants are properly typed with V2DeployGetDeploymentResponseDataStatus and have no name collisions at package scope. This is correctly generated code from oapi-codegen/v2 v2.5.1.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Copy link
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: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
svc/api/openapi/openapi-generated.yaml (2)

4271-4321: Docs: “appropriate permissions” claim is misleading until permission checks land.

The description says “Requires a valid root key with appropriate permissions” (Line 4276), but the PR objective explicitly notes deployment permission checks will be added later. Consider clarifying that auth is required and authorization is TBD (to avoid consumers inferring enforcement that doesn’t exist yet).


4325-4376: Docs: align wording with current enforcement (auth yes, authorization later).

Same issue as createDeployment: description states “appropriate permissions” (Line 4330-4331) while authorization is explicitly deferred in this PR.

♻️ Duplicate comments (1)
svc/api/openapi/openapi-generated.yaml (1)

2668-2724: Fix inconsistent deployment ID examples (dep_ vs d_).

V2DeployGetDeploymentResponseData.id example is "dep_abc123xyz" (Line 2677) while the request example is "d_abc123xyz" (Line 461). This mismatch has already caused confusion in prior review threads.

Proposed diff
 V2DeployGetDeploymentResponseData:
     type: object
     required:
         - id
         - status
     properties:
         id:
             type: string
             description: Unique deployment identifier
-            example: "dep_abc123xyz"
+            example: "d_abc123xyz"
🧹 Nitpick comments (1)
svc/api/openapi/openapi-generated.yaml (1)

452-471: Consider additionalProperties: false for the new request/response envelope schemas.

Many schemas in this spec are closed objects; these two aren’t, which weakens request validation / client typings.

Proposed diff
 V2DeployGetDeploymentRequestBody:
     type: object
+    additionalProperties: false
     required:
         - deploymentId
     properties:
         deploymentId:
             type: string
             minLength: 1
             description: Unique deployment identifier to retrieve
             example: "d_abc123xyz"
 V2DeployGetDeploymentResponseBody:
     type: object
+    additionalProperties: false
     required:
         - meta
         - data
     properties:
         meta:
             $ref: "#/components/schemas/Meta"
         data:
             $ref: "#/components/schemas/V2DeployGetDeploymentResponseData"
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 738f0b6 and ed4f251.

📒 Files selected for processing (2)
  • svc/api/openapi/openapi-generated.yaml
  • svc/api/openapi/spec/paths/v2/deploy/getDeployment/V2DeployGetDeploymentRequestBody.yaml
🚧 Files skipped from review as they are similar to previous changes (1)
  • svc/api/openapi/spec/paths/v2/deploy/getDeployment/V2DeployGetDeploymentRequestBody.yaml
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: imeyer
Repo: unkeyed/unkey PR: 3899
File: go/proto/metald/v1/deployment.proto:7-15
Timestamp: 2025-09-01T15:10:44.959Z
Learning: In the unkey/unkey repository, the metald service receives deployment_id values from upstream services rather than generating them internally. The deployment_id field in DeploymentRequest is part of a service-to-service communication pattern.
Learnt from: ogzhanolguncu
Repo: unkeyed/unkey PR: 3297
File: apps/dashboard/lib/trpc/routers/authorization/roles/query.ts:210-323
Timestamp: 2025-06-04T20:13:12.060Z
Learning: The user ogzhanolguncu prefers explicit, duplicated code over abstracted helper functions when it improves readability, even if it means some duplication in filter building functions in the authorization roles query module.
Learnt from: ogzhanolguncu
Repo: unkeyed/unkey PR: 2825
File: apps/dashboard/app/(app)/logs-v2/hooks/use-bookmarked-filters.ts:0-0
Timestamp: 2025-01-30T20:51:44.359Z
Learning: The user (ogzhanolguncu) prefers to handle refactoring suggestions in separate PRs to maintain focus in the current PR.
📚 Learning: 2025-10-15T10:12:40.810Z
Learnt from: Flo4604
Repo: unkeyed/unkey PR: 4098
File: go/proto/ctrl/v1/deployment.proto:33-36
Timestamp: 2025-10-15T10:12:40.810Z
Learning: In the Unkey codebase proto files (ctrl/v1/build.proto, ctrl/v1/deployment.proto, hydra/v1/deployment.proto), use `dockerfile_path` (not `docker_file_path`) for consistency in generated Go field names.

Applied to files:

  • svc/api/openapi/openapi-generated.yaml
📚 Learning: 2025-07-25T19:11:00.208Z
Learnt from: mcstepp
Repo: unkeyed/unkey PR: 3662
File: apps/dashboard/lib/trpc/routers/deployment/getOpenApiDiff.ts:110-147
Timestamp: 2025-07-25T19:11:00.208Z
Learning: In apps/dashboard/lib/trpc/routers/deployment/getOpenApiDiff.ts, the user mcstepp prefers to keep mock data fallbacks in POC/demonstration code for simplicity, even if it wouldn't be production-ready. This aligns with the PR being work-in-progress for demonstration purposes.

Applied to files:

  • svc/api/openapi/openapi-generated.yaml
📚 Learning: 2025-07-25T19:09:43.284Z
Learnt from: mcstepp
Repo: unkeyed/unkey PR: 3662
File: apps/dashboard/lib/trpc/routers/deployment/list.ts:11-11
Timestamp: 2025-07-25T19:09:43.284Z
Learning: In apps/dashboard/lib/trpc/routers/deployment/list.ts, the listDeployments procedure intentionally queries the versions table rather than a deployments table. The user mcstepp indicated that renaming the table would require a database migration, which was deferred for the current PR focused on UI features.

Applied to files:

  • svc/api/openapi/openapi-generated.yaml
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Lint Go Code / Lint
  • GitHub Check: Bazel / Build
  • GitHub Check: Bazel / Test
  • GitHub Check: autofix
🔇 Additional comments (1)
svc/api/openapi/openapi-generated.yaml (1)

4376-4429: Clarify authorization requirements in the endpoint documentation.

The status enum is correct and includes all expected values (BUILDING, DEPLOYING, NETWORK, READY, plus UNSPECIFIED, PENDING, FAILED). However, the description states "Requires a valid root key with appropriate permissions" while the implementation only validates root key presence via GetRootKey() without checking specific permissions. Either add permission validation logic or update the description to reflect that only a valid root key is required.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Copy link
Member

@Flo4604 Flo4604 left a comment

Choose a reason for hiding this comment

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

otherwise looks good

Copy link
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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
svc/api/routes/v2_deploy_create_deployment/404_test.go (1)

79-79: Typo in assertion comment.

The comment is incomplete - missing the expected status code.

📝 Suggested fix
-		require.Equal(t, http.StatusInternalServerError, res.Status, "expected , received: %s", res.RawBody)
+		require.Equal(t, http.StatusInternalServerError, res.Status, "expected 500, received: %s", res.RawBody)
🤖 Fix all issues with AI agents
In `@svc/api/openapi/openapi-generated.yaml`:
- Around line 4380-4433: The OpenAPI description for operationId
deploy.getDeployment (path /v2/deploy.getDeployment) incorrectly claims
per-deployment permission enforcement; update the description field to read:
"Requires a valid root key. No per-deployment permission checks are currently
enforced." Alternatively, if you prefer to enforce permissions, modify the
deploy.getDeployment handler (the code path that calls GetRootKey()) to perform
an explicit authorization check against the deployment's workspace/project
before returning the deployment (validate root key scope/ownership) and then
update the OpenAPI to reflect the enforced behavior.
♻️ Duplicate comments (1)
svc/api/routes/v2_deploy_get_deployment/404_test.go (1)

38-53: File name vs. expected status code mismatch.

As noted in the previous review, the file is named 404_test.go but expects HTTP 500. Consider renaming to 500_test.go or tracking the CTRL service fix to return proper 404 status.

🧹 Nitpick comments (5)
pkg/testutil/http.go (2)

192-206: Consider using an HTTP client with timeout for CTRL clients.

Using http.DefaultClient means no request timeout is configured. While this may be acceptable for tests, a hung CTRL service could cause tests to block indefinitely. Consider creating a client with a reasonable timeout.

♻️ Suggested improvement
+	httpClient := &http.Client{
+		Timeout: 30 * time.Second,
+	}
+
 	ctrlDeploymentClient := ctrlv1connect.NewDeploymentServiceClient(
-		http.DefaultClient,
+		httpClient,
 		ctrlURL,
 		connect.WithInterceptors(interceptor.NewHeaderInjector(map[string]string{
 			"Authorization": fmt.Sprintf("Bearer %s", ctrlToken),
 		})),
 	)

 	ctrlBuildClient := ctrlv1connect.NewBuildServiceClient(
-		http.DefaultClient,
+		httpClient,
 		ctrlURL,
 		connect.WithInterceptors(interceptor.NewHeaderInjector(map[string]string{
 			"Authorization": fmt.Sprintf("Bearer %s", ctrlToken),
 		})),
 	)

314-319: Consider a more descriptive default for ProjectSlug.

The default ProjectSlug: "production" could be confusing since "production" typically refers to an environment, not a project. A default like "test-project" (matching ProjectName) would be clearer.

♻️ Suggested improvement
 	config := CreateTestDeploymentSetupOptions{
 		ProjectName:     "test-project",
-		ProjectSlug:     "production",
+		ProjectSlug:     "test-project",
 		EnvironmentSlug: "production",
 		SkipEnvironment: false,
 	}
svc/api/openapi/openapi-generated.yaml (2)

454-474: Consider additionalProperties: false on new getDeployment request/response envelopes for consistency.

Most request/response bodies in this spec are strict; these new ones currently allow extra keys by default.

Proposed change (apply in the source OpenAPI, then regenerate)
 V2DeployGetDeploymentRequestBody:
     type: object
+    additionalProperties: false
     required:
         - deploymentId
     properties:
         deploymentId:
             type: string
             minLength: 1
             pattern: "^d_[a-zA-Z0-9]+$"
             description: Unique deployment identifier to retrieve
             example: "d_abc123xyz"

 V2DeployGetDeploymentResponseBody:
     type: object
+    additionalProperties: false
     required:
         - meta
         - data
     properties:
         meta:
             $ref: "#/components/schemas/Meta"
         data:
             $ref: "#/components/schemas/V2DeployGetDeploymentResponseData"

2672-2728: Document/verify steps[].status semantics vs top-level status enum.

Top-level status is an uppercase enum, while V2DeployDeploymentStep.status is a free-form string with a lowercase example (“completed”). If this is intentional, it’d help to explicitly document expected values/casing to avoid clients building the wrong assumptions.

Optional tightening (only if it matches real responses)
 V2DeployDeploymentStep:
     type: object
+    additionalProperties: false
+    required:
+        - status
+        - createdAt
     properties:
         status:
             type: string
             description: Step status
             example: "completed"
         message:
             type: string
             description: Step message
             example: "Image pulled successfully"
         errorMessage:
             type: string
             description: Error message if step failed
             example: "Connection timeout"
         createdAt:
             type: integer
             format: int64
             description: Unix timestamp in milliseconds
             example: 1704067200000
svc/api/routes/v2_deploy_get_deployment/400_test.go (1)

30-37: Consider removing unused project setup.

The projectID and h.CreateProject() are not used in any of the test requests since getDeployment only requires deploymentId. This setup can be removed to simplify the test.

♻️ Suggested cleanup
 	workspace := h.CreateWorkspace()
 	rootKey := h.CreateRootKey(workspace.ID)
-
-	projectID := uid.New(uid.ProjectPrefix)
-
-	h.CreateProject(seed.CreateProjectRequest{
-		WorkspaceID: workspace.ID,
-		Name:        "test-project",
-		ID:          projectID,
-		Slug:        "production",
-	})

You can also remove the unused imports:

 import (
 	"fmt"
 	"net/http"
 	"testing"

 	"github.com/stretchr/testify/require"
 	"github.com/unkeyed/unkey/pkg/testutil"
-	"github.com/unkeyed/unkey/pkg/testutil/seed"
-	"github.com/unkeyed/unkey/pkg/uid"
 	"github.com/unkeyed/unkey/svc/api/openapi"
 	handler "github.com/unkeyed/unkey/svc/api/routes/v2_deploy_get_deployment"
 )
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ed4f251 and 4cbc475.

📒 Files selected for processing (23)
  • pkg/testutil/BUILD.bazel
  • pkg/testutil/http.go
  • svc/api/openapi/openapi-generated.yaml
  • svc/api/openapi/spec/paths/v2/deploy/createDeployment/V2DeployCreateDeploymentRequestBody.yaml
  • svc/api/openapi/spec/paths/v2/deploy/createDeployment/V2DeployCreateDeploymentResponseData.yaml
  • svc/api/openapi/spec/paths/v2/deploy/generateUploadUrl/V2DeployGenerateUploadUrlRequestBody.yaml
  • svc/api/openapi/spec/paths/v2/deploy/getDeployment/V2DeployGetDeploymentRequestBody.yaml
  • svc/api/openapi/spec/paths/v2/deploy/getDeployment/V2DeployGetDeploymentResponseData.yaml
  • svc/api/routes/v2_deploy_create_deployment/200_test.go
  • svc/api/routes/v2_deploy_create_deployment/400_test.go
  • svc/api/routes/v2_deploy_create_deployment/401_test.go
  • svc/api/routes/v2_deploy_create_deployment/404_test.go
  • svc/api/routes/v2_deploy_create_deployment/BUILD.bazel
  • svc/api/routes/v2_deploy_generate_upload_url/200_test.go
  • svc/api/routes/v2_deploy_generate_upload_url/400_test.go
  • svc/api/routes/v2_deploy_generate_upload_url/401_test.go
  • svc/api/routes/v2_deploy_generate_upload_url/404_test.go
  • svc/api/routes/v2_deploy_generate_upload_url/BUILD.bazel
  • svc/api/routes/v2_deploy_get_deployment/200_test.go
  • svc/api/routes/v2_deploy_get_deployment/400_test.go
  • svc/api/routes/v2_deploy_get_deployment/401_test.go
  • svc/api/routes/v2_deploy_get_deployment/404_test.go
  • svc/api/routes/v2_deploy_get_deployment/BUILD.bazel
💤 Files with no reviewable changes (2)
  • svc/api/routes/v2_deploy_generate_upload_url/BUILD.bazel
  • svc/api/routes/v2_deploy_create_deployment/BUILD.bazel
🚧 Files skipped from review as they are similar to previous changes (3)
  • svc/api/routes/v2_deploy_get_deployment/BUILD.bazel
  • svc/api/openapi/spec/paths/v2/deploy/getDeployment/V2DeployGetDeploymentResponseData.yaml
  • svc/api/routes/v2_deploy_get_deployment/200_test.go
🧰 Additional context used
📓 Path-based instructions (3)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Organize Go imports in groups separated by blank lines: (1) Standard library, (2) External/third-party packages, (3) Internal packages (github.com/unkeyed/unkey/internal/...), (4) Package-level (github.com/unkeyed/unkey/pkg/...), (5) Service-level (github.com/unkeyed/unkey/svc/...), (6) Generated code (github.com/unkeyed/unkey/gen/...)
Use the fault package for structured error handling with fault.Wrap(), fault.Code(), fault.Internal(), and fault.Public() methods
Use snake_case.go for Go filenames and *_test.go for test files
Use PascalCase for exported Go functions and types, camelCase for unexported functions and types
Use short receiver names like (s *Service) or (h *Handler) in Go methods
Use SCREAMING_SNAKE_CASE for Go constants
Run make bazel after adding new Go files to sync BUILD.bazel files
Run make fmt before committing Go changes to format and lint code

Files:

  • svc/api/routes/v2_deploy_generate_upload_url/401_test.go
  • svc/api/routes/v2_deploy_get_deployment/400_test.go
  • svc/api/routes/v2_deploy_create_deployment/400_test.go
  • svc/api/routes/v2_deploy_create_deployment/401_test.go
  • svc/api/routes/v2_deploy_get_deployment/401_test.go
  • pkg/testutil/http.go
  • svc/api/routes/v2_deploy_create_deployment/404_test.go
  • svc/api/routes/v2_deploy_get_deployment/404_test.go
  • svc/api/routes/v2_deploy_create_deployment/200_test.go
  • svc/api/routes/v2_deploy_generate_upload_url/400_test.go
  • svc/api/routes/v2_deploy_generate_upload_url/200_test.go
  • svc/api/routes/v2_deploy_generate_upload_url/404_test.go
**/*_test.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*_test.go: Use testify/require for assertions in Go tests instead of manual assertions
Use nested test scenarios with t.Run() in Go tests for better organization and clarity

Files:

  • svc/api/routes/v2_deploy_generate_upload_url/401_test.go
  • svc/api/routes/v2_deploy_get_deployment/400_test.go
  • svc/api/routes/v2_deploy_create_deployment/400_test.go
  • svc/api/routes/v2_deploy_create_deployment/401_test.go
  • svc/api/routes/v2_deploy_get_deployment/401_test.go
  • svc/api/routes/v2_deploy_create_deployment/404_test.go
  • svc/api/routes/v2_deploy_get_deployment/404_test.go
  • svc/api/routes/v2_deploy_create_deployment/200_test.go
  • svc/api/routes/v2_deploy_generate_upload_url/400_test.go
  • svc/api/routes/v2_deploy_generate_upload_url/200_test.go
  • svc/api/routes/v2_deploy_generate_upload_url/404_test.go
svc/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Use the pkg/zen custom HTTP framework for Go HTTP handlers with the pattern of binding request bodies, handling business logic, and returning JSON responses

Files:

  • svc/api/routes/v2_deploy_generate_upload_url/401_test.go
  • svc/api/routes/v2_deploy_get_deployment/400_test.go
  • svc/api/routes/v2_deploy_create_deployment/400_test.go
  • svc/api/routes/v2_deploy_create_deployment/401_test.go
  • svc/api/routes/v2_deploy_get_deployment/401_test.go
  • svc/api/routes/v2_deploy_create_deployment/404_test.go
  • svc/api/routes/v2_deploy_get_deployment/404_test.go
  • svc/api/routes/v2_deploy_create_deployment/200_test.go
  • svc/api/routes/v2_deploy_generate_upload_url/400_test.go
  • svc/api/routes/v2_deploy_generate_upload_url/200_test.go
  • svc/api/routes/v2_deploy_generate_upload_url/404_test.go
🧠 Learnings (16)
📓 Common learnings
Learnt from: imeyer
Repo: unkeyed/unkey PR: 3899
File: go/proto/metald/v1/deployment.proto:7-15
Timestamp: 2025-09-01T15:10:44.959Z
Learning: In the unkey/unkey repository, the metald service receives deployment_id values from upstream services rather than generating them internally. The deployment_id field in DeploymentRequest is part of a service-to-service communication pattern.
Learnt from: ogzhanolguncu
Repo: unkeyed/unkey PR: 3297
File: apps/dashboard/lib/trpc/routers/authorization/roles/query.ts:210-323
Timestamp: 2025-06-04T20:13:12.060Z
Learning: The user ogzhanolguncu prefers explicit, duplicated code over abstracted helper functions when it improves readability, even if it means some duplication in filter building functions in the authorization roles query module.
Learnt from: ogzhanolguncu
Repo: unkeyed/unkey PR: 2825
File: apps/dashboard/app/(app)/logs-v2/hooks/use-bookmarked-filters.ts:0-0
Timestamp: 2025-01-30T20:51:44.359Z
Learning: The user (ogzhanolguncu) prefers to handle refactoring suggestions in separate PRs to maintain focus in the current PR.
📚 Learning: 2025-09-15T18:12:01.503Z
Learnt from: mcstepp
Repo: unkeyed/unkey PR: 3952
File: go/apps/ctrl/run.go:212-213
Timestamp: 2025-09-15T18:12:01.503Z
Learning: In Go connect-go generated handlers, mux.Handle can accept New*ServiceHandler functions directly even though they return (string, http.Handler) tuples. The pattern mux.Handle(ctrlv1connect.New*ServiceHandler(...)) is valid and compiles successfully in unkey codebase.

Applied to files:

  • svc/api/routes/v2_deploy_generate_upload_url/401_test.go
  • svc/api/routes/v2_deploy_create_deployment/401_test.go
  • svc/api/routes/v2_deploy_generate_upload_url/404_test.go
📚 Learning: 2025-09-25T18:49:28.948Z
Learnt from: mcstepp
Repo: unkeyed/unkey PR: 4010
File: go/apps/ctrl/run.go:224-233
Timestamp: 2025-09-25T18:49:28.948Z
Learning: In go/apps/ctrl/run.go, mcstepp prefers to keep ACME service handlers wrapped with auth interceptor during the demo phase for simplicity, even though typically ACME routes would be exempt from authentication. This is part of the temporary demo authentication system.

Applied to files:

  • svc/api/routes/v2_deploy_generate_upload_url/401_test.go
  • svc/api/routes/v2_deploy_create_deployment/401_test.go
  • svc/api/routes/v2_deploy_get_deployment/401_test.go
📚 Learning: 2026-01-13T17:46:51.335Z
Learnt from: CR
Repo: unkeyed/unkey PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T17:46:51.335Z
Learning: Applies to svc/**/*.go : Use the `pkg/zen` custom HTTP framework for Go HTTP handlers with the pattern of binding request bodies, handling business logic, and returning JSON responses

Applied to files:

  • svc/api/routes/v2_deploy_generate_upload_url/401_test.go
  • svc/api/routes/v2_deploy_create_deployment/401_test.go
  • svc/api/routes/v2_deploy_generate_upload_url/404_test.go
📚 Learning: 2025-08-14T16:25:48.167Z
Learnt from: Flo4604
Repo: unkeyed/unkey PR: 3785
File: go/apps/api/routes/v2_keys_reroll_key/401_test.go:52-61
Timestamp: 2025-08-14T16:25:48.167Z
Learning: User Flo4604 requested creation of a GitHub issue to track converting all test files to use table-driven test patterns as a broader codebase improvement, following the suggestion made during review of go/apps/api/routes/v2_keys_reroll_key/401_test.go.

Applied to files:

  • svc/api/routes/v2_deploy_get_deployment/400_test.go
  • svc/api/routes/v2_deploy_get_deployment/401_test.go
  • svc/api/routes/v2_deploy_get_deployment/404_test.go
  • svc/api/routes/v2_deploy_create_deployment/200_test.go
  • svc/api/routes/v2_deploy_generate_upload_url/200_test.go
📚 Learning: 2026-01-13T17:46:51.335Z
Learnt from: CR
Repo: unkeyed/unkey PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T17:46:51.335Z
Learning: Applies to **/*_test.go : Use `testify/require` for assertions in Go tests instead of manual assertions

Applied to files:

  • svc/api/routes/v2_deploy_create_deployment/400_test.go
  • svc/api/routes/v2_deploy_create_deployment/404_test.go
  • svc/api/routes/v2_deploy_generate_upload_url/400_test.go
📚 Learning: 2025-09-01T01:57:42.227Z
Learnt from: imeyer
Repo: unkeyed/unkey PR: 3899
File: go/proto/metald/v1/metald.proto:5-9
Timestamp: 2025-09-01T01:57:42.227Z
Learning: In the unkeyed/unkey repository, buf is configured to properly resolve metald proto imports like "metald/v1/vm.proto" without needing the full "go/proto/" prefix. The buf lint command `buf lint --path proto/metald` passes successfully with these relative import paths.

Applied to files:

  • svc/api/routes/v2_deploy_create_deployment/400_test.go
  • svc/api/routes/v2_deploy_generate_upload_url/400_test.go
  • pkg/testutil/BUILD.bazel
📚 Learning: 2025-09-25T15:12:21.739Z
Learnt from: mcstepp
Repo: unkeyed/unkey PR: 4010
File: go/cmd/deploy/control_plane.go:64-69
Timestamp: 2025-09-25T15:12:21.739Z
Learning: The API key authentication system in go/cmd/deploy/control_plane.go and related files is temporary demo code that will be replaced with a proper authentication system for private beta.

Applied to files:

  • svc/api/routes/v2_deploy_create_deployment/401_test.go
  • svc/api/routes/v2_deploy_create_deployment/200_test.go
📚 Learning: 2025-09-01T15:10:44.959Z
Learnt from: imeyer
Repo: unkeyed/unkey PR: 3899
File: go/proto/metald/v1/deployment.proto:7-15
Timestamp: 2025-09-01T15:10:44.959Z
Learning: In the unkey/unkey repository, the metald service receives deployment_id values from upstream services rather than generating them internally. The deployment_id field in DeploymentRequest is part of a service-to-service communication pattern.

Applied to files:

  • svc/api/openapi/spec/paths/v2/deploy/createDeployment/V2DeployCreateDeploymentResponseData.yaml
  • svc/api/openapi/spec/paths/v2/deploy/getDeployment/V2DeployGetDeploymentRequestBody.yaml
  • svc/api/openapi/openapi-generated.yaml
📚 Learning: 2025-08-08T15:09:01.312Z
Learnt from: Flo4604
Repo: unkeyed/unkey PR: 3753
File: go/apps/api/openapi/config.yaml:9-10
Timestamp: 2025-08-08T15:09:01.312Z
Learning: Repo unkeyed/unkey: For go/apps/api/openapi, oapi-codegen is used and does not support OpenAPI 3.1 union types like [T, "null"]; an overlay step is required to downconvert to 3.0-style nullable before code generation.

Applied to files:

  • pkg/testutil/http.go
  • svc/api/routes/v2_deploy_generate_upload_url/404_test.go
📚 Learning: 2025-09-11T08:17:21.423Z
Learnt from: Flo4604
Repo: unkeyed/unkey PR: 3944
File: go/apps/ctrl/services/deployment/fallbacks/k8s.go:236-247
Timestamp: 2025-09-11T08:17:21.423Z
Learning: The K8s fallback backend in go/apps/ctrl/services/deployment/fallbacks/k8s.go is specifically designed for scenarios where the ctrl service runs inside a Kubernetes cluster. It should not be used when ctrl runs outside the cluster - in those cases, the Docker fallback should be used instead.

Applied to files:

  • pkg/testutil/http.go
  • svc/api/routes/v2_deploy_create_deployment/404_test.go
  • svc/api/routes/v2_deploy_create_deployment/200_test.go
📚 Learning: 2025-10-15T10:12:40.810Z
Learnt from: Flo4604
Repo: unkeyed/unkey PR: 4098
File: go/proto/ctrl/v1/deployment.proto:33-36
Timestamp: 2025-10-15T10:12:40.810Z
Learning: In the Unkey codebase proto files (ctrl/v1/build.proto, ctrl/v1/deployment.proto, hydra/v1/deployment.proto), use `dockerfile_path` (not `docker_file_path`) for consistency in generated Go field names.

Applied to files:

  • svc/api/routes/v2_deploy_get_deployment/404_test.go
  • svc/api/openapi/spec/paths/v2/deploy/getDeployment/V2DeployGetDeploymentRequestBody.yaml
  • svc/api/openapi/openapi-generated.yaml
📚 Learning: 2025-09-12T18:11:33.481Z
Learnt from: mcstepp
Repo: unkeyed/unkey PR: 3952
File: apps/dashboard/lib/trpc/routers/deploy/rollback.ts:23-24
Timestamp: 2025-09-12T18:11:33.481Z
Learning: In apps/dashboard/lib/trpc/routers/deploy/rollback.ts, the CTRL_URL environment variable should fail fast with a clear error message if missing in non-development environments, rather than defaulting to localhost which can mask production configuration issues.

Applied to files:

  • svc/api/routes/v2_deploy_get_deployment/404_test.go
📚 Learning: 2025-01-30T20:51:44.359Z
Learnt from: ogzhanolguncu
Repo: unkeyed/unkey PR: 2825
File: apps/dashboard/app/(app)/logs-v2/hooks/use-bookmarked-filters.ts:0-0
Timestamp: 2025-01-30T20:51:44.359Z
Learning: The user (ogzhanolguncu) prefers to handle refactoring suggestions in separate PRs to maintain focus in the current PR.

Applied to files:

  • svc/api/openapi/spec/paths/v2/deploy/getDeployment/V2DeployGetDeploymentRequestBody.yaml
📚 Learning: 2025-07-25T19:11:00.208Z
Learnt from: mcstepp
Repo: unkeyed/unkey PR: 3662
File: apps/dashboard/lib/trpc/routers/deployment/getOpenApiDiff.ts:110-147
Timestamp: 2025-07-25T19:11:00.208Z
Learning: In apps/dashboard/lib/trpc/routers/deployment/getOpenApiDiff.ts, the user mcstepp prefers to keep mock data fallbacks in POC/demonstration code for simplicity, even if it wouldn't be production-ready. This aligns with the PR being work-in-progress for demonstration purposes.

Applied to files:

  • svc/api/openapi/openapi-generated.yaml
📚 Learning: 2025-07-25T19:09:43.284Z
Learnt from: mcstepp
Repo: unkeyed/unkey PR: 3662
File: apps/dashboard/lib/trpc/routers/deployment/list.ts:11-11
Timestamp: 2025-07-25T19:09:43.284Z
Learning: In apps/dashboard/lib/trpc/routers/deployment/list.ts, the listDeployments procedure intentionally queries the versions table rather than a deployments table. The user mcstepp indicated that renaming the table would require a database migration, which was deferred for the current PR focused on UI features.

Applied to files:

  • svc/api/openapi/openapi-generated.yaml
🧬 Code graph analysis (5)
svc/api/routes/v2_deploy_get_deployment/400_test.go (4)
pkg/testutil/http.go (1)
  • CallRoute (512-546)
svc/api/routes/v2_deploy_get_deployment/handler.go (3)
  • Handler (23-28)
  • Request (19-19)
  • Response (20-20)
pkg/testutil/seed/seed.go (1)
  • CreateProjectRequest (124-132)
svc/api/openapi/gen.go (2)
  • BadRequestErrorResponse (79-85)
  • Meta (293-296)
svc/api/routes/v2_deploy_get_deployment/401_test.go (3)
svc/api/routes/v2_deploy_generate_upload_url/401_test.go (1)
  • TestUnauthorizedAccess (14-51)
pkg/testutil/http.go (2)
  • NewHarness (67-238)
  • CallRoute (512-546)
svc/api/routes/v2_deploy_get_deployment/handler.go (3)
  • Handler (23-28)
  • Request (19-19)
  • Response (20-20)
pkg/testutil/http.go (2)
pkg/rpc/interceptor/header.go (1)
  • NewHeaderInjector (22-24)
pkg/testutil/seed/seed.go (3)
  • CreateProjectRequest (124-132)
  • New (39-46)
  • CreateEnvironmentRequest (168-176)
svc/api/routes/v2_deploy_get_deployment/404_test.go (3)
pkg/testutil/http.go (1)
  • CallRoute (512-546)
svc/api/routes/v2_deploy_get_deployment/handler.go (3)
  • Handler (23-28)
  • Request (19-19)
  • Response (20-20)
pkg/testutil/seed/seed.go (1)
  • CreateProjectRequest (124-132)
svc/api/routes/v2_deploy_generate_upload_url/200_test.go (2)
pkg/testutil/http.go (1)
  • CreateTestDeploymentSetupOptions (303-308)
svc/api/routes/v2_deploy_generate_upload_url/handler.go (1)
  • Request (19-19)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Lint Go Code / Lint
  • GitHub Check: Bazel / Build
  • GitHub Check: Bazel / Test
  • GitHub Check: autofix
🔇 Additional comments (23)
svc/api/openapi/spec/paths/v2/deploy/createDeployment/V2DeployCreateDeploymentResponseData.yaml (1)

8-8: LGTM!

The example value is now consistent with the actual deployment ID format (d_ prefix) as demonstrated in the PR testing instructions.

svc/api/openapi/spec/paths/v2/deploy/getDeployment/V2DeployGetDeploymentRequestBody.yaml (1)

1-10: LGTM!

The schema properly defines the request body with appropriate validation:

  • Required deploymentId field correctly specified
  • Regex pattern ^d_[a-zA-Z0-9]+$ validates the expected deployment ID format
  • Example now uses the correct d_ prefix matching actual deployment IDs

Good follow-up on the previous review feedback by adding the pattern validation and fixing the example format.

pkg/testutil/BUILD.bazel (1)

9-33: LGTM!

The new Bazel dependencies correctly support the CTRL client integration added to the test harness. The additions (ctrlv1connect, interceptor, uid, connect) align with the imports in http.go.

svc/api/openapi/spec/paths/v2/deploy/createDeployment/V2DeployCreateDeploymentRequestBody.yaml (1)

13-13: LGTM!

The pattern validation is consistent with the generateUploadUrl endpoint, ensuring uniform input validation across deployment-related endpoints.

svc/api/routes/v2_deploy_generate_upload_url/404_test.go (1)

18-23: LGTM!

Good refactor to use the harness-provided CtrlBuildClient instead of manually creating the CTRL client in each test. This reduces boilerplate and centralizes client configuration.

pkg/testutil/http.go (2)

62-63: LGTM!

Good addition of CTRL service clients to the harness, enabling deployment-related tests to use pre-configured clients.


310-362: Well-structured test helper.

The CreateTestDeploymentSetup helper effectively reduces boilerplate in deployment tests by encapsulating workspace, project, and environment creation with sensible defaults and customization options.

svc/api/openapi/spec/paths/v2/deploy/generateUploadUrl/V2DeployGenerateUploadUrlRequestBody.yaml (1)

10-10: Pattern is correct and matches UID generation format.

The pattern ^proj_[a-zA-Z0-9]+$ correctly validates project IDs generated by uid.New(uid.ProjectPrefix). The uid package encodes random bytes using the Bitcoin Base58 alphabet (which contains only alphanumeric characters, excluding 0, I, O, i, l, o), and the final format is proj_ followed by the base58-encoded value. The pattern appropriately enforces alphanumeric characters only, with no underscores, hyphens, or special characters in the encoded portion.

svc/api/openapi/openapi-generated.yaml (3)

2649-2657: Example deploymentId updated to d_… prefix — good.


4273-4380: Internal tagging looks consistent; double-check how these endpoints are surfaced to consumers.

x-speakeasy-group: internal + the “INTERNAL” disclaimer is clear, but these paths are still present in the generated OpenAPI. Verify your doc/SDK generation actually hides or segregates them as intended.


389-444: No action required on projectId regex pattern.

The regex ^proj_[a-zA-Z0-9]+$ already exists in the source spec files and is consistent with actual ID generation logic (proj_${generateRandomString(10)}). Examples like proj_your_project_id are intentionally treated as invalid placeholders in validation checks, not legitimate ID formats. The pattern correctly constrains to alphanumeric characters only and does not reject valid production IDs.

Likely an incorrect or invalid review comment.

svc/api/routes/v2_deploy_generate_upload_url/401_test.go (1)

17-22: LGTM!

The migration to harness-provided h.CtrlBuildClient is consistent with the test refactoring pattern across the PR and reduces boilerplate.

svc/api/routes/v2_deploy_create_deployment/401_test.go (1)

18-23: LGTM!

The migration to h.CtrlDeploymentClient is consistent with the harness-based pattern used across the test suite.

svc/api/routes/v2_deploy_get_deployment/401_test.go (1)

1-38: LGTM!

The new unauthorized access test follows the established pattern and correctly tests that invalid authorization tokens return 401. The minimal setup (no workspace/project creation) is appropriate since authorization validation precedes database lookups.

svc/api/routes/v2_deploy_create_deployment/404_test.go (2)

19-24: LGTM!

The migration to h.CtrlDeploymentClient is consistent with the harness-based pattern.


81-83: Verify error type for environment not found case.

The test expects project_not_found error type and "Project not found." detail when testing the environment not found scenario. This seems semantically incorrect - an environment-specific lookup failure should ideally return an environment-related error.

If this is the current handler behavior (returning project_not_found for environment lookups), consider opening an issue to track returning a more specific error like environment_not_found.

svc/api/routes/v2_deploy_get_deployment/404_test.go (1)

1-53: LGTM!

The test correctly validates the deployment not found scenario with proper harness setup and assertions.

svc/api/routes/v2_deploy_generate_upload_url/400_test.go (1)

23-23: LGTM!

The change to use h.CtrlBuildClient from the test harness simplifies test setup and aligns with the broader refactoring pattern across the deployment route tests.

svc/api/routes/v2_deploy_create_deployment/400_test.go (1)

23-23: LGTM!

The switch to h.CtrlDeploymentClient is consistent with the harness-based client approach used across other deployment route tests.

svc/api/routes/v2_deploy_generate_upload_url/200_test.go (1)

20-36: LGTM!

The refactoring to use h.CreateTestDeploymentSetup with SkipEnvironment: true is appropriate since upload URL generation doesn't require an environment. The test setup is now cleaner and consistent with the harness-based approach.

svc/api/routes/v2_deploy_get_deployment/400_test.go (1)

1-98: Well-structured test file for the new getDeployment endpoint.

The test covers essential bad request scenarios: missing/empty deploymentId and authorization issues. The implementation follows established patterns with proper use of testify/require and t.Run() for organization. As per coding guidelines, assertions use require consistently.

svc/api/routes/v2_deploy_create_deployment/200_test.go (2)

22-22: LGTM!

The switch to h.CtrlDeploymentClient aligns with the harness-based approach across deployment tests.


26-133: Good use of CreateTestDeploymentSetup with varied configurations.

Each subtest appropriately creates its own isolated setup with relevant options:

  • Default setup for basic docker image test
  • Custom project/environment slugs for build context test
  • Custom project name for git commit info test

This provides good test isolation and demonstrates the flexibility of the helper.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Copy link
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

🤖 Fix all issues with AI agents
In `@pkg/testutil/http.go`:
- Around line 314-319: The default ProjectSlug in the
CreateTestDeploymentSetupOptions literal is incorrectly set to "production";
update the ProjectSlug field to "test-project" so it matches ProjectName and
avoids confusion with EnvironmentSlug—locate the
CreateTestDeploymentSetupOptions initialization (the config variable) and change
ProjectSlug: "production" to ProjectSlug: "test-project".
♻️ Duplicate comments (1)
svc/api/openapi/openapi-generated.yaml (1)

4273-4326: Docs still claim “appropriate permissions” despite deployment authz being intentionally missing

Given the PR objective (“permission checks will be added later”), the OpenAPI descriptions should not imply authorization enforcement.

Proposed doc wording fix (apply to createDeployment, generateUploadUrl, getDeployment)
-                **Authentication**: Requires a valid root key with appropriate permissions.
+                **Authentication**: Requires a valid root key.
+                **Authorization**: No per-project/deployment permission checks are currently enforced.

Also applies to: 4327-4379, 4380-4433

🧹 Nitpick comments (2)
svc/api/openapi/openapi-generated.yaml (1)

2672-2729: Consider making V2DeployDeploymentStep.createdAt (and possibly status) required if always present

Right now V2DeployDeploymentStep has no required fields, so clients can’t rely on timestamps or status being set even when steps is returned.

pkg/testutil/http.go (1)

188-206: Consider extracting the shared interceptor to reduce duplication.

Both clients use the same authorization header interceptor. Extracting it to a variable improves readability and ensures consistency.

♻️ Suggested refactor
 // Get CTRL service URL and token
 ctrlURL, ctrlToken := containers.ControlPlane(t)

+// Create shared auth interceptor for CTRL clients
+ctrlAuthInterceptor := interceptor.NewHeaderInjector(map[string]string{
+	"Authorization": fmt.Sprintf("Bearer %s", ctrlToken),
+})
+
 // Create CTRL clients
 ctrlDeploymentClient := ctrlv1connect.NewDeploymentServiceClient(
 	http.DefaultClient,
 	ctrlURL,
-	connect.WithInterceptors(interceptor.NewHeaderInjector(map[string]string{
-		"Authorization": fmt.Sprintf("Bearer %s", ctrlToken),
-	})),
+	connect.WithInterceptors(ctrlAuthInterceptor),
 )

 ctrlBuildClient := ctrlv1connect.NewBuildServiceClient(
 	http.DefaultClient,
 	ctrlURL,
-	connect.WithInterceptors(interceptor.NewHeaderInjector(map[string]string{
-		"Authorization": fmt.Sprintf("Bearer %s", ctrlToken),
-	})),
+	connect.WithInterceptors(ctrlAuthInterceptor),
 )
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4cbc475 and 2459546.

📒 Files selected for processing (3)
  • pkg/testutil/http.go
  • svc/api/openapi/gen.go
  • svc/api/openapi/openapi-generated.yaml
🧰 Additional context used
📓 Path-based instructions (2)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Organize Go imports in groups separated by blank lines: (1) Standard library, (2) External/third-party packages, (3) Internal packages (github.com/unkeyed/unkey/internal/...), (4) Package-level (github.com/unkeyed/unkey/pkg/...), (5) Service-level (github.com/unkeyed/unkey/svc/...), (6) Generated code (github.com/unkeyed/unkey/gen/...)
Use the fault package for structured error handling with fault.Wrap(), fault.Code(), fault.Internal(), and fault.Public() methods
Use snake_case.go for Go filenames and *_test.go for test files
Use PascalCase for exported Go functions and types, camelCase for unexported functions and types
Use short receiver names like (s *Service) or (h *Handler) in Go methods
Use SCREAMING_SNAKE_CASE for Go constants
Run make bazel after adding new Go files to sync BUILD.bazel files
Run make fmt before committing Go changes to format and lint code

Files:

  • pkg/testutil/http.go
  • svc/api/openapi/gen.go
svc/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Use the pkg/zen custom HTTP framework for Go HTTP handlers with the pattern of binding request bodies, handling business logic, and returning JSON responses

Files:

  • svc/api/openapi/gen.go
🧠 Learnings (10)
📓 Common learnings
Learnt from: imeyer
Repo: unkeyed/unkey PR: 3899
File: go/proto/metald/v1/deployment.proto:7-15
Timestamp: 2025-09-01T15:10:44.959Z
Learning: In the unkey/unkey repository, the metald service receives deployment_id values from upstream services rather than generating them internally. The deployment_id field in DeploymentRequest is part of a service-to-service communication pattern.
Learnt from: ogzhanolguncu
Repo: unkeyed/unkey PR: 3297
File: apps/dashboard/lib/trpc/routers/authorization/roles/query.ts:210-323
Timestamp: 2025-06-04T20:13:12.060Z
Learning: The user ogzhanolguncu prefers explicit, duplicated code over abstracted helper functions when it improves readability, even if it means some duplication in filter building functions in the authorization roles query module.
Learnt from: ogzhanolguncu
Repo: unkeyed/unkey PR: 2825
File: apps/dashboard/app/(app)/logs-v2/hooks/use-bookmarked-filters.ts:0-0
Timestamp: 2025-01-30T20:51:44.359Z
Learning: The user (ogzhanolguncu) prefers to handle refactoring suggestions in separate PRs to maintain focus in the current PR.
📚 Learning: 2025-08-08T15:09:01.312Z
Learnt from: Flo4604
Repo: unkeyed/unkey PR: 3753
File: go/apps/api/openapi/config.yaml:9-10
Timestamp: 2025-08-08T15:09:01.312Z
Learning: Repo unkeyed/unkey: For go/apps/api/openapi, oapi-codegen is used and does not support OpenAPI 3.1 union types like [T, "null"]; an overlay step is required to downconvert to 3.0-style nullable before code generation.

Applied to files:

  • pkg/testutil/http.go
📚 Learning: 2025-09-11T08:17:21.423Z
Learnt from: Flo4604
Repo: unkeyed/unkey PR: 3944
File: go/apps/ctrl/services/deployment/fallbacks/k8s.go:236-247
Timestamp: 2025-09-11T08:17:21.423Z
Learning: The K8s fallback backend in go/apps/ctrl/services/deployment/fallbacks/k8s.go is specifically designed for scenarios where the ctrl service runs inside a Kubernetes cluster. It should not be used when ctrl runs outside the cluster - in those cases, the Docker fallback should be used instead.

Applied to files:

  • pkg/testutil/http.go
📚 Learning: 2025-10-15T10:12:40.810Z
Learnt from: Flo4604
Repo: unkeyed/unkey PR: 4098
File: go/proto/ctrl/v1/deployment.proto:33-36
Timestamp: 2025-10-15T10:12:40.810Z
Learning: In the Unkey codebase proto files (ctrl/v1/build.proto, ctrl/v1/deployment.proto, hydra/v1/deployment.proto), use `dockerfile_path` (not `docker_file_path`) for consistency in generated Go field names.

Applied to files:

  • svc/api/openapi/openapi-generated.yaml
📚 Learning: 2025-07-25T19:11:00.208Z
Learnt from: mcstepp
Repo: unkeyed/unkey PR: 3662
File: apps/dashboard/lib/trpc/routers/deployment/getOpenApiDiff.ts:110-147
Timestamp: 2025-07-25T19:11:00.208Z
Learning: In apps/dashboard/lib/trpc/routers/deployment/getOpenApiDiff.ts, the user mcstepp prefers to keep mock data fallbacks in POC/demonstration code for simplicity, even if it wouldn't be production-ready. This aligns with the PR being work-in-progress for demonstration purposes.

Applied to files:

  • svc/api/openapi/openapi-generated.yaml
📚 Learning: 2025-07-25T19:09:43.284Z
Learnt from: mcstepp
Repo: unkeyed/unkey PR: 3662
File: apps/dashboard/lib/trpc/routers/deployment/list.ts:11-11
Timestamp: 2025-07-25T19:09:43.284Z
Learning: In apps/dashboard/lib/trpc/routers/deployment/list.ts, the listDeployments procedure intentionally queries the versions table rather than a deployments table. The user mcstepp indicated that renaming the table would require a database migration, which was deferred for the current PR focused on UI features.

Applied to files:

  • svc/api/openapi/openapi-generated.yaml
📚 Learning: 2025-07-03T05:58:10.699Z
Learnt from: Flo4604
Repo: unkeyed/unkey PR: 3421
File: go/apps/api/openapi/openapi.yaml:196-200
Timestamp: 2025-07-03T05:58:10.699Z
Learning: In the Unkey codebase, OpenAPI 3.1 is used, which allows sibling keys (such as `description`) alongside `$ref` in schema objects. Do not flag this as an error in future reviews.

Applied to files:

  • svc/api/openapi/openapi-generated.yaml
📚 Learning: 2025-09-25T15:12:21.739Z
Learnt from: mcstepp
Repo: unkeyed/unkey PR: 4010
File: go/cmd/deploy/control_plane.go:64-69
Timestamp: 2025-09-25T15:12:21.739Z
Learning: The API key authentication system in go/cmd/deploy/control_plane.go and related files is temporary demo code that will be replaced with a proper authentication system for private beta.

Applied to files:

  • svc/api/openapi/openapi-generated.yaml
📚 Learning: 2025-07-16T17:51:57.297Z
Learnt from: chronark
Repo: unkeyed/unkey PR: 3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
Learning: In the Unkey API OpenAPI schema, the permissions query regex for the verifyKey endpoint intentionally allows all whitespace characters (including tabs and newlines) via `\s`. Do not flag this as an error in future reviews.

Applied to files:

  • svc/api/openapi/openapi-generated.yaml
📚 Learning: 2025-09-01T15:10:44.959Z
Learnt from: imeyer
Repo: unkeyed/unkey PR: 3899
File: go/proto/metald/v1/deployment.proto:7-15
Timestamp: 2025-09-01T15:10:44.959Z
Learning: In the unkey/unkey repository, the metald service receives deployment_id values from upstream services rather than generating them internally. The deployment_id field in DeploymentRequest is part of a service-to-service communication pattern.

Applied to files:

  • svc/api/openapi/openapi-generated.yaml
🧬 Code graph analysis (1)
pkg/testutil/http.go (3)
pkg/rpc/interceptor/header.go (1)
  • NewHeaderInjector (22-24)
svc/api/integration/harness.go (2)
  • Harness (36-47)
  • New (56-118)
pkg/testutil/seed/seed.go (3)
  • CreateProjectRequest (124-132)
  • New (39-46)
  • CreateEnvironmentRequest (168-176)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Bazel / Build
  • GitHub Check: Bazel / Test
🔇 Additional comments (12)
svc/api/openapi/gen.go (4)

30-39: LGTM - Status constants follow established patterns.

The new deployment status constants use SCREAMING_SNAKE_CASE as required by the coding guidelines for Go constants and are consistent with other status constant patterns in the file (e.g., V2KeysVerifyKeyResponseDataCode at lines 49-58).


757-770: LGTM - Deployment step type is well-structured.

The V2DeployDeploymentStep type correctly uses pointer types for all optional fields with appropriate omitempty JSON tags, following the established pattern in this generated file.


795-828: LGTM - GetDeployment request/response types are consistent.

The new types follow the established patterns in this file:

  • Request body contains only the required DeploymentId identifier
  • Response body uses the standard Data + Meta structure
  • Response data includes required fields (Id, Status) and appropriate optional fields
  • Status type provides type safety by referencing the constants defined at lines 30-39

2428-2429: LGTM - JSON request body type alias follows established pattern.

The DeployGetDeploymentJSONRequestBody alias is consistent with other request body aliases in this generated file (e.g., DeployCreateDeploymentJSONRequestBody at line 2423).

svc/api/openapi/openapi-generated.yaml (4)

433-444: Keep projectId validation consistent across deploy endpoints

Good to see the same projectId pattern applied here—just ensure it matches real IDs (same concern as createDeployment).


454-475: GetDeployment request/response envelopes look consistent with the rest of v2

Request body is minimal and the deploymentId pattern aligns with the d_ prefix used elsewhere.


2649-2657: Good: deployment ID example prefix corrected to d_

This avoids confusing clients with dep_ examples when the system uses d_….


389-423: No issues found with the projectId regex pattern. All project IDs in the codebase use alphanumeric characters only (e.g., proj_test456, proj_123abc, proj_flo). The pattern ^proj_[a-zA-Z0-9]+$ correctly matches the actual ID format in use and will not cause avoidable rejections.

pkg/testutil/http.go (4)

3-40: LGTM!

The new imports are correctly placed within the existing import structure and all appear to be used in the new code.


62-63: LGTM!

The new CTRL client fields are appropriately named and exported for test access.


222-223: LGTM!

The CTRL clients are correctly wired into the Harness struct.


311-366: LGTM!

The CreateTestDeploymentSetup method follows the established pattern in this file (similar to SetupAnalytics), correctly uses t.Helper(), and properly handles the optional environment creation based on SkipEnvironment. The resource creation order (workspace → root key → project → environment) is correct.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@ogzhanolguncu ogzhanolguncu merged commit d59f120 into main Jan 15, 2026
16 checks passed
@ogzhanolguncu ogzhanolguncu deleted the get-deployment-api branch January 15, 2026 11:45
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