diff --git a/.github/workflows/docs-and-linting.yml b/.github/workflows/docs-and-linting.yml index 5b2f82a64..733d08075 100644 --- a/.github/workflows/docs-and-linting.yml +++ b/.github/workflows/docs-and-linting.yml @@ -9,8 +9,9 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - # current Go releases plus the version in the go.mod are tested - go: ['go.mod', 'oldstable', 'stable'] + # Current Go releases plus the versions in **/go.mod are tested. + # See also BUILD_SPEC_MODULE_ONLY below. + go: ['go.mod', 'schema/go.mod', 'oldstable', 'stable'] # https://github.com/actions/setup-go/tree/v5#getting-go-version-from-the-gomod-file # https://github.com/actions/setup-go/tree/v5#using-stableoldstable-aliases @@ -26,19 +27,21 @@ jobs: - uses: actions/setup-go@v5 with: - go-version: ${{ matrix.go != 'go.mod' && matrix.go || null }} - go-version-file: ${{ matrix.go == 'go.mod' && 'go/src/github.com/opencontainers/image-spec/go.mod' || null }} - cache-dependency-path: go/src/github.com/opencontainers/image-spec/go.sum + go-version: ${{ !endsWith(matrix.go, 'go.mod') && matrix.go || null }} + go-version-file: ${{ endsWith(matrix.go, 'go.mod') && format('go/src/github.com/opencontainers/image-spec/{0}', matrix.go) || null }} + cache-dependency-path: go/src/github.com/opencontainers/image-spec/**/go.sum - name: Render and Lint env: GOPATH: /home/runner/work/image-spec/image-spec/go # do not automatically upgrade go to a different version: https://go.dev/doc/toolchain GOTOOLCHAIN: local + BUILD_SPEC_MODULE_ONLY: ${{ matrix.go == 'go.mod' }} run: | export PATH=$GOPATH/bin:$PATH cd go/src/github.com/opencontainers/image-spec make install.tools + go work init . schema go get -t -d ./... ls ../ make diff --git a/HACKING.md b/HACKING.md index 7f4329281..785c5d110 100644 --- a/HACKING.md +++ b/HACKING.md @@ -13,6 +13,12 @@ Prerequisites: - Go - latest version is recommended, see the [go.mod](go.mod) file for the minimum requirement - make +While developing features spanning `specs-go` and `schema`, to break the dependency cycle on an uncommitted work: + +```shell +go work init . schema +``` + The following make targets are relevant for any work involving the Go packages. ### Linting diff --git a/Makefile b/Makefile index 509ebe8aa..ef825617a 100644 --- a/Makefile +++ b/Makefile @@ -75,7 +75,7 @@ header.html: .tool/genheader.go specs-go/version.go .PHONY: validate-examples validate-examples: schema/schema.go ## validate examples in the specification markdown files - go test -run TestValidate ./schema + cd schema && go test -run TestValidate . .PHONY: check-license check-license: ## check license headers in source files @@ -91,6 +91,7 @@ lint: lint-go lint-md ## Run all linters lint-go: .install.lint ## lint check of Go files using golangci-lint @echo "checking Go lint" @GO111MODULE=on $(GOPATH)/bin/golangci-lint run + @[ "$$BUILD_SPEC_MODULE_ONLY" = true ] || { cd schema && GO111MODULE=on $(GOPATH)/bin/golangci-lint run; } .PHONY: lint-md lint-md: ## Run linting for markdown @@ -99,7 +100,8 @@ lint-md: ## Run linting for markdown .PHONY: test test: ## run the unit tests - go test -race -cover $(shell go list ./... | grep -v /vendor/) + go test -race -cover ./... + [ "$$BUILD_SPEC_MODULE_ONLY" = true ] || { cd schema && go test -race -cover ./...; } img/%.png: img/%.dot ## generate PNG from dot file dot -Tpng $^ > $@ diff --git a/go.mod b/go.mod index 9a45167ff..61c11935b 100644 --- a/go.mod +++ b/go.mod @@ -5,8 +5,4 @@ module github.com/opencontainers/image-spec // For example, updating this version to 1.19 first requires Go 1.21 to be released. go 1.18 -require ( - github.com/opencontainers/go-digest v1.0.0 - github.com/russross/blackfriday v1.6.0 - github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 -) +require github.com/opencontainers/go-digest v1.0.0 diff --git a/go.sum b/go.sum index dcb5a93b0..30f45e97f 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,2 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= -github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY= -github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4= -github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY= diff --git a/schema/defs-descriptor.json b/schema/defs-descriptor.json index 056a7999f..6d1506232 100644 --- a/schema/defs-descriptor.json +++ b/schema/defs-descriptor.json @@ -1,5 +1,6 @@ { "description": "Definitions particular to OpenContainer Descriptor Specification", + "$schema": "http://json-schema.org/draft-04/schema#", "definitions": { "mediaType": { "id": "https://opencontainers.org/schema/image/descriptor/mediaType", diff --git a/schema/go.mod b/schema/go.mod new file mode 100644 index 000000000..c8664c9b0 --- /dev/null +++ b/schema/go.mod @@ -0,0 +1,12 @@ +module github.com/opencontainers/image-spec/schema + +go 1.21 + +require ( + github.com/opencontainers/go-digest v1.0.0 + github.com/opencontainers/image-spec v1.1.1 + github.com/russross/blackfriday/v2 v2.1.0 + github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 +) + +require golang.org/x/text v0.14.0 // indirect diff --git a/schema/go.sum b/schema/go.sum new file mode 100644 index 000000000..0607fc501 --- /dev/null +++ b/schema/go.sum @@ -0,0 +1,10 @@ +github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= +github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= diff --git a/schema/spec_test.go b/schema/spec_test.go index 5cf849005..a12034f27 100644 --- a/schema/spec_test.go +++ b/schema/spec_test.go @@ -27,7 +27,7 @@ import ( "testing" "github.com/opencontainers/image-spec/schema" - "github.com/russross/blackfriday" + "github.com/russross/blackfriday/v2" ) var ( @@ -120,9 +120,12 @@ type renderer struct { fn func(text []byte, lang string) } -func (r *renderer) BlockCode(out *bytes.Buffer, text []byte, lang string) { - r.fn(text, lang) - r.Renderer.BlockCode(out, text, lang) +func (r *renderer) RenderNode(w io.Writer, node *blackfriday.Node, entering bool) blackfriday.WalkStatus { + if node.Type == blackfriday.CodeBlock { + lang := bytes.TrimRight(node.Info, "\t ") + r.fn(node.Literal, string(lang)) + } + return r.Renderer.RenderNode(w, node, entering) } type example struct { @@ -174,7 +177,7 @@ func extractExamples(rd io.Reader) ([]example, error) { var examples []example renderer := &renderer{ - Renderer: blackfriday.HtmlRenderer(0, "test test", ""), + Renderer: blackfriday.NewHTMLRenderer(blackfriday.HTMLRendererParameters{}), fn: func(text []byte, lang string) { examples = append(examples, parseExample(lang, string(text))) }, @@ -184,9 +187,9 @@ func extractExamples(rd io.Reader) ([]example, error) { // the side-effect of calling back for each code block. // TODO(stevvooe): Consider just parsing these with a scanner. It will be // faster and we can retain file, line no. - blackfriday.MarkdownOptions(p, renderer, blackfriday.Options{ - Extensions: blackfriday.EXTENSION_FENCED_CODE, - }) + _ = blackfriday.Run(p, + blackfriday.WithRenderer(renderer), + blackfriday.WithExtensions(blackfriday.FencedCode)) return examples, nil } diff --git a/schema/validator.go b/schema/validator.go index 6c823b588..e32367346 100644 --- a/schema/validator.go +++ b/schema/validator.go @@ -24,7 +24,7 @@ import ( digest "github.com/opencontainers/go-digest" v1 "github.com/opencontainers/image-spec/specs-go/v1" - "github.com/santhosh-tekuri/jsonschema/v5" + "github.com/santhosh-tekuri/jsonschema/v6" ) // Validator wraps a media type string identifier and implements validation against a JSON schema. @@ -83,11 +83,16 @@ func (v Validator) validateSchema(src io.Reader) error { if file.IsDir() { continue } - specBuf, err := specFS.ReadFile(file.Name()) + specFile, err := specFS.Open(file.Name()) if err != nil { return fmt.Errorf("could not read spec file %s: %w", file.Name(), err) } - err = c.AddResource(file.Name(), bytes.NewReader(specBuf)) + defer specFile.Close() + spec, err := jsonschema.UnmarshalJSON(specFile) + if err != nil { + return fmt.Errorf("could not decode spec file %s: %w", file.Name(), err) + } + err = c.AddResource(file.Name(), spec) if err != nil { return fmt.Errorf("failed to add spec file %s: %w", file.Name(), err) } @@ -96,7 +101,7 @@ func (v Validator) validateSchema(src io.Reader) error { return fmt.Errorf("spec file has no aliases: %s", file.Name()) } for _, specURL := range specURLs[file.Name()] { - err = c.AddResource(specURL, bytes.NewReader(specBuf)) + err = c.AddResource(specURL, spec) if err != nil { return fmt.Errorf("failed to add spec file %s as url %s: %w", file.Name(), specURL, err) } @@ -110,8 +115,7 @@ func (v Validator) validateSchema(src io.Reader) error { } // read in the user input and validate - var input interface{} - err = json.NewDecoder(src).Decode(&input) + input, err := jsonschema.UnmarshalJSON(src) if err != nil { return fmt.Errorf("unable to parse json to validate: %w", err) }