@@ -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+
266299def 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 ("\n Wrote 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 ("\n Wrote " + ", " .join (wrote ))
300338 return 0
301339
302340
0 commit comments