Automatically update CITATION.cff from your Python project's pyproject.toml, GitHub release data when enabled, and PyPI release data when enabled.
This README gives updateCitation specific commands and settings. For the surrounding tools, follow the tool maintainers' documentation: GitHub explains how CITATION.cff files appear on GitHub, Git explains Git hooks, uv explains uv tool, pre-commit explains pre-commit, and PyPA explains pyproject.toml.
Choose one of these:
| Goal | Good fit | File you create |
|---|---|---|
GitHub updates CITATION.cff after pushes to a GitHub repository. |
GitHub Action | .github/workflows/updateCitation.yml |
One computer updates CITATION.cff before local commits, using uv. |
Git hook |
.git/hooks/pre-commit |
| A shared local hook is configured in the repository for contributors. | pre-commit hook |
.pre-commit-config.yaml |
This is usually the most portable option for a GitHub-hosted repository. The workflow file is committed to the repository, so the update runs on GitHub after pushes and collaborators do not need updateCitation installed on their computers. GitHub's Actions quickstart explains GitHub Actions; the workflow below is the updateCitation-specific part.
- In the top level of your repository, create a folder named
.github. - Inside
.github, create a folder namedworkflows. - Inside
.github/workflows, create a file namedupdateCitation.yml. - Paste this into
.github/workflows/updateCitation.yml:
name: Update CITATION.cff
on:
push:
workflow_dispatch:
permissions:
contents: write
jobs:
updateCitation:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: '3.13'
- name: Run updateCitation
env:
GITHUB_TOKEN: ${{ github.token }}
run: pipx run updateCitation- Commit the file and push it to GitHub.
If the default settings work for your project, you are done. You do not need a [tool.updateCitation] section in pyproject.toml.
Running locally means updateCitation runs on your computer before changes are sent to GitHub, GitLab, Codeberg, or another Git host. You can do this with a plain Git hook or with pre-commit.
A plain Git hook is local to one clone on one computer. The hook file lives inside .git, so it is not committed or shared with collaborators. The plain hook below uses uv tool, so you need uv installed before using it. uv's own docs cover uv installation and the details of managing tools with uv tool.
For a shared local setup, pre-commit is often the practical option. The .pre-commit-config.yaml file is committed to the repository, and each collaborator runs pre-commit install once after cloning. That makes pre-commit the local-hook option in this README that can be shared across users and across Git hosting services.
Look in the top directory of your repository.
- If there is a file named
.pre-commit-config.yaml, your project is usingpre-commit. - If there is no
.pre-commit-config.yaml, your project is probably not usingpre-commityet. - You can also run
pre-commit --versionin a terminal. If it prints a version number, thepre-commitprogram is installed on your computer.
If your project already has .pre-commit-config.yaml, use the pre-commit instructions below. If not, the plain Git hook may be enough for one user on one machine.
A Git hook runs when you commit. This file is local to your computer and is not uploaded to GitHub or another Git host.
- Install
updateCitationas a uv-managed tool:
uv tool install updateCitationThis installs updateCitation outside your project virtual environment. You do not need to add updateCitation to the repository's own dependencies for this plain Git hook.
- In the top directory of your repository, find or create the hidden
.git/hooksfolder, and find or create a file namedpre-commit. - Paste this into
.git/hooks/pre-commit:
#!/usr/bin/env bash
set -euo pipefail
updateCitation- On macOS or Linux, make the file executable:
chmod +x .git/hooks/pre-commitNow each git commit runs updateCitation. If updateCitation changes CITATION.cff, review the change, add the changed citation file or files with git add, and commit again.
Use this if your project already uses pre-commit or if you want a shared local hook that collaborators can install. The pre-commit documentation explains repository-local hooks and pre-commit install; the configuration below is the updateCitation-specific part.
- In the top directory of your repository, create a file named
.pre-commit-config.yaml. - Paste this into
.pre-commit-config.yaml:
repos:
- repo: local
hooks:
- id: updatecitation
name: updateCitation
entry: updateCitation
language: python
additional_dependencies:
- updateCitation
files: ^$
pass_filenames: false
always_run: trueThis lets pre-commit install updateCitation in its own hook environment. It does not require updateCitation to be installed in the project's virtual environment.
- Install the hook:
pre-commit installNow each commit runs updateCitation through pre-commit before finalizing the commit.
The local options are not tied to GitHub. For GitLab, Codeberg, or another Git host, disable GitHub release metadata so updateCitation does not try to read GitHub releases:
[tool.updateCitation]
addGitHubRelease = falseIf the package is not published on PyPI, also set:
[tool.updateCitation]
addPyPIrelease = falseWith those settings, updateCitation can still use [project] metadata from pyproject.toml and the existing citation file.
uv tool install updateCitationFrom your project's virtual environment:
pip install updateCitationOnly for people who develop your project:
uv add --dev updateCitationupdateCitationFor Python code, the same workflow is available as:
import updateCitation
updateCitation.here()No updateCitation configuration is required when you are happy with the defaults.
updateCitation reads standard project metadata from [project] in pyproject.toml. PyPA's guide explains writing pyproject.toml, and the PyPA specification explains why tool-specific settings belong under the [tool] table. The most important [project] fields for updateCitation are:
[project]
name = "your-package-name"
version = "0.1.0"
authors = [{ name = "Ada Lovelace", email = "ada@invented.programming" }]
keywords = ["research-software", "citation"]
license = "MIT"
urls = { Homepage = "https://example.org", Repository = "https://github.com/example/project" }To change updateCitation behavior, add [tool.updateCitation] to pyproject.toml.
[tool.updateCitation]
filenameCitationDOTcff = "CITATION.cff"
pathFilenameCitationSSOT = "CITATION.cff"
addGitHubRelease = true
addPyPIrelease = true
projectURLTargets = ["homepage", "license", "repository"]
gitCommitMessage = "Update citations [skip ci]"
gitUserName = "updateCitation"
gitAmendFromGitHubAction = trueThese are the existing [tool.updateCitation] options:
| Setting | Type | Default | Purpose |
|---|---|---|---|
filenameCitationDOTcff |
string | "CITATION.cff" |
Filename for the repository-root citation file. |
pathFilenameCitationSSOT |
string | same path as the repository-root CITATION.cff |
Authoritative source citation file. Use this if your editable citation file lives somewhere like citations/CITATION.cff. |
addGitHubRelease |
boolean | true |
Add GitHub release metadata when available. |
addPyPIrelease |
boolean | true |
Add a PyPI artifact URL when available. Set this to false for packages not published on PyPI. |
projectURLTargets |
array of strings | ["homepage", "license", "repository"] |
Choose which keys from [project.urls] are copied into CITATION.cff. Supported values are homepage, license, and repository. |
gitCommitMessage |
string | "Update citations [skip ci]" |
Commit message used when updateCitation commits from GitHub Actions. |
gitUserName |
string | "updateCitation" |
Git username used for commits from GitHub Actions. |
gitUserEmail |
string | empty string | Git email used for commits. If omitted, updateCitation tries GitHub-derived noreply addresses first and then falls back to action@github.com. |
gitAmendFromGitHubAction |
boolean | true |
If true, updateCitation commits and pushes the updated citation file when running in GitHub Actions. |
pathFilenameCitationDOTcffRepository |
string | repository root CITATION.cff path |
Advanced full-path override for the repository-root citation file. |
pathRepository |
string | current working directory | Advanced override for the repository root. Usually you should run updateCitation from the repository root instead. |
filename_pyprojectDOTtoml |
string | "pyproject.toml" |
Advanced override for the settings filename after settings are loaded. |
pathReferences |
string | citations/ under the repository root |
Accepted by the settings object, but not currently used by the main workflow. |
GITHUB_TOKEN |
string or null |
null |
GitHub API token. Prefer the GITHUB_TOKEN environment variable instead of putting secrets in pyproject.toml. |
Do not set these internal fields in [tool.updateCitation]:
pathFilenamePackageSSOTtomlPackageData
addPyPIrelease = falsepreventsupdateCitationfrom generating a newrepository-artifactURL, but it does not delete an existingrepository-artifactalready present in your source citation file.projectURLTargetsonly mapshomepage,license, andrepository.- If you override a path-related setting such as
filenameCitationDOTcfforpathRepository, also override any dependent full-path setting you rely on.
For example, a repository that keeps its editable citation file under citations/ and is not published on PyPI can use:
[tool.updateCitation]
pathFilenameCitationSSOT = "citations/CITATION.cff"
addPyPIrelease = falseContributions are welcome. Please feel free to submit pull requests.
- 100% Python.
- Dynamic self-configuration whenever possible.
- 100% of the
updateCitationsettings inpyproject.toml. - All settings for external services, such as GitHub and PyPI, use the configuration from those services instead of creating new configuration for
updateCitation. - Highly extensible for current and future services.
