diff --git a/Makefile b/Makefile index 6247f6c8..8036526d 100644 --- a/Makefile +++ b/Makefile @@ -243,6 +243,8 @@ generate-jsonschema: build mkdir -p $(JSONSCHEMA_DIR) || echo "$(JSONSCHEMA_DIR) exists" + $(abspath $(BINARY)) generate --json-schema global > $(JSONSCHEMA_DIR)/config.json + for config_version in 1 2 ; do \ $(abspath $(BINARY)) generate --json-schema v$$config_version > $(JSONSCHEMA_DIR)/config-$$config_version.json ; \ for restic_version in 0.9 0.10 0.11 0.12 0.13 0.14 0.15 0.16 0.17 ; do \ diff --git a/commands_generate.go b/commands_generate.go index 0f5b825c..4b56d83e 100644 --- a/commands_generate.go +++ b/commands_generate.go @@ -18,6 +18,8 @@ import ( "github.com/creativeprojects/resticprofile/util/templates" ) +const pathTemplates = "contrib/templates" + //go:embed contrib/completion/bash-completion.sh var bashCompletionScript string @@ -75,7 +77,7 @@ func generateConfigReference(output io.Writer, args []string) error { data := config.NewTemplateInfoData(resticVersion) tpl := templates.New("config-reference", data.GetFuncs()) - templates, err := fs.Sub(configReferenceTemplates, "contrib/templates") + templates, err := fs.Sub(configReferenceTemplates, pathTemplates) if err != nil { return fmt.Errorf("cannot load templates: %w", err) } @@ -105,7 +107,7 @@ func generateConfigReference(output io.Writer, args []string) error { for _, staticPage := range staticPages { fmt.Fprintf(output, "generating %s...\n", staticPage.templateName) - err = generatePage(tpl, data, filepath.Join(destination, staticPage.fileName), staticPage.templateName) + err = generateFileFromTemplate(tpl, data, filepath.Join(destination, staticPage.fileName), staticPage.templateName) if err != nil { return fmt.Errorf("unable to generate page %s: %w", staticPage.fileName, err) } @@ -119,7 +121,7 @@ func generateConfigReference(output io.Writer, args []string) error { Section: profileSection, Weight: weight, } - err = generatePage(tpl, sectionData, filepath.Join(destination, "profile", profileSection.Name()+".md"), "profile.sub-section.gomd") + err = generateFileFromTemplate(tpl, sectionData, filepath.Join(destination, "profile", profileSection.Name()+".md"), "profile.sub-section.gomd") if err != nil { return fmt.Errorf("unable to generate profile section %s: %w", profileSection.Name(), err) } @@ -134,7 +136,7 @@ func generateConfigReference(output io.Writer, args []string) error { Section: nestedSection, Weight: weight, } - err = generatePage(tpl, sectionData, filepath.Join(destination, "nested", nestedSection.Name()+".md"), "profile.nested-section.gomd") + err = generateFileFromTemplate(tpl, sectionData, filepath.Join(destination, "nested", nestedSection.Name()+".md"), "profile.nested-section.gomd") if err != nil { return fmt.Errorf("unable to generate nested section %s: %w", nestedSection.Name(), err) } @@ -143,7 +145,7 @@ func generateConfigReference(output io.Writer, args []string) error { return nil } -func generatePage(tpl *template.Template, data any, fileName, templateName string) error { +func generateFileFromTemplate(tpl *template.Template, data any, fileName, templateName string) error { err := os.MkdirAll(filepath.Dir(fileName), 0o755) if err != nil { return fmt.Errorf("cannot create directory: %w", err) @@ -171,12 +173,34 @@ func generateJsonSchema(output io.Writer, args []string) (err error) { } } - version := config.Version02 - if len(args) > 0 && args[0] == "v1" { - version = config.Version01 + if len(args) == 0 { + return fmt.Errorf("missing type of json schema to generate (global, v1, v2)") } - return jsonschema.WriteJsonSchema(version, resticVersion, output) + switch args[0] { + case "global": + data := config.NewTemplateInfoData(resticVersion) + tpl := templates.New("", data.GetFuncs()) + templates, err := fs.Sub(configReferenceTemplates, pathTemplates) + if err != nil { + return fmt.Errorf("cannot load templates: %w", err) + } + tpl, err = tpl.ParseFS(templates, "config-schema.gojson") + if err != nil { + return fmt.Errorf("parsing failed: %w", err) + } + err = tpl.ExecuteTemplate(output, "config-schema.gojson", data) + if err != nil { + return fmt.Errorf("cannot execute template: %w", err) + } + return nil + case "v1": + return jsonschema.WriteJsonSchema(config.Version01, resticVersion, output) + case "v2": + return jsonschema.WriteJsonSchema(config.Version02, resticVersion, output) + default: + return fmt.Errorf("unknown json schema type: %s", args[0]) + } } // SectionInfoData is used as data for go templates that render profile section references diff --git a/commands_test.go b/commands_test.go index 3b7d40c9..045d0b6a 100644 --- a/commands_test.go +++ b/commands_test.go @@ -2,6 +2,7 @@ package main import ( "bytes" + "encoding/json" "fmt" "os" "sort" @@ -234,12 +235,28 @@ func TestGenerateCommand(t *testing.T) { assert.Contains(t, ref, "generating nested section") }) - t.Run("--json-schema", func(t *testing.T) { + t.Run("--json-schema global", func(t *testing.T) { buffer.Reset() - assert.NoError(t, generateCommand(buffer, contextWithArguments([]string{"--json-schema"}))) + assert.NoError(t, generateCommand(buffer, contextWithArguments([]string{"--json-schema", "global"}))) ref := buffer.String() - assert.Contains(t, ref, "\"profiles\":") + assert.Contains(t, ref, `"$schema"`) + assert.Contains(t, ref, "/jsonschema/config-1.json") assert.Contains(t, ref, "/jsonschema/config-2.json") + + decoder := json.NewDecoder(strings.NewReader(ref)) + content := make(map[string]any) + assert.NoError(t, decoder.Decode(&content)) + assert.Contains(t, content, `$schema`) + }) + + t.Run("--json-schema no-option", func(t *testing.T) { + buffer.Reset() + assert.Error(t, generateCommand(buffer, contextWithArguments([]string{"--json-schema"}))) + }) + + t.Run("--json-schema invalid-option", func(t *testing.T) { + buffer.Reset() + assert.Error(t, generateCommand(buffer, contextWithArguments([]string{"--json-schema", "_invalid_"}))) }) t.Run("--json-schema v1", func(t *testing.T) { @@ -249,6 +266,14 @@ func TestGenerateCommand(t *testing.T) { assert.Contains(t, ref, "/jsonschema/config-1.json") }) + t.Run("--json-schema v2", func(t *testing.T) { + buffer.Reset() + assert.NoError(t, generateCommand(buffer, contextWithArguments([]string{"--json-schema", "v2"}))) + ref := buffer.String() + assert.Contains(t, ref, "\"profiles\":") + assert.Contains(t, ref, "/jsonschema/config-2.json") + }) + t.Run("--json-schema --version 0.13 v1", func(t *testing.T) { buffer.Reset() assert.NoError(t, generateCommand(buffer, contextWithArguments([]string{"--json-schema", "--version", "0.13", "v1"}))) diff --git a/config/checkdoc/main.go b/config/checkdoc/main.go index 65853b17..8e608d7b 100644 --- a/config/checkdoc/main.go +++ b/config/checkdoc/main.go @@ -18,12 +18,12 @@ const ( configTag = "```" checkdocIgnore = "" goTemplate = "{{" - replaceURL = "http://localhost:1313/resticprofile/jsonschema/config-$1.json" + replaceURL = "http://localhost:1313/resticprofile/jsonschema/config$1.json" ) var ( - urlPattern = regexp.MustCompile(`{{< [^>}]+config-(\d)\.json"[^>}]+ >}}`) - _ = regexp.MustCompile(`{{< [^>}]+config-(\d)\.json"[^>}]+ >}}`) // Remove this when VS Code fixed the syntax highlighting issues + urlPattern = regexp.MustCompile(`{{< [^>}]+config(-\d)?\.json"[^>}]+ >}}`) + _ = regexp.MustCompile(`{{< [^>}]+config(-\d)?\.json"[^>}]+ >}}`) // Remove this when VS Code fixed the syntax highlighting issues ) var ( diff --git a/config/global.go b/config/global.go index d3da9f51..cd19e7ca 100644 --- a/config/global.go +++ b/config/global.go @@ -18,7 +18,7 @@ type Global struct { Initialize bool `mapstructure:"initialize" default:"false" description:"Initialize a repository if missing"` NoAutoRepositoryFile maybe.Bool `mapstructure:"prevent-auto-repository-file" default:"false" description:"Prevents using a repository file for repository definitions containing a password"` ResticBinary string `mapstructure:"restic-binary" description:"Full path of the restic executable (detected if not set)"` - ResticVersion string // not configurable at the moment. To be set after ResticBinary is known. + ResticVersion string `mapstructure:"restic-version" pattern:"^(|[0-9]+\\.[0-9]+(\\.[0-9]+)?)$" description:"Sets the restic version (detected if not set)"` FilterResticFlags bool `mapstructure:"restic-arguments-filter" default:"true" description:"Remove unknown flags instead of passing all configured flags to restic"` ResticLockRetryAfter time.Duration `mapstructure:"restic-lock-retry-after" default:"1m" description:"Time to wait before trying to get a lock on a restic repository - see https://creativeprojects.github.io/resticprofile/usage/locks/"` ResticStaleLockAge time.Duration `mapstructure:"restic-stale-lock-age" default:"1h" description:"The age an unused lock on a restic repository must have at least before resticprofile attempts to unlock - see https://creativeprojects.github.io/resticprofile/usage/locks/"` diff --git a/config/jsonschema/model.go b/config/jsonschema/model.go index 15c495f1..52cefe32 100644 --- a/config/jsonschema/model.go +++ b/config/jsonschema/model.go @@ -265,7 +265,7 @@ func newSchemaBool() *schemaTypeBase { type schemaObject struct { schemaTypeBase - AdditionalProperties bool `json:"additionalProperties"` + AdditionalProperties any `json:"additionalProperties,omitempty"` PatternProperties map[string]SchemaType `json:"patternProperties,omitempty"` Properties map[string]SchemaType `json:"properties,omitempty"` Required []string `json:"required,omitempty"` @@ -274,9 +274,10 @@ type schemaObject struct { func newSchemaObject() *schemaObject { return withBaseType(&schemaObject{ - PatternProperties: make(map[string]SchemaType), - Properties: make(map[string]SchemaType), - DependentRequired: make(map[string][]string), + AdditionalProperties: false, + PatternProperties: make(map[string]SchemaType), + Properties: make(map[string]SchemaType), + DependentRequired: make(map[string][]string), }, "object") } @@ -297,6 +298,15 @@ func (s *schemaObject) verify() (err error) { err = fmt.Errorf("type of %q in properties is undefined", name) } } + if err == nil { + switch s.AdditionalProperties.(type) { + case nil: + case bool: + case SchemaType: + default: + err = fmt.Errorf("additionalProperties must be nil, boolean or SchemaType") + } + } if err == nil { err = s.schemaTypeBase.verify() } @@ -438,6 +448,9 @@ func internalWalkTypes(into map[SchemaType]bool, current SchemaType, callback fu for name, property := range t.PatternProperties { t.PatternProperties[name] = internalWalkTypes(into, property, callback) } + if item, ok := t.AdditionalProperties.(SchemaType); ok { + t.AdditionalProperties = internalWalkTypes(into, item, callback) + } case *schemaArray: t.Items = internalWalkTypes(into, t.Items, callback) case *schemaTypeList: diff --git a/config/jsonschema/model_test.go b/config/jsonschema/model_test.go index 6ede9d42..99c30944 100644 --- a/config/jsonschema/model_test.go +++ b/config/jsonschema/model_test.go @@ -221,6 +221,12 @@ func TestVerify(t *testing.T) { obj.Properties["first"] = newSchemaString() assert.NoError(t, obj.verify()) + assert.Equal(t, false, obj.AdditionalProperties) + obj.AdditionalProperties = "-" + assert.ErrorContains(t, obj.verify(), `additionalProperties must be nil, boolean or SchemaType`) + obj.AdditionalProperties = newSchemaString() + assert.NoError(t, obj.verify()) + testBase(t, obj.base()) }) diff --git a/config/jsonschema/schema.go b/config/jsonschema/schema.go index 56cfaa11..9aa2189c 100644 --- a/config/jsonschema/schema.go +++ b/config/jsonschema/schema.go @@ -432,14 +432,7 @@ func applyListAppendSchema(target SchemaType) { func schemaForConfigV1(profileInfo config.ProfileInfo) (object *schemaObject) { object = schemaForProfile(profileInfo) - // exclude non-profile properties from profile-schema - profilesPattern := fmt.Sprintf(`^(?!%s).*$`, strings.Join([]string{ - constants.SectionConfigurationGlobal, - constants.SectionConfigurationGroups, - constants.SectionConfigurationIncludes, - constants.ParameterVersion, - }, "|")) - object.PatternProperties[profilesPattern] = object.PatternProperties[matchAll] + object.AdditionalProperties = object.PatternProperties[matchAll] delete(object.PatternProperties, matchAll) object.Description = "resticprofile configuration v1" diff --git a/config/jsonschema/schema_test.go b/config/jsonschema/schema_test.go index e5e75b85..3ba8b934 100644 --- a/config/jsonschema/schema_test.go +++ b/config/jsonschema/schema_test.go @@ -357,9 +357,9 @@ func TestSchemaForPropertySet(t *testing.T) { t.Run("AdditionalProperties", func(t *testing.T) { s := schemaForPropertySet(newMock(func(m *mocks.NamedPropertySet) { m.EXPECT().IsClosed().Return(false) })) - assert.True(t, s.AdditionalProperties) + assert.Equal(t, true, s.AdditionalProperties) s = schemaForPropertySet(newMock(func(m *mocks.NamedPropertySet) { m.EXPECT().IsClosed().Return(true) })) - assert.False(t, s.AdditionalProperties) + assert.Equal(t, false, s.AdditionalProperties) }) t.Run("TypedAdditionalProperty", func(t *testing.T) { @@ -374,7 +374,7 @@ func TestSchemaForPropertySet(t *testing.T) { m.EXPECT().OtherPropertyInfo().Return(pi) })) - assert.False(t, s.AdditionalProperties) + assert.Equal(t, false, s.AdditionalProperties) assert.Equal(t, newSchemaString(), s.PatternProperties[matchAll]) }) diff --git a/config/mocks/NamedPropertySet.go b/config/mocks/NamedPropertySet.go index 4cface2c..c606d31f 100644 --- a/config/mocks/NamedPropertySet.go +++ b/config/mocks/NamedPropertySet.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.44.2. DO NOT EDIT. +// Code generated by mockery v2.46.2. DO NOT EDIT. package mocks diff --git a/config/mocks/ProfileInfo.go b/config/mocks/ProfileInfo.go index 50942494..8bafba01 100644 --- a/config/mocks/ProfileInfo.go +++ b/config/mocks/ProfileInfo.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.44.2. DO NOT EDIT. +// Code generated by mockery v2.46.2. DO NOT EDIT. package mocks diff --git a/config/mocks/PropertyInfo.go b/config/mocks/PropertyInfo.go index 7ee42d0a..98e43991 100644 --- a/config/mocks/PropertyInfo.go +++ b/config/mocks/PropertyInfo.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.44.2. DO NOT EDIT. +// Code generated by mockery v2.46.2. DO NOT EDIT. package mocks diff --git a/config/mocks/SectionInfo.go b/config/mocks/SectionInfo.go index 8d43a28c..695def83 100644 --- a/config/mocks/SectionInfo.go +++ b/config/mocks/SectionInfo.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.44.2. DO NOT EDIT. +// Code generated by mockery v2.46.2. DO NOT EDIT. package mocks diff --git a/contrib/templates/config-schema.gojson b/contrib/templates/config-schema.gojson new file mode 100644 index 00000000..62c98aeb --- /dev/null +++ b/contrib/templates/config-schema.gojson @@ -0,0 +1,184 @@ +{{- /* ------------------------------------------------------------------------------ + + Template that generates a global json schema which redirects to versioned URLs + depending on "version" and "restic-version" properties + + Usage: resticprofile generate \ + --config-reference contrib/templates/config-schema.gojson + +------------------------------------------------------------------------------ */ -}} +{{- /*gotype: github.com/creativeprojects/resticprofile/config.TemplateInfoData*/ -}} +{{- $baseUrl := "https://creativeprojects.github.io/resticprofile/jsonschema" -}} +{{- if .Env.SCHEMA_BASE_URL -}} +{{- $baseUrl = .Env.SCHEMA_BASE_URL -}} +{{- end -}} +{{- $refBaseUrl := $baseUrl -}} +{{- if .Env.SCHEMA_REF_BASE_URL -}} +{{- $refBaseUrl = .Env.SCHEMA_REF_BASE_URL -}} +{{- end -}} +{ + "$id": "{{ $baseUrl | js }}/config.json", + "$schema": "https://json-schema.org/draft-07/schema", + "$defs": { + "version-1": { + "oneOf": [ + { + "type": "object", + "properties": { + "version": { + "type": "string", + "const": "1" + } + }, + "required": [ + "version" + ] + }, + { + "type": "object", + "properties": { + "version": { + "type": "string", + "maxLength": 0 + } + }, + "required": [ + "version" + ] + } + ] + }, + "version-2": { + "type": "object", + "properties": { + "version": { + "type": "string", + "const": "2" + } + }, + "required": [ + "version" + ] + }, + "no-version": { + "not": { + "type": "object", + "properties": { + "version": { + } + }, + "required": [ + "version" + ] + } + }, + "no-restic-version": { + "type": "object", + "properties": { + "global": { + "not": { + "type": "object", + "properties": { + "restic-version": { + } + }, + "required": [ + "restic-version" + ] + } + } + } + } + }, + "if": { + "$ref": "#/$defs/no-version" + }, + "then": { + "$ref": "{{ $refBaseUrl | js }}/config-1.json" + }, + "else": { + "if": { + "$ref": "#/$defs/no-restic-version" + }, + "then": { + "oneOf": [ + { + {{ block "noResticVersion" (list $refBaseUrl "2") -}} + {{- $base := index . 0 -}} + {{- $config := index . 1 -}} + "allOf": [ + { + "$ref": "#/$defs/version-{{ $config }}" + }, + { + "$ref": "{{ $base | js }}/config-{{ $config }}.json" + } + ] + {{ end }} + }, + { + {{ template "noResticVersion" (list $refBaseUrl "1") }} + } + ] + }, + "else": { + "oneOf": [ + {{ define "schemaWithResticVersion" }} + {{- $base := index . 0 -}} + {{- $config := index . 1 -}} + {{- $version := index . 2 -}} + { + "allOf": [ + { + "$ref": "#/$defs/version-{{ $config | js }}" + }, + { + "type": "object", + "properties": { + "global": { + "type": "object", + "properties": { + "restic-version": { + "anyOf": [ + { + "type": "string", + "pattern": "{{ $version | replace "." "\\." | js }}.*", + "default": "{{ $version | js }}", + "minLength": 3 + }, + { + "type": "string", + "const": "{{ $version | js }}" + } + ] + } + }, + "required": [ + "restic-version" + ] + } + }, + "required": [ + "global" + ] + }, + { + "$ref": "{{ $base | js }}/config-{{ $config | js }}-restic-{{ $version | replace "." "-" | js }}.json" + } + ] + } + {{ end -}} + {{- /* restic major version ( assuming it is "0.$major", may need to change when restic reaches v1 ) */ -}} + {{ range $index, $version := .KnownResticVersions -}} + {{- $version = slice ($version | split ".") 0 2 | join "." -}} + {{- if gt $index 0 -}}{{ "," }}{{- end -}} + {{- template "schemaWithResticVersion" (list $refBaseUrl "2" $version) -}} + {{ "," }} + {{- template "schemaWithResticVersion" (list $refBaseUrl "1" $version) -}} + {{- end }} + ] + } + }, + "title": "resticprofile configuration", + "type": "object" +} \ No newline at end of file diff --git a/contrib/templates/groups.gomd b/contrib/templates/groups.gomd index 72460098..9b22b808 100644 --- a/contrib/templates/groups.gomd +++ b/contrib/templates/groups.gomd @@ -24,6 +24,9 @@ weight: 15 {{- end }} {{- $webBaseUrl := "https://creativeprojects.github.io/resticprofile" -}} +{{- if .Env.WEB_BASE_URL -}} + {{- $webBaseUrl = .Env.WEB_BASE_URL -}} +{{- end -}} {{- $configWebUrl := "{base}/configuration" | replace "{base}" $webBaseUrl -}} {{ $layoutHeadings -}}## Section **groups** diff --git a/contrib/templates/json-schema.gomd b/contrib/templates/json-schema.gomd index b5f3caae..d42a51ef 100644 --- a/contrib/templates/json-schema.gomd +++ b/contrib/templates/json-schema.gomd @@ -24,6 +24,9 @@ weight: 30 {{- end }} {{- $webBaseUrl := "https://creativeprojects.github.io/resticprofile" -}} +{{- if .Env.WEB_BASE_URL -}} + {{- $webBaseUrl = .Env.WEB_BASE_URL -}} +{{- end -}} {{- $configWebUrl := "{base}/configuration" | replace "{base}" $webBaseUrl -}} resticprofile provides a JSON schema for v1 & v2 configuration files. The schema may be diff --git a/contrib/templates/nested.gomd b/contrib/templates/nested.gomd index a45b0ff6..396f4369 100644 --- a/contrib/templates/nested.gomd +++ b/contrib/templates/nested.gomd @@ -25,6 +25,9 @@ alwaysopen: false {{- end }} {{- $webBaseUrl := "https://creativeprojects.github.io/resticprofile" -}} +{{- if .Env.WEB_BASE_URL -}} + {{- $webBaseUrl = .Env.WEB_BASE_URL -}} +{{- end -}} {{- $configWebUrl := "{base}/configuration" | replace "{base}" $webBaseUrl -}} {{ $layoutHeadings -}}## Nested profile sections diff --git a/contrib/templates/profile.gomd b/contrib/templates/profile.gomd index bd4b7708..01f07206 100644 --- a/contrib/templates/profile.gomd +++ b/contrib/templates/profile.gomd @@ -27,6 +27,9 @@ alwaysopen: false {{- $webBaseUrl := "https://creativeprojects.github.io/resticprofile" -}} +{{- if .Env.WEB_BASE_URL -}} + {{- $webBaseUrl = .Env.WEB_BASE_URL -}} +{{- end -}} {{- $configWebUrl := "{base}/configuration" | replace "{base}" $webBaseUrl -}} {{ $layoutHeadings -}}## Profile sections diff --git a/docs/content/configuration/getting_started/index.md b/docs/content/configuration/getting_started/index.md index 7b90f383..d34c7a46 100644 --- a/docs/content/configuration/getting_started/index.md +++ b/docs/content/configuration/getting_started/index.md @@ -85,7 +85,7 @@ version = "1" {{% tab title="yaml" %}} ```yaml -# yaml-language-server: $schema={{< absolute "jsonschema/config-1.json" nohtml >}} +# yaml-language-server: $schema={{< absolute "jsonschema/config.json" nohtml >}} version: "1" @@ -119,7 +119,7 @@ default { ```json { - "$schema": "{{< absolute "jsonschema/config-1.json" nohtml >}}", + "$schema": "{{< absolute "jsonschema/config.json" nohtml >}}", "version": "1", "default": { "repository": "local:/backup", @@ -254,7 +254,7 @@ version = "1" {{% tab title="yaml" %}} ```yaml -# yaml-language-server: $schema={{< absolute "jsonschema/config-1.json" nohtml >}} +# yaml-language-server: $schema={{< absolute "jsonschema/config.json" nohtml >}} version: "1" @@ -290,7 +290,7 @@ default { ```json { - "$schema": "{{< absolute "jsonschema/config-1.json" nohtml >}}", + "$schema": "{{< absolute "jsonschema/config.json" nohtml >}}", "version": "1", "default": { "repository": "local:/backup", diff --git a/docs/content/configuration/jsonschema/index.md b/docs/content/configuration/jsonschema/index.md index 6b969a38..d5a755dc 100644 --- a/docs/content/configuration/jsonschema/index.md +++ b/docs/content/configuration/jsonschema/index.md @@ -8,36 +8,16 @@ weight: 55 JSON, YAML and TOML configuration files can benefit from a JSON schema that describes the config structure depending on the selected *restic* and configuration file version. -## Schema URLs +## Schema URL -JSON schema URLs for **any** *restic* version: - -* Config V1: {{< absolute "jsonschema/config-1.json" >}} -* Config V2: {{< absolute "jsonschema/config-2.json" >}} - -These universal schemas contain all flags and commands of all known *restic* versions and -may allow setting flags that are not supported by a particular *restic* version. Descriptions -and deprecation markers indicate what is supported and what should no longer be used. - -JSON schema URLs for a **specific** *restic* version (list of [available URLs]({{% relref "reference/json-schema" %}})): - -* `.../config-1-restic-{MAJOR}-{MINOR}.json` -* `.../config-2-restic-{MAJOR}-{MINOR}.json` +**{{< absolute "jsonschema/config.json" >}}** -These schemas contain only flags and commands of a specific *restic* version. The schema will -validate a config only when flags are used that the selected *restic* version supports -according to its manual pages. - -{{% notice style="info" %}} -As an alternative to URLs, schemas can be generated locally with: -`resticprofile generate --json-schema [--version RESTIC_VERSION] [v2|v1]` - -You can prefix the command with the environment variable `SCHEMA_BASE_URL` to change the base URL (defaults to `https://creativeprojects.github.io/resticprofile/jsonschema/`). This can be useful if you're working in an offline environment or need to use a custom schema location. -{{% /notice %}} +This schema detects config and restic version and redirects to the corresponding [versioned URL](#versioned-urls). +Detection requires full support for JSON Schema Draft 7. Use a versioned URL where not supported (e.g. TOML). ## Usage (vscode) -To use a schema with **vscode**, begin your config files with: +To use a schema with **vscode**, begin your config files with: {{< tabs groupid="config-with-json" >}} {{% tab title="toml" %}} @@ -50,7 +30,7 @@ version = "2" {{% /tab %}} {{% tab title="yaml" %}} ``````yaml -# yaml-language-server: $schema={{< absolute "jsonschema/config-2.json" nohtml >}} +# yaml-language-server: $schema={{< absolute "jsonschema/config.json" nohtml >}} version: "2" @@ -59,7 +39,7 @@ version: "2" {{% tab title="json" %}} ``````json { - "$schema": "{{< absolute "jsonschema/config-2.json" nohtml >}}", + "$schema": "{{< absolute "jsonschema/config.json" nohtml >}}", "version": "2" } `````` @@ -74,4 +54,34 @@ YAML & TOML validation with JSON schema is not supported out of the box. Additio {{< figure src="/configuration/jsonschema/jsonschema-vsc.gif" >}} -**Extension**: `redhat.vscode-yaml` \ No newline at end of file +**Extension**: `redhat.vscode-yaml` + + +## Versioned URLs + +The following versioned schemas are referenced from `config.json`. Which URL is selected depends +on config and restic version. You may use the URLs directly if you need a self-contained schema +file or to enforce a certain version of configuration format and/or restic flags + +JSON schema URLs for **any** *restic* version: + +* Config V1: {{< absolute "jsonschema/config-1.json" >}} +* Config V2: {{< absolute "jsonschema/config-2.json" >}} + +These universal schemas contain all flags and commands of all known *restic* versions and +may allow to set flags that are not supported by a particular *restic* version. Descriptions +and deprecation markers indicate what is supported and what should no longer be used. + +JSON schema URLs for a **specific** *restic* version (list of [available URLs]({{% relref "reference/json-schema" %}})): + +* `.../config-1-restic-{MAJOR}-{MINOR}.json` +* `.../config-2-restic-{MAJOR}-{MINOR}.json` + +These schemas contain only flags and commands of a specific *restic* version. The schema will +validate a config only when flags are used that the selected *restic* version supports +according to its manual pages. + +{{% notice style="tip" %}} +As an alternative to URLs, schemas can be generated locally with: +`resticprofile generate --json-schema [--version RESTIC_VERSION] [global|v1|v2]` +{{% /notice %}} diff --git a/docs/content/configuration/variables.md b/docs/content/configuration/variables.md index 39986cb1..10014e76 100644 --- a/docs/content/configuration/variables.md +++ b/docs/content/configuration/variables.md @@ -592,6 +592,6 @@ default { {{% /tab %}} {{< /tabs >}} -{{% notice style="hint" %}} +{{% notice style="tip" %}} Use `$$` to escape a single `$` in configuration values that support variable expansion. E.g. on Windows you might want to exclude `$RECYCLE.BIN`. Specify it as: `exclude = ["$$RECYCLE.BIN"]`. {{% /notice %}} diff --git a/examples/crond.yaml b/examples/crond.yaml index a64683d8..ded826f1 100644 --- a/examples/crond.yaml +++ b/examples/crond.yaml @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=https://creativeprojects.github.io/resticprofile/jsonschema/config-1.json +# yaml-language-server: $schema=https://creativeprojects.github.io/resticprofile/jsonschema/config.json global: default-command: snapshots diff --git a/examples/dev.yaml b/examples/dev.yaml index cdf70b8e..fd42439e 100644 --- a/examples/dev.yaml +++ b/examples/dev.yaml @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=https://creativeprojects.github.io/resticprofile/jsonschema/config-1.json +# yaml-language-server: $schema=https://creativeprojects.github.io/resticprofile/jsonschema/config.json version: "1" diff --git a/examples/linux.yaml b/examples/linux.yaml index ffc80720..45c0bd28 100644 --- a/examples/linux.yaml +++ b/examples/linux.yaml @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=https://creativeprojects.github.io/resticprofile/jsonschema/config-1.json +# yaml-language-server: $schema=https://creativeprojects.github.io/resticprofile/jsonschema/config.json version: "1" diff --git a/examples/v2.yaml b/examples/v2.yaml index 72709038..fbb1961e 100644 --- a/examples/v2.yaml +++ b/examples/v2.yaml @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=https://creativeprojects.github.io/resticprofile/jsonschema/config-2.json +# yaml-language-server: $schema=https://creativeprojects.github.io/resticprofile/jsonschema/config.json version: "2" global: diff --git a/examples/windows.yaml b/examples/windows.yaml index 1da874e6..be01f49a 100644 --- a/examples/windows.yaml +++ b/examples/windows.yaml @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=https://creativeprojects.github.io/resticprofile/jsonschema/config-1.json +# yaml-language-server: $schema=https://creativeprojects.github.io/resticprofile/jsonschema/config.json version: "1" diff --git a/monitor/mocks/OutputAnalysis.go b/monitor/mocks/OutputAnalysis.go index be451dce..a65cd599 100644 --- a/monitor/mocks/OutputAnalysis.go +++ b/monitor/mocks/OutputAnalysis.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.44.2. DO NOT EDIT. +// Code generated by mockery v2.46.2. DO NOT EDIT. package mocks diff --git a/schedule/mocks/Handler.go b/schedule/mocks/Handler.go index 50ab4176..75c4ba37 100644 --- a/schedule/mocks/Handler.go +++ b/schedule/mocks/Handler.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.44.2. DO NOT EDIT. +// Code generated by mockery v2.46.2. DO NOT EDIT. package mocks