Skip to content

Commit 9775d69

Browse files
feat: slimmer CLI flags with sensible defaults (#4056)
* update keyspaceId to apiId * add sensible defaults' ' * infer workspace id from project id * remove workspace id from the create deployment request * update init's json output * remove GetProject references * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes * feat: clean up cli * merge conflict fix * fix underscore * [autofix.ci] apply automated fixes --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
1 parent 3dff665 commit 9775d69

File tree

17 files changed

+55
-108
lines changed

17 files changed

+55
-108
lines changed

go/apps/ctrl/services/deployment/create_deployment.go

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,7 @@ func (s *Service) CreateDeployment(
2727
ctx context.Context,
2828
req *connect.Request[ctrlv1.CreateDeploymentRequest],
2929
) (*connect.Response[ctrlv1.CreateDeploymentResponse], error) {
30-
// Validate workspace exists
31-
_, err := db.Query.FindWorkspaceByID(ctx, s.db.RO(), req.Msg.GetWorkspaceId())
32-
if err != nil {
33-
if db.IsNotFound(err) {
34-
return nil, connect.NewError(connect.CodeNotFound,
35-
fmt.Errorf("workspace not found: %s", req.Msg.GetWorkspaceId()))
36-
}
37-
return nil, connect.NewError(connect.CodeInternal, err)
38-
}
39-
40-
// Validate project exists and belongs to workspace
30+
// Lookup project and infer workspace from it
4131
project, err := db.Query.FindProjectById(ctx, s.db.RO(), req.Msg.GetProjectId())
4232
if err != nil {
4333
if db.IsNotFound(err) {
@@ -47,23 +37,18 @@ func (s *Service) CreateDeployment(
4737
return nil, connect.NewError(connect.CodeInternal, err)
4838
}
4939

50-
// Verify project belongs to the specified workspace
51-
if project.WorkspaceID != req.Msg.GetWorkspaceId() {
52-
return nil, connect.NewError(connect.CodeInvalidArgument,
53-
fmt.Errorf("project %s does not belong to workspace %s",
54-
req.Msg.GetProjectId(), req.Msg.GetWorkspaceId()))
55-
}
40+
workspaceID := project.WorkspaceID
5641

5742
env, err := db.Query.FindEnvironmentByProjectIdAndSlug(ctx, s.db.RO(), db.FindEnvironmentByProjectIdAndSlugParams{
58-
WorkspaceID: req.Msg.GetWorkspaceId(),
43+
WorkspaceID: workspaceID,
5944
ProjectID: project.ID,
6045
Slug: req.Msg.GetEnvironmentSlug(),
6146
})
6247
if err != nil {
6348
if db.IsNotFound(err) {
6449
return nil, connect.NewError(connect.CodeNotFound,
6550
fmt.Errorf("environment '%s' not found in workspace '%s'",
66-
req.Msg.GetEnvironmentSlug(), req.Msg.GetWorkspaceId()))
51+
req.Msg.GetEnvironmentSlug(), workspaceID))
6752
}
6853
return nil, connect.NewError(connect.CodeInternal,
6954
fmt.Errorf("failed to lookup environment: %w", err))
@@ -112,7 +97,7 @@ func (s *Service) CreateDeployment(
11297
// Insert deployment into database
11398
err = db.Query.InsertDeployment(ctx, s.db.RW(), db.InsertDeploymentParams{
11499
ID: deploymentID,
115-
WorkspaceID: req.Msg.GetWorkspaceId(),
100+
WorkspaceID: workspaceID,
116101
ProjectID: req.Msg.GetProjectId(),
117102
EnvironmentID: env.ID,
118103
RuntimeConfig: json.RawMessage(`{
@@ -138,16 +123,21 @@ func (s *Service) CreateDeployment(
138123

139124
s.logger.Info("starting deployment workflow for deployment",
140125
"deployment_id", deploymentID,
141-
"workspace_id", req.Msg.GetWorkspaceId(),
126+
"workspace_id", workspaceID,
142127
"project_id", req.Msg.GetProjectId(),
143128
"environment", env.ID,
144129
)
145130

146131
// Start the deployment workflow directly
132+
keyspaceID := req.Msg.GetKeyspaceId()
133+
var keyAuthID *string
134+
if keyspaceID != "" {
135+
keyAuthID = &keyspaceID
136+
}
147137
deployReq := &hydrav1.DeployRequest{
148138
DeploymentId: deploymentID,
149139
DockerImage: req.Msg.GetDockerImage(),
150-
KeyAuthId: req.Msg.KeyspaceId,
140+
KeyAuthId: keyAuthID,
151141
}
152142
// this is ugly, but we're waiting for
153143
// https://github.com/restatedev/sdk-go/issues/103

go/apps/ctrl/services/deployment/create_deployment_simple_test.go

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,6 @@ func TestGitFieldValidation_NullHandling(t *testing.T) {
9898

9999
// Test empty protobuf fields
100100
req := &ctrlv1.CreateDeploymentRequest{
101-
WorkspaceId: "ws_test",
102101
ProjectId: "proj_test",
103102
GitCommitMessage: "",
104103
GitCommitAuthorAvatarUrl: "",
@@ -158,7 +157,6 @@ func TestCreateVersionTimestampValidation_InvalidSecondsFormat(t *testing.T) {
158157

159158
// Create proto request directly with seconds timestamp (should be rejected)
160159
req := &ctrlv1.CreateDeploymentRequest{
161-
WorkspaceId: "ws_test123",
162160
ProjectId: "proj_test456",
163161
Branch: "main",
164162
SourceType: ctrlv1.SourceType_SOURCE_TYPE_GIT,
@@ -177,7 +175,6 @@ func TestCreateVersionTimestampValidation_ValidMillisecondsFormat(t *testing.T)
177175

178176
// Create proto request directly with milliseconds timestamp
179177
req := &ctrlv1.CreateDeploymentRequest{
180-
WorkspaceId: "ws_test123",
181178
ProjectId: "proj_test456",
182179
Branch: "main",
183180
SourceType: ctrlv1.SourceType_SOURCE_TYPE_GIT,
@@ -275,7 +272,6 @@ func TestCreateVersionFieldMapping(t *testing.T) {
275272
{
276273
name: "all_git_fields_populated",
277274
request: &ctrlv1.CreateDeploymentRequest{
278-
WorkspaceId: "ws_test123",
279275
ProjectId: "proj_test456",
280276
Branch: "feature/test-branch",
281277
SourceType: ctrlv1.SourceType_SOURCE_TYPE_GIT,
@@ -320,7 +316,6 @@ func TestCreateVersionFieldMapping(t *testing.T) {
320316
{
321317
name: "empty_git_fields",
322318
request: &ctrlv1.CreateDeploymentRequest{
323-
WorkspaceId: "ws_test123",
324319
ProjectId: "proj_test456",
325320
Branch: "main",
326321
SourceType: ctrlv1.SourceType_SOURCE_TYPE_GIT,
@@ -365,7 +360,6 @@ func TestCreateVersionFieldMapping(t *testing.T) {
365360
{
366361
name: "mixed_populated_and_empty_fields",
367362
request: &ctrlv1.CreateDeploymentRequest{
368-
WorkspaceId: "ws_test123",
369363
ProjectId: "proj_test456",
370364
Branch: "hotfix/urgent-fix",
371365
SourceType: ctrlv1.SourceType_SOURCE_TYPE_GIT,
@@ -417,7 +411,7 @@ func TestCreateVersionFieldMapping(t *testing.T) {
417411
// This tests the actual field wiring that happens in the service
418412
params := db.InsertDeploymentParams{
419413
ID: "test_deployment_id",
420-
WorkspaceID: tt.request.GetWorkspaceId(),
414+
WorkspaceID: "ws_test123", // In production, this is inferred from ProjectID via DB lookup, just hardcoded for the test
421415
ProjectID: tt.request.GetProjectId(),
422416
EnvironmentID: "todo",
423417
// Git field mappings - this is what we're testing

go/cmd/deploy/config.go

Lines changed: 17 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,19 @@ import (
99
)
1010

1111
var (
12-
ErrWorkspaceIDRequired = errors.New("workspace ID is required (use --workspace-id flag or edit unkey.json)")
13-
ErrProjectIDRequired = errors.New("project ID is required (use --project-id flag or edit unkey.json)")
14-
ErrConfigPathResolve = errors.New("failed to resolve config path")
15-
ErrConfigFileRead = errors.New("failed to read config file")
16-
ErrConfigFileParse = errors.New("failed to parse config file")
17-
ErrConfigFileWrite = errors.New("failed to write config file")
18-
ErrConfigMarshal = errors.New("failed to marshal config")
19-
ErrDirectoryCreate = errors.New("failed to create directory")
12+
ErrProjectIDRequired = errors.New("project ID is required (use --project-id flag or edit unkey.json)")
13+
ErrConfigPathResolve = errors.New("failed to resolve config path")
14+
ErrConfigFileRead = errors.New("failed to read config file")
15+
ErrConfigFileParse = errors.New("failed to parse config file")
16+
ErrConfigFileWrite = errors.New("failed to write config file")
17+
ErrConfigMarshal = errors.New("failed to marshal config")
18+
ErrDirectoryCreate = errors.New("failed to create directory")
2019
)
2120

2221
type Config struct {
23-
WorkspaceID string `json:"workspace_id"`
24-
KeyspaceID string `json:"keyspace_id"`
25-
ProjectID string `json:"project_id"`
26-
Context string `json:"context"`
22+
KeyspaceID string `json:"keyspace_id"`
23+
ProjectID string `json:"project_id"`
24+
Context string `json:"context"`
2725
}
2826

2927
// loadConfig loads configuration from unkey.json in the specified directory.
@@ -80,16 +78,15 @@ func getConfigFilePath(configDir string) string {
8078
}
8179

8280
// createConfigWithValues creates a new unkey.json file with the provided values
83-
func createConfigWithValues(configDir, workspaceID, projectID, context string) error {
81+
func createConfigWithValues(configDir, projectID, context string) error {
8482
// Create directory if it doesn't exist
8583
if err := os.MkdirAll(configDir, 0755); err != nil {
8684
return fmt.Errorf("%w %s: %w", ErrDirectoryCreate, configDir, err)
8785
}
8886

8987
config := &Config{
90-
WorkspaceID: workspaceID,
91-
ProjectID: projectID,
92-
Context: context,
88+
ProjectID: projectID,
89+
Context: context,
9390
}
9491

9592
configPath := filepath.Join(configDir, "unkey.json")
@@ -115,17 +112,13 @@ func writeConfig(configPath string, config *Config) error {
115112
}
116113

117114
// mergeWithFlags merges config values with command flags, with flags taking precedence
118-
func (c *Config) mergeWithFlags(workspaceID, projectID, keyspaceID, context string) *Config {
115+
func (c *Config) mergeWithFlags(projectID, keyspaceID, context string) *Config {
119116
merged := &Config{
120-
WorkspaceID: c.WorkspaceID,
121-
KeyspaceID: c.KeyspaceID,
122-
ProjectID: c.ProjectID,
123-
Context: c.Context,
117+
KeyspaceID: c.KeyspaceID,
118+
ProjectID: c.ProjectID,
119+
Context: c.Context,
124120
}
125121
// Flags override config values
126-
if workspaceID != "" {
127-
merged.WorkspaceID = workspaceID
128-
}
129122
if projectID != "" {
130123
merged.ProjectID = projectID
131124
}
@@ -144,9 +137,6 @@ func (c *Config) mergeWithFlags(workspaceID, projectID, keyspaceID, context stri
144137

145138
// validate checks if required fields are present and not placeholder values
146139
func (c *Config) validate() error {
147-
if c.WorkspaceID == "" || c.WorkspaceID == "ws_your_workspace_id" {
148-
return ErrWorkspaceIDRequired
149-
}
150140
if c.ProjectID == "" || c.ProjectID == "proj_your_project_id" {
151141
return ErrProjectIDRequired
152142
}

go/cmd/deploy/control_plane.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ func (c *ControlPlaneClient) CreateDeployment(ctx context.Context, dockerImage s
5454
// Get git commit information
5555
commitInfo := git.GetInfo()
5656
createReq := connect.NewRequest(&ctrlv1.CreateDeploymentRequest{
57-
WorkspaceId: c.opts.WorkspaceID,
5857
ProjectId: c.opts.ProjectID,
5958
KeyspaceId: &c.opts.KeyspaceID,
6059
Branch: c.opts.Branch,

go/cmd/deploy/init.go

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,6 @@ func handleInit(cmd *cli.Command, ui *UI) error {
4040
// Interactive prompts for configuration
4141
fmt.Printf("Please provide the following configuration details:\n\n")
4242

43-
fmt.Printf("Workspace ID: ")
44-
workspaceID := readLine()
45-
if workspaceID == "" {
46-
return fmt.Errorf("workspace ID is required")
47-
}
48-
4943
fmt.Printf("Project ID: ")
5044
projectID := readLine()
5145
if projectID == "" {
@@ -62,7 +56,7 @@ func handleInit(cmd *cli.Command, ui *UI) error {
6256

6357
// Create configuration with user input
6458
ui.Print("Creating configuration file")
65-
if err := createConfigWithValues(configDir, workspaceID, projectID, context); err != nil {
59+
if err := createConfigWithValues(configDir, projectID, context); err != nil {
6660
ui.PrintError("Failed to create config file")
6761
return fmt.Errorf("failed to create config file: %w", err)
6862
}
@@ -80,7 +74,7 @@ func printInitNextSteps() {
8074
fmt.Printf(" unkey deploy\n")
8175
fmt.Printf("\n")
8276
fmt.Printf("Or override specific values:\n")
83-
fmt.Printf(" unkey deploy --workspace-id=ws_different\n")
77+
fmt.Printf(" unkey deploy --project-id=proj_different\n")
8478
fmt.Printf(" unkey deploy --context=./other-app\n")
8579
}
8680

go/cmd/deploy/main.go

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,13 @@ const (
1616
DefaultBranch = "main"
1717
DefaultDockerfile = "Dockerfile"
1818
DefaultRegistry = "ghcr.io/unkeyed/deploy"
19-
DefaultControlPlaneURL = "http://localhost:7091"
19+
DefaultControlPlaneURL = "https://ctrl.unkey.cloud"
2020
DefaultAuthToken = "ctrl-secret-token"
21-
DefaultEnvironment = "Production"
21+
DefaultEnvironment = "preview"
2222

2323
// Environment variables
24-
EnvWorkspaceID = "UNKEY_WORKSPACE_ID"
25-
EnvKeyspaceID = "UNKEY_KEYSPACE_ID"
26-
EnvRegistry = "UNKEY_REGISTRY"
24+
EnvKeyspaceID = "UNKEY_KEYSPACE_ID"
25+
EnvRegistry = "UNKEY_REGISTRY"
2726

2827
// URL prefixes
2928
HTTPSPrefix = "https://"
@@ -81,7 +80,6 @@ var stepSequence = map[string]string{
8180

8281
// DeployOptions contains all configuration for deployment
8382
type DeployOptions struct {
84-
WorkspaceID string
8583
ProjectID string
8684
KeyspaceID string
8785
Context string
@@ -106,22 +104,21 @@ var DeployFlags = []cli.Flag{
106104
cli.Bool("init", "Initialize configuration file in the specified directory"),
107105
cli.Bool("force", "Force overwrite existing configuration file when using --init"),
108106
// Required flags (can be provided via config file)
109-
cli.String("workspace-id", "Workspace ID", cli.EnvVar(EnvWorkspaceID)),
110107
cli.String("project-id", "Project ID", cli.EnvVar("UNKEY_PROJECT_ID")),
111108
cli.String("keyspace-id", "Keyspace ID for API key authentication", cli.EnvVar(EnvKeyspaceID)),
112109
// Optional flags with defaults
113-
cli.String("context", "Build context path"),
110+
cli.String("context", "Build context path", cli.Default(".")),
114111
cli.String("branch", "Git branch", cli.Default(DefaultBranch)),
115112
cli.String("docker-image", "Pre-built docker image"),
116113
cli.String("dockerfile", "Path to Dockerfile", cli.Default(DefaultDockerfile)),
117114
cli.String("commit", "Git commit SHA"),
118115
cli.String("registry", "Container registry",
119116
cli.Default(DefaultRegistry),
120117
cli.EnvVar(EnvRegistry)),
121-
cli.String("env", "Environment slug to deploy to", cli.Default("preview")),
118+
cli.String("env", "Environment slug to deploy to", cli.Default(DefaultEnvironment)),
122119
cli.Bool("skip-push", "Skip pushing to registry (for local testing)"),
123120
cli.Bool("verbose", "Show detailed output for build and deployment operations"),
124-
cli.Bool("linux", "Build Docker image for linux/amd64 platform (for deployment to cloud clusters)"),
121+
cli.Bool("linux", "Build Docker image for linux/amd64 platform (for deployment to cloud clusters)", cli.Default(true)),
125122
// Control plane flags (internal)
126123
cli.String("control-plane-url", "Control plane URL", cli.Default(DefaultControlPlaneURL)),
127124
cli.String("auth-token", "Control plane auth token", cli.Default(DefaultAuthToken)),
@@ -153,7 +150,6 @@ unkey deploy --init --config=./my-project # Initialize with custom location
153150
unkey deploy --init --force # Force overwrite existing configuration
154151
unkey deploy # Standard deployment (uses ./unkey.json)
155152
unkey deploy --config=./production # Deploy from specific config directory
156-
unkey deploy --workspace-id=ws_production_123 # Override workspace from config file
157153
unkey deploy --context=./api # Deploy with custom build context
158154
unkey deploy --skip-push # Local development (build only, no push)
159155
unkey deploy --docker-image=ghcr.io/user/app:v1.0.0 # Deploy pre-built image
@@ -182,7 +178,6 @@ func DeployAction(ctx context.Context, cmd *cli.Command) error {
182178

183179
// Merge config with command flags (flags take precedence)
184180
finalConfig := cfg.mergeWithFlags(
185-
cmd.String("workspace-id"),
186181
cmd.String("project-id"),
187182
cmd.String("keyspace-id"),
188183
cmd.String("context"),
@@ -194,7 +189,6 @@ func DeployAction(ctx context.Context, cmd *cli.Command) error {
194189
}
195190

196191
opts := DeployOptions{
197-
WorkspaceID: finalConfig.WorkspaceID,
198192
KeyspaceID: finalConfig.KeyspaceID,
199193
ProjectID: finalConfig.ProjectID,
200194
Context: finalConfig.Context,

go/gen/proto/ctrl/v1/deployment.pb.go

Lines changed: 6 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

go/proto/ctrl/v1/deployment.proto

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ enum SourceType {
2323
}
2424

2525
message CreateDeploymentRequest {
26-
string workspace_id = 1;
26+
reserved 1; // was workspace_id, now inferred from project_id
2727
string project_id = 2;
2828
string branch = 3;
2929
// Source information

0 commit comments

Comments
 (0)