From 1ad7fac25646f70c78c4fa0d27eaed2324954c6d Mon Sep 17 00:00:00 2001 From: "Kevin P. Fleming" Date: Tue, 12 Nov 2024 11:03:23 -0500 Subject: [PATCH 1/2] feat(compute/build): Support 'upper bound' constraints on Rust versions. Changes planned in the upcoming Rust 1.84 release may not be compatible with the Fastly Compute platform (until further analysis is done, which may result in the need for platform changes). To ensure that customers do not build Compute packages which do not operate correctly in the platform, this PR adds a toolchain version constraint "upper bound" in addition to the existing "lower bound", only for Rust. The upper bound is currently set to "<1.84.0", so any versions currently available, plus all releases of Rust 1.83.x, will be permitted. Because this is a hard requirement of the platform, the check has been changed to report an error (not a warning), if the version of Rust in use does not meet the requirements. While this is potentially a breaking change (since the 'fastly compute build' process will stop, instead of continuing), it can only affect users with Rust versions older than 1.56.1, which are not actually usable at this time anyway. In addition, this PR changes the messages generated for both Go and Rust version-constraint failures to include the details of how the check failed. --- .fastly/config.toml | 2 +- pkg/commands/compute/language_go.go | 6 +++-- pkg/commands/compute/language_rust.go | 37 +++++++++++++++++---------- 3 files changed, 28 insertions(+), 17 deletions(-) diff --git a/.fastly/config.toml b/.fastly/config.toml index 097f79b38..2e2a6ac36 100644 --- a/.fastly/config.toml +++ b/.fastly/config.toml @@ -18,7 +18,7 @@ toolchain_constraint = ">= 1.21" # Go toolchain constraint for use wit toolchain_constraint_tinygo = ">= 1.18" # Go toolchain constraint for use with TinyGo. [language.rust] -toolchain_constraint = ">= 1.56.1" +toolchain_constraint = ">= 1.56.1, <1.84.0" wasm_wasi_target = "wasm32-wasi" [wasm-tools] diff --git a/pkg/commands/compute/language_go.go b/pkg/commands/compute/language_go.go index aeadc66ab..161863d84 100644 --- a/pkg/commands/compute/language_go.go +++ b/pkg/commands/compute/language_go.go @@ -2,6 +2,7 @@ package compute import ( "bufio" + "errors" "fmt" "io" "os" @@ -315,7 +316,8 @@ func (g *Go) toolchainConstraint(toolchain, pattern, constraint string) { return } - if !c.Check(v) { - text.Warning(g.output, "The %s version '%s' didn't meet the constraint '%s'\n\n", toolchain, version, constraint) + valid, errs := c.Validate(v) + if !valid { + text.Warning(g.output, "The %s version requirement was not satisfied: %s", toolchain, errors.Join(errs...)) } } diff --git a/pkg/commands/compute/language_rust.go b/pkg/commands/compute/language_rust.go index 2ae33deca..c57bddba3 100644 --- a/pkg/commands/compute/language_rust.go +++ b/pkg/commands/compute/language_rust.go @@ -3,6 +3,7 @@ package compute import ( "bytes" "encoding/json" + "errors" "fmt" "io" "os" @@ -157,7 +158,10 @@ func (r *Rust) Build() error { text.Info(r.output, "No [scripts.build] found in %s. The following default build command for Rust will be used: `%s`\n\n", r.manifestFilename, r.build) } - version := r.toolchainConstraint() + version, err := r.toolchainConstraint() + if err != nil { + return err + } if version != nil { err := r.checkCargoConfigFileName(version) @@ -296,12 +300,8 @@ func (r *Rust) modifyCargoPackageName(noBuildScript bool) error { return nil } -// toolchainConstraint warns the user if the required constraint is not met. -// -// NOTE: We don't stop the build as their toolchain may compile successfully. -// The warning is to help a user know something isn't quite right and gives them -// the opportunity to do something about it if they choose. -func (r *Rust) toolchainConstraint() *semver.Version { +// toolchainConstraint generates an error if the toolchain constraint is not met. +func (r *Rust) toolchainConstraint() (*semver.Version, error) { if r.verbose { text.Info(r.output, "The Fastly CLI requires a Rust version '%s'.\n\n", r.config.ToolchainConstraint) } @@ -318,31 +318,40 @@ func (r *Rust) toolchainConstraint() *semver.Version { stdout, err := cmd.Output() output := string(stdout) if err != nil { - return nil + return nil, err } versionPattern := regexp.MustCompile(`cargo (?P\d[^\s]+)`) match := versionPattern.FindStringSubmatch(output) if len(match) < 2 { // We expect a pattern with one capture group. - return nil + return nil, fmt.Errorf("unable to obtain a version number from the 'cargo' command") } version := match[1] v, err := semver.NewVersion(version) if err != nil { - return nil + return nil, fmt.Errorf("the version string '%s' reported by the 'cargo' command is not a valid version number", version) } c, err := semver.NewConstraint(r.config.ToolchainConstraint) if err != nil { - return nil + return nil, fmt.Errorf("the 'toolchain_constraint' value '%s' (from the config.toml file) is not a valid version constraint", r.config.ToolchainConstraint) } - if !c.Check(v) { - text.Warning(r.output, "The Rust version '%s' didn't meet the constraint '%s'\n\n", version, r.config.ToolchainConstraint) + valid, errs := c.Validate(v) + if !valid { + for _, err = range errs { + // if an 'upper bound' constraint was + // violated, generate an error message + // specific to that situation + if strings.Contains(err.Error(), "is greater than") { + return nil, fmt.Errorf("version '%s' of Rust has not been validated for use with Fastly Compute", v) + } + } + return nil, fmt.Errorf("the Rust version requirement was not satisfied: '%w'", errors.Join(errs...)) } - return v + return v, nil } func (r *Rust) checkCargoConfigFileName(rustVersion *semver.Version) error { From 54d8d050f2617727ad78a0591bf4c6f01e7aaa26 Mon Sep 17 00:00:00 2001 From: "Kevin P. Fleming" Date: Wed, 20 Nov 2024 14:54:24 -0500 Subject: [PATCH 2/2] Improve error message for too-high Rust version. Use a RemediationError to give the user a link to a documentation page where they can get more information about supported versions. --- pkg/commands/compute/language_rust.go | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/pkg/commands/compute/language_rust.go b/pkg/commands/compute/language_rust.go index c57bddba3..b7167447a 100644 --- a/pkg/commands/compute/language_rust.go +++ b/pkg/commands/compute/language_rust.go @@ -340,15 +340,22 @@ func (r *Rust) toolchainConstraint() (*semver.Version, error) { valid, errs := c.Validate(v) if !valid { - for _, err = range errs { + err = nil + for _, e := range errs { // if an 'upper bound' constraint was // violated, generate an error message // specific to that situation - if strings.Contains(err.Error(), "is greater than") { - return nil, fmt.Errorf("version '%s' of Rust has not been validated for use with Fastly Compute", v) + if strings.Contains(e.Error(), "is greater than") { + err = fmt.Errorf("version '%s' of Rust has not been validated for use with Fastly Compute", v) } } - return nil, fmt.Errorf("the Rust version requirement was not satisfied: '%w'", errors.Join(errs...)) + if err == nil { + err = fmt.Errorf("the Rust version requirement was not satisfied: '%w'", errors.Join(errs...)) + } + return nil, fsterr.RemediationError{ + Inner: err, + Remediation: "Consult the Rust guide for Compute at https://www.fastly.com/documentation/guides/compute/rust/ for more information.", + } } return v, nil