Running rs-metadata#
How to run the validator in CI, locally, from a pre-commit hook, from a CI system other than GitHub Actions, and inside your editor.
The GitHub Action and the command-line tool share one engine, so a result you get locally is exactly the result CI will get.
GitHub Actions#
The common case needs no configuration:
name: Validate software metadata
on: [push, pull_request]
jobs:
metadata:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: LUMC-DCC/rs-metadata@v1
You get inline annotations on the pull request diff, a job summary, and a
rs-metadata-report.json build artifact.
Inputs#
Every input is an escape hatch. An ordinary repository needs none of them.
Input |
Default |
Purpose |
|---|---|---|
|
|
Path to the anchor record, if it is not in the repository root. Shorthand for |
|
|
Paths to metadata files kept outside the root, one |
|
|
Directory to validate |
|
|
Lowest severity that fails the job: |
|
|
Where the JSON report is written |
|
|
Upload the report as a build artifact |
|
|
Name of that artifact |
|
|
Python used to run the validator |
Outputs#
Output |
Value |
|---|---|
|
|
|
Finding counts |
|
Path to the JSON report |
Common variations#
Treat recommendations as blocking. Reasonable once your repositories are already clean; not a good first move.
- uses: LUMC-DCC/rs-metadata@v1
with:
fail-on: warning
Metadata kept outside the root.
- uses: LUMC-DCC/rs-metadata@v1
with:
codemeta: metadata/codemeta.json
One record, packaging metadata elsewhere. When codemeta.json describes
the repository as a whole but the packaging metadata sits in a subdirectory,
name the files rather than moving them:
- uses: LUMC-DCC/rs-metadata@v1
with:
sources: |
pyproject=backend/pyproject.toml
package-json=frontend/package.json
Ids are the ones the report lists under Metadata sources — codemeta,
cff, github, zenodo, biotools, pyproject, package-json,
r-description, cargo, julia-project and dockerfile. A
path given here is asserted to exist: if the file is not there, that is an
error rather than a silent skip, so a typo cannot quietly validate nothing.
A monorepo with several packages, each with its own codemeta.json. Run
the action once per package with a distinct artifact name:
jobs:
metadata:
runs-on: ubuntu-latest
strategy:
matrix:
package: [tool-a, tool-b]
steps:
- uses: actions/checkout@v4
- uses: LUMC-DCC/rs-metadata@v1
with:
working-directory: packages/${{ matrix.package }}
artifact-name: rs-metadata-report-${{ matrix.package }}
Artifact names must be unique within a workflow run. In a matrix, either give each job a distinct
artifact-nameas above, or setupload-report: false.
Report only, never fail. Useful while rolling the standard out across existing repositories.
- uses: LUMC-DCC/rs-metadata@v1
with:
fail-on: never
Act on the outcome in a later step.
- uses: LUMC-DCC/rs-metadata@v1
id: metadata
with:
fail-on: never
- if: steps.metadata.outputs.status != 'passed'
run: echo "::notice::Metadata needs attention before the next release."
Command line#
pip install rs-metadata
Command |
What it does |
|---|---|
|
Create any missing metadata files and CI workflow |
|
Validate the current directory |
|
Validate another repository |
|
Also show informational findings |
|
Treat warnings as failures |
|
Print the machine-readable report |
|
Also write the report to a file |
|
Explain one diagnostic code |
|
List every diagnostic code |
|
Tool, profile and CodeMeta versions |
Other flags: --source ID=PATH (repeatable) to name a metadata file kept
outside the repository root, --codemeta PATH as shorthand for
--source codemeta=PATH, --fail-on {error,warning,never}, and
--color {auto,always,never}. Color also honors the NO_COLOR and
FORCE_COLOR environment variables.
rs-metadata validate --source pyproject=backend/pyproject.toml
Exit status#
Code |
Meaning |
|---|---|
|
Passed, possibly with warnings |
|
Findings at or above the |
|
Usage error — bad arguments, or the path is not a directory |
Other CI systems#
Only the annotation renderer is GitHub-specific. Elsewhere, install the package and run the CLI.
GitLab CI
validate-metadata:
image: python:3.12
script:
- pip install rs-metadata
- rs-metadata validate --report rs-metadata-report.json
artifacts:
when: always
paths:
- rs-metadata-report.json
Jenkins, Azure Pipelines, a cron job, anything else
pip install rs-metadata
rs-metadata validate --format json --report rs-metadata-report.json
The JSON report is a stable interface meant to be read outside CI.
Pre-commit#
To catch problems before they reach CI:
# .pre-commit-config.yaml
repos:
- repo: local
hooks:
- id: rs-metadata
name: Validate software metadata
entry: rs-metadata validate
language: python
additional_dependencies: ["rs-metadata"]
pass_filenames: false
files: '^(codemeta\.json|CITATION\.cff|\.zenodo\.json|biotools\.json|pyproject\.toml|package\.json|DESCRIPTION|Cargo\.toml|Project\.toml|Dockerfile)$'
pass_filenames: false matters: the validator takes a repository, not a list
of files. The files pattern only decides when the hook runs.
Editor setup#
Both schemas are published by this documentation site, at the URL each one’s
own $id names:
Schema |
URL |
|---|---|
Profile |
|
Report |
|
The path carries the release version, so a $ref written against one keeps
resolving to the schema it was written against. There is no floating “latest”
URL, deliberately: a schema that changes under a stable URL breaks consumers
silently. A test asserts each $id matches where the site actually serves it.
Point your editor at the profile schema for completion, inline errors and
hover documentation while writing codemeta.json.
VS Code — .vscode/settings.json:
{
"json.schemas": [
{
"fileMatch": ["codemeta.json"],
"url": "https://lumc-dcc.github.io/rs-metadata/schema/1.0.0/codemeta-lumc.schema.json"
}
]
}
Any editor with a JSON language server — add $schema to the file itself:
{
"$schema": "https://lumc-dcc.github.io/rs-metadata/schema/1.0.0/codemeta-lumc.schema.json",
"@context": ["https://w3id.org/codemeta/3.1"]
}
$schema is not a CodeMeta property, but it describes the file rather than
the software, so rs-metadata accepts it without complaint and JSON-LD ignores
it.
The schema gives structure and completion. Run the validator for check digits, SPDX identifiers and cross-file consistency.
Reproducible output#
Set SOURCE_DATE_EPOCH to freeze the report timestamp, making two runs
byte-for-byte identical:
SOURCE_DATE_EPOCH=1780000000 rs-metadata validate --format json
Useful for diffing reports across commits.
Comparing against GitHub’s own metadata#
A repository’s description, topics, homepage and detected license live on GitHub rather than in any file, and they drift like any other copy — more easily, in fact, because they are edited in a web form rather than committed alongside the code.
Inside GitHub Actions this needs no configuration. The runner already writes
the event payload to disk and points GITHUB_EVENT_PATH at it, and that
payload carries the repository object, so rs-metadata reads it from there. No
network call, no token, no permissions to grant.
Anywhere else, fetch it once and hand it over:
gh api repos/OWNER/REPO > github-repo.json
rs-metadata picks up a github-repo.json in the repository root
automatically, or take it from elsewhere:
rs-metadata validate . --source github=/tmp/github-repo.json
The validator still makes no network call of its own — you made it, which keeps the run reproducible and lets it work offline.
Nothing is read from a fork. A fork has its own URL, and a description and
topics copied at the moment it was made, while its codemeta.json still
describes the upstream software; comparing them would fail every fork’s CI on
metadata that is not wrong.