Skip to content

Commit 865eb85

Browse files
authored
Merge pull request #22 from ScienceLiveHub/fix/fill-readme-doi-on-release
Fill README/index Zenodo DOI badge on release
2 parents 81c0013 + 2ae169a commit 865eb85

3 files changed

Lines changed: 72 additions & 3 deletions

File tree

.github/workflows/release-identifiers.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,14 +96,14 @@ jobs:
9696
run: |
9797
set -euo pipefail
9898
99-
if git diff --quiet -- CITATION.cff codemeta.json ro-crate-metadata.json; then
99+
if git diff --quiet -- CITATION.cff codemeta.json ro-crate-metadata.json README.md index.md; then
100100
echo "::notice::Identifiers already recorded for ${{ steps.rev.outputs.tag }} — nothing to commit."
101101
exit 0
102102
fi
103103
104104
git config user.name "github-actions[bot]"
105105
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
106-
git add CITATION.cff codemeta.json ro-crate-metadata.json
106+
git add CITATION.cff codemeta.json ro-crate-metadata.json README.md index.md
107107
git commit -m "Record ${{ steps.rev.outputs.tag }} identifiers (version DOI + SWHID)"
108108
git push
109109
echo "::notice::Recorded ${{ steps.rev.outputs.tag }} identifiers on ${{ github.event.repository.default_branch }}."

scripts/set_release_identifiers.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,39 @@ def update_ro_crate(path: Path, version_doi: str, swhid: str, tag: str) -> None:
263263
path.write_text(json.dumps(data, indent=2, ensure_ascii=False) + "\n")
264264

265265

266+
DOI_TOKEN = "{{ZENODO_DOI}}"
267+
268+
269+
def substitute_prose_doi(path: Path, concept_doi: str) -> bool:
270+
"""Fill the {{ZENODO_DOI}} placeholder in a prose/badge file (README.md,
271+
index.md) with the CONCEPT DOI. The token is used in two shapes that need
272+
different forms: a Zenodo badge image path wants the BARE DOI, while a
273+
markdown link target wants the RESOLVER URL. The metadata writers above only
274+
ever touched CITATION.cff / codemeta / ro-crate, so these badges shipped
275+
still showing the literal token after the first release — and the FAIR4RS
276+
checklist audit excludes the token, so nothing flagged it.
277+
278+
Returns True if the file changed; a no-op if the file is absent or the token
279+
is already gone (so it is safe to re-run and safe when index.md does not
280+
exist).
281+
"""
282+
if not path.exists():
283+
return False
284+
original = path.read_text()
285+
url = f"https://doi.org/{concept_doi}"
286+
text = (
287+
original
288+
.replace(f"DOI/{DOI_TOKEN}.svg", f"DOI/{concept_doi}.svg") # badge image path -> bare DOI
289+
.replace(f"[{DOI_TOKEN}]", f"[{concept_doi}]") # link/label text -> bare DOI
290+
.replace(f"({DOI_TOKEN})", f"({url})") # markdown link target -> resolver URL
291+
.replace(DOI_TOKEN, url) # any remainder -> resolver URL
292+
)
293+
if text != original:
294+
path.write_text(text)
295+
return True
296+
return False
297+
298+
266299
def main(argv: list[str] | None = None) -> int:
267300
p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
268301
p.add_argument("--repo", required=True, help="owner/name")
@@ -296,7 +329,12 @@ def main(argv: list[str] | None = None) -> int:
296329
update_citation_cff(args.root / "CITATION.cff", version_doi, concept_doi, swhid, args.tag)
297330
update_codemeta(args.root / "codemeta.json", version_doi, concept_doi, swhid)
298331
update_ro_crate(args.root / "ro-crate-metadata.json", version_doi, swhid, args.tag)
299-
print("\nWrote CITATION.cff, codemeta.json, ro-crate-metadata.json")
332+
wrote = ["CITATION.cff", "codemeta.json", "ro-crate-metadata.json"]
333+
for name in ("README.md", "index.md"):
334+
# the concept DOI is the "cite this project" identity the README badge shows
335+
if substitute_prose_doi(args.root / name, concept_doi):
336+
wrote.append(name)
337+
print("\nWrote " + ", ".join(wrote))
300338
return 0
301339

302340

tests/test_set_release_identifiers.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
from set_release_identifiers import ( # noqa: E402
2929
extract_dois,
3030
find_zenodo_record,
31+
substitute_prose_doi,
3132
swhid_for,
3233
update_citation_cff,
3334
update_codemeta,
@@ -308,3 +309,33 @@ def test_ro_crate_without_root_entity_fails_loudly(tmp_path):
308309
p = _write(tmp_path, "ro-crate-metadata.json", json.dumps({"@graph": [{"@id": "other"}]}))
309310
with pytest.raises(SystemExit):
310311
update_ro_crate(p, "10.5281/zenodo.20811615", swhid_for(COMMIT, ORIGIN), TAG)
312+
313+
314+
# --------------------------------------------------------------------------
315+
# substitute_prose_doi (README.md / index.md badge + link)
316+
# --------------------------------------------------------------------------
317+
def test_readme_badge_and_link_get_the_concept_doi(tmp_path):
318+
"""The DOI badge/link placeholder release-identifiers never used to fill is
319+
substituted context-aware: the bare DOI in the Zenodo badge image path, the
320+
resolver URL in the markdown link target."""
321+
readme = _write(
322+
tmp_path,
323+
"README.md",
324+
"[![DOI](https://zenodo.org/badge/DOI/{{ZENODO_DOI}}.svg)]({{ZENODO_DOI}})\n"
325+
"cite: [{{ZENODO_DOI}}]({{ZENODO_DOI}}).\n",
326+
)
327+
changed = substitute_prose_doi(readme, "10.5281/zenodo.20811614")
328+
out = readme.read_text()
329+
330+
assert changed is True
331+
assert "{{ZENODO_DOI}}" not in out
332+
assert "badge/DOI/10.5281/zenodo.20811614.svg" in out # bare DOI in the badge path
333+
assert "](https://doi.org/10.5281/zenodo.20811614)" in out # resolver URL in the link target
334+
assert "[10.5281/zenodo.20811614]" in out # bare DOI as link text
335+
336+
337+
def test_substitute_prose_doi_is_a_safe_noop(tmp_path):
338+
"""Idempotent, and silent when the file is absent (index.md is optional)."""
339+
p = _write(tmp_path, "README.md", "no token here\n")
340+
assert substitute_prose_doi(p, "10.5281/zenodo.1") is False # token already gone
341+
assert substitute_prose_doi(tmp_path / "index.md", "10.5281/zenodo.1") is False # file absent

0 commit comments

Comments
 (0)