Metadata-Version: 2.4
Name: commonmeta-schema
Version: 1.0rc13
Summary: Commonmeta JSON Schemas and conformance fixtures
Project-URL: Homepage, https://github.com/front-matter/commonmeta-schema
Project-URL: Repository, https://github.com/front-matter/commonmeta-schema
Project-URL: Issues, https://github.com/front-matter/commonmeta-schema/issues
Author: Front Matter
License-Expression: MIT
License-File: LICENSE
Keywords: commonmeta,fixtures,json-schema,metadata,schema
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Text Processing
Requires-Python: >=3.9
Description-Content-Type: text/markdown

# commonmeta-schema

Language-neutral JSON Schema definitions and conformance fixtures for
[Commonmeta](https://commonmeta.org), the scholarly metadata interchange format.

This repository is the shared source of truth consumed by the following Commonmeta
implementations:

- [commonmeta-rs](https://github.com/front-matter/commonmeta-rs) (Rust)
- [commonmeta-py](https://github.com/front-matter/commonmeta-py) (Python)

Keeping the schema and the golden fixtures in one place lets every
implementation validate against the same contract and run the same
cross-format conformance tests.

## Layout

```
schemas/
  commonmeta_v1.0.json         # the Commonmeta JSON Schema (current)
  commonmeta_v1.0rc*.json      # earlier release candidates, retained but not exported
fixtures/
  commonmeta/                  # canonical Commonmeta records (round-trip + expected output)
  <format>/                    # input fixtures in a given source format
  <format>_out/                # expected writer output (Commonmeta -> format)
  <format>_commonmeta/         # expected reader output for non-JSON inputs
```

## Schema versions

Schemas are versioned by filename. The current version is `commonmeta_v1.0.json`,
and the `schema_version` field of a Commonmeta record is the URL
`https://commonmeta.org/commonmeta_v1.0.json`.

Earlier release candidates (`commonmeta_v1.0rc1`…`rc7`) are kept in `schemas/`
for reference, but the packages export only `v1.0` — new versions are added
alongside existing files rather than replacing them, so implementations can pin
a version.

## Fixture conventions

The conformance harness in each implementation follows these naming rules
(a missing pair is skipped, so partial coverage is fine):

| Test kind            | Input                                  | Expected                                    |
|----------------------|----------------------------------------|---------------------------------------------|
| Round-trip           | `fixtures/commonmeta/<name>.json`      | itself (re-serialized, semantically equal)  |
| Reader (JSON input)  | `fixtures/<format>/<name>.json`        | `fixtures/commonmeta/<name>.json`           |
| Reader (text input)  | `fixtures/<format>/<name>.<ext>`       | `fixtures/<format>_commonmeta/<name>.json`  |
| Writer               | `fixtures/commonmeta/<name>.json`      | `fixtures/<format>_out/<name>.<ext>`        |

Formats currently covered: `crossref`, `crossref_xml`, `datacite`,
`datacite_xml`, `schemaorg`, `csl`, `bibtex`, `cff`, `ris`, `jsonfeed`,
`inveniordm`, `codemeta`, `orcid`, `orcid_xml`.

Most fixtures are `work` entities; `orcid` and `orcid_xml` cover `person`
instead. Both read a full ORCID record — with employments and educations as
affiliations — from two different sources:

- `orcid/<id>.json` is the ORCID REST API `/record` response (JSON). Because it
  carries the full person, its commonmeta output also has `description` and
  `urls`. → `commonmeta/<id>.json`
- `orcid_xml/<id>.xml` is the ORCID Public Data File record (XML), a summary
  that omits biography and researcher URLs. → `orcid_xml_commonmeta/<id>.json`

Both fixtures trim the record's works list to the 10 most recent (the readers
convert person identity and affiliations, not works).

### Semantic comparison

Fixtures are compared as parsed JSON trees, not as strings, using an
omitempty-aware and numeric-aware diff: key order and whitespace are
irrelevant, an absent field equals an empty/zero/null value, and `52`
equals `52.0`. This keeps hand-authored fixtures robust across
implementations.

## Using this repository

Each implementation vendors (copies) the schema and fixtures it needs from
here. Update the canonical files in this repository first, then sync them
into `commonmeta-rs` and `commonmeta-py`.

## Packaging and publishing

This repository can be published as both:

- a Python package on PyPI (`commonmeta-schema`)
- a Rust crate on crates.io (`commonmeta-schema`)

Publishing is done explicitly via CLI (no automated release pipeline in this repo).

The packages are release candidates on the way to 1.0; both ship the `v1.0`
schema:

- PyPI uses PEP 440 form: `1.0rc11`
- crates.io uses SemVer form: `1.0.0-rc11`

`scripts/sync_versions.py` is the single entry point for preparing a release. It
does three things, in this order:

1. Rewrites `schemas/commonmeta_v1.0.json` from the newest `commonmeta_v1.0rc*.json`,
   resetting `$id`, `title`, and the `schema_version` const to the stable v1.0 URL.
2. Mirrors the canonical `schemas/` and `fixtures/` into `rust/schemas/` and
   `rust/fixtures/` (the crate can only package files under `rust/`).
3. Syncs `rust/Cargo.toml`'s version with `pyproject.toml`'s.

```bash
python scripts/sync_versions.py
```

The ordering matters and is why the mirror lives in the script rather than in a
separate `rsync` step: mirroring before step 1 ships a crate whose
`commonmeta_v1.0.json` still carries the previous rc's `schema_version` const,
which then rejects the crate's own v1.0-stamped fixtures.

To verify sync in CI/local checks without changing files:

```bash
python scripts/sync_versions.py --check
```

Recommended pre-release checks:

```bash
python scripts/sync_versions.py --check
uv build
cargo test --manifest-path rust/Cargo.toml
cargo package --manifest-path rust/Cargo.toml
```

### Python (PyPI) via `uv publish`

1. Ensure you are authenticated for PyPI (recommended: trusted publisher or API token).
2. Build distribution artifacts:

  ```bash
  uv build
  ```

3. Optionally validate the build metadata:

  ```bash
  uvx twine check dist/*
  ```

4. Publish to PyPI:

  ```bash
  uv publish
  ```

### Rust (crates.io) via `cargo publish`

1. Log in once with a crates.io token:

  ```bash
  cargo login
  ```

2. Validate package contents:

  ```bash
  cargo package --manifest-path rust/Cargo.toml --allow-dirty
  ```

3. Publish:

  ```bash
  cargo publish --manifest-path rust/Cargo.toml
  ```

### Recommended release order

1. Publish `1.0rc11` to PyPI with `uv publish`.
2. Publish `1.0.0-rc11` to crates.io with `cargo publish`.
3. Create a git tag `v1.0rc11` after both uploads succeed.

### Copy-paste release run

Run this from the repository root:

```bash
set -euo pipefail

# 1) Regenerate the v1.0 alias, mirror assets into rust/, sync the crate version
python scripts/sync_versions.py

# 2) Fail the release if anything is still out of sync
python scripts/sync_versions.py --check

# 3) Validate both package builds
uv build
cargo test --manifest-path rust/Cargo.toml
cargo package --manifest-path rust/Cargo.toml

# 4) Publish Python package
uv publish

# 5) Publish Rust crate
cargo publish --manifest-path rust/Cargo.toml

# 6) Tag release (replace with the release version)
git tag v1.0rc11
git push origin v1.0rc11
```

## License

[MIT](LICENSE) © 2026 Front Matter
