From aa747deda4db33990a596a833dfb1116ae3884e9 Mon Sep 17 00:00:00 2001 From: Andrew Nester Date: Wed, 27 Mar 2024 12:58:54 +0100 Subject: [PATCH 1/8] Allow specifying minimum and maximum CLI version required to run the bundle --- bundle/config/bundle.go | 6 + bundle/config/mutator/mutator.go | 3 + bundle/config/mutator/verify_cli_version.go | 59 ++++++++++ .../config/mutator/verify_cli_version_test.go | 105 ++++++++++++++++++ internal/build/variables.go | 6 + 5 files changed, 179 insertions(+) create mode 100644 bundle/config/mutator/verify_cli_version.go create mode 100644 bundle/config/mutator/verify_cli_version_test.go diff --git a/bundle/config/bundle.go b/bundle/config/bundle.go index 21278151f9..e812d3e97d 100644 --- a/bundle/config/bundle.go +++ b/bundle/config/bundle.go @@ -43,4 +43,10 @@ type Bundle struct { // Deployment section specifies deployment related configuration for bundle Deployment Deployment `json:"deployment"` + + // Minimum Databricks CLI version required to run the bundle. + MinDatabricksCliVersion string `json:"min_databricks_cli_version,omitempty"` + + // Maximum Databricks CLI version required to run the bundle. + MaxDatabricksCliVersion string `json:"max_databricks_cli_version,omitempty"` } diff --git a/bundle/config/mutator/mutator.go b/bundle/config/mutator/mutator.go index 99b7e9ac99..fda1182716 100644 --- a/bundle/config/mutator/mutator.go +++ b/bundle/config/mutator/mutator.go @@ -12,6 +12,9 @@ func DefaultMutators() []bundle.Mutator { loader.EntryPoint(), loader.ProcessRootIncludes(), + // Verify that the CLI version is within the specified range. + VerifyCliVersion(), + // Execute preinit script after loading all configuration files. scripts.Execute(config.ScriptPreInit), EnvironmentsToTargets(), diff --git a/bundle/config/mutator/verify_cli_version.go b/bundle/config/mutator/verify_cli_version.go new file mode 100644 index 0000000000..ae613d0ba8 --- /dev/null +++ b/bundle/config/mutator/verify_cli_version.go @@ -0,0 +1,59 @@ +package mutator + +import ( + "context" + "fmt" + + "github.com/databricks/cli/bundle" + "github.com/databricks/cli/internal/build" + "github.com/databricks/cli/libs/diag" + "golang.org/x/mod/semver" +) + +func VerifyCliVersion() bundle.Mutator { + return &verifyCliVersion{} +} + +type verifyCliVersion struct { +} + +func (v *verifyCliVersion) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { + currentVersion := "v" + build.GetInfo().Version + if b.Config.Bundle.MinDatabricksCliVersion != "" { + v := "v" + b.Config.Bundle.MinDatabricksCliVersion + if err := validateVersion(v); err != nil { + return diag.FromErr(err) + } + + if isLowerVersion(currentVersion, v) { + return diag.Errorf("minimum Databricks CLI version required: %s, current version: %s", v, currentVersion) + } + } + + if b.Config.Bundle.MaxDatabricksCliVersion != "" { + v := "v" + b.Config.Bundle.MaxDatabricksCliVersion + if err := validateVersion(v); err != nil { + return diag.FromErr(err) + } + + if isLowerVersion(v, currentVersion) { + return diag.Errorf("maximum Databricks CLI version required: %s, current version: %s", v, currentVersion) + } + } + return nil +} + +func (v *verifyCliVersion) Name() string { + return "VerifyCliVersion" +} + +func isLowerVersion(v1, v2 string) bool { + return semver.Compare(v1, v2) < 0 +} + +func validateVersion(v string) error { + if !semver.IsValid(v) { + return fmt.Errorf("invalid version %q specified. Please specify the version in the format 0.0.0", v) + } + return nil +} diff --git a/bundle/config/mutator/verify_cli_version_test.go b/bundle/config/mutator/verify_cli_version_test.go new file mode 100644 index 0000000000..f165b39a07 --- /dev/null +++ b/bundle/config/mutator/verify_cli_version_test.go @@ -0,0 +1,105 @@ +package mutator + +import ( + "context" + "fmt" + "testing" + + "github.com/databricks/cli/bundle" + "github.com/databricks/cli/bundle/config" + "github.com/databricks/cli/internal/build" + "github.com/stretchr/testify/require" +) + +type testCase struct { + currentVersion string + minVersion string + maxVersion string + expectedError string +} + +func TestVerifyCliVersion(t *testing.T) { + testCases := []testCase{ + { + currentVersion: "0.0.1", + }, + { + currentVersion: "0.0.1", + minVersion: "0.100.0", + expectedError: "minimum Databricks CLI version required: v0.100.0, current version: v0.0.1", + }, + { + currentVersion: "0.100.0", + minVersion: "0.100.0", + }, + { + currentVersion: "0.100.1", + minVersion: "0.100.0", + }, + { + currentVersion: "0.100.0", + maxVersion: "1.0.0", + }, + { + currentVersion: "1.0.0", + maxVersion: "1.0.0", + }, + { + currentVersion: "1.0.0", + maxVersion: "0.100.0", + expectedError: "maximum Databricks CLI version required: v0.100.0, current version: v1.0.0", + }, + { + currentVersion: "0.99.0", + minVersion: "0.100.0", + maxVersion: "0.100.2", + expectedError: "minimum Databricks CLI version required: v0.100.0, current version: v0.99.0", + }, + { + currentVersion: "0.100.0", + minVersion: "0.100.0", + maxVersion: "0.100.2", + }, + { + currentVersion: "0.100.1", + minVersion: "0.100.0", + maxVersion: "0.100.2", + }, + { + currentVersion: "0.100.2", + minVersion: "0.100.0", + maxVersion: "0.100.2", + }, + { + currentVersion: "0.101.0", + minVersion: "0.100.0", + maxVersion: "0.100.2", + expectedError: "maximum Databricks CLI version required: v0.100.2, current version: v0.101.0", + }, + } + + t.Cleanup(func() { + build.SetBuildVersion(build.DefaultSemver) + }) + + for i, tc := range testCases { + t.Run(fmt.Sprintf("testcase #%d", i), func(t *testing.T) { + build.SetBuildVersion(tc.currentVersion) + b := &bundle.Bundle{ + Config: config.Root{ + Bundle: config.Bundle{ + MinDatabricksCliVersion: tc.minVersion, + MaxDatabricksCliVersion: tc.maxVersion, + }, + }, + } + diags := bundle.Apply(context.Background(), b, VerifyCliVersion()) + if tc.expectedError != "" { + require.NotEmpty(t, diags) + require.Equal(t, tc.expectedError, diags.Error().Error()) + } else { + require.Empty(t, diags) + } + }) + } +} diff --git a/internal/build/variables.go b/internal/build/variables.go index 096657c6e0..197dee9c30 100644 --- a/internal/build/variables.go +++ b/internal/build/variables.go @@ -16,3 +16,9 @@ var buildPatch string = "0" var buildPrerelease string = "" var buildIsSnapshot string = "false" var buildTimestamp string = "0" + +// This function is used to set the build version for testing purposes. +func SetBuildVersion(version string) { + buildVersion = version + info.Version = version +} From 652b97372745e1bf8b987ca89dd78c0a81e63267 Mon Sep 17 00:00:00 2001 From: Andrew Nester Date: Thu, 28 Mar 2024 11:24:39 +0100 Subject: [PATCH 2/8] added prerelease test cases --- .../config/mutator/verify_cli_version_test.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/bundle/config/mutator/verify_cli_version_test.go b/bundle/config/mutator/verify_cli_version_test.go index f165b39a07..6f38d8c9e4 100644 --- a/bundle/config/mutator/verify_cli_version_test.go +++ b/bundle/config/mutator/verify_cli_version_test.go @@ -76,6 +76,23 @@ func TestVerifyCliVersion(t *testing.T) { maxVersion: "0.100.2", expectedError: "maximum Databricks CLI version required: v0.100.2, current version: v0.101.0", }, + { + currentVersion: "0.100.0-beta", + minVersion: "0.100.0", + maxVersion: "0.100.2", + expectedError: "minimum Databricks CLI version required: v0.100.0, current version: v0.100.0-beta", + }, + { + currentVersion: "0.100.1-beta", + minVersion: "0.100.0", + maxVersion: "0.100.2", + }, + { + currentVersion: "0.100.3-beta", + minVersion: "0.100.0", + maxVersion: "0.100.2", + expectedError: "maximum Databricks CLI version required: v0.100.2, current version: v0.100.3-beta", + }, } t.Cleanup(func() { From bcbf6489b58c0e9f992b88152a1ca02172e33cc5 Mon Sep 17 00:00:00 2001 From: Andrew Nester Date: Thu, 28 Mar 2024 14:54:35 +0100 Subject: [PATCH 3/8] use version constraints --- bundle/config/bundle.go | 7 +- bundle/config/mutator/verify_cli_version.go | 49 +++++------- .../config/mutator/verify_cli_version_test.go | 76 +++++++++++-------- go.mod | 1 + go.sum | 2 + 5 files changed, 67 insertions(+), 68 deletions(-) diff --git a/bundle/config/bundle.go b/bundle/config/bundle.go index ff6c5a1073..78648dfd7f 100644 --- a/bundle/config/bundle.go +++ b/bundle/config/bundle.go @@ -44,9 +44,6 @@ type Bundle struct { // Deployment section specifies deployment related configuration for bundle Deployment Deployment `json:"deployment,omitempty"` - // Minimum Databricks CLI version required to run the bundle. - MinDatabricksCliVersion string `json:"min_databricks_cli_version,omitempty"` - - // Maximum Databricks CLI version required to run the bundle. - MaxDatabricksCliVersion string `json:"max_databricks_cli_version,omitempty"` + // Databricks CLI version constraints required to run the bundle. + DatabricksCliVersion string `json:"databricks_cli_version,omitempty"` } diff --git a/bundle/config/mutator/verify_cli_version.go b/bundle/config/mutator/verify_cli_version.go index ae613d0ba8..e0247b09e1 100644 --- a/bundle/config/mutator/verify_cli_version.go +++ b/bundle/config/mutator/verify_cli_version.go @@ -2,12 +2,11 @@ package mutator import ( "context" - "fmt" + semver "github.com/Masterminds/semver/v3" "github.com/databricks/cli/bundle" "github.com/databricks/cli/internal/build" "github.com/databricks/cli/libs/diag" - "golang.org/x/mod/semver" ) func VerifyCliVersion() bundle.Mutator { @@ -18,42 +17,30 @@ type verifyCliVersion struct { } func (v *verifyCliVersion) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { - currentVersion := "v" + build.GetInfo().Version - if b.Config.Bundle.MinDatabricksCliVersion != "" { - v := "v" + b.Config.Bundle.MinDatabricksCliVersion - if err := validateVersion(v); err != nil { - return diag.FromErr(err) - } - - if isLowerVersion(currentVersion, v) { - return diag.Errorf("minimum Databricks CLI version required: %s, current version: %s", v, currentVersion) - } + // No constraints specified, skip the check. + if b.Config.Bundle.DatabricksCliVersion == "" { + return nil } - if b.Config.Bundle.MaxDatabricksCliVersion != "" { - v := "v" + b.Config.Bundle.MaxDatabricksCliVersion - if err := validateVersion(v); err != nil { - return diag.FromErr(err) - } + constraint := b.Config.Bundle.DatabricksCliVersion + currentVersion := build.GetInfo().Version + c, err := semver.NewConstraint(constraint) + if err != nil { + return diag.Errorf("invalid version constraint %q specified. Please specify the version constraint in the format 0.0.0", constraint) + } + + version, err := semver.NewVersion(currentVersion) + if err != nil { + return diag.Errorf("parsing CLI version %q failed", currentVersion) + } - if isLowerVersion(v, currentVersion) { - return diag.Errorf("maximum Databricks CLI version required: %s, current version: %s", v, currentVersion) - } + if !c.Check(version) { + return diag.Errorf("Databricks CLI version constraint not satisfied. Required: %s, current: %s", constraint, currentVersion) } + return nil } func (v *verifyCliVersion) Name() string { return "VerifyCliVersion" } - -func isLowerVersion(v1, v2 string) bool { - return semver.Compare(v1, v2) < 0 -} - -func validateVersion(v string) error { - if !semver.IsValid(v) { - return fmt.Errorf("invalid version %q specified. Please specify the version in the format 0.0.0", v) - } - return nil -} diff --git a/bundle/config/mutator/verify_cli_version_test.go b/bundle/config/mutator/verify_cli_version_test.go index 6f38d8c9e4..b15a6bf08e 100644 --- a/bundle/config/mutator/verify_cli_version_test.go +++ b/bundle/config/mutator/verify_cli_version_test.go @@ -13,8 +13,7 @@ import ( type testCase struct { currentVersion string - minVersion string - maxVersion string + constraint string expectedError string } @@ -25,73 +24,87 @@ func TestVerifyCliVersion(t *testing.T) { }, { currentVersion: "0.0.1", - minVersion: "0.100.0", - expectedError: "minimum Databricks CLI version required: v0.100.0, current version: v0.0.1", + constraint: "0.100.0", + expectedError: "Databricks CLI version constraint not satisfied. Required: 0.100.0, current: 0.0.1", + }, + { + currentVersion: "0.0.1", + constraint: ">= 0.100.0", + expectedError: "Databricks CLI version constraint not satisfied. Required: >= 0.100.0, current: 0.0.1", }, { currentVersion: "0.100.0", - minVersion: "0.100.0", + constraint: "0.100.0", }, { currentVersion: "0.100.1", - minVersion: "0.100.0", + constraint: "0.100.0", + expectedError: "Databricks CLI version constraint not satisfied. Required: 0.100.0, current: 0.100.1", + }, + { + currentVersion: "0.100.1", + constraint: ">= 0.100.0", }, { currentVersion: "0.100.0", - maxVersion: "1.0.0", + constraint: "<= 1.0.0", }, { currentVersion: "1.0.0", - maxVersion: "1.0.0", + constraint: "<= 1.0.0", }, { currentVersion: "1.0.0", - maxVersion: "0.100.0", - expectedError: "maximum Databricks CLI version required: v0.100.0, current version: v1.0.0", + constraint: "<= 0.100.0", + expectedError: "Databricks CLI version constraint not satisfied. Required: <= 0.100.0, current: 1.0.0", }, { currentVersion: "0.99.0", - minVersion: "0.100.0", - maxVersion: "0.100.2", - expectedError: "minimum Databricks CLI version required: v0.100.0, current version: v0.99.0", + constraint: ">= 0.100.0 <= 0.100.2", + expectedError: "Databricks CLI version constraint not satisfied. Required: >= 0.100.0 <= 0.100.2, current: 0.99.0", }, { currentVersion: "0.100.0", - minVersion: "0.100.0", - maxVersion: "0.100.2", + constraint: ">= 0.100.0 <= 0.100.2", }, { currentVersion: "0.100.1", - minVersion: "0.100.0", - maxVersion: "0.100.2", + constraint: ">= 0.100.0 <= 0.100.2", }, { currentVersion: "0.100.2", - minVersion: "0.100.0", - maxVersion: "0.100.2", + constraint: ">= 0.100.0 <= 0.100.2", }, { currentVersion: "0.101.0", - minVersion: "0.100.0", - maxVersion: "0.100.2", - expectedError: "maximum Databricks CLI version required: v0.100.2, current version: v0.101.0", + constraint: ">= 0.100.0 <= 0.100.2", + expectedError: "Databricks CLI version constraint not satisfied. Required: >= 0.100.0 <= 0.100.2, current: 0.101.0", }, { currentVersion: "0.100.0-beta", - minVersion: "0.100.0", - maxVersion: "0.100.2", - expectedError: "minimum Databricks CLI version required: v0.100.0, current version: v0.100.0-beta", + constraint: ">= 0.100.0 <= 0.100.2", + expectedError: "Databricks CLI version constraint not satisfied. Required: >= 0.100.0 <= 0.100.2, current: 0.100.0-beta", + }, + { + currentVersion: "0.100.0-beta", + constraint: ">= 0.100.0-0 <= 0.100.2-0", }, { currentVersion: "0.100.1-beta", - minVersion: "0.100.0", - maxVersion: "0.100.2", + constraint: ">= 0.100.0-0 <= 0.100.2-0", }, { currentVersion: "0.100.3-beta", - minVersion: "0.100.0", - maxVersion: "0.100.2", - expectedError: "maximum Databricks CLI version required: v0.100.2, current version: v0.100.3-beta", + constraint: ">= 0.100.0 <= 0.100.2", + expectedError: "Databricks CLI version constraint not satisfied. Required: >= 0.100.0 <= 0.100.2, current: 0.100.3-beta", + }, + { + currentVersion: "0.100.123", + constraint: "0.100.*", + }, + { + currentVersion: "0.100.123", + constraint: "^0.100", }, } @@ -105,8 +118,7 @@ func TestVerifyCliVersion(t *testing.T) { b := &bundle.Bundle{ Config: config.Root{ Bundle: config.Bundle{ - MinDatabricksCliVersion: tc.minVersion, - MaxDatabricksCliVersion: tc.maxVersion, + DatabricksCliVersion: tc.constraint, }, }, } diff --git a/go.mod b/go.mod index d9e6c24f08..dbdafafb69 100644 --- a/go.mod +++ b/go.mod @@ -34,6 +34,7 @@ require gopkg.in/yaml.v3 v3.0.1 require ( cloud.google.com/go/compute v1.23.4 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect + github.com/Masterminds/semver/v3 v3.2.1 // indirect github.com/ProtonMail/go-crypto v1.1.0-alpha.0 // indirect github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect diff --git a/go.sum b/go.sum index a4a6eb40b8..fc978c8413 100644 --- a/go.sum +++ b/go.sum @@ -6,6 +6,8 @@ cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2Aawl dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= +github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/ProtonMail/go-crypto v1.1.0-alpha.0 h1:nHGfwXmFvJrSR9xu8qL7BkO4DqTHXE9N5vPhgY2I+j0= From d02c9df861c5013dc00a1dac511f6827a8061605 Mon Sep 17 00:00:00 2001 From: Andrew Nester Date: Thu, 28 Mar 2024 15:00:05 +0100 Subject: [PATCH 4/8] mod tidy --- go.mod | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index dbdafafb69..6e84eccdec 100644 --- a/go.mod +++ b/go.mod @@ -29,12 +29,14 @@ require ( gopkg.in/ini.v1 v1.67.0 // Apache 2.0 ) -require gopkg.in/yaml.v3 v3.0.1 +require ( + github.com/Masterminds/semver/v3 v3.2.1 + gopkg.in/yaml.v3 v3.0.1 +) require ( cloud.google.com/go/compute v1.23.4 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect - github.com/Masterminds/semver/v3 v3.2.1 // indirect github.com/ProtonMail/go-crypto v1.1.0-alpha.0 // indirect github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect From c308dc474dead863465daa4e80cbe8b31b94cdec Mon Sep 17 00:00:00 2001 From: Andrew Nester Date: Thu, 28 Mar 2024 16:01:32 +0100 Subject: [PATCH 5/8] added comma --- .../config/mutator/verify_cli_version_test.go | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/bundle/config/mutator/verify_cli_version_test.go b/bundle/config/mutator/verify_cli_version_test.go index b15a6bf08e..02d0faccac 100644 --- a/bundle/config/mutator/verify_cli_version_test.go +++ b/bundle/config/mutator/verify_cli_version_test.go @@ -60,43 +60,43 @@ func TestVerifyCliVersion(t *testing.T) { }, { currentVersion: "0.99.0", - constraint: ">= 0.100.0 <= 0.100.2", - expectedError: "Databricks CLI version constraint not satisfied. Required: >= 0.100.0 <= 0.100.2, current: 0.99.0", + constraint: ">= 0.100.0, <= 0.100.2", + expectedError: "Databricks CLI version constraint not satisfied. Required: >= 0.100.0, <= 0.100.2, current: 0.99.0", }, { currentVersion: "0.100.0", - constraint: ">= 0.100.0 <= 0.100.2", + constraint: ">= 0.100.0, <= 0.100.2", }, { currentVersion: "0.100.1", - constraint: ">= 0.100.0 <= 0.100.2", + constraint: ">= 0.100.0, <= 0.100.2", }, { currentVersion: "0.100.2", - constraint: ">= 0.100.0 <= 0.100.2", + constraint: ">= 0.100.0, <= 0.100.2", }, { currentVersion: "0.101.0", - constraint: ">= 0.100.0 <= 0.100.2", - expectedError: "Databricks CLI version constraint not satisfied. Required: >= 0.100.0 <= 0.100.2, current: 0.101.0", + constraint: ">= 0.100.0, <= 0.100.2", + expectedError: "Databricks CLI version constraint not satisfied. Required: >= 0.100.0, <= 0.100.2, current: 0.101.0", }, { currentVersion: "0.100.0-beta", - constraint: ">= 0.100.0 <= 0.100.2", - expectedError: "Databricks CLI version constraint not satisfied. Required: >= 0.100.0 <= 0.100.2, current: 0.100.0-beta", + constraint: ">= 0.100.0, <= 0.100.2", + expectedError: "Databricks CLI version constraint not satisfied. Required: >= 0.100.0, <= 0.100.2, current: 0.100.0-beta", }, { currentVersion: "0.100.0-beta", - constraint: ">= 0.100.0-0 <= 0.100.2-0", + constraint: ">= 0.100.0-0, <= 0.100.2-0", }, { currentVersion: "0.100.1-beta", - constraint: ">= 0.100.0-0 <= 0.100.2-0", + constraint: ">= 0.100.0-0, <= 0.100.2-0", }, { currentVersion: "0.100.3-beta", - constraint: ">= 0.100.0 <= 0.100.2", - expectedError: "Databricks CLI version constraint not satisfied. Required: >= 0.100.0 <= 0.100.2, current: 0.100.3-beta", + constraint: ">= 0.100.0, <= 0.100.2", + expectedError: "Databricks CLI version constraint not satisfied. Required: >= 0.100.0, <= 0.100.2, current: 0.100.3-beta", }, { currentVersion: "0.100.123", From 3cd89620753a0bcdd14fb69e2a8aa73186be90d3 Mon Sep 17 00:00:00 2001 From: Andrew Nester Date: Tue, 2 Apr 2024 11:49:57 +0200 Subject: [PATCH 6/8] enforce syntax --- bundle/config/mutator/verify_cli_version.go | 38 ++++++++++++++++++- .../config/mutator/verify_cli_version_test.go | 36 ++++++++++++++++++ 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/bundle/config/mutator/verify_cli_version.go b/bundle/config/mutator/verify_cli_version.go index e0247b09e1..ac6f08922b 100644 --- a/bundle/config/mutator/verify_cli_version.go +++ b/bundle/config/mutator/verify_cli_version.go @@ -2,6 +2,8 @@ package mutator import ( "context" + "fmt" + "regexp" semver "github.com/Masterminds/semver/v3" "github.com/databricks/cli/bundle" @@ -23,10 +25,13 @@ func (v *verifyCliVersion) Apply(ctx context.Context, b *bundle.Bundle) diag.Dia } constraint := b.Config.Bundle.DatabricksCliVersion + if err := validateConstraintSyntax(constraint); err != nil { + return diag.FromErr(err) + } currentVersion := build.GetInfo().Version c, err := semver.NewConstraint(constraint) if err != nil { - return diag.Errorf("invalid version constraint %q specified. Please specify the version constraint in the format 0.0.0", constraint) + return diag.FromErr(err) } version, err := semver.NewVersion(currentVersion) @@ -44,3 +49,34 @@ func (v *verifyCliVersion) Apply(ctx context.Context, b *bundle.Bundle) diag.Dia func (v *verifyCliVersion) Name() string { return "VerifyCliVersion" } + +// validateConstraintSyntax validates the syntax of the version constraint. +func validateConstraintSyntax(constraint string) error { + r := generateConstraintSyntaxRegexp() + if !r.MatchString(constraint) { + return fmt.Errorf("invalid version constraint %q specified. Please specify the version constraint in the format (>=) 0.0.0(, <= 1.0.0)", constraint) + } + + return nil +} + +// Generate regexp which matches the supported version constraint syntax. +func generateConstraintSyntaxRegexp() *regexp.Regexp { + // Supported formats are: + // 1. 0.0.0 + // 2. >= 0.0.0 + // 3. <= 0.0.0 + // 4. > 0.0.0 + // 5. < 0.0.0 + // 6. != 0.0.0 + // 7. 0.0.* + // 8. 0.* + // 9. >= 0.0.0, <= 1.0.0 + // 10. 0.0.0-0 + // 11. 0.0.0-beta + // 12. >= 0.0.0-0, <= 1.0.0-0 + + matchVersion := `(\d+\.\d+\.\d+(\-\w+)?|\d+\.\d+.\*|\d+\.\*)` + matchOperators := `(>=|<=|>|<|!=)?` + return regexp.MustCompile(fmt.Sprintf(`^%s ?%s(, %s %s)?$`, matchOperators, matchVersion, matchOperators, matchVersion)) +} diff --git a/bundle/config/mutator/verify_cli_version_test.go b/bundle/config/mutator/verify_cli_version_test.go index 02d0faccac..6fe3166df5 100644 --- a/bundle/config/mutator/verify_cli_version_test.go +++ b/bundle/config/mutator/verify_cli_version_test.go @@ -105,6 +105,7 @@ func TestVerifyCliVersion(t *testing.T) { { currentVersion: "0.100.123", constraint: "^0.100", + expectedError: "invalid version constraint \"^0.100\" specified. Please specify the version constraint in the format (>=) 0.0.0(, <= 1.0.0)", }, } @@ -132,3 +133,38 @@ func TestVerifyCliVersion(t *testing.T) { }) } } + +func TestValidateConstraint(t *testing.T) { + testCases := []struct { + constraint string + expected bool + }{ + {"0.0.0", true}, + {">= 0.0.0", true}, + {"<= 0.0.0", true}, + {"> 0.0.0", true}, + {"< 0.0.0", true}, + {"!= 0.0.0", true}, + {"0.0.*", true}, + {"0.*", true}, + {">= 0.0.0, <= 1.0.0", true}, + {">= 0.0.0-0, <= 1.0.0-0", true}, + {"0.0.0-0", true}, + {"0.0.0-beta", true}, + {"^0.0.0", false}, + {"~0.0.0", false}, + {"0.0.0 1.0.0", false}, + {"> 0.0.0 < 1.0.0", false}, + } + + for _, tc := range testCases { + t.Run(tc.constraint, func(t *testing.T) { + err := validateConstraintSyntax(tc.constraint) + if tc.expected { + require.NoError(t, err) + } else { + require.Error(t, err) + } + }) + } +} From 189f9baf12ad97cbf26b2dc52e57585c419cebf2 Mon Sep 17 00:00:00 2001 From: Andrew Nester Date: Tue, 2 Apr 2024 14:27:09 +0200 Subject: [PATCH 7/8] Update bundle/config/mutator/verify_cli_version.go Co-authored-by: Lennart Kats (databricks) --- bundle/config/mutator/verify_cli_version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundle/config/mutator/verify_cli_version.go b/bundle/config/mutator/verify_cli_version.go index ac6f08922b..9c32fcc9d4 100644 --- a/bundle/config/mutator/verify_cli_version.go +++ b/bundle/config/mutator/verify_cli_version.go @@ -62,7 +62,7 @@ func validateConstraintSyntax(constraint string) error { // Generate regexp which matches the supported version constraint syntax. func generateConstraintSyntaxRegexp() *regexp.Regexp { - // Supported formats are: + // We intentionally only support the format supported by requirements.txt: // 1. 0.0.0 // 2. >= 0.0.0 // 3. <= 0.0.0 From 37a3655954ab5e69dfca9e291f1cd2ddb7ef1884 Mon Sep 17 00:00:00 2001 From: Andrew Nester Date: Tue, 2 Apr 2024 14:49:57 +0200 Subject: [PATCH 8/8] fixes --- NOTICE | 4 ++++ bundle/config/mutator/verify_cli_version_test.go | 4 ++++ go.mod | 5 +---- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/NOTICE b/NOTICE index fdc2a88cf9..e356d028ed 100644 --- a/NOTICE +++ b/NOTICE @@ -73,6 +73,10 @@ ghodss/yaml - https://github.com/ghodss/yaml Copyright (c) 2014 Sam Ghods License - https://github.com/ghodss/yaml/blob/master/LICENSE +Masterminds/semver - https://github.com/Masterminds/semver +Copyright (C) 2014-2019, Matt Butcher and Matt Farina +License - https://github.com/Masterminds/semver/blob/master/LICENSE.txt + mattn/go-isatty - https://github.com/mattn/go-isatty Copyright (c) Yasuhiro MATSUMOTO https://github.com/mattn/go-isatty/blob/master/LICENSE diff --git a/bundle/config/mutator/verify_cli_version_test.go b/bundle/config/mutator/verify_cli_version_test.go index 6fe3166df5..24f6567453 100644 --- a/bundle/config/mutator/verify_cli_version_test.go +++ b/bundle/config/mutator/verify_cli_version_test.go @@ -110,6 +110,10 @@ func TestVerifyCliVersion(t *testing.T) { } t.Cleanup(func() { + // Reset the build version to the default version + // so that it doesn't affect other tests + // It doesn't really matter what we configure this to when testing + // as long as it is a valid semver version. build.SetBuildVersion(build.DefaultSemver) }) diff --git a/go.mod b/go.mod index 6e84eccdec..88fb8faeb9 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/databricks/cli go 1.21 require ( + github.com/Masterminds/semver/v3 v3.2.1 // MIT github.com/briandowns/spinner v1.23.0 // Apache 2.0 github.com/databricks/databricks-sdk-go v0.36.0 // Apache 2.0 github.com/fatih/color v1.16.0 // MIT @@ -27,10 +28,6 @@ require ( golang.org/x/term v0.18.0 golang.org/x/text v0.14.0 gopkg.in/ini.v1 v1.67.0 // Apache 2.0 -) - -require ( - github.com/Masterminds/semver/v3 v3.2.1 gopkg.in/yaml.v3 v3.0.1 )