From a578ef54d06dfc8a310fbecfea37db554f57aff9 Mon Sep 17 00:00:00 2001 From: James Cuzella Date: Mon, 23 Oct 2023 17:29:06 -0600 Subject: [PATCH] commands/apps: refactor validate-offline -> --schema-only (Fixes #1449) (#1) Thanks to @andrewsomething for the suggestion! Reference: - https://github.com/digitalocean/doctl/pull/1450#pullrequestreview-1693429116 --- commands/apps.go | 48 ++++++++--------------------------- integration/apps_spec_test.go | 47 ---------------------------------- 2 files changed, 10 insertions(+), 85 deletions(-) diff --git a/commands/apps.go b/commands/apps.go index c44f703977..32eccbdfd9 100644 --- a/commands/apps.go +++ b/commands/apps.go @@ -704,15 +704,10 @@ Optionally, pass a deployment ID to get the spec of that specific deployment.`, AddStringFlag(getCmd, doctl.ArgAppDeployment, "", "", "optional: a deployment ID") AddStringFlag(getCmd, doctl.ArgFormat, "", "yaml", `the format to output the spec in; either "yaml" or "json"`) - validateCmd := CmdBuilder(cmd, RunAppsSpecValidate, "validate ", "Validate an application spec", `Use this command to check whether a given app spec (YAML or JSON) is valid. - -You may pass - as the filename to read from stdin.`, Writer) - AddBoolFlag(validateCmd, doctl.ArgSchemaOnly, "", false, "Only validate the spec schema and not the correctness of the spec.") - - cmdBuilderWithInit(cmd, RunAppsSpecValidateOffline, "validate-offline ", "Validate an application spec offline (schema-only)", `Use this command to check whether a given app spec (YAML or JSON) is valid -without connecting to DigitalOcean API. (schema-only) + validateCmd := cmdBuilderWithInit(cmd, RunAppsSpecValidate, "validate ", "Validate an application spec", `Use this command to check whether a given app spec (YAML or JSON) is valid. You may pass - as the filename to read from stdin.`, Writer, false) + AddBoolFlag(validateCmd, doctl.ArgSchemaOnly, "", false, "Only validate the spec schema and not the correctness of the spec.") return cmd } @@ -766,36 +761,8 @@ func RunAppsSpecGet(c *CmdConfig) error { } } -// ValidateAppSpecSchema validates an app spec (schema-only) -// returns the marshaled yaml spec as a byte array, or error -func ValidateAppSpecSchema(appSpec *godo.AppSpec) ([]byte, error) { - ymlSpec, err := yaml.Marshal(appSpec) - if err != nil { - return []byte{}, fmt.Errorf("marshaling the spec as yaml: %v", err) - } - return ymlSpec, err -} - -// RunAppsSpecValidateOffline validates an app spec file without requiring auth & connection to the API -func RunAppsSpecValidateOffline(c *CmdConfig) error { - if len(c.Args) < 1 { - return doctl.NewMissingArgsErr(c.NS) - } - - specPath := c.Args[0] - appSpec, err := apps.ReadAppSpec(os.Stdin, specPath) - if err != nil { - return err - } - ymlSpec, err := ValidateAppSpecSchema(appSpec) - if err != nil { - return err - } - _, err = c.Out.Write(ymlSpec) - return err -} - // RunAppsSpecValidate validates an app spec file +// doesn't require auth & connection to the API with doctl.ArgSchemaOnly flag func RunAppsSpecValidate(c *CmdConfig) error { if len(c.Args) < 1 { return doctl.NewMissingArgsErr(c.NS) @@ -812,15 +779,20 @@ func RunAppsSpecValidate(c *CmdConfig) error { return err } + // validate schema only (offline) if schemaOnly { - ymlSpec, err := ValidateAppSpecSchema(appSpec) + ymlSpec, err := yaml.Marshal(appSpec) if err != nil { - return err + return fmt.Errorf("marshaling the spec as yaml: %v", err) } _, err = c.Out.Write(ymlSpec) return err } + // validate the spec against the API + if err := c.initServices(c); err != nil { + return err + } res, err := c.Apps().Propose(&godo.AppProposeRequest{ Spec: appSpec, }) diff --git a/integration/apps_spec_test.go b/integration/apps_spec_test.go index 281bc2a009..2b74fe1563 100644 --- a/integration/apps_spec_test.go +++ b/integration/apps_spec_test.go @@ -238,50 +238,3 @@ services: expect.Equal(expectedOutput, strings.TrimSpace(string(output))) }) }) - -var _ = suite("apps/spec/validate-offline", func(t *testing.T, when spec.G, it spec.S) { - var ( - expect *require.Assertions - ) - - it.Before(func() { - expect = require.New(t) - }) - - it("accepts a valid spec", func() { - cmd := exec.Command(builtBinaryPath, - "apps", "spec", "validate-offline", - "-", - ) - byt, err := json.Marshal(testAppSpec) - expect.NoError(err) - - cmd.Stdin = bytes.NewReader(byt) - - output, err := cmd.CombinedOutput() - expect.NoError(err) - - expectedOutput := "name: test\nservices:\n- github:\n branch: main\n repo: digitalocean/doctl\n name: service" - expect.Equal(expectedOutput, strings.TrimSpace(string(output))) - }) - - it("fails on invalid specs", func() { - cmd := exec.Command(builtBinaryPath, - "apps", "spec", "validate-offline", - "-", - ) - testSpec := `name: test -services: - name: service - github: - repo: digitalocean/doctl -` - cmd.Stdin = strings.NewReader(testSpec) - - output, err := cmd.CombinedOutput() - expect.Equal("exit status 1", err.Error()) - - expectedOutput := "Error: parsing app spec: json: cannot unmarshal object into Go struct field AppSpec.services of type []*godo.AppServiceSpec" - expect.Equal(expectedOutput, strings.TrimSpace(string(output))) - }) -})