Skip to content

Commit

Permalink
commands/apps: refactor validate-offline -> --schema-only (Fixes digi…
Browse files Browse the repository at this point in the history
…talocean#1449)

Thanks to @andrewsomething for the suggestion!

Reference:

- digitalocean#1450 (review)
  • Loading branch information
trinitronx committed Oct 23, 2023
1 parent 6204426 commit cfe6ca4
Show file tree
Hide file tree
Showing 2 changed files with 10 additions and 85 deletions.
48 changes: 10 additions & 38 deletions commands/apps.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 <spec file>", "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 <spec file>", "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 <spec file>", "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
}
Expand Down Expand Up @@ -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)
Expand All @@ -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,
})
Expand Down
47 changes: 0 additions & 47 deletions integration/apps_spec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)))
})
})

0 comments on commit cfe6ca4

Please sign in to comment.