From 81a5907f26446676839c4aea46dd8e41e8792265 Mon Sep 17 00:00:00 2001 From: Fabian Kammel Date: Thu, 10 Nov 2022 10:27:24 +0100 Subject: [PATCH] consistently use stdout and stderr (#502) * consistently use stdout and stderr Signed-off-by: Fabian Kammel --- cli/internal/cloudcmd/upgrade.go | 14 ++++++------- cli/internal/cloudcmd/upgrade_test.go | 4 ++-- cli/internal/cmd/configfetchmeasurements.go | 6 +++--- cli/internal/cmd/configinstancetypes.go | 3 +-- cli/internal/cmd/create.go | 16 +++++++-------- cli/internal/cmd/init.go | 10 +++++----- cli/internal/cmd/miniup.go | 22 ++++++++++----------- cli/internal/cmd/readconfig.go | 12 +++++------ cli/internal/cmd/recover.go | 2 +- cli/internal/cmd/terminate.go | 2 +- cli/internal/cmd/upgradeplan.go | 12 +++++------ cli/internal/cmd/upgradeplan_test.go | 13 +++++++----- cli/internal/cmd/verify.go | 2 +- cli/internal/cmd/version.go | 2 +- 14 files changed, 61 insertions(+), 59 deletions(-) diff --git a/cli/internal/cloudcmd/upgrade.go b/cli/internal/cloudcmd/upgrade.go index 5d08c11d6..298f921e4 100644 --- a/cli/internal/cloudcmd/upgrade.go +++ b/cli/internal/cloudcmd/upgrade.go @@ -29,11 +29,11 @@ type Upgrader struct { measurementsUpdater measurementsUpdater imageUpdater imageUpdater - writer io.Writer + outWriter io.Writer } // NewUpgrader returns a new Upgrader. -func NewUpgrader(writer io.Writer) (*Upgrader, error) { +func NewUpgrader(outWriter io.Writer) (*Upgrader, error) { kubeConfig, err := clientcmd.BuildConfigFromFlags("", constants.AdminConfFilename) if err != nil { return nil, fmt.Errorf("building kubernetes config: %w", err) @@ -53,7 +53,7 @@ func NewUpgrader(writer io.Writer) (*Upgrader, error) { return &Upgrader{ measurementsUpdater: &kubeMeasurementsUpdater{client: kubeClient}, imageUpdater: &kubeImageUpdater{client: unstructuredClient}, - writer: writer, + outWriter: outWriter, }, nil } @@ -118,7 +118,7 @@ func (u *Upgrader) updateMeasurements(ctx context.Context, measurements map[uint } if !changed { // measurements are the same, nothing to be done - fmt.Fprintln(u.writer, "Cluster is already using the chosen measurements, skipping measurements upgrade") + fmt.Fprintln(u.outWriter, "Cluster is already using the chosen measurements, skipping measurements upgrade") return nil } } @@ -136,7 +136,7 @@ func (u *Upgrader) updateMeasurements(ctx context.Context, measurements map[uint return fmt.Errorf("setting new measurements: %w", err) } - fmt.Fprintln(u.writer, "Successfully updated the cluster's expected measurements") + fmt.Fprintln(u.outWriter, "Successfully updated the cluster's expected measurements") return nil } @@ -147,7 +147,7 @@ func (u *Upgrader) updateImage(ctx context.Context, image string) error { } if currentImageDefinition == image { - fmt.Fprintln(u.writer, "Cluster is already using the chosen image, skipping image upgrade") + fmt.Fprintln(u.outWriter, "Cluster is already using the chosen image, skipping image upgrade") return nil } @@ -156,7 +156,7 @@ func (u *Upgrader) updateImage(ctx context.Context, image string) error { return fmt.Errorf("setting new image: %w", err) } - fmt.Fprintln(u.writer, "Successfully updated the cluster's image, upgrades will be applied automatically") + fmt.Fprintln(u.outWriter, "Successfully updated the cluster's image, upgrades will be applied automatically") return nil } diff --git a/cli/internal/cloudcmd/upgrade_test.go b/cli/internal/cloudcmd/upgrade_test.go index 68278dc8c..e9d2668c7 100644 --- a/cli/internal/cloudcmd/upgrade_test.go +++ b/cli/internal/cloudcmd/upgrade_test.go @@ -76,7 +76,7 @@ func TestUpdateMeasurements(t *testing.T) { upgrader := &Upgrader{ measurementsUpdater: tc.updater, - writer: &bytes.Buffer{}, + outWriter: &bytes.Buffer{}, } err := upgrader.updateMeasurements(context.Background(), tc.newMeasurements) @@ -203,7 +203,7 @@ func TestUpdateImage(t *testing.T) { upgrader := &Upgrader{ imageUpdater: tc.updater, - writer: &bytes.Buffer{}, + outWriter: &bytes.Buffer{}, } err := upgrader.updateImage(context.Background(), tc.newImage) diff --git a/cli/internal/cmd/configfetchmeasurements.go b/cli/internal/cmd/configfetchmeasurements.go index 894f29093..e66e50dfd 100644 --- a/cli/internal/cmd/configfetchmeasurements.go +++ b/cli/internal/cmd/configfetchmeasurements.go @@ -63,7 +63,7 @@ func configFetchMeasurements(cmd *cobra.Command, verifier rekorVerifier, fileHan } if conf.IsDebugImage() { - cmd.Println("Configured image doesn't look like a released production image. Double check image before deploying to production.") + cmd.PrintErrln("Configured image doesn't look like a released production image. Double check image before deploying to production.") } if err := flags.updateURLs(conf); err != nil { @@ -79,8 +79,8 @@ func configFetchMeasurements(cmd *cobra.Command, verifier rekorVerifier, fileHan } if err := verifyWithRekor(cmd.Context(), verifier, hash); err != nil { - cmd.Printf("Ignoring Rekor related error: %v\n", err) - cmd.Println("Make sure the downloaded measurements are trustworthy!") + cmd.PrintErrf("Ignoring Rekor related error: %v\n", err) + cmd.PrintErrln("Make sure the downloaded measurements are trustworthy!") } conf.UpdateMeasurements(fetchedMeasurements) diff --git a/cli/internal/cmd/configinstancetypes.go b/cli/internal/cmd/configinstancetypes.go index c8429436a..52161cf3b 100644 --- a/cli/internal/cmd/configinstancetypes.go +++ b/cli/internal/cmd/configinstancetypes.go @@ -7,7 +7,6 @@ SPDX-License-Identifier: AGPL-3.0-only package cmd import ( - "fmt" "strings" "github.com/edgelesssys/constellation/v2/internal/config/instancetypes" @@ -27,7 +26,7 @@ func newConfigInstanceTypesCmd() *cobra.Command { } func printSupportedInstanceTypes(cmd *cobra.Command, args []string) { - fmt.Printf(`AWS instance families: + cmd.Printf(`AWS instance families: %v Azure Confidential VM instance types: %v diff --git a/cli/internal/cmd/create.go b/cli/internal/cmd/create.go index 8003b3cd9..f0f1c4675 100644 --- a/cli/internal/cmd/create.go +++ b/cli/internal/cmd/create.go @@ -41,7 +41,7 @@ func NewCreateCmd() *cobra.Command { func runCreate(cmd *cobra.Command, args []string) error { fileHandler := file.NewHandler(afero.NewOsFs()) - spinner := newSpinner(cmd.OutOrStdout()) + spinner := newSpinner(cmd.ErrOrStderr()) defer spinner.Stop() creator := cloudcmd.NewCreator(spinner) @@ -59,34 +59,34 @@ func create(cmd *cobra.Command, creator cloudCreator, fileHandler file.Handler, return err } - config, err := readConfig(cmd.OutOrStdout(), fileHandler, flags.configPath) + config, err := readConfig(cmd.ErrOrStderr(), fileHandler, flags.configPath) if err != nil { return fmt.Errorf("reading and validating config: %w", err) } var printedAWarning bool if config.IsDebugImage() { - cmd.Println("Configured image doesn't look like a released production image. Double check image before deploying to production.") + cmd.PrintErrln("Configured image doesn't look like a released production image. Double check image before deploying to production.") printedAWarning = true } if config.IsDebugCluster() { - cmd.Println("WARNING: Creating a debug cluster. This cluster is not secure and should only be used for debugging purposes.") - cmd.Println("DO NOT USE THIS CLUSTER IN PRODUCTION.") + cmd.PrintErrln("WARNING: Creating a debug cluster. This cluster is not secure and should only be used for debugging purposes.") + cmd.PrintErrln("DO NOT USE THIS CLUSTER IN PRODUCTION.") printedAWarning = true } if config.IsAzureNonCVM() { - cmd.Println("Disabling Confidential VMs is insecure. Use only for evaluation purposes.") + cmd.PrintErrln("Disabling Confidential VMs is insecure. Use only for evaluation purposes.") printedAWarning = true if config.EnforcesIDKeyDigest() { - cmd.Println("Your config asks for enforcing the idkeydigest. This is only available on Confidential VMs. It will not be enforced.") + cmd.PrintErrln("Your config asks for enforcing the idkeydigest. This is only available on Confidential VMs. It will not be enforced.") } } // Print an extra new line later to separate warnings from the prompt message of the create command if printedAWarning { - cmd.Println("") + cmd.PrintErrln("") } provider := config.GetProvider() diff --git a/cli/internal/cmd/init.go b/cli/internal/cmd/init.go index 25605567f..6c32697c6 100644 --- a/cli/internal/cmd/init.go +++ b/cli/internal/cmd/init.go @@ -79,7 +79,7 @@ func initialize(cmd *cobra.Command, newDialer func(validator *cloudcmd.Validator return err } - config, err := readConfig(cmd.OutOrStdout(), fileHandler, flags.configPath) + config, err := readConfig(cmd.ErrOrStderr(), fileHandler, flags.configPath) if err != nil { return fmt.Errorf("reading and validating config: %w", err) } @@ -94,13 +94,13 @@ func initialize(cmd *cobra.Command, newDialer func(validator *cloudcmd.Validator return fmt.Errorf("validating kubernetes version: %w", err) } if versions.IsPreviewK8sVersion(k8sVersion) { - cmd.Printf("Warning: Constellation with Kubernetes %v is still in preview. Use only for evaluation purposes.\n", k8sVersion) + cmd.PrintErrf("Warning: Constellation with Kubernetes %v is still in preview. Use only for evaluation purposes.\n", k8sVersion) } provider := config.GetProvider() checker := license.NewChecker(quotaChecker, fileHandler) if err := checker.CheckLicense(cmd.Context(), provider, config.Provider, cmd.Printf); err != nil { - cmd.Printf("License check failed: %v", err) + cmd.PrintErrf("License check failed: %v", err) } var sshUsers []*ssh.UserKey @@ -290,7 +290,7 @@ type masterSecret struct { } // readOrGenerateMasterSecret reads a base64 encoded master secret from file or generates a new 32 byte secret. -func readOrGenerateMasterSecret(writer io.Writer, fileHandler file.Handler, filename string) (masterSecret, error) { +func readOrGenerateMasterSecret(outWriter io.Writer, fileHandler file.Handler, filename string) (masterSecret, error) { if filename != "" { var secret masterSecret if err := fileHandler.ReadJSON(filename, &secret); err != nil { @@ -323,7 +323,7 @@ func readOrGenerateMasterSecret(writer io.Writer, fileHandler file.Handler, file if err := fileHandler.WriteJSON(constants.MasterSecretFilename, secret, file.OptNone); err != nil { return masterSecret{}, err } - fmt.Fprintf(writer, "Your Constellation master secret was successfully written to ./%s\n", constants.MasterSecretFilename) + fmt.Fprintf(outWriter, "Your Constellation master secret was successfully written to ./%s\n", constants.MasterSecretFilename) return secret, nil } diff --git a/cli/internal/cmd/miniup.go b/cli/internal/cmd/miniup.go index 1f5498cfd..e5d2cacb1 100644 --- a/cli/internal/cmd/miniup.go +++ b/cli/internal/cmd/miniup.go @@ -50,7 +50,7 @@ func newMiniUpCmd() *cobra.Command { } func runUp(cmd *cobra.Command, args []string) error { - spinner := newSpinner(cmd.OutOrStdout()) + spinner := newSpinner(cmd.ErrOrStderr()) defer spinner.Stop() creator := cloudcmd.NewCreator(spinner) @@ -58,7 +58,7 @@ func runUp(cmd *cobra.Command, args []string) error { } func up(cmd *cobra.Command, creator cloudCreator, spinner spinnerInterf) error { - if err := checkSystemRequirements(cmd.OutOrStdout()); err != nil { + if err := checkSystemRequirements(cmd.ErrOrStderr()); err != nil { return fmt.Errorf("system requirements not met: %w", err) } @@ -163,7 +163,7 @@ func prepareConfig(cmd *cobra.Command, fileHandler file.Handler) (*config.Config // check for existing config if configPath != "" { - config, err := readConfig(cmd.OutOrStdout(), fileHandler, configPath) + config, err := readConfig(cmd.ErrOrStderr(), fileHandler, configPath) if err != nil { return nil, err } @@ -178,7 +178,7 @@ func prepareConfig(cmd *cobra.Command, fileHandler file.Handler) (*config.Config _, err = fileHandler.Stat(constants.ConfigFilename) if err == nil { // config already exists, prompt user to overwrite - cmd.Println("A config file already exists in the current workspace.") + cmd.PrintErrln("A config file already exists in the current workspace.") ok, err := askToConfirm(cmd, "Do you want to overwrite it?") if err != nil { return nil, err @@ -194,7 +194,7 @@ func prepareConfig(cmd *cobra.Command, fileHandler file.Handler) (*config.Config cmd.Printf("Using existing image at %s\n\n", imagePath) } else if errors.Is(err, os.ErrNotExist) { cmd.Printf("Downloading image to %s\n", imagePath) - if err := installImage(cmd.Context(), cmd.OutOrStdout(), versions.ConstellationQEMUImageURL, imagePath); err != nil { + if err := installImage(cmd.Context(), cmd.ErrOrStderr(), versions.ConstellationQEMUImageURL, imagePath); err != nil { return nil, fmt.Errorf("downloading image to %s: %w", imagePath, err) } } else { @@ -226,10 +226,10 @@ func initializeMiniCluster(cmd *cobra.Command, fileHandler file.Handler, spinner // clean up cluster resources if initialization fails defer func() { if retErr != nil { - cmd.Printf("An error occurred: %s\n", retErr) - cmd.Println("Attempting to roll back.") + cmd.PrintErrf("An error occurred: %s\n", retErr) + cmd.PrintErrln("Attempting to roll back.") _ = runDown(cmd, []string{}) - cmd.Printf("Rollback succeeded.\n\n") + cmd.PrintErrf("Rollback succeeded.\n\n") } }() newDialer := func(validator *cloudcmd.Validator) *dialer.Dialer { @@ -247,7 +247,7 @@ func initializeMiniCluster(cmd *cobra.Command, fileHandler file.Handler, spinner } // installImage downloads the image from sourceURL to the destination. -func installImage(ctx context.Context, out io.Writer, sourceURL, destination string) error { +func installImage(ctx context.Context, errWriter io.Writer, sourceURL, destination string) error { req, err := http.NewRequestWithContext(ctx, http.MethodGet, sourceURL, nil) if err != nil { return fmt.Errorf("creating request: %w", err) @@ -270,7 +270,7 @@ func installImage(ctx context.Context, out io.Writer, sourceURL, destination str bar := progressbar.NewOptions64( resp.ContentLength, - progressbar.OptionSetWriter(out), + progressbar.OptionSetWriter(errWriter), progressbar.OptionShowBytes(true), progressbar.OptionSetPredictTime(true), progressbar.OptionFullWidth(), @@ -282,7 +282,7 @@ func installImage(ctx context.Context, out io.Writer, sourceURL, destination str BarEnd: "]", }), progressbar.OptionClearOnFinish(), - progressbar.OptionOnCompletion(func() { fmt.Fprintf(out, "Done.\n\n") }), + progressbar.OptionOnCompletion(func() { fmt.Fprintf(errWriter, "Done.\n\n") }), ) defer bar.Close() diff --git a/cli/internal/cmd/readconfig.go b/cli/internal/cmd/readconfig.go index 3d748bc9f..3b0785b3c 100644 --- a/cli/internal/cmd/readconfig.go +++ b/cli/internal/cmd/readconfig.go @@ -15,30 +15,30 @@ import ( "github.com/edgelesssys/constellation/v2/internal/file" ) -func readConfig(out io.Writer, fileHandler file.Handler, name string) (*config.Config, error) { +func readConfig(errWriter io.Writer, fileHandler file.Handler, name string) (*config.Config, error) { cnf, err := config.FromFile(fileHandler, name) if err != nil { return nil, err } - if err := validateConfig(out, cnf); err != nil { + if err := validateConfig(errWriter, cnf); err != nil { return nil, err } return cnf, nil } -func validateConfig(out io.Writer, cnf *config.Config) error { +func validateConfig(errWriter io.Writer, cnf *config.Config) error { msgs, err := cnf.Validate() if err != nil { return fmt.Errorf("performing config validation: %w", err) } if len(msgs) > 0 { - fmt.Fprintln(out, "Invalid fields in config file:") + fmt.Fprintln(errWriter, "Invalid fields in config file:") for _, m := range msgs { - fmt.Fprintln(out, "\t"+m) + fmt.Fprintln(errWriter, "\t"+m) } - fmt.Fprintln(out, "Fix the invalid entries or generate a new configuration using `constellation config generate`") + fmt.Fprintln(errWriter, "Fix the invalid entries or generate a new configuration using `constellation config generate`") return errors.New("invalid configuration") } diff --git a/cli/internal/cmd/recover.go b/cli/internal/cmd/recover.go index ae1af2b95..1b06641a4 100644 --- a/cli/internal/cmd/recover.go +++ b/cli/internal/cmd/recover.go @@ -66,7 +66,7 @@ func recover( return err } - config, err := readConfig(cmd.OutOrStdout(), fileHandler, flags.configPath) + config, err := readConfig(cmd.ErrOrStderr(), fileHandler, flags.configPath) if err != nil { return fmt.Errorf("reading and validating config: %w", err) } diff --git a/cli/internal/cmd/terminate.go b/cli/internal/cmd/terminate.go index 249b2a408..9535608c9 100644 --- a/cli/internal/cmd/terminate.go +++ b/cli/internal/cmd/terminate.go @@ -36,7 +36,7 @@ func NewTerminateCmd() *cobra.Command { // runTerminate runs the terminate command. func runTerminate(cmd *cobra.Command, args []string) error { fileHandler := file.NewHandler(afero.NewOsFs()) - spinner := newSpinner(cmd.OutOrStdout()) + spinner := newSpinner(cmd.ErrOrStderr()) defer spinner.Stop() terminator := cloudcmd.NewTerminator() diff --git a/cli/internal/cmd/upgradeplan.go b/cli/internal/cmd/upgradeplan.go index 68cbe4826..2b33d7acd 100644 --- a/cli/internal/cmd/upgradeplan.go +++ b/cli/internal/cmd/upgradeplan.go @@ -93,18 +93,18 @@ func upgradePlan(cmd *cobra.Command, planner upgradePlanner, } compatibleImages := getCompatibleImages(csp, version, images) if len(compatibleImages) == 0 { - cmd.Println("No compatible images found to upgrade to.") + cmd.PrintErrln("No compatible images found to upgrade to.") return nil } // get expected measurements for each image - if err := getCompatibleImageMeasurements(cmd.Context(), client, rekor, []byte(flags.cosignPubKey), compatibleImages); err != nil { + if err := getCompatibleImageMeasurements(cmd.Context(), cmd, client, rekor, []byte(flags.cosignPubKey), compatibleImages); err != nil { return fmt.Errorf("fetching measurements for compatible images: %w", err) } // interactive mode if flags.filePath == "" { - fmt.Fprintf(cmd.OutOrStdout(), "Current version: %s\n", version) + cmd.Printf("Current version: %s\n", version) return upgradePlanInteractive( &nopWriteCloser{cmd.OutOrStdout()}, io.NopCloser(cmd.InOrStdin()), @@ -179,7 +179,7 @@ func getCompatibleImages(csp cloudprovider.Provider, currentVersion string, imag } // getCompatibleImageMeasurements retrieves the expected measurements for each image. -func getCompatibleImageMeasurements(ctx context.Context, client *http.Client, rekor rekorVerifier, pubK []byte, images map[string]config.UpgradeConfig) error { +func getCompatibleImageMeasurements(ctx context.Context, cmd *cobra.Command, client *http.Client, rekor rekorVerifier, pubK []byte, images map[string]config.UpgradeConfig) error { for idx, img := range images { measurementsURL, err := url.Parse(constants.S3PublicBucket + strings.ToLower(img.Image) + "/measurements.yaml") if err != nil { @@ -197,8 +197,8 @@ func getCompatibleImageMeasurements(ctx context.Context, client *http.Client, re } if err = verifyWithRekor(ctx, rekor, hash); err != nil { - fmt.Printf("Warning: Unable to verify '%s' in Rekor.\n", hash) - fmt.Printf("Make sure measurements are correct.\n") + cmd.PrintErrf("Warning: Unable to verify '%s' in Rekor.\n", hash) + cmd.PrintErrf("Make sure measurements are correct.\n") } images[idx] = img diff --git a/cli/internal/cmd/upgradeplan_test.go b/cli/internal/cmd/upgradeplan_test.go index 2f7f675b5..be44090bf 100644 --- a/cli/internal/cmd/upgradeplan_test.go +++ b/cli/internal/cmd/upgradeplan_test.go @@ -21,6 +21,7 @@ import ( "github.com/edgelesssys/constellation/v2/internal/constants" "github.com/edgelesssys/constellation/v2/internal/file" "github.com/spf13/afero" + "github.com/spf13/cobra" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "golang.org/x/mod/semver" @@ -271,7 +272,7 @@ func TestGetCompatibleImageMeasurements(t *testing.T) { pubK := []byte("-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUs5fDUIz9aiwrfr8BK4VjN7jE6sl\ngz7UuXsOin8+dB0SGrbNHy7TJToa2fAiIKPVLTOfvY75DqRAtffhO1fpBA==\n-----END PUBLIC KEY-----") - err := getCompatibleImageMeasurements(context.Background(), client, singleUUIDVerifier(), pubK, testImages) + err := getCompatibleImageMeasurements(context.Background(), &cobra.Command{}, client, singleUUIDVerifier(), pubK, testImages) assert.NoError(err) for _, image := range testImages { @@ -456,8 +457,10 @@ func TestUpgradePlan(t *testing.T) { cmd := newUpgradePlanCmd() cmd.SetContext(context.Background()) - var out bytes.Buffer - cmd.SetOut(&out) + var outTarget bytes.Buffer + cmd.SetOut(&outTarget) + var errTarget bytes.Buffer + cmd.SetErr(&errTarget) client := newTestClient(func(req *http.Request) *http.Response { if req.URL.String() == imageReleaseURL { @@ -497,13 +500,13 @@ func TestUpgradePlan(t *testing.T) { assert.NoError(err) if !tc.wantUpgrade { - assert.Contains(out.String(), "No compatible images") + assert.Contains(errTarget.String(), "No compatible images") return } var availableUpgrades map[string]config.UpgradeConfig if tc.flags.filePath == "-" { - require.NoError(yaml.Unmarshal(out.Bytes(), &availableUpgrades)) + require.NoError(yaml.Unmarshal(outTarget.Bytes(), &availableUpgrades)) } else { require.NoError(fileHandler.ReadYAMLStrict(tc.flags.filePath, &availableUpgrades)) } diff --git a/cli/internal/cmd/verify.go b/cli/internal/cmd/verify.go index 792ef624a..4e2d6d5c8 100644 --- a/cli/internal/cmd/verify.go +++ b/cli/internal/cmd/verify.go @@ -59,7 +59,7 @@ func verify(cmd *cobra.Command, fileHandler file.Handler, verifyClient verifyCli return err } - config, err := readConfig(cmd.OutOrStdout(), fileHandler, flags.configPath) + config, err := readConfig(cmd.ErrOrStderr(), fileHandler, flags.configPath) if err != nil { return fmt.Errorf("reading and validating config: %w", err) } diff --git a/cli/internal/cmd/version.go b/cli/internal/cmd/version.go index 9a38625d6..83dffa261 100644 --- a/cli/internal/cmd/version.go +++ b/cli/internal/cmd/version.go @@ -28,7 +28,7 @@ func NewVersionCmd() *cobra.Command { func runVersion(cmd *cobra.Command, args []string) { buildInfo, ok := debug.ReadBuildInfo() if !ok { - cmd.Printf("Unable to retrieve build info. Is buildvcs enabled?") + cmd.PrintErrf("Unable to retrieve build info. Is buildvcs enabled?") return }