Skip to content

feat: slimmer CLI flags with sensible defaults#4056

Merged
mcstepp merged 14 commits intomainfrom
pretty-cmd
Oct 7, 2025
Merged

feat: slimmer CLI flags with sensible defaults#4056
mcstepp merged 14 commits intomainfrom
pretty-cmd

Conversation

@mcstepp
Copy link
Collaborator

@mcstepp mcstepp commented Oct 2, 2025

What does this PR do?

This PR simplifies the unkey deploy command by automatically inferring the workspace ID from the project ID on the server side, eliminating the need for users to specify it.

⚠️ Ctrl Service needs to be deployed after merging. ⚠️

Changes:

Protocol Buffers:

  • Retired workspace_id, since its inferred from project_id now

Server-side (ctrl service):

  • Updated CreateDeployment to look up project by project_id and extract workspace_id from it
  • Removed workspace validation logic (now validated implicitly through project lookup)

CLI:

  • Removed --workspace-id flag and UNKEY_WORKSPACE_ID environment variable
  • Removed WorkspaceID from Config struct (no longer in unkey.json)
  • Removed WorkspaceID from DeployOptions struct
  • Updated control_plane.go to not send workspace_id in deployment requests
  • Set sensible defaults: --context defaults to . (current directory) and --linux defaults to true

Tests:

  • Updated create_deployment_simple_test.go to remove WorkspaceId from test requests
  • Fixed test to use hardcoded workspace ID since DB lookup isn't performed in unit tests

User Impact:
Users can now deploy with just:

unkey deploy --project-id=proj_123

Instead of:

unkey deploy --workspace-id=ws_456 --project-id=proj_123

The workspace is automatically determined from the project on the server side.

How should this be tested?

Prerequisites:

  1. Set up test project:
    Create a workspace and project
    Note the project_id for testing

  2. Build and Install:

cd go
make generate  # Regenerate proto files
make build     # Build the binary
cp unkey ~/go/bin/unkey  # Install to PATH
  1. Set your API key for ctrl service in env vars (or pass it in as a flag)
export API_KEY=<your-ctrl-service-api-key>

Test Scenarios

  1. Test init command:
unkey deploy --init
# Should prompt for:
# - Project ID
# - Build context path
# Should NOT prompt for Workspace ID

Expected unkey.json:

{
  "project_id": "proj_123",
  "context": "."
}
  1. Test deployment with config file:
unkey deploy

Expected: Deployment succeeds using project_id from config and API_KEY from env, workspace inferred automatically

  1. Test deployment with only project-id flag:
unkey deploy --project-id=<your-project-id>

Expected: Deployment succeeds without requiring --workspace-id

  1. Test error handling:
unkey deploy --project-id=invalid_project

Expected: Error message "project not found: invalid_project"

  1. Verify workspace inference:
    Check ctrl service logs to confirm workspace ID is correctly inferred from project
    Verify deployment is created under the correct workspace in the database

Run Tests:

cd go
make test-unit  # Run all unit tests
# Or specifically:
go test -v ./apps/ctrl/services/deployment/

Breaking Changes

⚠️ This is a breaking change:

  • Existing CLI users who pass --workspace-id will get an "unknown flag" error
  • Existing unkey.json files with workspace_id field will have it ignored (but won't break)
  • Any external services calling CreateDeployment RPC must be updated to not send workspace_id

Migration path:
Remove --workspace-id flag from any scripts/CI pipelines
Remove workspace_id from unkey.json config files (optional, but recommended)
Update any code calling the CreateDeployment RPC endpoint

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

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
  • 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

@changeset-bot
Copy link

changeset-bot bot commented Oct 2, 2025

⚠️ No Changeset found

Latest commit: a397556

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@vercel
Copy link

vercel bot commented Oct 2, 2025

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

Project Deployment Preview Comments Updated (UTC)
dashboard Ready Ready Preview Comment Oct 7, 2025 5:45pm
1 Skipped Deployment
Project Deployment Preview Comments Updated (UTC)
engineering Ignored Ignored Preview Oct 7, 2025 5:45pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 2, 2025

📝 Walkthrough

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The description thoroughly outlines the changes, testing steps, breaking changes, and type of change, but it omits the required “Fixes #” reference and does not list any dependencies as specified by the repository’s template. Please add a “Fixes #” line to reference the related issue and include any dependency information in the PR description to fully comply with the repository template.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title Check ✅ Passed The title succinctly and accurately summarizes the main user-facing change of slimming the CLI flags and introducing sensible defaults, which aligns with the bulk of the modifications in this pull request.
✨ 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 pretty-cmd

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fd6a57c and 5f9703e.

⛔ Files ignored due to path filters (9)
  • internal/proto/generated/ctrl/v1/acme_pb.ts is excluded by !**/generated/**
  • internal/proto/generated/ctrl/v1/build_pb.ts is excluded by !**/generated/**
  • internal/proto/generated/ctrl/v1/deployment_pb.ts is excluded by !**/generated/**
  • internal/proto/generated/ctrl/v1/openapi_pb.ts is excluded by !**/generated/**
  • internal/proto/generated/ctrl/v1/service_pb.ts is excluded by !**/generated/**
  • internal/proto/generated/krane/v1/deployment_pb.ts is excluded by !**/generated/**
  • internal/proto/generated/partition/v1/gateway_pb.ts is excluded by !**/generated/**
  • internal/proto/generated/vault/v1/object_pb.ts is excluded by !**/generated/**
  • internal/proto/generated/vault/v1/service_pb.ts is excluded by !**/generated/**
📒 Files selected for processing (1)
  • go/cmd/deploy/init.go (2 hunks)
🔇 Additional comments (2)
go/cmd/deploy/init.go (2)

59-59: LGTM!

The removal of the workspaceID parameter from createConfigWithValues correctly implements the PR objective to infer workspace from project server-side. The function call now matches the updated signature, and the interactive flow no longer prompts users for workspace_id.


77-77: Previous issue resolved.

The flag name now correctly uses a hyphen (--project-id) as noted in the past review comments. The help text accurately reflects the available CLI flags.


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.

@github-actions
Copy link
Contributor

github-actions bot commented Oct 2, 2025

Thank you for following the naming conventions for pull request titles! 🙏

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 (4)
go/cmd/deploy/control_plane.go (1)

56-68: Avoid sending an “empty but present” ApiId (use nil when absent).

Setting ApiId to &c.opts.ApiID sends presence=true even when empty, which can be treated as invalid by the server. Only set the pointer when non-empty.

-	createReq := connect.NewRequest(&ctrlv1.CreateDeploymentRequest{
-		ProjectId:                c.opts.ProjectID,
-		ApiId:                    &c.opts.ApiID,
+	var apiID *string
+	if c.opts.ApiID != "" {
+		apiID = &c.opts.ApiID
+	}
+	createReq := connect.NewRequest(&ctrlv1.CreateDeploymentRequest{
+		ProjectId:                c.opts.ProjectID,
+		ApiId:                    apiID,
 		Branch:                   c.opts.Branch,
 		SourceType:               ctrlv1.SourceType_SOURCE_TYPE_CLI_UPLOAD,
 		EnvironmentSlug:          c.opts.Environment,
 		DockerImage:              dockerImage,
 		GitCommitSha:             commitInfo.CommitSHA,
 		GitCommitMessage:         commitInfo.Message,
 		GitCommitAuthorHandle:    commitInfo.AuthorHandle,
 		GitCommitAuthorAvatarUrl: commitInfo.AuthorAvatarURL,
 		GitCommitTimestamp:       commitInfo.CommitTimestamp,
 	})
go/apps/ctrl/services/deployment/deploy_workflow.go (1)

666-690: Remove unused createGatewayConfig function
This helper isn’t referenced anywhere; delete it to eliminate dead code drift.

go/pkg/codes/constants_gen.go (1)

31-35: Refine URN mapping for connect.CodeUnauthenticated

  • In control_plane.go:251, the connect.CodeUnauthenticated branch always uses UnkeyAuthErrorsAuthenticationMalformed, even for valid-but-invalid credentials.
  • Restrict AuthenticationMalformed to actual format errors.
  • Map missing credentials to UnkeyAuthErrorsAuthenticationMissing or introduce a generic AuthenticationFailed URN for other auth failures.
go/apps/ctrl/services/deployment/create_deployment.go (1)

90-117: Use sanitized values to set sql.NullString.Valid; trim commit message whitespace.

Currently Valid derives from the raw request, which can produce Valid=true with an empty sanitized string. Also trim commit message spaces to avoid persisting whitespace-only values.

Apply this diff:

-	gitCommitSha := req.Msg.GetGitCommitSha()
-	gitCommitMessage := trimLength(req.Msg.GetGitCommitMessage(), 10240)
-	gitCommitAuthorHandle := trimLength(strings.TrimSpace(req.Msg.GetGitCommitAuthorHandle()), 256)
-	gitCommitAuthorAvatarUrl := trimLength(strings.TrimSpace(req.Msg.GetGitCommitAuthorAvatarUrl()), 512)
+	gitCommitSha := req.Msg.GetGitCommitSha()
+	gitCommitMessage := trimLength(strings.TrimSpace(req.Msg.GetGitCommitMessage()), 10240)
+	gitCommitAuthorHandle := trimLength(strings.TrimSpace(req.Msg.GetGitCommitAuthorHandle()), 256)
+	gitCommitAuthorAvatarUrl := trimLength(strings.TrimSpace(req.Msg.GetGitCommitAuthorAvatarUrl()), 512)
@@
-		GitCommitMessage:         sql.NullString{String: gitCommitMessage, Valid: req.Msg.GetGitCommitMessage() != ""},
-		GitCommitAuthorHandle:    sql.NullString{String: gitCommitAuthorHandle, Valid: req.Msg.GetGitCommitAuthorHandle() != ""},
-		GitCommitAuthorAvatarUrl: sql.NullString{String: gitCommitAuthorAvatarUrl, Valid: req.Msg.GetGitCommitAuthorAvatarUrl() != ""},
+		GitCommitMessage:         sql.NullString{String: gitCommitMessage, Valid: gitCommitMessage != ""},
+		GitCommitAuthorHandle:    sql.NullString{String: gitCommitAuthorHandle, Valid: gitCommitAuthorHandle != ""},
+		GitCommitAuthorAvatarUrl: sql.NullString{String: gitCommitAuthorAvatarUrl, Valid: gitCommitAuthorAvatarUrl != ""},
🧹 Nitpick comments (4)
go/pkg/db/bulk_deployment_insert.sql_generated.go (1)

50-51: Keep gofmt-compliant indentation.

These lines now use spaces instead of the gofmt-standard tabs. Please re-run gofmt (or restore tabs) so the file stays consistent with Go formatting.

-    _, err := db.ExecContext(ctx, bulkQuery, allArgs...)
-    return err
+	_, err := db.ExecContext(ctx, bulkQuery, allArgs...)
+	return err
go/apps/ctrl/services/deployment/create_deployment_simple_test.go (1)

410-416: Prefer a shared test constant for the hardcoded workspace ID.

Improves clarity and avoids typos across tests.

+const testWorkspaceID = "ws_test123"
@@
-				WorkspaceID:   "ws_test123",  // In production, this is inferred from ProjectID via DB lookup, just hardcoded for the test
+				WorkspaceID:   testWorkspaceID, // In production, inferred from ProjectID; hardcoded for the test
go/cmd/deploy/main.go (1)

108-109: Clarify api-id flag description.

Slightly tweak wording to avoid confusion with API keys.

Apply this diff:

-	cli.String("api-id", "API ID for API key authentication", cli.EnvVar(EnvApiID)),
+	cli.String("api-id", "Target API ID (optional)", cli.EnvVar(EnvApiID)),
go/cmd/deploy/config.go (1)

81-90: Consider including api_id in generated config for discoverability.

Even if optional, adding api_id (empty) helps users know it exists.

Apply this diff:

-	config := &Config{
-		ProjectID: projectID,
-		Context:   context,
-	}
+	config := &Config{
+		ApiID:     "",
+		ProjectID: projectID,
+		Context:   context,
+	}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 027ce45 and 70a8149.

⛔ Files ignored due to path filters (3)
  • go/gen/proto/ctrl/v1/ctrlv1connect/deployment.connect.go is excluded by !**/gen/**
  • go/gen/proto/ctrl/v1/deployment.pb.go is excluded by !**/*.pb.go, !**/gen/**
  • internal/proto/generated/ctrl/v1/deployment_pb.ts is excluded by !**/generated/**
📒 Files selected for processing (35)
  • go/apps/ctrl/services/deployment/create_deployment.go (3 hunks)
  • go/apps/ctrl/services/deployment/create_deployment_simple_test.go (1 hunks)
  • go/apps/ctrl/services/deployment/deploy_workflow.go (2 hunks)
  • go/cmd/deploy/config.go (3 hunks)
  • go/cmd/deploy/control_plane.go (1 hunks)
  • go/cmd/deploy/init.go (2 hunks)
  • go/cmd/deploy/main.go (5 hunks)
  • go/pkg/codes/constants_gen.go (5 hunks)
  • go/pkg/db/bulk_acme_challenge_insert.sql_generated.go (1 hunks)
  • go/pkg/db/bulk_acme_user_insert.sql_generated.go (1 hunks)
  • go/pkg/db/bulk_api_insert.sql_generated.go (1 hunks)
  • go/pkg/db/bulk_audit_log_insert.sql_generated.go (1 hunks)
  • go/pkg/db/bulk_audit_log_target_insert.sql_generated.go (1 hunks)
  • go/pkg/db/bulk_deployment_insert.sql_generated.go (1 hunks)
  • go/pkg/db/bulk_deployment_step_insert.sql_generated.go (1 hunks)
  • go/pkg/db/bulk_domain_insert.sql_generated.go (1 hunks)
  • go/pkg/db/bulk_identity_insert.sql_generated.go (1 hunks)
  • go/pkg/db/bulk_identity_insert_ratelimit.sql_generated.go (1 hunks)
  • go/pkg/db/bulk_key_encryption_insert.sql_generated.go (1 hunks)
  • go/pkg/db/bulk_key_insert.sql_generated.go (1 hunks)
  • go/pkg/db/bulk_key_insert_ratelimit.sql_generated.go (1 hunks)
  • go/pkg/db/bulk_key_permission_insert.sql_generated.go (1 hunks)
  • go/pkg/db/bulk_key_role_insert.sql_generated.go (1 hunks)
  • go/pkg/db/bulk_keyring_insert.sql_generated.go (1 hunks)
  • go/pkg/db/bulk_permission_insert.sql_generated.go (1 hunks)
  • go/pkg/db/bulk_project_insert.sql_generated.go (1 hunks)
  • go/pkg/db/bulk_ratelimit_namespace_insert.sql_generated.go (1 hunks)
  • go/pkg/db/bulk_ratelimit_override_insert.sql_generated.go (1 hunks)
  • go/pkg/db/bulk_role_insert.sql_generated.go (1 hunks)
  • go/pkg/db/bulk_role_permission_insert.sql_generated.go (1 hunks)
  • go/pkg/db/bulk_workspace_insert.sql_generated.go (1 hunks)
  • go/pkg/partition/db/bulk_certificate_insert.sql_generated.go (1 hunks)
  • go/pkg/partition/db/bulk_gateway_upsert.sql_generated.go (1 hunks)
  • go/pkg/partition/db/bulk_vm_upsert.sql_generated.go (1 hunks)
  • go/proto/ctrl/v1/deployment.proto (3 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-17T14:24:20.403Z
Learnt from: Flo4604
PR: unkeyed/unkey#3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

Applied to files:

  • go/pkg/db/bulk_key_insert_ratelimit.sql_generated.go
  • go/pkg/db/bulk_keyring_insert.sql_generated.go
  • go/pkg/db/bulk_api_insert.sql_generated.go
  • go/pkg/db/bulk_key_insert.sql_generated.go
  • go/pkg/db/bulk_key_permission_insert.sql_generated.go
🧬 Code graph analysis (4)
go/apps/ctrl/services/deployment/create_deployment.go (3)
go/pkg/db/handle_err_no_rows.go (1)
  • IsNotFound (8-10)
go/pkg/db/environment_find_by_project_id_and_slug.sql_generated.go (1)
  • FindEnvironmentByProjectIdAndSlugParams (21-25)
go/apps/ctrl/services/deployment/deploy_workflow.go (1)
  • DeployRequest (62-69)
go/cmd/deploy/main.go (1)
go/pkg/cli/flag.go (4)
  • String (419-451)
  • EnvVar (320-339)
  • Default (364-416)
  • Bool (493-529)
go/apps/ctrl/services/deployment/deploy_workflow.go (2)
internal/proto/generated/partition/v1/gateway_pb.ts (1)
  • AuthConfig (130-135)
go/gen/proto/partition/v1/gateway.pb.go (3)
  • AuthConfig (253-258)
  • AuthConfig (271-271)
  • AuthConfig (286-288)
go/pkg/codes/constants_gen.go (1)
go/pkg/urn/urn.go (1)
  • URN (12-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: Test Go API Local / Test
  • GitHub Check: Build / Build
  • GitHub Check: Test API / API Test Local
  • GitHub Check: Test Packages / Test
🔇 Additional comments (18)
go/pkg/db/bulk_acme_user_insert.sql_generated.go (1)

38-39: No issues found.

Indentation change only; behavior is unchanged.

go/pkg/db/bulk_permission_insert.sql_generated.go (1)

41-42: No issues spotted.

Pure whitespace tweak; behavior unchanged.

go/pkg/db/bulk_api_insert.sql_generated.go (1)

42-43: Formatting tweak looks good.

Indentation change preserves behavior; no further action needed.

go/pkg/db/bulk_workspace_insert.sql_generated.go (1)

40-41: Indentation tweak looks good.

Whitespace-only adjustment, no behavioral change. Thanks for keeping the generated file consistent.

go/pkg/db/bulk_key_insert.sql_generated.go (1)

50-51: Whitespace-only tweak is fine

Indentation change keeps the generated file consistent; no functional impact detected.

go/pkg/db/bulk_domain_insert.sql_generated.go (1)

44-45: Indentation tweak looks harmless.

No behavioral impact here.

go/pkg/db/bulk_audit_log_target_insert.sql_generated.go (1)

45-46: No functional change observed.

Indentation tweak only; bulk insert logic remains intact.

go/pkg/db/bulk_identity_insert_ratelimit.sql_generated.go (1)

48-49: Formatting-only change looks good.

Indentation update is a no-op; query and args remain identical.

go/apps/ctrl/services/deployment/deploy_workflow.go (2)

62-69: ApiID added to DeployRequest: LGTM.

Field name/tag align with downstream usage.


423-427: AuthConfig is conditionally set based on ApiID: LGTM.

Mapping ApiID to AuthConfig.KeyAuthId is correct and preserves previous behavior when empty.

go/cmd/deploy/init.go (1)

59-62: Signature update looks correct.

createConfigWithValues now matches the new (configDir, projectID, context) signature.

go/apps/ctrl/services/deployment/create_deployment.go (3)

29-40: Workspace inference from project looks good.

Project lookup with proper NotFound mapping and deriving workspaceID from the project is correct.


41-51: Environment lookup scoped by inferred workspace is appropriate.

Using both workspaceID and projectID for the environment query and returning NotFound is sound.


130-138: ApiID propagation to workflow request is correct.

DeployRequest now includes ApiID and inferred workspace; matches the new flow.

go/cmd/deploy/main.go (1)

100-126: Flags and defaults look consistent with the new model.

  • api-id flag with UNKEY_API_ID env is good.
  • context default "." and env default "preview" align with goals.
  • linux default true is reasonable for cloud deploys.
go/cmd/deploy/config.go (1)

114-136: mergeWithFlags logic is sound.

Flag precedence over file values and defaulting context to "." are correct.

go/proto/ctrl/v1/deployment.proto (2)

141-150: New GetProject messages look fine.

Schema is straightforward; aligns with workspace inference needs.


165-167: GetProject RPC addition looks good.

Complements CreateDeployment’s workspace inference path.

Copy link
Collaborator

@chronark chronark left a comment

Choose a reason for hiding this comment

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

looks good overall, just small stuff

@graphite-app
Copy link

graphite-app bot commented Oct 7, 2025

Sports gif. A WWE official avatar swings a rubbery arm around and gives an elongated thumbs up. (Added via Giphy)

@graphite-app
Copy link

graphite-app bot commented Oct 7, 2025

Graphite Automations

"Post a GIF when PR approved" took an action on this PR • (10/07/25)

1 gif was posted to this PR based on Andreas Thomas's automation.

@mcstepp mcstepp enabled auto-merge October 7, 2025 17:36
@mcstepp mcstepp dismissed chronark’s stale review October 7, 2025 17:37

delegated review to Oz

@mcstepp mcstepp added this pull request to the merge queue Oct 7, 2025
Merged via the queue into main with commit 9775d69 Oct 7, 2025
17 checks passed
@mcstepp mcstepp deleted the pretty-cmd branch October 7, 2025 19:24
@coderabbitai coderabbitai bot mentioned this pull request Oct 15, 2025
18 tasks
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.

4 participants