{"id":31534074,"url":"https://github.com/gwyli/amati","last_synced_at":"2026-04-26T15:00:59.997Z","repository":{"id":265725849,"uuid":"896449948","full_name":"gwyli/amati","owner":"gwyli","description":"Validates that a file conforms to the Open API Specification","archived":false,"fork":false,"pushed_at":"2026-04-19T17:26:47.000Z","size":2036,"stargazers_count":2,"open_issues_count":5,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-04-19T19:23:38.225Z","etag":null,"topics":["oas","oas3","openapi-specification","parsing","python","python-3","validation"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gwyli.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-11-30T11:53:31.000Z","updated_at":"2026-04-19T17:26:50.000Z","dependencies_parsed_at":"2025-04-17T17:20:59.374Z","dependency_job_id":"f027d3a0-01be-476e-ade9-fe83f4cbd2d9","html_url":"https://github.com/gwyli/amati","commit_stats":null,"previous_names":["ben-alexander/spec-validator","ben-alexander/amati","gwyli/amati"],"tags_count":74,"template":false,"template_full_name":null,"purl":"pkg:github/gwyli/amati","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gwyli%2Famati","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gwyli%2Famati/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gwyli%2Famati/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gwyli%2Famati/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gwyli","download_url":"https://codeload.github.com/gwyli/amati/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gwyli%2Famati/sbom","scorecard":{"id":1238749,"data":{"date":"2025-10-12T16:51:58Z","repo":{"name":"github.com/gwyli/amati","commit":"0fa772975be12766ace8d831793501f028f68a97"},"scorecard":{"version":"v5.3.0","commit":"c22063e786c11f9dd714d777a687ff7c4599b600"},"score":7.9,"checks":[{"name":"Maintained","score":10,"reason":"30 commit(s) and 2 issue activity found in the last 90 days -- score normalized to 10","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#maintained"}},{"name":"Code-Review","score":1,"reason":"Found 2/19 approved changesets -- score normalized to 1","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#code-review"}},{"name":"Security-Policy","score":10,"reason":"security policy file detected","details":["Info: security policy file detected: SECURITY.md:1","Info: Found linked content: SECURITY.md:1","Info: Found disclosure, vulnerability, and/or timelines in security policy: SECURITY.md:1","Info: Found text in security policy: SECURITY.md:1"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#security-policy"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#binary-artifacts"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#dangerous-workflow"}},{"name":"Dependency-Update-Tool","score":10,"reason":"update tool detected","details":["Info: detected update tool: Dependabot: .github/dependabot.yml:1"],"documentation":{"short":"Determines if the project uses a dependency update tool.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#dependency-update-tool"}},{"name":"Pinned-Dependencies","score":10,"reason":"all dependencies are pinned","details":["Info:  17 out of  17 GitHub-owned GitHubAction dependencies pinned","Info:  22 out of  22 third-party GitHubAction dependencies pinned","Info:   1 out of   1 containerImage dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#pinned-dependencies"}},{"name":"Token-Permissions","score":10,"reason":"GitHub workflow tokens follow principle of least privilege","details":["Warn: jobLevel 'contents' permission set to 'write': .github/workflows/checks.yaml:15","Info: jobLevel 'actions' permission set to 'read': .github/workflows/checks.yaml:16","Info: jobLevel 'packages' permission set to 'read': .github/workflows/codeql.yml:37","Info: jobLevel 'actions' permission set to 'read': .github/workflows/codeql.yml:40","Info: jobLevel 'contents' permission set to 'read': .github/workflows/codeql.yml:41","Info: jobLevel 'contents' permission set to 'read': .github/workflows/publish.yaml:17","Info: jobLevel 'checks' permission set to 'read': .github/workflows/scorecards.yml:39","Info: jobLevel 'contents' permission set to 'read': .github/workflows/scorecards.yml:33","Info: jobLevel 'actions' permission set to 'read': .github/workflows/scorecards.yml:34","Info: jobLevel 'issues' permission set to 'read': .github/workflows/scorecards.yml:36","Info: jobLevel 'pull-requests' permission set to 'read': .github/workflows/scorecards.yml:37","Info: topLevel 'contents' permission set to 'read': .github/workflows/checks.yaml:8","Info: topLevel 'contents' permission set to 'read': .github/workflows/codeql.yml:21","Info: topLevel 'contents' permission set to 'read': .github/workflows/coverage.yaml:11","Info: topLevel 'contents' permission set to 'read': .github/workflows/data-refresh.yaml:10","Info: topLevel 'contents' permission set to 'read': .github/workflows/dependency-review.yml:13","Info: topLevel 'contents' permission set to 'read': .github/workflows/publish.yaml:9","Info: topLevel permissions set to 'read-all': .github/workflows/scorecards.yml:22","Info: topLevel 'contents' permission set to 'read': .github/workflows/tag-and-create-release.yaml:20"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#token-permissions"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#vulnerabilities"}},{"name":"CII-Best-Practices","score":2,"reason":"badge detected: InProgress","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#cii-best-practices"}},{"name":"SAST","score":10,"reason":"SAST tool is run on all commits","details":["Info: SAST configuration detected: CodeQL","Info: all commits (22) are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#sast"}},{"name":"Packaging","score":10,"reason":"packaging workflow detected","details":["Info: Project packages its releases by way of GitHub Actions.: .github/workflows/publish.yaml:12"],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#packaging"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#signed-releases"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#fuzzing"}},{"name":"Branch-Protection","score":4,"reason":"branch protection is not maximal on development and all release branches","details":["Info: 'allow deletion' disabled on branch 'main'","Info: 'force pushes' disabled on branch 'main'","Warn: 'branch protection settings apply to administrators' is disabled on branch 'main'","Info: 'stale review dismissal' is required to merge on branch 'main'","Warn: branch 'main' does not require approvers","Warn: codeowners review is not required on branch 'main'","Warn: 'last push approval' is disabled on branch 'main'","Info: 'up-to-date branches' is required to merge on branch 'main'","Info: status check found to merge onto on branch 'main'","Info: PRs are required in order to make changes on branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#branch-protection"}},{"name":"Contributors","score":0,"reason":"project has 0 contributing companies or organizations -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project has a set of contributors from multiple organizations (e.g., companies).","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#contributors"}},{"name":"CI-Tests","score":10,"reason":"22 out of 22 merged PRs checked by a CI test -- score normalized to 10","details":null,"documentation":{"short":"Determines if the project runs tests before pull requests are merged.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#ci-tests"}}]},"last_synced_at":"2025-10-13T02:26:17.088Z","repository_id":265725849,"created_at":"2025-10-13T02:26:17.098Z","updated_at":"2025-10-13T02:26:17.098Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32301330,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-26T09:34:17.070Z","status":"ssl_error","status_checked_at":"2026-04-26T09:34:00.993Z","response_time":129,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["oas","oas3","openapi-specification","parsing","python","python-3","validation"],"created_at":"2025-10-04T05:16:59.414Z","updated_at":"2026-04-26T15:00:59.978Z","avatar_url":"https://github.com/gwyli.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# amati\n\namati is designed to validate that a file conforms to the [OpenAPI Specification v3.x](https://spec.openapis.org/) (OAS).\n\n## Name\n\n\"amati\" means to observe in Malay, especially with attention to detail. It's also one of the plurals of beloved or favourite in Italian.\n\n## Usage\n\n```sh\npython amati/amati.py validate --help\nusage: amati validate [-h] -s SPEC [--consistency-check] [--local] [--html-report]\n\noptions:\n  -h, --help            show this help message and exit\n  -s, --spec SPEC       The specification to be validated\n  --consistency-check   Runs a consistency check between the input specification and the parsed specification\n  --local               Store errors local to the caller in .amati/\u003cfile-name\u003e.errors.json\n  --html-report         Creates an HTML report of the errors, called \u003cfile-name\u003e.errors.html, alongside \u003cfilename\u003e.errors.json\n```\n\n### Docker\n\nA Dockerfile is available on [DockerHub](https://hub.docker.com/r/benale/amati/tags) or `docker pull benale/amati:alpha`.\n\nWhilst an alpha build, only the image tagged `alpha` will be maintained. If there are breaking API changes these will be detailed in releases. Releases can be separately watched using the custom option when watching this repository.\n\nTo run against a specific specification the location of the specification needs to be mounted in the container.\n\n```sh\ndocker run -v \"\u003cpath-to-specification\u003e:/\u003cmount-name\u003e benale/amati:alpha validate --spec \u003cpath-to-spec\u003e \u003coptions\u003e\n```\n\ne.g. where you have a specification located in `/Users/myuser/myrepo/myspec.yaml` and create a mount `/data`:\n\n```sh\ndocker run -v /Users/myuser/myrepo:/data benale/amati:alpha validate --spec /data/myspec.yaml --html-report\n```\n\n### PyPI\n\namati is [available on PyPI](https://pypi.org/project/amati/), to run everything:\n\n```py\n\u003e\u003e\u003e from amati import amati\n\u003e\u003e\u003e amati.run('tests/data/openapi.yaml', consistency_check=True, local=True, html_report=True)\nTrue\n```\n\n## Architecture\n\namati uses [Pydantic](https://docs.pydantic.dev/latest/), especially the validation, and [typing](https://docs.python.org/3/library/typing.html) to construct the entire OAS as a single data type. Passing a dictionary to the top-level data type runs all the validation in the Pydantic models constructing a single set of inherited classes and datatypes that validate that the API specification is accurate. To the extent that Pydantic is functional, amati has a [functional core and an imperative shell](https://www.destroyallsoftware.com/screencasts/catalog/functional-core-imperative-shell).\n\nWhere the specification conforms, but relies on implementation-defined behavior (e.g. [data type formats](https://spec.openapis.org/oas/v3.1.1.html#data-type-format)), a warning will be raised.\n\n## Contributing\n\n### Prerequisites\n\n* The latest version of [uv](https://docs.astral.sh/uv/)\n* [git 2.49+](https://git-scm.com/downloads/linux)\n* [Docker](https://docs.docker.com/engine/install/)\n\n### Starting\n\nThe project uses a [`pyproject.toml` file](https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#writing-pyproject-toml) to determine what to build.\n\nTo get started run:\n\n```sh\nsh bin/startup.sh\n```\n\n### Testing and formatting\n\nThis project uses:\n\n* [Pytest](https://docs.pytest.org/en/stable/) as a testing framework\n* [Pyright](https://microsoft.github.io/pyright/#/) on strict mode for type checking\n* [Ruff](https://docs.astral.sh/ruff/) as a linter and formatter\n* [Hypothesis](https://hypothesis.readthedocs.io/en/latest/index.html) for test data generation\n* [Coverage](https://coverage.readthedocs.io/en/7.6.8/) on both the tests and code for test coverage\n* [Shellcheck](https://github.com/koalaman/shellcheck/wiki) for as SAST for shell scripts\n* [deptry](https://deptry.com/) to check for missing or unused dependencies\n\nIt's expected that there are no errors and 100% of the code is reached and executed. The strategy for test coverage is based on parsing test specifications and not unit tests.\namati runs tests on the external specifications, detailed in `tests/data/.amati.tests.yaml`. To be able to run these tests the GitHub repos containing the specifications need to be available locally. Specific revisions of the repos can be downloaded by running the following, which will clone the repos into `.amati/amati-tests-specs/\u003crepo-name\u003e`.\n\n```sh\npython scripts/setup_test_specs.py\n```\n\nIf there are some issues with the specification a JSON file detailing those should be placed into `tests/data/` and the name of that file noted in `tests/data/.amati.tests.yaml` for the test suite to pick it up and check that the errors are expected. Any specifications that close the coverage gap are gratefully received.\n\nTo run everything, from linting, type checking to downloading test specs and building and testing the Docker image run:\n\n```sh\nsh bin/checks.sh\n```\n\n### Docker\n\nA `Dockerfile` is provided, to build:\n\n```sh\ndocker build -t amati -f Dockerfile .\n```\n\nto run against a specific specification the location of the specification needs to be mounted in the container.\n\n```sh\ndocker run -v \"\u003cpath-to-specification\u003e:/\u003cmount-name\u003e amati validate -s \u003cpath-to-spec\u003e \u003coptions\u003e\n```\n\nThis can be tested against a provided specification, from the root directory\n\n```sh\ndocker run --detach -v \"$(pwd):/data\" amati validate  -s \u003cpath-to-spec\u003e \u003coptions\u003e\n```\n\n\n### Data\n\nThere are some scripts to create the data needed by the project, for example, all the registered TLDs. To refresh the data, run:\n\n```py\npython amati/amati.py refresh\n```\n\n[![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/gwyli/amati/badge)](https://scorecard.dev/viewer/?uri=github.com/gwyli/amati)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgwyli%2Famati","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgwyli%2Famati","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgwyli%2Famati/lists"}