From e1b10346c876f4d030b1f44b5ac5f4c7a62f8bcf Mon Sep 17 00:00:00 2001 From: "David E. Wheeler" Date: Fri, 28 Jun 2024 17:34:40 -0400 Subject: [PATCH] Implement JSON Schema for PGXN Meta Spec v1 Based on the descriptions in `spec.md` and the validation in pgxn/pgxn-meta-validator, write JSON Schema v2020 schemas to validate PGXN Meta Spec `META.json` files. The implementation should be at least as good as the old one, now portable for use in other languages. In the process, correct a few issues and errors discovered in `spec.md` and increment its version to v1.0.2. Add a test suite in Rust that uses the boon crate to verify that the schemas, their IDs, and their examples are valid. This sets the stage for writing a validation utility in Rust. To keep things tidy and somewhat standardized, add a pre-commit configuration to lint the JSON and Rust code. Then setup a GitHub workflow to run the tests on macOS, Windows, and Linux, and the pre-commit linting on Linux. Overkill for now, since there is no Rust library, just tests for the JSON Schema, but likely to be needed before long. --- .github/workflows/test-and-lint.yml | 35 +++ .gitignore | 6 +- .pre-commit-config.yaml | 29 ++ Cargo.lock | 320 ++++++++++++++++++++++ Cargo.toml | 17 ++ Makefile | 11 + README.md | 19 ++ schema/v1/bugtracker.schema.json | 39 +++ schema/v1/distribution.schema.json | 131 +++++++++ schema/v1/extension.schema.json | 49 ++++ schema/v1/license.schema.json | 83 ++++++ schema/v1/maintainer.schema.json | 24 ++ schema/v1/meta-spec.schema.json | 33 +++ schema/v1/no_index.schema.json | 46 ++++ schema/v1/prereq_phase.schema.json | 47 ++++ schema/v1/prereq_relationship.schema.json | 22 ++ schema/v1/prereqs.schema.json | 63 +++++ schema/v1/provides.schema.json | 26 ++ schema/v1/repository.schema.json | 49 ++++ schema/v1/resources.schema.json | 39 +++ schema/v1/tags.schema.json | 20 ++ schema/v1/term.schema.json | 10 + schema/v1/version.schema.json | 16 ++ schema/v1/version_range.schema.json | 25 ++ spec.md | 26 +- tests/test.rs | 59 ++++ 26 files changed, 1230 insertions(+), 14 deletions(-) create mode 100644 .github/workflows/test-and-lint.yml create mode 100644 .pre-commit-config.yaml create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 Makefile create mode 100644 schema/v1/bugtracker.schema.json create mode 100644 schema/v1/distribution.schema.json create mode 100644 schema/v1/extension.schema.json create mode 100644 schema/v1/license.schema.json create mode 100644 schema/v1/maintainer.schema.json create mode 100644 schema/v1/meta-spec.schema.json create mode 100644 schema/v1/no_index.schema.json create mode 100644 schema/v1/prereq_phase.schema.json create mode 100644 schema/v1/prereq_relationship.schema.json create mode 100644 schema/v1/prereqs.schema.json create mode 100644 schema/v1/provides.schema.json create mode 100644 schema/v1/repository.schema.json create mode 100644 schema/v1/resources.schema.json create mode 100644 schema/v1/tags.schema.json create mode 100644 schema/v1/term.schema.json create mode 100644 schema/v1/version.schema.json create mode 100644 schema/v1/version_range.schema.json create mode 100644 tests/test.rs diff --git a/.github/workflows/test-and-lint.yml b/.github/workflows/test-and-lint.yml new file mode 100644 index 0000000..6360125 --- /dev/null +++ b/.github/workflows/test-and-lint.yml @@ -0,0 +1,35 @@ +name: đŸ§Ș Test and Lint +on: + push: + branches-ignore: [wip/**] +jobs: + test: + strategy: + matrix: + os: [[🐧, Ubuntu], [🍎, macOS], [đŸȘŸ, Windows]] + toolchain: ["stable", "beta", "nightly"] + name: 🩀 ${{ matrix.toolchain }} on ${{ matrix.os[0] }} ${{ matrix.os[1] }} + runs-on: ${{ matrix.os[1] }}-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Rust Cache + uses: Swatinem/rust-cache@v2 + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + with: { toolchain: "${{ matrix.toolchain }}" } + - name: Test + run: make test + + lint: + name: 🔎 Lint and Cover + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Rust Cache + uses: Swatinem/rust-cache@v2 + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + - name: Run pre-commit + uses: pre-commit/action@v3.0.1 diff --git a/.gitignore b/.gitignore index 598ae79..3d4ce7e 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,5 @@ -/.vscode +.DS_Store +.idea/ +/target +.vscode/ +vendor/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..e2de3a8 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,29 @@ +# auth-global hooks will be managed / updated across all auth repos. +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.6.0 + hooks: + - id: trailing-whitespace + name: Lint trailing whitespace + exclude_types: [image] + - id: end-of-file-fixer + name: Lint end-of-file newline + exclude_types: [image] + - id: check-added-large-files + name: Don't permit large files + exclude_types: [image] + + - repo: https://github.com/backplane/pre-commit-rust-hooks + rev: v1.1.0 + hooks: + - id: fmt + - id: check + - id: clippy + - id: test + + - repo: https://github.com/pre-commit/mirrors-prettier + rev: v3.1.0 + hooks: + - id: prettier + name: JSON and YAML formatting + types_or: [json, yaml] diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..80b06a9 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,320 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "appendlist" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e149dc73cd30538307e7ffa2acd3d2221148eaeed4871f246657b1c3eaa1cbd2" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "boon" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9672cb0edeadf721484e298c0ed4dd70b0eaa3acaed5b4fd0bd73ca32e51d814" +dependencies = [ + "ahash", + "appendlist", + "base64", + "fluent-uri", + "idna", + "once_cell", + "percent-encoding", + "regex", + "regex-syntax", + "serde", + "serde_json", + "url", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "fluent-uri" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17c704e9dbe1ddd863da1e6ff3567795087b1eb201ce80d8fa81162e1516500d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pgxn-meta-spec" +version = "0.1.0" +dependencies = [ + "boon", + "serde_json", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "serde" +version = "1.0.203" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.203" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d947f6b3163d8857ea16c4fa0dd4840d52f3041039a85decd46867eb1abef2e4" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "syn" +version = "2.0.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tinyvec" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c55115c6fbe2d2bef26eb09ad74bde02d8255476fc0c7b515ef09fbb35742d82" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "url" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "zerocopy" +version = "0.7.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..6002b8e --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "pgxn-meta-spec" +version = "0.1.0" +description = "The PGXN distribution metadata specification" +repository = "https://github.com/pgxn/pgxn-meta-spec" +authors = ["David E. Wheeler "] +keywords = ["pgxn", "postgres", "postgresql", "extension", "validation", "distribution"] +license = "PostgreSQL" +categories = ["web-programming", "database"] +edition = "2021" +exclude = [ ".github", ".gitattributes", "target", ".vscode", ".gitignore" ] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dev-dependencies] +boon = "0.6" +serde_json = "1.0" diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7be34ba --- /dev/null +++ b/Makefile @@ -0,0 +1,11 @@ +.PHONY: test # Validate the JSON schema. +test: + @cargo test -- --show-output + +.git/hooks/pre-commit: + @printf "#!/bin/sh\nmake lint\n" > $@ + @chmod +x $@ + +.PHONY: lint # Lint the project +lint: .pre-commit-config.yaml + @pre-commit run --show-diff-on-failure --color=always --all-files diff --git a/README.md b/README.md index 05f03f0..1d8b4e2 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,23 @@ the [`LICENSE.md`](LICENSE.md) file in this repository. Typos and grammatical errors can go straight to a pull-request. When in doubt, start on the [mailing-list](#mailing-list). +### Testing + +This project includes test written in [Rust]. Use this command to install +Rust: + +``` sh +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh +``` + +Once it's installed, run tests with `make test`. + +### Linting + +This project uses [pre-commit] to keep the code tidy and warning and +error-free. Install [pre-commit], use `make lint` to run the linters, and +`make .git/hooks/pre-commit` to force pre-commit to run before every commit. + ## Chat PGXN discussion happens in the following chat rooms: @@ -30,6 +47,8 @@ PGXN discussion happens in the following chat rooms: [PGXN]: https://pgxn.org "PGXN: PostgreSQL Extension Network" [pgxn/pgxn-meta-spec]: https://github.com/pgxn/pgxn-meta-spec + [Rust]: https://www.rust-lang.org "The Rust Programming Language" + [pre-commit]: https://pre-commit.com "A framework for managing and maintaining multi-language pre-commit hooks." [Postgres Slack]: https://pgtreats.info/slack-invite [Postgres Discord]: https://discord.com/invite/bW2hsax8We [PGXN Discussions]: https://github.com/orgs/pgxn/discussions/ diff --git a/schema/v1/bugtracker.schema.json b/schema/v1/bugtracker.schema.json new file mode 100644 index 0000000..d31623e --- /dev/null +++ b/schema/v1/bugtracker.schema.json @@ -0,0 +1,39 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://pgxn.org/meta/v1/bugtracker.schema.json", + "title": "Bug Tracker", + "type": "object", + "description": "This field describes the bug tracking system for this distribution.", + "properties": { + "web": { + "type": "string", + "format": "uri", + "description": "A URI pointing to a web front-end for the bug tracker" + }, + "mailto": { + "type": "string", + "format": "email", + "description": "An email address to which bug reports can be sent" + } + }, + "anyOf": [ + { "required": ["web"] }, + { "required": ["mailto"] }, + { "required": ["web", "mailto"] } + ], + "patternProperties": { + "^[xX]_": { + "description": "Custom key" + } + }, + "additionalProperties": false, + "examples": [ + { + "web": "https://github.com/theory/pgtap/issues" + }, + { + "web": "https://github.com/theory/pgtap/issues", + "mailto": "pgxn-bugs@example.com" + } + ] +} diff --git a/schema/v1/distribution.schema.json b/schema/v1/distribution.schema.json new file mode 100644 index 0000000..58679ce --- /dev/null +++ b/schema/v1/distribution.schema.json @@ -0,0 +1,131 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://pgxn.org/meta/v1/distribution.schema.json", + "title": "Distribution", + "description": "The PGXN distribution metadata specification", + "type": "object", + "properties": { + "name": { + "$ref": "term.schema.json", + "description": "The name of the distribution. This is usually the same as the name of the “main extension” in the distribution, but may be completely unrelated to the extensions within the distribution. This value will be used in the distribution file name on PGXN.", + "examples": ["pgTAP", "vector"] + }, + "version": { + "$ref": "version.schema.json", + "description": "The version of the distribution to which the metadata structure refers. Its value must be a [SemVer](https://semver.org)." + }, + "abstract": { + "description": "A short description of the purpose of the distribution.", + "type": "string", + "minLength": 1, + "examples": [ + "Unit testing for PostgreSQL", + "Open-source vector similarity search for Postgres" + ] + }, + "maintainer": { "$ref": "maintainer.schema.json" }, + "license": { "$ref": "license.schema.json" }, + "provides": { "$ref": "provides.schema.json" }, + "meta-spec": { "$ref": "meta-spec.schema.json" }, + "description": { + "description": "A longer, more complete description of the purpose or intended use of the distribution than the one provided by the `abstract` key.", + "type": "string", + "minLength": 1, + "examples": [ + "pgTAP is a suite of database functions that make it easy to write TAP-emitting unit tests in psql scripts or xUnit-style test functions." + ] + }, + "generated_by": { + "description": "This field indicates the tool that was used to create this metadata. There are no defined semantics for this field, but it is traditional to use a string in the form “Software package version 1.23” or the maintainer’s ame, if the file was generated by hand.", + "type": "string", + "minLength": 1, + "examples": ["Module::Build::PGXN version 0.42"] + }, + "tags": { "$ref": "tags.schema.json" }, + "no_index": { "$ref": "no_index.schema.json" }, + "prereqs": { "$ref": "prereqs.schema.json" }, + "release_status": { + "description": "This field specifies the release status of this distribution. It **must** have one of the following values:\n\n* **stable**: Indicates an ordinary, “final” release that should be indexed by PGXN.\n\n* **testing**: Indicates a “beta” release that is substantially complete, but has an elevated risk of bugs and requires additional testing. The distribution should not be installed over a stable release without an explicit request or other confirmation from a user. This release status may also be used for “release candidate” versions of a distribution.\n\n* **unstable**: Indicates an “alpha” release that is under active development, but has been released for early feedback or testing and may be missing features or may have serious bugs. The distribution should not be installed over a stable release without an explicit request or other confirmation from a user.", + "enum": ["stable", "testing", "unstable"] + }, + "resources": { "$ref": "resources.schema.json" } + }, + "patternProperties": { + "^[xX]_": { + "description": "Custom key" + } + }, + "additionalProperties": false, + "required": [ + "name", + "version", + "abstract", + "maintainer", + "license", + "provides", + "meta-spec" + ], + "examples": [ + { + "name": "pgTAP", + "abstract": "Unit testing for PostgreSQL", + "description": "pgTAP is a suite of database functions that make it easy to write TAP-emitting unit tests in psql scripts or xUnit-style test functions.", + "version": "0.26.0", + "maintainer": [ + "David E. Wheeler ", + "pgTAP List " + ], + "license": { + "PostgreSQL": "https://www.postgresql.org/about/licence" + }, + "prereqs": { + "runtime": { + "requires": { + "plpgsql": 0, + "PostgreSQL": "8.0.0" + }, + "recommends": { + "PostgreSQL": "8.4.0" + } + } + }, + "provides": { + "pgtap": { + "file": "sql/pgtap.sql", + "docfile": "doc/pgtap.mmd", + "version": "0.2.4", + "abstract": "Unit testing assertions for PostgreSQL" + }, + "schematap": { + "file": "sql/schematap.sql", + "docfile": "doc/schematap.mmd", + "version": "0.2.4", + "abstract": "Schema testing assertions for PostgreSQL" + } + }, + "resources": { + "homepage": "https://pgtap.org/", + "bugtracker": { + "web": "https://github.com/theory/pgtap/issues" + }, + "repository": { + "url": "https://github.com/theory/pgtap.git", + "web": "https://github.com/theory/pgtap", + "type": "git" + } + }, + "generated_by": "David E. Wheeler", + "meta-spec": { + "version": "1.0.0", + "url": "https://pgxn.org/meta/spec.txt" + }, + "tags": [ + "testing", + "unit testing", + "tap", + "tddd", + "test driven database development" + ] + } + ] +} diff --git a/schema/v1/extension.schema.json b/schema/v1/extension.schema.json new file mode 100644 index 0000000..7967a4b --- /dev/null +++ b/schema/v1/extension.schema.json @@ -0,0 +1,49 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://pgxn.org/meta/v1/extension.schema.json", + "title": "Extension", + "description": "An Extension is provided by a distribution.", + "type": "object", + "properties": { + "file": { + "type": "string", + "description": "The value must contain a relative file path from the root of the distribution to the file containing the extension. The path must be specified with unix conventions.", + "minLength": 1 + }, + "version": { + "$ref": "version.schema.json", + "description": "This field contains a version for the extension. All extensions must have versions." + }, + "abstract": { + "type": "string", + "description": "A short String value describing the extension.", + "minLength": 1 + }, + "docfile": { + "type": "string", + "description": "The value must contain a relative file path from the root of the distribution to the file containing documentation for the extension. The path must be specified with unix conventions.", + "minLength": 1 + } + }, + "required": ["file", "version"], + "patternProperties": { + "^[xX]_": { + "description": "Custom key" + } + }, + "additionalProperties": false, + "examples": [ + { + "file": "sql/pgtap.sql", + "docfile": "doc/pgtap.md", + "version": "0.2.4", + "abstract": "Unit testing assertions for PostgreSQL" + }, + { + "file": "sql/schematap.sql", + "docfile": "doc/schematap.md", + "version": "0.2.4", + "abstract": "Schema testing assertions for PostgreSQL" + } + ] +} diff --git a/schema/v1/license.schema.json b/schema/v1/license.schema.json new file mode 100644 index 0000000..6cbfce5 --- /dev/null +++ b/schema/v1/license.schema.json @@ -0,0 +1,83 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://pgxn.org/meta/v1/license.schema.json", + "title": "License", + "description": "One or more licenses that apply to some or all of the files in the distribution. If multiple licenses are listed, the distribution documentation should be consulted to clarify the interpretation of multiple licenses.", + "oneOf": [ + { + "$ref": "#/$defs/validLicense", + "description": "A shortcut to identify a well-known license for the distribution.", + "examples": ["apache_2_0", "postgresql"] + }, + { + "type": "array", + "items": { "$ref": "#/$defs/validLicense" }, + "description": "A list of shortcuts to identify well-known licenses for the distribution.", + "minItems": 1, + "examples": [["apache_2_0", "postgresql"], ["mit"]] + }, + { + "type": "object", + "description": "Describes the distribution license or licenses. Each subkey may be any string naming a license. All values must be URIs that link to the appropriate license.", + "minProperties": 1, + "additionalProperties": { + "type": "string", + "format": "uri", + "examples": [ + { + "PostgreSQL": "https://www.postgresql.org/about/licence" + }, + { + "Perl 5": "https://dev.perl.org/licenses/", + "BSD": "https://www.opensource.org/licenses/bsd-license.html" + } + ] + } + } + ], + "examples": [ + "perl_5", + ["apache_2_0", "mozilla_1_0"], + { + "PostgreSQL": "https://www.postgresql.org/about/licence" + }, + { + "Perl 5": "https://dev.perl.org/licenses/", + "BSD": "https://www.opensource.org/licenses/bsd-license.html" + } + ], + "$defs": { + "validLicense": { + "enum": [ + "agpl_3", + "apache_1_1", + "apache_2_0", + "artistic_1", + "artistic_2", + "bsd", + "freebsd", + "gfdl_1_2", + "gfdl_1_3", + "gpl_1", + "gpl_2", + "gpl_3", + "lgpl_2_1", + "lgpl_3_0", + "mit", + "mozilla_1_0", + "mozilla_1_1", + "openssl", + "perl_5", + "postgresql", + "qpl_1_0", + "ssleay", + "sun", + "zlib", + "open_source", + "restricted", + "unrestricted", + "unknown" + ] + } + } +} diff --git a/schema/v1/maintainer.schema.json b/schema/v1/maintainer.schema.json new file mode 100644 index 0000000..8c02cf8 --- /dev/null +++ b/schema/v1/maintainer.schema.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://pgxn.org/meta/v1/maintainer.schema.json", + "title": "Maintainer", + "description": "This string or array of strings lists the person(s) to contact concerning the distribution. The preferred form of the contact string is: `contact-name `.\n\nThis field provides a general contact list independent of other structured fields provided within the resources object, such as bugtracker. The addressee(s) can be contacted for any purpose including but not limited to: (security) problems with the distribution, questions about the distribution, or bugs in the distribution.\n\nA distribution’s original author is usually the contact listed within this field. Co-maintainers, successor maintainers, or mailing lists devoted to the distribution may also be listed in addition to or instead of the original author.", + "oneOf": [ + { + "type": "string", + "minLength": 1 + }, + { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "minItems": 1 + } + ], + "examples": [ + "David E. Wheeler ", + ["David E. Wheeler ", "Josh Berkus "] + ] +} diff --git a/schema/v1/meta-spec.schema.json b/schema/v1/meta-spec.schema.json new file mode 100644 index 0000000..2f76ee9 --- /dev/null +++ b/schema/v1/meta-spec.schema.json @@ -0,0 +1,33 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://pgxn.org/meta/v1/meta-spec.schema.json", + "title": "Meta Spec", + "description": "This field indicates the Version of the PGXN Meta Spec that should be used to interpret the metadata. Consumers must check this key as soon as possible and abort further metadata processing if the meta-spec Version is not supported by the consumer.", + "type": "object", + "properties": { + "version": { + "type": "string", + "pattern": "^1[.]0[.][[:digit:]]+$", + "description": "The version of the PGXN Meta Spec against which the document was generated." + }, + "url": { + "type": "string", + "const": "https://pgxn.org/meta/spec.txt", + "description": "The URI of the metadata specification document corresponding to the given version. This is strictly for human-consumption and should not impact the interpretation of the document." + } + }, + "required": ["version"], + "patternProperties": { + "^[xX]_": { + "description": "Custom key" + } + }, + "additionalProperties": false, + "examples": [ + { "version": "1.0.0" }, + { + "version": "1.0.2", + "url": "https://pgxn.org/meta/spec.txt" + } + ] +} diff --git a/schema/v1/no_index.schema.json b/schema/v1/no_index.schema.json new file mode 100644 index 0000000..7fd4057 --- /dev/null +++ b/schema/v1/no_index.schema.json @@ -0,0 +1,46 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://pgxn.org/meta/v1/no_index.schema.json", + "title": "No Index", + "description": "This field describes any files or directories that are private to the packaging or implementation of the distribution and should be ignored by indexing or search tools.", + "type": "object", + "properties": { + "file": { + "description": "A list of relative paths to files. Paths **must be** specified with unix conventions.", + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "description": "Relative path in unix convention to a file to ignore.", + "minLength": 1 + } + }, + "directory": { + "description": "A list of relative paths to directories. Paths **must be** specified with unix conventions.", + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "description": "Relative path in unix convention to a directory to ignore.", + "minLength": 1 + } + } + }, + "anyOf": [ + { "required": ["file"] }, + { "required": ["directory"] }, + { "required": ["file", "directory"] } + ], + "patternProperties": { + "^[xX]_": { + "description": "Custom key" + } + }, + "additionalProperties": false, + "examples": [ + { + "file": ["src/file.sql"], + "directory": ["src/private"] + } + ] +} diff --git a/schema/v1/prereq_phase.schema.json b/schema/v1/prereq_phase.schema.json new file mode 100644 index 0000000..5f0904c --- /dev/null +++ b/schema/v1/prereq_phase.schema.json @@ -0,0 +1,47 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://pgxn.org/meta/v1/prereq_phase.schema.json", + "title": "Prerequisite Phase", + "description": "A Prerequisite Phase maps prerequisite relationships, such as `requires`, `recommends`, `suggests`, and `conflicts`, to their prerequisites.", + "type": "object", + "properties": { + "requires": { + "$ref": "prereq_relationship.schema.json", + "description": "These dependencies **must** be installed for proper completion of the phase." + }, + "recommends": { + "$ref": "prereq_relationship.schema.json", + "description": "Recommended dependencies are *strongly* encouraged and should be satisfied except in resource constrained environments." + }, + "suggests": { + "$ref": "prereq_relationship.schema.json", + "description": "These dependencies are optional, but are suggested for enhanced operation of the described distribution." + }, + "conflicts": { + "$ref": "prereq_relationship.schema.json", + "description": "These dependencies cannot be installed when the phase is in operation. This is a very rare situation, and the conflicts relationship should be used with great caution, or not at all." + } + }, + "patternProperties": { + "^[xX]_": { + "description": "Custom key" + } + }, + "additionalProperties": false, + "minProperties": 1, + "$comment": "Really should require at least one of the named properties; this allows for a single _x property. Good enough for now.", + "examples": [ + { + "requires": { + "PostgreSQL": "8.0.0", + "PostGIS": "1.5.0" + }, + "recommends": { + "PostgreSQL": "8.4.0" + }, + "suggests": { + "sha1": 0 + } + } + ] +} diff --git a/schema/v1/prereq_relationship.schema.json b/schema/v1/prereq_relationship.schema.json new file mode 100644 index 0000000..0277570 --- /dev/null +++ b/schema/v1/prereq_relationship.schema.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://pgxn.org/meta/v1/prereq_relationship.schema.json", + "title": "Prerequisite Relationship", + "description": "A Prerequisite Relationship lists prerequisites and their version ranges.", + "type": "object", + "minProperties": 1, + "propertyNames": { "$ref": "term.schema.json" }, + "additionalProperties": { "$ref": "version_range.schema.json" }, + "examples": [ + { + "PostgreSQL": "8.0.0", + "PostGIS": "1.5.0" + }, + { + "PostgreSQL": "8.4.0" + }, + { + "sha1": 0 + } + ] +} diff --git a/schema/v1/prereqs.schema.json b/schema/v1/prereqs.schema.json new file mode 100644 index 0000000..1da32ad --- /dev/null +++ b/schema/v1/prereqs.schema.json @@ -0,0 +1,63 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://pgxn.org/meta/v1/prereqs.schema.json", + "title": "Prerequisites", + "description": "This field describes all the prerequisites of the distribution. The keys are phases of activity, such as `configure`, `build`, `test`, and `runtime`. Values are objects in which the keys name the type of prerequisite relationship such as `requires`, `recommends`, `suggests`, or `conflicts`, and the values provide sets of prerequisite relations. The sets of relations **must** be specified as objects mapping extension names to version ranges.\n\nThe prereq spec structure divides prerequisites into *Phases* of activity in the installation process and *Relationships* that indicate how prerequisites should be resolved.\n\nNote that the `prereqs` key may not be used to specify prerequisites distributed outside PGXN or the PostgreSQL core and its contrib extensions.\n\nRequirements for regular use must be listed in the `runtime` phase. Other requirements should be listed in the earliest stage in which they are required and consumers must accumulate and satisfy requirements across phases before executing the activity. For example, `build` requirements must also be available during the `test` phase.", + "type": "object", + "properties": { + "configure": { + "$ref": "prereq_phase.schema.json", + "description": "The configure phase occurs before any dynamic configuration has been attempted. Extensions required by the configure phase **must** be available for use before the distribution building tool has been executed." + }, + "build": { + "$ref": "prereq_phase.schema.json", + "description": "The build phase is when the distribution’s source code is compiled (if necessary) and otherwise made ready for installation." + }, + "test": { + "$ref": "prereq_phase.schema.json", + "description": "The test phase is when the distribution’s automated test suite is run. Any extension needed only for testing and not for subsequent use should be listed here." + }, + "runtime": { + "$ref": "prereq_phase.schema.json", + "description": "The runtime phase refers not only to when the distribution’s contents are installed, but also to its continued use. Any extension that is a prerequisite for regular use of this distribution should be indicated here." + }, + "develop": { + "$ref": "prereq_phase.schema.json", + "description": "The develop phase’s prereqs are extensions needed to work on the distribution’s source code as its maintainer does. These tools might be needed to build a release tarball, to run maintainer-only tests, or to perform other tasks related to developing new versions of the distribution." + } + }, + "minProperties": 1, + "$comment": "Really should require at least one of the named properties; this allows for a single _x property. Good enough for now.", + "patternProperties": { + "^[xX]_": { + "description": "Custom key" + } + }, + "additionalProperties": false, + "examples": [ + { + "runtime": { + "requires": { + "PostgreSQL": "8.0.0", + "PostGIS": "1.5.0" + }, + "recommends": { + "PostgreSQL": "8.4.0" + }, + "suggests": { + "sha1": 0 + } + }, + "build": { + "requires": { + "prefix": 0 + } + }, + "test": { + "recommends": { + "pgTAP": 0 + } + } + } + ] +} diff --git a/schema/v1/provides.schema.json b/schema/v1/provides.schema.json new file mode 100644 index 0000000..6c4296f --- /dev/null +++ b/schema/v1/provides.schema.json @@ -0,0 +1,26 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://pgxn.org/meta/v1/provides.schema.json", + "title": "Provides", + "description": "This describes all extensions provided by this distribution. This information is used by PGXN to build indexes identifying in which distributions various extensions can be found.\n\nThe keys of provides are Terms that name the extensions found within the distribution.", + "type": "object", + "minProperties": 1, + "additionalProperties": { "$ref": "extension.schema.json" }, + "propertyNames": { "$ref": "term.schema.json" }, + "examples": [ + { + "pgtap": { + "file": "sql/pgtap.sql", + "docfile": "doc/pgtap.md", + "version": "0.2.4", + "abstract": "Unit testing assertions for PostgreSQL" + }, + "schematap": { + "file": "sql/schematap.sql", + "docfile": "doc/schematap.md", + "version": "0.2.4", + "abstract": "Schema testing assertions for PostgreSQL" + } + } + ] +} diff --git a/schema/v1/repository.schema.json b/schema/v1/repository.schema.json new file mode 100644 index 0000000..a31b1e8 --- /dev/null +++ b/schema/v1/repository.schema.json @@ -0,0 +1,49 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://pgxn.org/meta/v1/repository.schema.json", + "title": "Bug Tracker", + "type": "object", + "description": "This fields describes the source control repository for this distribution.\n\nBecause a URI like `https://myrepo.example.com/` is ambiguous as to type, producers should provide a `type` whenever a `url` key is given. The `type` field should be the name of the most common program used to work with the repository, e.g. git, svn, cvs, darcs, bzr or hg.", + "properties": { + "url": { + "type": "string", + "format": "uri", + "description": "A URI pointing to the repository itself." + }, + "web": { + "type": "string", + "format": "uri", + "description": "A URI pointing to a web front-end for the repository." + }, + "type": { + "type": "string", + "format": "email", + "description": "a lowercase string indicating the VCS used." + } + }, + "anyOf": [ + { "required": ["url", "type"] }, + { "required": ["web"] }, + { "required": ["web", "url", "type"] } + ], + "patternProperties": { + "^[xX]_": { + "description": "Custom key" + } + }, + "additionalProperties": false, + "examples": [ + { + "url": "https://github.com/theory/pgtap.git", + "web": "https://github.com/theory/pgtap", + "type": "git" + }, + { + "url": "https://github.com/theory/pgtap.git", + "type": "git" + }, + { + "web": "https://github.com/theory/pgtap" + } + ] +} diff --git a/schema/v1/resources.schema.json b/schema/v1/resources.schema.json new file mode 100644 index 0000000..8ec9b33 --- /dev/null +++ b/schema/v1/resources.schema.json @@ -0,0 +1,39 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://pgxn.org/meta/v1/resources.schema.json", + "title": "Source Control Repository", + "description": "An Extension is provided by a distribution.", + "type": "object", + "properties": { + "homepage": { + "type": "string", + "format": "uri", + "description": "A URI for the official home of this project on the web." + }, + "bugtracker": { "$ref": "bugtracker.schema.json" }, + "repository": { "$ref": "repository.schema.json" } + }, + "patternProperties": { + "^[xX]_": { + "description": "Custom key" + } + }, + "additionalProperties": false, + "minProperties": 1, + "$comment": "Really should require at least one of the named properties; this allows for a single _x property. Good enough for now.", + "examples": [ + { + "homepage": "https://pgxn.org/", + "bugtracker": { + "web": "https://github.com/theory/pgtap/issues", + "mailto": "pgxn-bugs@example.com" + }, + "repository": { + "url": "git://github.com/theory/pgtap.git", + "web": "https://github.com/theory/pgtap/", + "type": "git" + }, + "x_twitter": "https://twitter.com/pgtap/" + } + ] +} diff --git a/schema/v1/tags.schema.json b/schema/v1/tags.schema.json new file mode 100644 index 0000000..357d132 --- /dev/null +++ b/schema/v1/tags.schema.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://pgxn.org/meta/v1/tags.schema.json", + "title": "Tags", + "description": "A list of keywords that describe the distribution.", + "type": "array", + "minItems": 1, + "items": { + "title": "Tag", + "description": "A Tag is a subtype of String that must be fewer than 256 characters long contain no slash (/), backslash (\\), or control characters.", + "type": "string", + "minLength": 2, + "maxLength": 255, + "pattern": "^[^/\\\\\\p{Cntrl}]{2,}$" + }, + "examples": [ + ["jsonschema", "validation", "json", "schema", "constraint"], + ["testing", "unit testing", "tap", "tddd"] + ] +} diff --git a/schema/v1/term.schema.json b/schema/v1/term.schema.json new file mode 100644 index 0000000..2d480b3 --- /dev/null +++ b/schema/v1/term.schema.json @@ -0,0 +1,10 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://pgxn.org/meta/v1/term.schema.json", + "title": "Term", + "description": "A Term is a subtype of string that must be at least two characters long contain no slash (/), backslash (\\), space, or control characters.", + "type": "string", + "minLength": 2, + "pattern": "^[^/\\\\\\p{Space}\\p{Cntrl}]{2,}$", + "examples": ["hi", "go-on", "pg_vectorize"] +} diff --git a/schema/v1/version.schema.json b/schema/v1/version.schema.json new file mode 100644 index 0000000..e715155 --- /dev/null +++ b/schema/v1/version.schema.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://pgxn.org/meta/v1/version.schema.json", + "title": "Version", + "description": "A Version is a subtype of string containing a value that describes the version number of extensions or distributions.\n\nVersion numbers must be treated as strings, and adhere to the [Semantic Versioning 2.0.0 Specification](https://semver.org). Semantic versions take a dotted-integer format consisting of three positive integers separated by full stop characters (i.e. “dots”, “periods” or “decimal points”). A “pre-release version” by appending a dash followed by an arbitrary ASCII string immediately following the patch version. Please see [the specification](https://semver.org) for all details on the format.", + "type": "string", + "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$", + "$comment": "https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string", + "examples": [ + "1.3.6", + "10.20.30", + "1.0.0-alpha", + "1.1.2+meta", + "1.0.0-alpha-a.b-c-something-long+build.1-aef.1-its-okay" + ] +} diff --git a/schema/v1/version_range.schema.json b/schema/v1/version_range.schema.json new file mode 100644 index 0000000..bc054e7 --- /dev/null +++ b/schema/v1/version_range.schema.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://pgxn.org/meta/v1/version_range.schema.json", + "title": "Version Range", + "description": "A Version Range describes a range of Versions that may be present or installed to fulfill prerequisites.\n\nThe simplest format for a Version Range is just the version number itself, e.g. `2.4.0`. This means that **at least** version 2.4.0 must be present. To indicate that **any** version of a prerequisite is okay, even if the prerequisite doesn’t define a version at all, use the version `0`.\n\nAlternatively, a version range **may** use the operators `<` (less than), `<=` (less than or equal), `>` (greater than), `>=` (greater than or equal), `==` (equal), and `!=` (not equal). For example, the specification `< 2.0.0` means that any version of the prerequisite less than 2.0.0 is suitable.\n\nFor more complicated situations, version specifications **may** be AND-ed together using commas. The specification `>= 1.2.0, != 1.5.0, < 2.0.0` indicates a version that must be **at least** 1.2.0, **less than** 2.0.0, and **not equal to** 1.5.0.", + "oneOf": [ + { + "type": "string", + "pattern": "^(([=!]=|[<>]=?)\\s*)?((0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?|0)(,\\s*((([=!]=|[<>]=?)\\s*)?)(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)*$", + "$comment": "https://regex101.com/r/Uy7XWK/1" + }, + { + "const": 0 + } + ], + "examples": [ + 0, + "0", + "1.3.6", + "==0.0.4", + "<= 1.1.2+meta", + ">= 2.0.0, 1.5.6", + ">= 1.2.0, != 1.5.0, < 2.0.0" + ] +} diff --git a/spec.md b/spec.md index 62918ca..7854e5e 100644 --- a/spec.md +++ b/spec.md @@ -6,7 +6,7 @@ PGXN Meta Spec - The PGXN distribution metadata specification Version ======= -1.0.1 +1.0.2 Synopsis ======== @@ -16,10 +16,10 @@ Synopsis "name": "pgTAP", "abstract": "Unit testing for PostgreSQL", "description": "pgTAP is a suite of database functions that make it easy to write TAP-emitting unit tests in psql scripts or xUnit-style test functions.", - "version": "0.2.5", + "version": "0.26.0", "maintainer": [ "David E. Wheeler ", - "pgTAP List " + "pgTAP List " ], "license": { "PostgreSQL": "https://www.postgresql.org/about/licence" @@ -80,9 +80,9 @@ Description This document describes version 1.0.0 of the PGXN distribution metadata specification, also known as the "PGXN Meta Spec." It is formatted using the -[Github Flavored Markdown] variant of [Markdown], and the canonical copy may -always be found at [master.pgxn.org/meta/spec.txt]. A generated HTML-formatted -copy found at [pgxn.org/spec/] may also be considered canonical. +[MultiMarkdown] variant of [Markdown], and the canonical copy may always be +found at [master.pgxn.org/meta/spec.txt]. A generated HTML-formatted copy +found at [pgxn.org/spec/] may also be considered canonical. This document is stable. Any revisions to this specification for typo corrections and prose clarifications may be issued as "PGXN Meta Spec @@ -182,7 +182,7 @@ Tag --- A *Tag* is a subtype of [String](#String) that **must** be fewer than 256 -characters long contain no slash (`/`), backslash (`\`), control, or space +characters long contain no slash (`/`), backslash (`\`), or control characters. URI @@ -214,7 +214,7 @@ describes valid keys within the [Map](#Map). Any keys not described in this specification document (whether top-level or within compound data structures described herein) are considered *custom keys* -and **must** begin with an "x" or "X" and be followed by an underscore; i.e.l, +and **must** begin with an "x" or "X" and be followed by an underscore; i.e., they must match the pattern: `/\Ax_/i`. If a custom key refers to a compound data structure, subkeys within it do not need an "x_" or "X_" prefix. @@ -329,7 +329,7 @@ The [List](#List) type may be used as a shortcut to identify one or more well-known licenses. The following list of [License Strings](#License.String) are valid in the [List](#List) representation: - string | description + string | description --------------|------------------------------------------------------ agpl_3 | GNU Affero General Public License, Version 3 apache_1_1 | Apache Software License, Version 1.1 @@ -359,7 +359,7 @@ are valid in the [List](#List) representation: The following [License Strings](#License.String) are also valid and indicate other licensing not described above: - string | description + string | description --------------|------------------------------------------------------ open_source | Other Open Source Initiative (OSI) approved license restricted | Requires special permission from copyright holder @@ -518,7 +518,7 @@ Example: ``` json "no_index": { "file": [ "src/file.sql" ], - "directory": [ "src/private" ], + "directory": [ "src/private" ] } ``` @@ -574,7 +574,7 @@ distribution. The keys are phases of activity, such as `configure`, `build`, `test`, or `runtime`. Values are [Maps](#Map) in which the keys name the type of prerequisite relationship such as `requires`, `recommends`, `suggests`, or `conflicts`, and the values provide sets of prerequisite relations. The sets -of relations **must** be specified as a [Map](#Map) of extension names to +of relations **must** be specified as [Maps](#Map) of extension names to [Version Ranges](#Version.Ranges). The full definition for this field is given in the [Prereq Spec](#Prereq.Spec) @@ -865,7 +865,7 @@ The PGXN Meta Spec borrows heavily from the [CPAN Meta Spec], which was originally written by Ken Williams in 2003 and has since been updated by Randy Sims, David Golden, and Ricardo Signes. Ported to PGXN by David E. Wheeler. - [Github Flavored Markdown]: https://github.github.com/gfm/ + [MultiMarkdown]: https://fletcherpenney.net/multimarkdown/ [Markdown]: https://daringfireball.net/projects/markdown/ [master.pgxn.org/meta/spec.txt]: https://master.pgxn.org/meta/spec.txt [pgxn.org/spec/]: https://pgxn.org/spec/ diff --git a/tests/test.rs b/tests/test.rs new file mode 100644 index 0000000..36d32d1 --- /dev/null +++ b/tests/test.rs @@ -0,0 +1,59 @@ +use std::{ + collections::HashMap, + error::Error, + fs::{self, File}, +}; + +use boon::{Compiler, Schemas}; +use serde_json::Value; + +#[test] +fn test_schema_v1() -> Result<(), Box> { + let mut compiler = Compiler::new(); + let mut loaded: HashMap> = HashMap::new(); + + let paths = fs::read_dir("./schema/v1")?; + for path in paths { + let path = path?.path(); + let bn = path.file_name().unwrap().to_str().unwrap(); + if bn.ends_with(".schema.json") { + let schema: Value = serde_json::from_reader(File::open(path.clone())?)?; + if let Value::String(s) = &schema["$id"] { + // Make sure that the ID is correct. + assert_eq!(format!("https://pgxn.org/meta/v1/{bn}"), *s); + + // Add the schema to the compiler. + compiler.add_resource(s, schema.to_owned())?; + + // Grab the examples, if any, to test later. + if let Value::Array(a) = &schema["examples"] { + loaded.insert(s.clone(), a.to_owned()); + } else { + loaded.insert(s.clone(), Vec::new()); + } + } else { + panic!("Unable to find ID in {}", path.display()); + } + } + } + + // Make sure we found schemas. + assert!(!loaded.is_empty()); + + // Make sure each schema we loaded is valid. + let mut schemas = Schemas::new(); + for (id, examples) in loaded { + let index = compiler.compile(id.as_str(), &mut schemas)?; + println!("{} ok", id); + + // Test the schema's examples. + for (i, example) in examples.iter().enumerate() { + if let Err(e) = schemas.validate(example, index) { + panic!("Example {i} failed: {e}"); + } + println!(" Example {i} ok"); + } + } + + Ok(()) +}