diff --git a/.github/scripts/publish_to_zenodo.py b/.github/scripts/publish_to_zenodo.py new file mode 100644 index 0000000..4e7cbe2 --- /dev/null +++ b/.github/scripts/publish_to_zenodo.py @@ -0,0 +1,151 @@ +# Based on examples on https://developers.zenodo.org/ + + +import json +import os +import requests +import yaml + +ACCESS_TOKEN = os.environ["ZENODO_TOKEN"] +CONCEPT_ID = os.environ["ZENODO_CONCEPT_ID"] + +BASE_URL = "https://zenodo.org/api" + +json_headers = { + "Authorization": f"Bearer {ACCESS_TOKEN}", + "Content-Type": "application/json" +} + +upload_headers = { + "Authorization": f"Bearer {ACCESS_TOKEN}" +} + +def load_citation(): + with open("CITATION.cff", encoding="utf-8") as f: + return yaml.safe_load(f) + + +def cff_to_creators(authors): + creators = [] + + for author in authors: + creators.append( + { + "name": ( + f"{author['family-names']}, " + f"{author['given-names']}" + ) + } + ) + + return creators + + + +# Load metadata from CITATION.cff + +cff = load_citation() + +metadata = { + "title": cff["title"], + "upload_type": "dataset", + "description": cff.get("abstract", ""), + "creators": cff_to_creators(cff["authors"]), + "keywords": cff.get("keywords", []), + "version": cff.get("version"), + "license": cff.get("license") +} + + +# Create new version of existing concept DOI + +r = requests.post( + f"{BASE_URL}/deposit/depositions/{CONCEPT_ID}/actions/newversion", + headers=json_headers) + +r.raise_for_status() + +latest_draft = r.json()["links"]["latest_draft"] + + +# Get draft deposition information + +r = requests.get( + latest_draft, + headers=json_headers, +) + +r.raise_for_status() + +deposition = r.json() + +deposition_id = deposition["id"] +bucket_url = deposition["links"]["bucket"] + +print(f"Draft deposition: {deposition_id}") + + +# Upload release archive + + +tag = os.environ["GITHUB_REF_NAME"] +repo_name = os.environ['GITHUB_REPOSITORY'] + + +archive_url = ( + f"https://github.com/" + f"{repo_name}" + f"/archive/refs/tags/{tag}.zip" +) + +archive_name = f"{tag}.zip" + +download = requests.get(archive_url) +download.raise_for_status() + +with open(archive_name, "wb") as fp: + fp.write(download.content) + +with open(archive_name, "rb") as fp: + r = requests.put( + f"{bucket_url}/{archive_name}", + data=fp, + headers=upload_headers + ) + +r.raise_for_status() + +print(f"Uploaded {archive_name}") + + +# Update metadata + +data = { + "metadata": metadata +} + +r = requests.put( + f"{BASE_URL}/deposit/depositions/{deposition_id}", + data=json.dumps(data), + headers=json_headers +) + +r.raise_for_status() + +print("Metadata updated") + + +# Publish + + +r = requests.post( + f"{BASE_URL}/deposit/depositions/{deposition_id}/actions/publish", + headers=json_headers, +) + +r.raise_for_status() + +record = r.json() + +print(f"Published DOI: {record.get('doi')}") +print(f"Record URL: {record['links']['html']}") \ No newline at end of file diff --git a/.github/workflows/zenodo.yml b/.github/workflows/zenodo.yml new file mode 100644 index 0000000..9317a9a --- /dev/null +++ b/.github/workflows/zenodo.yml @@ -0,0 +1,26 @@ +name: Publish Release to Zenodo + +on: + release: + types: [published] + +jobs: + zenodo: + runs-on: ubuntu-latest + + permissions: + contents: read + + env: + ZENODO_TOKEN: ${{ secrets.ZENODO_TOKEN }} + ZENODO_CONCEPT_ID: ${{ vars.ZENODO_CONCEPT_ID }} + + steps: + - uses: actions/checkout@v4 + + - name: Install dependencies + run: | + pip install requests pyyaml + + - name: Create Zenodo deposition + run: python .github/scripts/publish_to_zenodo.py \ No newline at end of file