Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Skill for the publishing infrastructure module providing academic publishing workflows including BibTeX CLI citation generation, APA/MLA citation helper functions, DOI management, Zenodo publication, arXiv submission preparation, GitHub releases, PyPI and TestPyPI package distribution, static-site deployment to GitHub Pages / Cloudflare Pages / Netlify, multi-target long-horizon archival (IPFS Pinata, Web3.Storage, Software Heritage), a central platform registry, and publication readiness valida
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-12 | ✗→✓ | ▲ Improved | -13% | 0% |
| case-01 | ✗→✓ | ▲ Improved | 79% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 75% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 59% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 203% | 0% |
Academic publishing and dissemination tools for research projects.
For the release-operator workflow, start with docs/guides/publication-runbook.md. It is the front door for standalone public GitHub mirrors, real Zenodo DOI minting, new-version releases, optional mirrors, status blocks, and archival handoff.
| Path | Use when | | --- | --- | | infrastructure.publishing.zenodo | Zenodo deposit, upload, publish, DOI | | infrastructure.publishing.github | GitHub release + assets | | infrastructure.publishing.arxiv | arXiv tarball from manuscript tree | | infrastructure.publishing.pypi | PyPI / TestPyPI package distribution | | infrastructure.publishing.static_site | GitHub Pages, Cloudflare Pages, or Netlify deployment | | infrastructure.publishing.archival | IPFS Pinata, Web3.Storage, Software Heritage, Zenodo mirror | | infrastructure.publishing.registry | Enumerate or look up all platform adapters |
Root exports (infrastructure.publishing) and platforms.py / api.py remain for backwards compatibility.
pythonfrom infrastructure.publishing import PublicationMetadata, extract_publication_metadata metadata = extract_publication_metadata([Path("manuscript/01_introduction.md")])
The publication: block in manuscript/config.yaml records durable identifiers (doi, version_doi, version_record, github_repository, repository_url). The publication.published_artifacts key — a {platform_name: durable_url} map — records artifacts published outside those fields (e.g. an IPFS CID or a HuggingFace dataset URL) and is consumed by status_report.py to mark those platforms as published.
The Python API exposes BibTeX, APA, and MLA helpers. The infrastructure.publishing.cli generate-citation command currently exposes BibTeX only.
pythonfrom infrastructure.publishing import ( generate_citation_bibtex, generate_citation_apa, generate_citation_mla, ) bibtex = generate_citation_bibtex(metadata)
pythonfrom infrastructure.publishing.zenodo import publish_to_zenodo, ZenodoClient, ZenodoConfig result = publish_to_zenodo(metadata, [Path("output/paper.pdf")], access_token="...", sandbox=True) print(result.doi) config = ZenodoConfig(access_token="...", sandbox=True) client = ZenodoClient(config) dep = client.create_deposition({"title": metadata.title, "upload_type": "publication"}) client.upload_file(dep.bucket_url, Path("paper.pdf")) doi = client.publish(dep.deposition_id)
pythonfrom infrastructure.publishing.arxiv import prepare_arxiv_submission package = prepare_arxiv_submission(output_dir, metadata)
pythonfrom infrastructure.publishing.github import create_github_release url = create_github_release( "v1.0.0", "Release 1.0.0", "Notes", [Path("paper.pdf")], token, "owner/repo" )
pypi/ implements a build, check, upload pipeline via uv build and twine. dry_run=True is the default at every layer — no accidental uploads.
pythonfrom infrastructure.publishing.pypi import PyPIAdapter, PyPIConfig, run_pypi_release # Using the adapter directly adapter = PyPIAdapter(PyPIConfig(test=True)) # reads TESTPYPI_TOKEN from env result = adapter.publish(Path("."), dry_run=False) print(result.status, result.url) # One-liner with post-upload verification result = run_pypi_release( Path("."), test=False, # production PyPI dry_run=False, verify=True, package_name="mylib", )
Environment: PYPI_TOKEN (production), TESTPYPI_TOKEN (test). Both resolved from os.environ when config.token is None.
static_site/ provides GitHubPagesAdapter, CloudflarePagesAdapter, and NetlifyAdapter behind a common deploy(dry_run=True) interface.
pythonfrom infrastructure.publishing.static_site import ( SiteDeployConfig, SiteHosting, get_adapter, ) config = SiteDeployConfig( hosting=SiteHosting.GITHUB_PAGES, site_dir="output/docs", repo="owner/repo", branch="gh-pages", ) result = get_adapter(config).deploy(dry_run=False) print(result.status, result.url)
Credentials: GITHUB_TOKEN (GitHub Pages), CLOUDFLARE_API_TOKEN (Cloudflare), NETLIFY_AUTH_TOKEN (Netlify). All adapters never raise on failure — inspect result.status and result.error.
registry.py is the single source of truth for all adapter metadata. It currently records 12 first-class platforms plus documented ebook, print, retail, and payment targets that do not yet have live adapters.
pythonfrom infrastructure.publishing.registry import get_platform, list_platforms, PublishingTier info = get_platform("pypi") print(info.env_vars) # ('PYPI_TOKEN', 'TESTPYPI_TOKEN') for p in list_platforms(tag="static_site"): print(p.name, p.adapter_class)
status_report.py turns the registry (which platforms exist) plus a project's manuscript/config.yaml (what has been published) into a regenerable README block delimited by <!-- PUBLISHING-STATUS:START ... --> / <!-- PUBLISHING-STATUS:END --> markers. The committed block reflects durable publication state (DOIs, repo, published_artifacts) only — never machine-specific credential presence.
pythonfrom pathlib import Path from infrastructure.publishing.status_report import ( compile_publishing_status, render_status_block, update_readme_block, ) report = compile_publishing_status(Path("projects/templates/template_gold_refinement")) print(report.published_count) # e.g. 7 block = render_status_block(report) # marker-wrapped Markdown new_text, changed = update_readme_block( readme_text, report, init_after_heading="## Publication and rendering" )
A caller-supplied published={"ipfs_pinata": "https://..."} map upgrades extra rows to PUBLISHED, overriding config. PublicationState is PUBLISHED, AVAILABLE, or PLANNED.
bashuv run python -m infrastructure.publishing.status_report --project <path> --write \ --init-after "## Publication and rendering" uv run python -m infrastructure.publishing.status_report --project <path> --check # CI gate: exits non-zero on drift
credential_check.py is a read-only, non-destructive check that publishing credentials authenticate. It sends a single GET per platform and never writes, uploads, or prints token values. PROBES covers github, huggingface_hub, osf, ipfs_pinata, zenodo, netlify, cloudflare_pages, gumroad, leanpub, and stripe; pypi has no read-only endpoint and reports no-endpoint.
pythonfrom infrastructure.publishing.credential_check import check_all, format_results results = check_all(only=["github", "zenodo"]) # ProbeResult.status: pass|fail|skipped|no-endpoint print(format_results(results))
bashuv run python -m infrastructure.publishing.credential_check [--only NAMES] [--env-file .env] # exits 1 if any present credential fails
upload_runner.py houses the reusable per-platform upload helpers and the batch orchestrator (extracted from scripts/publish/upload_gold_refinement.py, now a thin CLI). Every uploader is dry-run by default and returns a receipt dict; a per-platform failure is captured, never aborting the batch.
pythonfrom pathlib import Path from infrastructure.publishing.upload_runner import ( UploadTargets, select_jobs, run_uploads, ) targets = UploadTargets( project_root=Path("."), pdf=Path("output/paper.pdf"), web_dir=Path("output/web"), hf_repo_id="org/dataset", github_repo="owner/repo", osf_title="My Project", ) jobs = select_jobs(include_github=False, include_static=False) # CORE_UPLOADERS only run = run_uploads(targets, jobs=jobs, commit=False) # dry-run print(run.mode, run.ok, run.results)
CORE_UPLOADERS are pinata, huggingface, osf, testpypi; OPTIONAL_UPLOADERS (github, netlify, cloudflare) are opt-in via include_github / include_static. commit=True performs real uploads.
pythonfrom infrastructure.publishing import validate_publication_readiness, create_submission_checklist readiness = validate_publication_readiness(markdown_files, pdf_files) # both list[Path] # -> dict: ready_for_publication, completeness_score, missing_elements, recommendations, skipped_files checklist = create_submission_checklist(metadata)
pythonfrom infrastructure.publishing.executable_bundle import bundle_project manifest_path = bundle_project(project_root, output_dir)
archival/ is a proper subpackage. The flat archival.py shim that used to sit at the package root has been removed; import infrastructure.publishing.archival resolves directly to the package.
pythonfrom infrastructure.publishing.archival import ( ZenodoProvider, IPFSPinataProvider, SoftwareHeritageProvider, archive_publication, ) run = archive_publication( Path("output/project/executable_bundle"), providers=[ZenodoProvider(token=None), SoftwareHeritageProvider()], dry_run=True, )
CLI dry-run:
bashuv run python -m infrastructure.publishing.archival_cli \ --bundle output/templates/template_code_project/executable_bundle \ --providers zenodo software_heritage ipfs_pinata ipfs_web3storage
See archival/README.md and scripts/runner/archive_publication.py.
Other measured skills in the registry, with their headline benchmark lift.