Skip to content

Latest commit

 

History

History
409 lines (338 loc) · 25.7 KB

README.dev.md

File metadata and controls

409 lines (338 loc) · 25.7 KB

Documentation for developers

Installing

# get a copy of the cffconvert software
git clone https://github.com/scicodes/cffconvert.git

# change directory into cffconvert
cd cffconvert

# create a virtual environment and install cffconvert with dev and testing dependencies
uv venv && source .venv/bin/activate
uv pip install --editable .[dev,testing]

There are various sets of dependencies that you may want to install depending on the work you're planning to do.

uv pip install --editable .[dev]       # build, pre-commit, pyroma, ruff, setuptools
uv pip install --editable .[gcloud]    # flask (for Google Cloud Function deployment)
uv pip install --editable .[testing]   # pytest, pytest-cov

You can combine these into one command like so, e.g.:

uv pip install --editable .[dev,testing]

Alternatively, you can use the Makefile targets:

make dev-install    # install with dev + testing deps (editable)
make install        # install runtime deps only

Interim distribution

The official interim source distribution is the Git tag attached to a published GitHub Release. The current release tag is v2026.09.1. Publish a GitHub Release for that tag to trigger GHCR; use main only for development, not as a distribution source.

Testing

# (from the project root)
# run all tests in Docker (builds a test image automatically)
make test

# or run tests locally (requires dev-install)
make test-local

# run tests for consistent versioning
make test-version

# run pytest on a subset of the files, e.g.
cd tests/lib/cff_1_2_0/ && pytest .

Alternatively, use the Makefile:

make test            # run full test suite (in Docker)
make test-local      # run full test suite on the host (requires dev-install)
make test-version    # run version-consistency checks (in Docker)
make test-marker M=bibtex  # run tests for a specific marker (in Docker)

Tests pertaining to a specific exporter have been marked accordingly with one of the following markers (see also pytest configuration section in pyproject.toml):

  1. apalike
  2. bibtex
  3. codemeta
  4. endnote
  5. ris
  6. schemaorg
  7. zenodo

Additionally, there are markers for CLI tests and for library tests:

  1. cli
  2. lib

You can instruct pytest to run only the tests for one or some of these marked sets. As an example, if you want to run only the tests related to exporting as Endnote, you should call pytest with the -m flag as follows:

pytest -m endnote

Markers can also be combined with or and and and not, e.g.

pytest -m 'endnote and lib'
pytest -m 'cli and not zenodo'
pytest -m 'schemaorg or codemeta'
# etc

Linting

Running the linters requires that the development tools have been installed:

uv pip install --editable .[dev]

Linting the code base using ruff:

ruff check src/cffconvert tests

Assessing the package metadata using 'pyroma' (https://github.com/regebro/pyroma):

# from the project root
pyroma .

The linting tools are also usable via pre-commit:

# Run all tools
pre-commit run --all-files

# Run a specific tool, see .pre-commit-config.yaml for their IDs
pre-commit run --all-files <ID of the task>

Alternatively, the linting tools can be set up to run automatically whenever you issue a git commit command, as follows:

pre-commit install

Alternatively, you can run all linters via the Makefile:

make lint            # ruff, pyroma in Docker
make lint-local      # ruff, pyroma locally (requires dev-install)
make precommit       # pre-commit run --all-files

Construction of author keys

There are various source keys in CFF that can be used to convert to a target format. The code uses a pattern of first identifiying what information is present, then summarizing this as a key, then using that key to retrieve a method which is tailored only to that specific combination of source keys. As an example of this mapping, see the setup in https://github.com/SciCodes/cffconvert/blob/main/src/cffconvert/lib/cff_1_x_x/authors/base.py

Source keys:

  1. authors[i].given-names
  2. authors[i].family-names (including name particle and suffix)
  3. authors[i].alias
  4. authors[i].name
  5. authors[i].affiliation
  6. authors[i].orcid
  7. authors[i].email

The table below lists how the key name is constructed based what information was provided in the CITATION.cff file:

key has given name has family name has alias has name has affiliation has orcid has email
GFANAOE True True True True True True True
GFANA_E True True True True True False True
GFAN_OE True True True True False True True
GFAN__E True True True True False False True
GFA_AOE True True True False True True True
GFA_A_E True True True False True False True
GFA__OE True True True False False True True
GFA___E True True True False False False True
GF_NAOE True True False True True True True
GF_NA_E True True False True True False True
GF_N_OE True True False True False True True
GF_N__E True True False True False False True
GF__AOE True True False False True True True
GF__A_E True True False False True False True
GF___OE True True False False False True True
GF____E True True False False False False True
G_ANAOE True False True True True True True
G_ANA_E True False True True True False True
G_AN_OE True False True True False True True
G_AN__E True False True True False False True
G_A_AOE True False True False True True True
G_A_A_E True False True False True False True
G_A__OE True False True False False True True
G_A___E True False True False False False True
G__NAOE True False False True True True True
G__NA_E True False False True True False True
G__N_OE True False False True False True True
G__N__E True False False True False False True
G___AOE True False False False True True True
G___A_E True False False False True False True
G____OE True False False False False True True
G_____E True False False False False False True
_FANAOE False True True True True True True
_FANA_E False True True True True False True
_FAN_OE False True True True False True True
_FAN__E False True True True False False True
_FA_AOE False True True False True True True
_FA_A_E False True True False True False True
_FA__OE False True True False False True True
_FA___E False True True False False False True
_F_NAOE False True False True True True True
_F_NA_E False True False True True False True
_F_N_OE False True False True False True True
_F_N__E False True False True False False True
_F__AOE False True False False True True True
_F__A_E False True False False True False True
_F___OE False True False False False True True
_F____E False True False False False False True
__ANAOE False False True True True True True
__ANA_E False False True True True False True
__AN_OE False False True True False True True
__AN__E False False True True False False True
__A_AOE False False True False True True True
__A_A_E False False True False True False True
__A__OE False False True False False True True
__A___E False False True False False False True
___NAOE False False False True True True True
___NA_E False False False True True False True
___N_OE False False False True False True True
___N__E False False False True False False True
____AOE False False False False True True True
____A_E False False False False True False True
_____OE False False False False False True True
______E False False False False False False True
GFANAO_ True True True True True True False
GFANA__ True True True True True False False
GFAN_O_ True True True True False True False
GFAN___ True True True True False False False
GFA_AO_ True True True False True True False
GFA_A__ True True True False True False False
GFA__O_ True True True False False True False
GFA____ True True True False False False False
GF_NAO_ True True False True True True False
GF_NA__ True True False True True False False
GF_N_O_ True True False True False True False
GF_N___ True True False True False False False
GF__AO_ True True False False True True False
GF__A__ True True False False True False False
GF___O_ True True False False False True False
GF_____ True True False False False False False
G_ANAO_ True False True True True True False
G_ANA__ True False True True True False False
G_AN_O_ True False True True False True False
G_AN___ True False True True False False False
G_A_AO_ True False True False True True False
G_A_A__ True False True False True False False
G_A__O_ True False True False False True False
G_A____ True False True False False False False
G__NAO_ True False False True True True False
G__NA__ True False False True True False False
G__N_O_ True False False True False True False
G__N___ True False False True False False False
G___AO_ True False False False True True False
G___A__ True False False False True False False
G____O_ True False False False False True False
G______ True False False False False False False
_FANAO_ False True True True True True False
_FANA__ False True True True True False False
_FAN_O_ False True True True False True False
_FAN___ False True True True False False False
_FA_AO_ False True True False True True False
_FA_A__ False True True False True False False
_FA__O_ False True True False False True False
_FA____ False True True False False False False
_F_NAO_ False True False True True True False
_F_NA__ False True False True True False False
_F_N_O_ False True False True False True False
_F_N___ False True False True False False False
_F__AO_ False True False False True True False
_F__A__ False True False False True False False
_F___O_ False True False False False True False
_F_____ False True False False False False False
__ANAO_ False False True True True True False
__ANA__ False False True True True False False
__AN_O_ False False True True False True False
__AN___ False False True True False False False
__A_AO_ False False True False True True False
__A_A__ False False True False True False False
__A__O_ False False True False False True False
__A____ False False True False False False False
___NAO_ False False False True True True False
___NA__ False False False True True False False
___N_O_ False False False True False True False
___N___ False False False True False False False
____AO_ False False False False True True False
____A__ False False False False True False False
_____O_ False False False False False True False
_______ False False False False False False False

Construction of identifier keys

There are various source keys in CFF that can be used to convert to a target format. The code uses a pattern of first identifiying what information is present, then summarizing this as a key, then using that key to retrieve a method which is tailored only to that specific combination of source keys.

Source keys:

  • doi
  • identifiers[i].type==doi

The table below lists how the key name is constructed based what information was provided in the CITATION.cff file:

key has doi has identifiers doi
__ False False
_I False True
D_ True False
DI True True

Construction of URL keys

There are various source keys in CFF that can be used to convert to a target format. The code uses a pattern of first identifiying what information is present, then summarizing this as a key, then using that key to retrieve a method which is tailored only to that specific combination of source keys.

Source keys:

  • identifiers[i].type==url
  • repository
  • repository-artifact
  • repository-code
  • url

The table below lists how the key name is constructed based what information was provided in the CITATION.cff file:

key has indentifiers url has repository has repository-artifact has repository-code has url
IRACU True True True True True
IRAC_ True True True True False
IRA_U True True True False True
IRA__ True True True False False
IR_CU True True False True True
IR_C_ True True False True False
IR__U True True False False True
IR___ True True False False False
I_ACU True False True True True
I_AC_ True False True True False
I_A_U True False True False True
I_A__ True False True False False
I__CU True False False True True
I__C_ True False False True False
I___U True False False False True
I____ True False False False False
_RACU False True True True True
_RAC_ False True True True False
_RA_U False True True False True
_RA__ False True True False False
_R_CU False True False True True
_R_C_ False True False True False
_R__U False True False False True
_R___ False True False False False
__ACU False False True True True
__AC_ False False True True False
__A_U False False True False True
__A__ False False True False False
___CU False False False True True
___C_ False False False True False
____U False False False False True
_____ False False False False False

For maintainers

Making a release

See CONTRIBUTING.md for the full tag-and-push procedure, version checks, GHCR preparation, and the release checklist.

Building the docker image

# (builds a self-contained image from the local source tree at version v2026.09.1)
docker build --tag cffconvert:v2026.09.1 .

The image installs cffconvert independently and uses /work as its working directory, so no -w flag is needed.

See if the Docker image works as expected:

docker run --rm cffconvert:v2026.09.1 --version
docker run --rm -v "$PWD:/work:ro" cffconvert:v2026.09.1 -f bibtex
# etc

Publishing on GHCR

Publishing a GitHub Release for the release tag triggers the publish-to-ghcr.yml workflow, which publishes ghcr.io/scicodes/cffconvert:<release-tag> and uses the ghcr environment as a deployment gate. The current release tag is v2026.09.1. No secrets are needed for that environment. Replace <release-tag> with the published GitHub Release tag in the commands below.

Before the first publish, configure the ghcr environment in GitHub and check the package settings after upload so the package is linked to SciCodes/cffconvert and public.

The image will only exist after a successful workflow run, so use a local smoke test first:

docker build --tag ghcr.io/scicodes/cffconvert:<release-tag> .
docker run --rm ghcr.io/scicodes/cffconvert:<release-tag> --version
docker run --rm ghcr.io/scicodes/cffconvert:<release-tag> --help