From dcef74890bc71039bd383b6c8173eb7fc7c28c94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antti=20Kivim=C3=A4ki?= Date: Tue, 31 Oct 2023 13:03:27 +0200 Subject: [PATCH] Improve documentation --- cmd/docs/templates/main.tmpl | 2 +- .../{recipe-schema.tmpl => schema.tmpl} | 47 +++++++++++-------- docs/site/docs/usage.md | 2 +- examples/minimal/recipe.yml | 2 +- examples/with-tests/recipe.yml | 6 +++ examples/with-tests/templates/README.md | 1 + examples/with-tests/tests/bar/files/README.md | 1 + examples/with-tests/tests/bar/test.yml | 3 ++ examples/with-tests/tests/foo/files/README.md | 1 + examples/with-tests/tests/foo/test.yml | 3 ++ internal/cli/check.go | 5 ++ internal/cli/eject.go | 1 + internal/cli/execute.go | 18 +++++++ internal/cli/pull.go | 28 +++++------ internal/cli/push.go | 27 +++++------ internal/cli/test.go | 8 ++++ internal/cli/upgrade.go | 18 +++++++ internal/cli/validate.go | 9 ++-- internal/cli/why.go | 1 + pkg/recipe/test.go | 2 +- 20 files changed, 127 insertions(+), 58 deletions(-) rename cmd/docs/templates/{recipe-schema.tmpl => schema.tmpl} (57%) create mode 100644 examples/with-tests/recipe.yml create mode 100644 examples/with-tests/templates/README.md create mode 100644 examples/with-tests/tests/bar/files/README.md create mode 100644 examples/with-tests/tests/bar/test.yml create mode 100644 examples/with-tests/tests/foo/files/README.md create mode 100644 examples/with-tests/tests/foo/test.yml diff --git a/cmd/docs/templates/main.tmpl b/cmd/docs/templates/main.tmpl index 35407bdc..9f94430b 100644 --- a/cmd/docs/templates/main.tmpl +++ b/cmd/docs/templates/main.tmpl @@ -13,4 +13,4 @@ title: API Reference --- {{- end}} -{{ template "recipe-schema" .RecipeSchema }} +{{ template "schema" . }} diff --git a/cmd/docs/templates/recipe-schema.tmpl b/cmd/docs/templates/schema.tmpl similarity index 57% rename from cmd/docs/templates/recipe-schema.tmpl rename to cmd/docs/templates/schema.tmpl index 0ccb8810..acc97d71 100644 --- a/cmd/docs/templates/recipe-schema.tmpl +++ b/cmd/docs/templates/schema.tmpl @@ -1,37 +1,44 @@ -{{- define "recipe-schema" }} +{{- define "schema" }} ## Recipe schema (`recipe.yml`) | Attribute | Type | Default | Description | | --- | --- | --- | --- | -| `apiVersion` | `string` | `v1` | Version of the recipe metadata API schema. Currently should have value "v1" | -| `name` | `string` | | Name of the recipe | -| `version` | `string` | | Version of the recipe. Must be valid [semver](https://semver.org/) | +| `apiVersion` | `string` | `v1` | Version of the recipe metadata API schema. Currently should have value "v1". | +| `name` | `string` | | Name of the recipe. | +| `version` | `string` | | Version of the recipe. Must be valid [semver](https://semver.org/). | | `description` | `string` | | Description of what the recipe does | -| `sources` | `[]string` | | A list of URLs to source code for this recipe | -| `initHelp` | `string` | | A message which will be showed to an user after a succesful recipe execution. Can be used to guide the user what should be done next in the project directory | -| `ignorePatterns` | `[]string` | | Glob patterns for ignoring generated files from future recipe upgrades. Ignored files will not be regenerated even if their templates change in future versions of the recipe | -| `vars` | `[]Variable` | | An array of variables which can be used in templates | +| `sources` | `[]string` | | A list of URLs to source code for this recipe. | +| `initHelp` | `string` | | A message which will be showed to an user after a succesful recipe execution. Can be used to guide the user what should be done next in the project directory. | +| `ignorePatterns` | `[]string` | | Glob patterns for ignoring generated files from future recipe upgrades. Ignored files will not be regenerated even if their templates change in future versions of the recipe. | +| `vars` | `[]Variable` | | An array of variables which can be used in templates. | ### Variable | Attribute | Type | Default | Description | | --- | --- | --- | --- | -| `name` | `string` | | The name of the variable. It is also used as unique identifier | -| `description` | `string` | | Description of the variable. Will be shown to the user when "show help" is activated | -| `default` | `string` | | Default value of the variable | -| `confirm` | `bool` | `false` | If set to true, the prompt will be yes/no question, and the value type will be boolean | -| `optional` | `bool` | `false` | If set to true, the variable can be left empty | -| `options` | `[]string` | | The user selects the value from a list of options | -| `validators` | `[]Validator` | | Validators for the variable | -| `if` | `string` | | Makes the variable conditional based on the result of the expression. The result of the evaluation needs to be a boolean value. Uses https://github.com/antonmedv/expr | -| `columns` | `[]string` | | Set the variable as a table type with columns defined by this property | +| `name` | `string` | | The name of the variable. It is also used as unique identifier. | +| `description` | `string` | | Description of the variable. Will be shown to the user when "show help" is activated. | +| `default` | `string` | | Default value of the variable. | +| `confirm` | `bool` | `false` | If set to true, the prompt will be yes/no question, and the value type will be boolean. | +| `optional` | `bool` | `false` | If set to true, the variable can be left empty. | +| `options` | `[]string` | | The user selects the value from a list of options. | +| `validators` | `[]Validator` | | Validators for the variable. | +| `if` | `string` | | Makes the variable conditional based on the result of the expression. The result of the evaluation needs to be a boolean value. Uses https://github.com/antonmedv/expr. | +| `columns` | `[]string` | | Set the variable as a table type with columns defined by this property. | ### Validator | Attribute | Type | Default | Description | | --- | --- | --- | --- | -| `pattern` | `string` | | Regular expression pattern to match the input against | -| `help` | `string` | | If the regular expression validation fails, this help message will be shown to the user | -| `column` | `string` | | Apply the validator to a column if the variable type is table | +| `pattern` | `string` | | Regular expression pattern to match the input against. | +| `help` | `string` | | If the regular expression validation fails, this help message will be shown to the user. | +| `column` | `string` | | Apply the validator to a column if the variable type is table. | + +## Test schema (`test.yml`) + +| Attribute | Type | Default | Description | +| --- | --- | --- | --- | +| `values` | `map[string]any` | | Values to use to render the recipe templates. Map key is the name of the variable and value is the variable value. | +| `ignoreExtraFiles` | `bool` | | If true, test will pass even though templates generated more files than the test specifies. This is useful when creating specific test cases for large recipes. | {{- end }} \ No newline at end of file diff --git a/docs/site/docs/usage.md b/docs/site/docs/usage.md index 4c4dd37a..4f42dbb2 100644 --- a/docs/site/docs/usage.md +++ b/docs/site/docs/usage.md @@ -4,7 +4,7 @@ slug: /usage/ title: Usage --- -# Usage +# Usage [WIP] ## Getting started diff --git a/examples/minimal/recipe.yml b/examples/minimal/recipe.yml index 56b0ff32..39bf2e04 100644 --- a/examples/minimal/recipe.yml +++ b/examples/minimal/recipe.yml @@ -1,4 +1,4 @@ apiVersion: v1 name: minimal -version: v0.0.1 +version: v0.0.0 description: Minimal recipe which just creates a README.md diff --git a/examples/with-tests/recipe.yml b/examples/with-tests/recipe.yml new file mode 100644 index 00000000..e22a2179 --- /dev/null +++ b/examples/with-tests/recipe.yml @@ -0,0 +1,6 @@ +apiVersion: v1 +name: with-tests +version: v0.0.0 +description: This recipe has tests which you can run with `jalapeno test .` +vars: + - name: MY_VAR diff --git a/examples/with-tests/templates/README.md b/examples/with-tests/templates/README.md new file mode 100644 index 00000000..2fc8ab88 --- /dev/null +++ b/examples/with-tests/templates/README.md @@ -0,0 +1 @@ +.Variables.MY_VAR is {{ .Variables.MY_VAR }} diff --git a/examples/with-tests/tests/bar/files/README.md b/examples/with-tests/tests/bar/files/README.md new file mode 100644 index 00000000..d7744c2f --- /dev/null +++ b/examples/with-tests/tests/bar/files/README.md @@ -0,0 +1 @@ +.Variables.MY_VAR is bar diff --git a/examples/with-tests/tests/bar/test.yml b/examples/with-tests/tests/bar/test.yml new file mode 100644 index 00000000..81cbd157 --- /dev/null +++ b/examples/with-tests/tests/bar/test.yml @@ -0,0 +1,3 @@ +values: + MY_VAR: bar +ignoreExtraFiles: false diff --git a/examples/with-tests/tests/foo/files/README.md b/examples/with-tests/tests/foo/files/README.md new file mode 100644 index 00000000..838bf8e2 --- /dev/null +++ b/examples/with-tests/tests/foo/files/README.md @@ -0,0 +1 @@ +.Variables.MY_VAR is foo diff --git a/examples/with-tests/tests/foo/test.yml b/examples/with-tests/tests/foo/test.yml new file mode 100644 index 00000000..fa19b1c2 --- /dev/null +++ b/examples/with-tests/tests/foo/test.yml @@ -0,0 +1,3 @@ +values: + MY_VAR: foo +ignoreExtraFiles: false diff --git a/internal/cli/check.go b/internal/cli/check.go index 0a69b0aa..7b5ce96a 100644 --- a/internal/cli/check.go +++ b/internal/cli/check.go @@ -30,6 +30,11 @@ func NewCheckCmd() *cobra.Command { Run: func(cmd *cobra.Command, args []string) { runCheck(cmd, opts) }, + Example: `# Check updates for all recipes in the project +jalapeno check + +# Check updates for a single recipe +jalapeno check --recipe my-recipe`, } cmd.Flags().StringVar(&opts.RecipeName, "recipe", "", "Name of the recipe to check for new versions") diff --git a/internal/cli/eject.go b/internal/cli/eject.go index ecc0fc92..b6198323 100644 --- a/internal/cli/eject.go +++ b/internal/cli/eject.go @@ -25,6 +25,7 @@ func NewEjectCmd() *cobra.Command { Run: func(cmd *cobra.Command, args []string) { runEject(cmd, opts) }, + Example: `jalapeno eject`, } if err := option.ApplyFlags(&opts, cmd.Flags()); err != nil { diff --git a/internal/cli/execute.go b/internal/cli/execute.go index f14851d8..15f8b1d9 100644 --- a/internal/cli/execute.go +++ b/internal/cli/execute.go @@ -41,6 +41,24 @@ func NewExecuteCmd() *cobra.Command { Run: func(cmd *cobra.Command, args []string) { runExecute(cmd, opts) }, + Example: `# Execute local recipe +jalapeno execute path/to/recipe + +# Execute recipe from OCI repository +jalapeno execute oci://ghcr.io/user/my-recipe:latest + +# Execute recipe from OCI repository with inline authentication +jalapeno execute oci://ghcr.io/user/my-recipe:latest --username user --password pass + +# Execute recipe from OCI repository with Docker authentication +docker login ghcr.io +jalapeno execute oci://ghcr.io/user/my-recipe:latest + +# Execute recipe to different directory +jalapeno execute path/to/recipe --dir other/dir + +# Predefine variable values +jalapeno execute path/to/recipe --set MY_VAR=foo --set MY_OTHER_VAR=bar`, } if err := option.ApplyFlags(&opts, cmd.Flags()); err != nil { diff --git a/internal/cli/pull.go b/internal/cli/pull.go index 513e769c..7c2dec6b 100644 --- a/internal/cli/pull.go +++ b/internal/cli/pull.go @@ -31,6 +31,18 @@ func NewPullCmd() *cobra.Command { Run: func(cmd *cobra.Command, args []string) { runPull(cmd, opts) }, + Example: `# Pull recipe from OCI repository +jalapeno pull ghcr.io/user/recipe:latest + +# Pull recipe from OCI repository with inline authentication +jalapeno pull oci://ghcr.io/user/my-recipe:latest --username user --password pass + +# Pull recipe from OCI repository with Docker authentication +docker login ghcr.io +jalapeno pull oci://ghcr.io/user/my-recipe:latest + +# Pull recipe to different directory +jalapeno pull oci://ghcr.io/user/my-recipe:latest --dir other/dir`, } if err := option.ApplyFlags(&opts, cmd.Flags()); err != nil { @@ -43,21 +55,7 @@ func NewPullCmd() *cobra.Command { func runPull(cmd *cobra.Command, opts pullOptions) { ctx := context.Background() - err := oci.SaveRemoteRecipe(ctx, opts.Dir, - oci.Repository{ - Reference: opts.TargetRef, - PlainHTTP: opts.PlainHTTP, - Credentials: oci.Credentials{ - Username: opts.Username, - Password: opts.Password, - DockerConfigs: opts.Configs, - }, - TLS: oci.TLSConfig{ - CACertFilePath: opts.CACertFilePath, - Insecure: opts.Insecure, - }, - }, - ) + err := oci.SaveRemoteRecipe(ctx, opts.Dir, opts.Repository(opts.TargetRef)) if err != nil { if strings.Contains(err.Error(), "not found") { diff --git a/internal/cli/push.go b/internal/cli/push.go index 7eaf8019..821d1007 100644 --- a/internal/cli/push.go +++ b/internal/cli/push.go @@ -10,7 +10,7 @@ import ( type pushOptions struct { RecipePath string - TargetRef string + TargetURL string option.OCIRepository option.Common } @@ -24,12 +24,21 @@ func NewPushCmd() *cobra.Command { Args: cobra.ExactArgs(2), PreRunE: func(cmd *cobra.Command, args []string) error { opts.RecipePath = args[0] - opts.TargetRef = args[1] + opts.TargetURL = args[1] return option.Parse(&opts) }, Run: func(cmd *cobra.Command, args []string) { runPush(cmd, opts) }, + Example: `# Push recipe to OCI repository +jalapeno push path/to/recipe ghcr.io/user/recipe:latest + +# Push recipe to OCI repository with inline authentication +jalapeno push path/to/recipe oci://ghcr.io/user/my-recipe:latest --username user --password pass + +# Push recipe to OCI repository with Docker authentication +docker login ghcr.io +jalapeno push path/to/recipe oci://ghcr.io/user/my-recipe:latest`, } if err := option.ApplyFlags(&opts, cmd.Flags()); err != nil { @@ -42,19 +51,7 @@ func NewPushCmd() *cobra.Command { func runPush(cmd *cobra.Command, opts pushOptions) { ctx := context.Background() - err := oci.PushRecipe(ctx, opts.RecipePath, oci.Repository{ - Reference: opts.TargetRef, - PlainHTTP: opts.PlainHTTP, - Credentials: oci.Credentials{ - Username: opts.Username, - Password: opts.Password, - DockerConfigs: opts.Configs, - }, - TLS: oci.TLSConfig{ - CACertFilePath: opts.CACertFilePath, - Insecure: opts.Insecure, - }, - }) + err := oci.PushRecipe(ctx, opts.RecipePath, opts.Repository(opts.TargetURL)) if err != nil { cmd.PrintErrf("Error: %s\n", err) diff --git a/internal/cli/test.go b/internal/cli/test.go index 6f5166f2..3e39fabc 100644 --- a/internal/cli/test.go +++ b/internal/cli/test.go @@ -30,6 +30,14 @@ func NewTestCmd() *cobra.Command { Run: func(cmd *cobra.Command, args []string) { runTest(cmd, opts) }, + Example: `# Run recipe tests +jalapeno test path/to/recipe + +# Bootstrap a new test case +jalapeno test path/to/recipe --create + +# Update test file snapshots with the current outputs +jalapeno test path/to/recipe --update-snapshots`, } cmd.Flags().BoolVarP(&opts.UpdateSnapshots, "update-snapshots", "u", false, "Update test file snapshots") diff --git a/internal/cli/upgrade.go b/internal/cli/upgrade.go index 56ddb3d8..0c551de3 100644 --- a/internal/cli/upgrade.go +++ b/internal/cli/upgrade.go @@ -40,6 +40,24 @@ func NewUpgradeCmd() *cobra.Command { Run: func(cmd *cobra.Command, args []string) { runUpgrade(cmd, opts) }, + Example: `# Upgrade recipe with local recipe +jalapeno upgrade path/to/recipe + +# Upgrade recipe with remote recipe from OCI repository +jalapeno upgrade oci://ghcr.io/user/my-recipe:v2.0.0 + +# Upgrade recipe with remote recipe from OCI repository with inline authentication +jalapeno upgrade oci://ghcr.io/user/my-recipe:v2.0.0 --username user --password pass + +# Upgrade recipe with remote recipe from OCI repository with Docker authentication +docker login ghcr.io +jalapeno upgrade oci://ghcr.io/user/my-recipe:v2.0.0 + +# Upgrade recipe to different directory +jalapeno upgrade path/to/recipe --dir other/dir + +# Predefine values for new variables introduced in the upgrade +jalapeno upgrade path/to/recipe --set NEW_VAR=foo`, } if err := option.ApplyFlags(&opts, cmd.Flags()); err != nil { diff --git a/internal/cli/validate.go b/internal/cli/validate.go index 7a00a458..6b11ce24 100644 --- a/internal/cli/validate.go +++ b/internal/cli/validate.go @@ -7,7 +7,7 @@ import ( ) type validateOptions struct { - TargetPath string + RecipePath string option.Common } @@ -18,13 +18,14 @@ func NewValidateCmd() *cobra.Command { Short: "Validate a recipe", Long: "Validate a recipe in a local path.", PreRunE: func(cmd *cobra.Command, args []string) error { - opts.TargetPath = args[0] + opts.RecipePath = args[0] return option.Parse(&opts) }, Run: func(cmd *cobra.Command, args []string) { runValidate(cmd, opts) }, - Args: cobra.ExactArgs(1), + Args: cobra.ExactArgs(1), + Example: `jalapeno validate path/to/recipe`, } if err := option.ApplyFlags(&opts, cmd.Flags()); err != nil { @@ -35,7 +36,7 @@ func NewValidateCmd() *cobra.Command { } func runValidate(cmd *cobra.Command, opts validateOptions) { - r, err := recipe.LoadRecipe(opts.TargetPath) + r, err := recipe.LoadRecipe(opts.RecipePath) if err != nil { cmd.PrintErrf("Error: could not load the recipe: %s\n", err) } diff --git a/internal/cli/why.go b/internal/cli/why.go index 7a1bb8f0..643e8de1 100644 --- a/internal/cli/why.go +++ b/internal/cli/why.go @@ -30,6 +30,7 @@ func NewWhyCmd() *cobra.Command { Run: func(cmd *cobra.Command, args []string) { runWhy(cmd, opts) }, + Example: `jalapeno why path/to/file`, } if err := option.ApplyFlags(&opts, cmd.Flags()); err != nil { diff --git a/pkg/recipe/test.go b/pkg/recipe/test.go index b898c23e..98d52ced 100644 --- a/pkg/recipe/test.go +++ b/pkg/recipe/test.go @@ -9,7 +9,7 @@ import ( ) type Test struct { - // Name of the test case. Defined by the test filename + // Name of the test case. Defined by directory name of the test case Name string `yaml:"-"` // Values to use to render the recipe templates