diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index f4c507e..7fa7633 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -15,7 +15,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: - go-version: '1.25.5' + go-version: '1.26.1' cache: true - name: golangci-lint uses: golangci/golangci-lint-action@v8 diff --git a/main.go b/main.go index 9de3dd8..7649885 100644 --- a/main.go +++ b/main.go @@ -18,7 +18,7 @@ import ( var ( // use build flags to set these values - ie: go build -ldflags "-X main.version=1.0.0" - apiVersion = "v0.4" + apiVersion = "v0.5" version = "dev" buildDate = "2021-09-01T00:00:00Z" commit = "0000" diff --git a/mocks b/mocks new file mode 100755 index 0000000..28c7077 Binary files /dev/null and b/mocks differ diff --git a/pkg/api/deployment.go b/pkg/api/deployment.go index fe19616..e853607 100644 --- a/pkg/api/deployment.go +++ b/pkg/api/deployment.go @@ -216,9 +216,12 @@ type Capabilities struct { type CreatePackageResponse struct { PackageID string `json:"packageId"` + Name string `json:"name"` + Version int `json:"version"` } type CreatePackageArgs struct { + Name string `json:"name"` SourceCodeKey string `json:"sourceCodeKey"` Entrypoint []string `json:"entrypoint"` Capabilities Capabilities `json:"capabilities"` diff --git a/pkg/api/deployment_test.go b/pkg/api/deployment_test.go index 29ac8f9..3947986 100644 --- a/pkg/api/deployment_test.go +++ b/pkg/api/deployment_test.go @@ -653,11 +653,11 @@ func TestCreatePackage(t *testing.T) { { name: "201-happy-path", mock: mock{ - mockResponse: `{"packageId":"package-id"}`, + mockResponse: `{"packageId":"package-id","name":"my-app","version":1}`, status: http.StatusCreated, }, want: want{ - output: CreatePackageResponse{PackageID: "package-id"}, + output: CreatePackageResponse{PackageID: "package-id", Name: "my-app", Version: 1}, err: nil, }, }, @@ -718,7 +718,7 @@ func TestCreatePackage(t *testing.T) { deploymentClient := NewDeploymentClient("https://example.com", "v0.3", client, nil) - createPackageArgs := CreatePackageArgs{SourceCodeKey: "source-code-key", Entrypoint: []string{"node", "index.js"}, Capabilities: Capabilities{Messages: "v1"}} + createPackageArgs := CreatePackageArgs{Name: "my-app", SourceCodeKey: "source-code-key", Entrypoint: []string{"node", "index.js"}, Capabilities: Capabilities{Messages: "v1"}} output, err := deploymentClient.CreatePackage(t.Context(), createPackageArgs) if tt.want.err != nil { require.EqualError(t, err, tt.want.err.Error()) diff --git a/pkg/format/format.go b/pkg/format/format.go index db5be80..d7eb07b 100644 --- a/pkg/format/format.go +++ b/pkg/format/format.go @@ -253,6 +253,15 @@ func PrintAPIError(out *iostreams.IOStreams, err error, httpErr *api.Error) stri var errorRegex = regexp.MustCompile("^[^:]+") +var dockerNameRe = regexp.MustCompile(`[^a-z0-9._-]`) + +func SanitizeDockerImageName(name string) string { + name = strings.ToLower(name) + name = dockerNameRe.ReplaceAllString(name, "-") + name = strings.Trim(name, ".-_") + return name +} + func extractFinalErrorMessage(err error) (string, error) { matches := errorRegex.FindStringSubmatch(err.Error()) if len(matches) > 0 { diff --git a/pkg/format/format_test.go b/pkg/format/format_test.go index df3092c..f4db74b 100644 --- a/pkg/format/format_test.go +++ b/pkg/format/format_test.go @@ -294,6 +294,56 @@ func TestGetRegionOptions(t *testing.T) { require.Equal(t, expectedOptions.AliasLookup, options.AliasLookup) } +func TestSanitizeDockerImageName(t *testing.T) { + tests := []struct { + name string + input string + want string + }{ + { + name: "already-valid", + input: "my-app-dev", + want: "my-app-dev", + }, + { + name: "uppercase-conversion", + input: "My-App-Dev", + want: "my-app-dev", + }, + { + name: "special-chars-replaced", + input: "my app@dev!", + want: "my-app-dev", + }, + { + name: "leading-trailing-separators-trimmed", + input: "---my-app---", + want: "my-app", + }, + { + name: "periods-preserved", + input: "my.app.dev", + want: "my.app.dev", + }, + { + name: "underscores-preserved", + input: "my_app_dev", + want: "my_app_dev", + }, + { + name: "mixed-special-chars", + input: "...Project Name!!--instance", + want: "project-name----instance", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := SanitizeDockerImageName(tt.input) + require.Equal(t, tt.want, got) + }) + } +} + func TestGetTemplateOptions(t *testing.T) { templateNames := []api.Product{ {Name: "template1", ID: "1"}, diff --git a/tests/integration/mocks/main.go b/tests/integration/mocks/main.go index 7ea61ac..7c1ec54 100644 --- a/tests/integration/mocks/main.go +++ b/tests/integration/mocks/main.go @@ -10,11 +10,11 @@ import ( ) func main() { - http.HandleFunc("/v0.4/packages/source", uploadTgzHandler) - http.HandleFunc("/v0.4/packages", createPackageHandler) - http.HandleFunc("/v0.4/packages/test-package-id/build/watch", watchDeploymentHandler) - http.HandleFunc("/v0.4/deployments/validate", validateDeploymentHandler) - http.HandleFunc("/v0.4/deployments", deployInstanceHandler) + http.HandleFunc("/v0.5/packages/source", uploadTgzHandler) + http.HandleFunc("/v0.5/packages", createPackageHandler) + http.HandleFunc("/v0.5/packages/test-package-id/build/watch", watchDeploymentHandler) + http.HandleFunc("/v0.5/deployments/validate", validateDeploymentHandler) + http.HandleFunc("/v0.5/deployments", deployInstanceHandler) http.HandleFunc("/releases/latest", getLatestReleaseHandler) fmt.Println("Server started on port 80") @@ -42,10 +42,12 @@ func uploadTgzHandler(w http.ResponseWriter, _ *http.Request) { type CreatePackageResponse struct { PackageID string `json:"packageId"` + Name string `json:"name"` + Version int `json:"version"` } func createPackageHandler(w http.ResponseWriter, _ *http.Request) { - mockResponse := CreatePackageResponse{PackageID: "test-package-id"} + mockResponse := CreatePackageResponse{PackageID: "test-package-id", Name: "test-dev", Version: 1} w.Header().Set("Content-Type", "application/json") jsonResponse, err := json.Marshal(mockResponse) if err != nil { diff --git a/vcr/deploy/deploy.go b/vcr/deploy/deploy.go index b08bfc4..a433787 100644 --- a/vcr/deploy/deploy.go +++ b/vcr/deploy/deploy.go @@ -523,7 +523,10 @@ func createPackage(ctx context.Context, opts *Options, uploadResp api.UploadResp return api.CreatePackageResponse{}, fmt.Errorf("failed to get runtime: %w", err) } + packageName := format.SanitizeDockerImageName(opts.ProjectName + "-" + opts.InstanceName) + createPackageArgs := api.CreatePackageArgs{ + Name: packageName, SourceCodeKey: uploadResp.SourceCodeKey, Entrypoint: opts.manifest.Instance.Entrypoint, BuildScriptPath: opts.manifest.Instance.BuildScript, @@ -537,7 +540,7 @@ func createPackage(ctx context.Context, opts *Options, uploadResp api.UploadResp return api.CreatePackageResponse{}, fmt.Errorf("failed to create package: %w", err) } - fmt.Fprintf(io.Out, "%s Package created: package_id=%q\n", c.SuccessIcon(), createPkgResp.PackageID) + fmt.Fprintf(io.Out, "%s Package created: package_id=%q name=%q version=%d\n", c.SuccessIcon(), createPkgResp.PackageID, createPkgResp.Name, createPkgResp.Version) fmt.Fprintf(io.Out, "%s Waiting for build to start...\n", c.Blue(cmdutil.InfoIcon)) err = opts.DeploymentClient().WatchDeployment(ctx, opts.IOStreams(), createPkgResp.PackageID) diff --git a/vcr/deploy/deploy_test.go b/vcr/deploy/deploy_test.go index e723d52..19deaa2 100644 --- a/vcr/deploy/deploy_test.go +++ b/vcr/deploy/deploy_test.go @@ -105,6 +105,7 @@ func TestDeploy(t *testing.T) { DeployUploadTgzReturnErr: nil, DeployCreatePackageArgs: api.CreatePackageArgs{ + Name: "test-dev", SourceCodeKey: "test-key", Entrypoint: []string{"node", "index.js"}, BuildScriptPath: "", @@ -112,7 +113,7 @@ func TestDeploy(t *testing.T) { Runtime: "nodejs16", }, DeployCreatePackageTimes: 1, - DeployReturnCreatePackageResponse: api.CreatePackageResponse{PackageID: "test-package-id"}, + DeployReturnCreatePackageResponse: api.CreatePackageResponse{PackageID: "test-package-id", Name: "test-dev", Version: 1}, DeployCreatePackageReturnErr: nil, DeployWatchDeploymentPackageID: "test-package-id", @@ -135,7 +136,7 @@ func TestDeploy(t *testing.T) { DeployDeployInstanceReturnErr: nil, }, want: want{ - stdout: "✓ Project \"test\" retrieved: project_id=\"id\"\n✓ Deployment parameters validated\n✓ Source code uploaded.\n✓ Package created: package_id=\"test-package-id\"\nℹ Waiting for build to start...\n✓ Package \"test-package-id\" built successfully\n/-------\n| Instance has been deployed!\n| Instance id: test-instance-id\n| Instance service name: test-service-name\n| Instance host address: \x1b[0;1;33mtest-host-url\x1b[0m\n\\-------\n", + stdout: "✓ Project \"test\" retrieved: project_id=\"id\"\n✓ Deployment parameters validated\n✓ Source code uploaded.\n✓ Package created: package_id=\"test-package-id\" name=\"test-dev\" version=1\nℹ Waiting for build to start...\n✓ Package \"test-package-id\" built successfully\n/-------\n| Instance has been deployed!\n| Instance id: test-instance-id\n| Instance service name: test-service-name\n| Instance host address: \x1b[0;1;33mtest-host-url\x1b[0m\n\\-------\n", }, }, @@ -164,6 +165,7 @@ func TestDeploy(t *testing.T) { DeployUploadTgzReturnErr: nil, DeployCreatePackageArgs: api.CreatePackageArgs{ + Name: "test-dev", SourceCodeKey: "test-key", Entrypoint: []string{"node", "index.js"}, BuildScriptPath: "", @@ -171,7 +173,7 @@ func TestDeploy(t *testing.T) { Runtime: "nodejs16", }, DeployCreatePackageTimes: 1, - DeployReturnCreatePackageResponse: api.CreatePackageResponse{PackageID: "test-package-id"}, + DeployReturnCreatePackageResponse: api.CreatePackageResponse{PackageID: "test-package-id", Name: "test-dev", Version: 1}, DeployCreatePackageReturnErr: nil, DeployWatchDeploymentPackageID: "test-package-id", @@ -194,7 +196,7 @@ func TestDeploy(t *testing.T) { DeployDeployInstanceReturnErr: nil, }, want: want{ - stdout: "✓ Project \"test\" retrieved: project_id=\"id\"\n✓ Deployment parameters validated\n✓ Source code uploaded.\n✓ Package created: package_id=\"test-package-id\"\nℹ Waiting for build to start...\n✓ Package \"test-package-id\" built successfully\n/-------\n| Instance has been deployed!\n| Instance id: test-instance-id\n| Instance service name: test-service-name\n| Instance host address: \x1b[0;1;33mtest-host-url\x1b[0m\n\\-------\n", + stdout: "✓ Project \"test\" retrieved: project_id=\"id\"\n✓ Deployment parameters validated\n✓ Source code uploaded.\n✓ Package created: package_id=\"test-package-id\" name=\"test-dev\" version=1\nℹ Waiting for build to start...\n✓ Package \"test-package-id\" built successfully\n/-------\n| Instance has been deployed!\n| Instance id: test-instance-id\n| Instance service name: test-service-name\n| Instance host address: \x1b[0;1;33mtest-host-url\x1b[0m\n\\-------\n", }, }, { @@ -222,6 +224,7 @@ func TestDeploy(t *testing.T) { DeployUploadTgzReturnErr: nil, DeployCreatePackageArgs: api.CreatePackageArgs{ + Name: "test-dev", SourceCodeKey: "test-key", Entrypoint: []string{"node", "index.js"}, BuildScriptPath: "", @@ -229,7 +232,7 @@ func TestDeploy(t *testing.T) { Runtime: "nodejs16", }, DeployCreatePackageTimes: 1, - DeployReturnCreatePackageResponse: api.CreatePackageResponse{PackageID: "test-package-id"}, + DeployReturnCreatePackageResponse: api.CreatePackageResponse{PackageID: "test-package-id", Name: "test-dev", Version: 1}, DeployCreatePackageReturnErr: nil, DeployWatchDeploymentPackageID: "test-package-id", @@ -260,7 +263,7 @@ func TestDeploy(t *testing.T) { DeployDeployInstanceReturnErr: nil, }, want: want{ - stdout: "✓ Project \"test\" retrieved: project_id=\"id\"\n✓ Deployment parameters validated\n✓ Source code uploaded.\n✓ Package created: package_id=\"test-package-id\"\nℹ Waiting for build to start...\n✓ Package \"test-package-id\" built successfully\n/-------\n| Instance has been deployed!\n| Instance id: test-instance-id\n| Instance service name: test-service-name\n| Instance host address: \x1b[0;1;33mtest-host-url\x1b[0m\n\\-------\n", + stdout: "✓ Project \"test\" retrieved: project_id=\"id\"\n✓ Deployment parameters validated\n✓ Source code uploaded.\n✓ Package created: package_id=\"test-package-id\" name=\"test-dev\" version=1\nℹ Waiting for build to start...\n✓ Package \"test-package-id\" built successfully\n/-------\n| Instance has been deployed!\n| Instance id: test-instance-id\n| Instance service name: test-service-name\n| Instance host address: \x1b[0;1;33mtest-host-url\x1b[0m\n\\-------\n", }, }, { @@ -288,6 +291,7 @@ func TestDeploy(t *testing.T) { DeployUploadTgzReturnErr: nil, DeployCreatePackageArgs: api.CreatePackageArgs{ + Name: "test-dev", SourceCodeKey: "test-key", Entrypoint: []string{"node", "index.js"}, BuildScriptPath: "", @@ -295,7 +299,7 @@ func TestDeploy(t *testing.T) { Runtime: "nodejs16", }, DeployCreatePackageTimes: 1, - DeployReturnCreatePackageResponse: api.CreatePackageResponse{PackageID: "test-package-id"}, + DeployReturnCreatePackageResponse: api.CreatePackageResponse{PackageID: "test-package-id", Name: "test-dev", Version: 1}, DeployCreatePackageReturnErr: nil, DeployWatchDeploymentPackageID: "test-package-id", @@ -319,7 +323,7 @@ func TestDeploy(t *testing.T) { DeployDeployInstanceReturnErr: nil, }, want: want{ - stdout: "✓ Project \"test\" retrieved: project_id=\"id\"\n✓ Deployment parameters validated\n✓ Source code uploaded.\n✓ Package created: package_id=\"test-package-id\"\nℹ Waiting for build to start...\n✓ Package \"test-package-id\" built successfully\n/-------\n| Instance has been deployed!\n| Instance id: test-instance-id\n| Instance service name: test-service-name\n| Instance host address: \x1b[0;1;33mtest-host-url\x1b[0m\n\\-------\n", + stdout: "✓ Project \"test\" retrieved: project_id=\"id\"\n✓ Deployment parameters validated\n✓ Source code uploaded.\n✓ Package created: package_id=\"test-package-id\" name=\"test-dev\" version=1\nℹ Waiting for build to start...\n✓ Package \"test-package-id\" built successfully\n/-------\n| Instance has been deployed!\n| Instance id: test-instance-id\n| Instance service name: test-service-name\n| Instance host address: \x1b[0;1;33mtest-host-url\x1b[0m\n\\-------\n", }, }, { @@ -347,6 +351,7 @@ func TestDeploy(t *testing.T) { DeployUploadTgzReturnErr: nil, DeployCreatePackageArgs: api.CreatePackageArgs{ + Name: "test-dev", SourceCodeKey: "test-key", Entrypoint: []string{"node", "index.js"}, BuildScriptPath: "", @@ -354,7 +359,7 @@ func TestDeploy(t *testing.T) { Runtime: "nodejs16", }, DeployCreatePackageTimes: 1, - DeployReturnCreatePackageResponse: api.CreatePackageResponse{PackageID: "test-package-id"}, + DeployReturnCreatePackageResponse: api.CreatePackageResponse{PackageID: "test-package-id", Name: "test-dev", Version: 1}, DeployCreatePackageReturnErr: nil, DeployWatchDeploymentPackageID: "test-package-id", @@ -385,7 +390,7 @@ func TestDeploy(t *testing.T) { DeployDeployInstanceReturnErr: nil, }, want: want{ - stdout: "✓ Project \"test\" retrieved: project_id=\"id\"\n✓ Deployment parameters validated\n✓ Source code uploaded.\n✓ Package created: package_id=\"test-package-id\"\nℹ Waiting for build to start...\n✓ Package \"test-package-id\" built successfully\n/-------\n| Instance has been deployed!\n| Instance id: test-instance-id\n| Instance service name: test-service-name\n| Instance host address: \x1b[0;1;33mtest-host-url\x1b[0m\n\\-------\n", + stdout: "✓ Project \"test\" retrieved: project_id=\"id\"\n✓ Deployment parameters validated\n✓ Source code uploaded.\n✓ Package created: package_id=\"test-package-id\" name=\"test-dev\" version=1\nℹ Waiting for build to start...\n✓ Package \"test-package-id\" built successfully\n/-------\n| Instance has been deployed!\n| Instance id: test-instance-id\n| Instance service name: test-service-name\n| Instance host address: \x1b[0;1;33mtest-host-url\x1b[0m\n\\-------\n", }, }, {