diff --git a/product/consul.go b/product/consul.go index 03b0326..05db0cc 100644 --- a/product/consul.go +++ b/product/consul.go @@ -5,6 +5,7 @@ package product import ( "context" + "encoding/json" "fmt" "os/exec" "regexp" @@ -26,25 +27,15 @@ var Consul = Product{ return "consul" }, GetVersion: func(ctx context.Context, path string) (*version.Version, error) { - cmd := exec.CommandContext(ctx, path, "version") - - out, err := cmd.Output() - if err != nil { - return nil, err - } - - stdout := strings.TrimSpace(string(out)) - - submatches := consulVersionOutputRe.FindStringSubmatch(stdout) - if len(submatches) != 2 { - return nil, fmt.Errorf("unexpected number of version matches %d for %s", len(submatches), stdout) - } - v, err := version.NewVersion(submatches[1]) - if err != nil { - return nil, fmt.Errorf("unable to parse version %q: %w", submatches[1], err) + v, err := consulJsonVersion(ctx, path) + if err == nil { + return v, nil } - return v, err + // JSON output was added in 1.9.0 + // See https://github.com/hashicorp/consul/pull/8268 + // We assume that error implies older version. + return legacyConsulVersion(ctx, path) }, BuildInstructions: &BuildInstructions{ GitRepoURL: "https://github.com/hashicorp/consul.git", @@ -52,3 +43,44 @@ var Consul = Product{ Build: &build.GoBuild{}, }, } + +type consulJsonVersionOutput struct { + Version *version.Version `json:"Version"` +} + +func consulJsonVersion(ctx context.Context, path string) (*version.Version, error) { + cmd := exec.CommandContext(ctx, path, "version", "-format=json") + out, err := cmd.CombinedOutput() + if err != nil { + return nil, err + } + + var vOut consulJsonVersionOutput + err = json.Unmarshal(out, &vOut) + if err != nil { + return nil, err + } + + return vOut.Version, nil +} + +func legacyConsulVersion(ctx context.Context, path string) (*version.Version, error) { + cmd := exec.CommandContext(ctx, path, "version") + out, err := cmd.Output() + if err != nil { + return nil, err + } + + stdout := strings.TrimSpace(string(out)) + + submatches := consulVersionOutputRe.FindStringSubmatch(stdout) + if len(submatches) != 2 { + return nil, fmt.Errorf("unexpected number of version matches %d for %s", len(submatches), stdout) + } + v, err := version.NewVersion(submatches[1]) + if err != nil { + return nil, fmt.Errorf("unable to parse version %q: %w", submatches[1], err) + } + + return v, err +} diff --git a/product/terraform.go b/product/terraform.go index 39e81f6..a95fe0d 100644 --- a/product/terraform.go +++ b/product/terraform.go @@ -5,6 +5,7 @@ package product import ( "context" + "encoding/json" "fmt" "os/exec" "regexp" @@ -26,25 +27,15 @@ var Terraform = Product{ return "terraform" }, GetVersion: func(ctx context.Context, path string) (*version.Version, error) { - cmd := exec.CommandContext(ctx, path, "version") - - out, err := cmd.Output() - if err != nil { - return nil, err - } - - stdout := strings.TrimSpace(string(out)) - - submatches := terraformVersionOutputRe.FindStringSubmatch(stdout) - if len(submatches) != 2 { - return nil, fmt.Errorf("unexpected number of version matches %d for %s", len(submatches), stdout) - } - v, err := version.NewVersion(submatches[1]) - if err != nil { - return nil, fmt.Errorf("unable to parse version %q: %w", submatches[1], err) + v, err := terraformJsonVersion(ctx, path) + if err == nil { + return v, nil } - return v, err + // JSON output was added in 0.13.0 + // See https://github.com/hashicorp/terraform/pull/25252 + // We assume that error implies older version. + return legacyTerraformVersion(ctx, path) }, BuildInstructions: &BuildInstructions{ GitRepoURL: "https://github.com/hashicorp/terraform.git", @@ -52,3 +43,44 @@ var Terraform = Product{ Build: &build.GoBuild{DetectVendoring: true}, }, } + +type terraformJsonVersionOutput struct { + Version *version.Version `json:"terraform_version"` +} + +func terraformJsonVersion(ctx context.Context, path string) (*version.Version, error) { + cmd := exec.CommandContext(ctx, path, "version", "-json") + out, err := cmd.CombinedOutput() + if err != nil { + return nil, err + } + + var vOut terraformJsonVersionOutput + err = json.Unmarshal(out, &vOut) + if err != nil { + return nil, err + } + + return vOut.Version, nil +} + +func legacyTerraformVersion(ctx context.Context, path string) (*version.Version, error) { + cmd := exec.CommandContext(ctx, path, "version") + out, err := cmd.Output() + if err != nil { + return nil, err + } + + stdout := strings.TrimSpace(string(out)) + + submatches := terraformVersionOutputRe.FindStringSubmatch(stdout) + if len(submatches) != 2 { + return nil, fmt.Errorf("unexpected number of version matches %d for %s", len(submatches), stdout) + } + v, err := version.NewVersion(submatches[1]) + if err != nil { + return nil, fmt.Errorf("unable to parse version %q: %w", submatches[1], err) + } + + return v, err +}