Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions .github/ISSUE_TEMPLATE/release-checklist.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
name: Release checklist
about: Checklist for cutting a new pycmor release
title: "Release vX.Y.Z"
labels: release
assignees: ''
---

## Pre-release

- [ ] All planned PRs merged
- [ ] `CHANGELOG` / release notes up to date
- [ ] `version` in `CITATION.cff` and `codemeta.json` matches the new version
- [ ] `date-released` in `CITATION.cff` updated

## Release

- [ ] Tag pushed / GitHub Release created
- [ ] Zenodo DOI auto-generated (check https://zenodo.org — linked via `.zenodo.json` or GitHub integration)
- [ ] Update `doi` in `CITATION.cff` and `identifier[doi]` in `codemeta.json` with the new Zenodo DOI

## Post-release: Software Heritage SWHID

The GitHub Actions workflow triggers an SWH archival request automatically.
After ~1 hour, run the helper script to fetch the new SWHID and update the files:

```bash
python scripts/update_swhid.py
# Follow the output — it will print the SWHID and patch CITATION.cff + codemeta.json
```

Then open a PR with the updated files.

- [ ] `identifiers[swh]` in `CITATION.cff` updated with new SWHID
- [ ] `identifier[swh]` in `codemeta.json` updated with new SWHID
- [ ] SWHID also updated in the Helmholtz RSD: https://helmholtz.software/software/pycmor/edit/software-heritage

## Done

- [ ] Release announcement (if applicable)
34 changes: 34 additions & 0 deletions .github/workflows/software-heritage.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Archive on Software Heritage

on:
release:
types: [published]

jobs:
save-to-swh:
runs-on: ubuntu-latest
steps:
- name: Request Software Heritage archival
run: |
ORIGIN="https://github.com/esm-tools/pycmor"
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" -X POST \
"https://archive.softwareheritage.org/api/1/origin/save/git/url/${ORIGIN}/")
echo "SWH save request HTTP status: $RESPONSE"

- name: Post release notes reminder
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ github.event.release.tag_name }}
run: |
ORIGIN="https://github.com/esm-tools/pycmor"
SWH_URL="https://archive.softwareheritage.org/browse/origin/directory/?origin_url=${ORIGIN}"
gh release edit "$TAG" \
--notes "$(gh release view "$TAG" --json body -q .body)

---
**Software Heritage archival** was requested automatically for this release.
Once processed (usually within 1 hour), retrieve the snapshot SWHID at:
${SWH_URL}

Then update \`CITATION.cff\` (identifiers.swh) and \`codemeta.json\` (identifier[swh]) with the new SWHID.
You can use the helper script: \`python scripts/update_swhid.py\`" || true
42 changes: 37 additions & 5 deletions CITATION.cff
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
cff-version: 1.2.0
message: "If you use ESM-Tools software, please cite using the metadata below."
message: "If you use pycmor, please cite it using the metadata below."
type: software
title: "pycmor"
abstract: >-
pycmor (py-cmor) is a Python package that simplifies the conversion of climate
model output into Climate Model Output Rewriter (CMOR) compliant formats,
supporting both CMIP6 and CMIP7 standards. It provides a modular, plugin-based
pipeline architecture for processing NetCDF data from diverse Earth system
models, enabling compliance with WCRP Controlled Vocabularies and CF
conventions. Developed at the Alfred Wegener Institute Helmholtz Centre for
Polar and Marine Research.
authors:
- family-names: "Gierz"
given-names: "Paul"
Expand All @@ -17,8 +27,30 @@ authors:
given-names: "Jan"
orcid: "https://orcid.org/0000-0001-9515-3322"
affiliation: "Alfred-Wegener-Institut Helmholtz-Zentrum für Polar- und Meeresforschung"

title: "pycmor"
doi: 10.5281/zenodo.15530131
version: "1.0.2"
date-released: 2025-05-27
url: "https://github.com/esm-tools/pycmor"
license: MIT
repository-code: "https://github.com/esm-tools/pycmor"
url: "https://pymor.readthedocs.io"
doi: 10.5281/zenodo.15530131
identifiers:
- type: doi
value: "10.5281/zenodo.15530131"
description: "Zenodo DOI for pycmor v1.0.2"
- type: swh
value: "swh:1:snp:e4d5216be639a8e2dd2be23be1e78981de3fc67d"
description: "Software Heritage snapshot (HEAD, 2026-06-16)"
keywords:
- CMOR
- CMORization
- CMIP6
- CMIP7
- climate
- climate model output
- NetCDF
- CF-conventions
- earth system model
- WCRP
- research software
- Python
- AWI
100 changes: 100 additions & 0 deletions codemeta.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
{
"@context": "https://doi.org/10.5063/schema/codemeta-2.0",
"@type": "SoftwareSourceCode",
"name": "pycmor",
"description": "pycmor (py-cmor) is a Python package that simplifies the conversion of climate model output into Climate Model Output Rewriter (CMOR) compliant formats, supporting both CMIP6 and CMIP7 standards. It provides a modular, plugin-based pipeline architecture for processing NetCDF data from diverse Earth system models, enabling compliance with WCRP Controlled Vocabularies and CF conventions.",
"identifier": [
{
"@type": "PropertyValue",
"propertyID": "doi",
"value": "https://doi.org/10.5281/zenodo.15530131"
},
{
"@type": "PropertyValue",
"propertyID": "swh",
"value": "swh:1:snp:e4d5216be639a8e2dd2be23be1e78981de3fc67d"
}
],
"version": "1.0.2",
"datePublished": "2025-05-27",
"license": "https://spdx.org/licenses/MIT",
"codeRepository": "https://github.com/esm-tools/pycmor",
"issueTracker": "https://github.com/esm-tools/pycmor/issues",
"url": "https://pymor.readthedocs.io",
"downloadUrl": "https://pypi.org/project/py-cmor/",
"programmingLanguage": {
"@type": "ComputerLanguage",
"name": "Python",
"url": "https://www.python.org"
},
"operatingSystem": ["Linux", "macOS", "Windows"],
"softwareRequirements": "Python >= 3.9",
"developmentStatus": "active",
"keywords": [
"CMOR",
"CMORization",
"CMIP6",
"CMIP7",
"climate",
"climate model output",
"NetCDF",
"CF-conventions",
"earth system model",
"WCRP",
"research software",
"AWI"
],
"author": [
{
"@type": "Person",
"givenName": "Paul",
"familyName": "Gierz",
"@id": "https://orcid.org/0000-0002-4512-087X",
"affiliation": {
"@type": "Organization",
"name": "Alfred-Wegener-Institut Helmholtz-Zentrum für Polar- und Meeresforschung",
"url": "https://www.awi.de"
}
},
{
"@type": "Person",
"givenName": "Miguel",
"familyName": "Andrés-Martínez",
"@id": "https://orcid.org/0000-0002-1525-5546",
"affiliation": {
"@type": "Organization",
"name": "Alfred-Wegener-Institut Helmholtz-Zentrum für Polar- und Meeresforschung",
"url": "https://www.awi.de"
}
},
{
"@type": "Person",
"givenName": "Pavan Kumar",
"familyName": "Siligam",
"@id": "https://orcid.org/0009-0003-8054-7021",
"affiliation": {
"@type": "Organization",
"name": "Alfred-Wegener-Institut Helmholtz-Zentrum für Polar- und Meeresforschung",
"url": "https://www.awi.de"
}
},
{
"@type": "Person",
"givenName": "Jan",
"familyName": "Streffing",
"@id": "https://orcid.org/0000-0001-9515-3322",
"affiliation": {
"@type": "Organization",
"name": "Alfred-Wegener-Institut Helmholtz-Zentrum für Polar- und Meeresforschung",
"url": "https://www.awi.de"
}
}
],
"funder": {
"@type": "Organization",
"name": "Alfred-Wegener-Institut Helmholtz-Zentrum für Polar- und Meeresforschung",
"url": "https://www.awi.de"
},
"contIntegration": "https://github.com/esm-tools/pycmor/actions",
"readme": "https://github.com/esm-tools/pycmor/blob/main/README.rst"
}
99 changes: 99 additions & 0 deletions scripts/update_swhid.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#!/usr/bin/env python3
"""
Fetch the latest Software Heritage snapshot SWHID for pycmor and update
CITATION.cff and codemeta.json in-place.

Usage:
python scripts/update_swhid.py

Run this after a release, once the SWH archival is complete (~1 hour after tag push).
"""
import json
import re
import sys
import urllib.request
from pathlib import Path

ORIGIN = "https://github.com/esm-tools/pycmor"
SWH_API = "https://archive.softwareheritage.org/api/1"
REPO_ROOT = Path(__file__).parent.parent


def get_latest_swhid() -> str:
url = f"{SWH_API}/origin/{ORIGIN}/visit/latest/?require_snapshot=true"
try:
with urllib.request.urlopen(url, timeout=15) as r:
data = json.load(r)
except Exception as e:
sys.exit(f"Failed to fetch latest SWH visit: {e}\n"
f"Is the repository archived? Check: "
f"https://archive.softwareheritage.org/browse/origin/?origin_url={ORIGIN}")

snapshot = data.get("snapshot")
if not snapshot:
sys.exit("Latest SWH visit has no snapshot yet — archival may still be in progress. "
"Try again in a few minutes.")

return f"swh:1:snp:{snapshot}"


def update_citation_cff(swhid: str) -> None:
path = REPO_ROOT / "CITATION.cff"
content = path.read_text()

# Replace existing swh identifier value
pattern = r'(- type: swh\s+value: ")[^"]+"'
replacement = rf'\g<1>{swhid}"'
new_content, count = re.subn(pattern, replacement, content)

if count == 0:
print("WARNING: could not find '- type: swh' block in CITATION.cff — "
"please update identifiers.swh manually.")
print(f" SWHID to add: {swhid}")
return

path.write_text(new_content)
print(f"Updated CITATION.cff: identifiers.swh = {swhid}")


def update_codemeta(swhid: str) -> None:
path = REPO_ROOT / "codemeta.json"
data = json.loads(path.read_text())

identifiers = data.get("identifier", [])
if isinstance(identifiers, str):
identifiers = [{"@type": "PropertyValue", "propertyID": "doi", "value": identifiers}]

updated = False
for entry in identifiers:
if isinstance(entry, dict) and entry.get("propertyID") == "swh":
entry["value"] = swhid
updated = True
break

if not updated:
identifiers.append({"@type": "PropertyValue", "propertyID": "swh", "value": swhid})

data["identifier"] = identifiers
path.write_text(json.dumps(data, indent=2, ensure_ascii=False) + "\n")
print(f"Updated codemeta.json: identifier[swh] = {swhid}")


def main() -> None:
print(f"Fetching latest Software Heritage snapshot for {ORIGIN} ...")
swhid = get_latest_swhid()
print(f"SWHID: {swhid}\n")

update_citation_cff(swhid)
update_codemeta(swhid)

print("\nDone. Next steps:")
print(" 1. git diff # review the changes")
print(" 2. git add CITATION.cff codemeta.json && git commit -m 'chore: update SWHID for release'")
print(" 3. Open a PR or push directly to main")
print(f" 4. Update the Helmholtz RSD: "
f"https://helmholtz.software/software/pycmor/edit/software-heritage")


if __name__ == "__main__":
main()
Loading