Use this procedure for release candidates. Routine changes use the scoped development checks; the complete release acceptance and source-bound receipt described below remain required for a release.
- Confirm that the working tree contains only intended changes.
- Update
CHANGELOG.md. - Set the same version in:
pyproject.toml;src/diffractscout/__init__.py;CITATION.cff.
- Update screenshots, validation counts, paper statements, and source-lineage notes when affected.
Normal development only needs .[test]. Before a complete local release
preflight, install both the test and release tools:
python -m pip install -e ".[test,release]"python scripts/check_release.pyThe default output directory is dist. It must be empty; the preflight never
deletes or overwrites existing distributions. When dist/ already contains
artifacts, use an empty directory outside the checkout, for example:
python scripts/check_release.py --dist-dir /tmp/diffractscout-release-distOn Windows / PowerShell, use the same Python environment that holds the installed checkout and release tools, with a fresh temporary target:
$releaseDist = Join-Path $env:TEMP ("diffractscout-release-" + [guid]::NewGuid().ToString("N"))
.\.venv\Scripts\python.exe scripts/check_release.py --dist-dir $releaseDistThis checks required files, version consistency, bibliography keys, compilation,
tests, the offline demo, analytic benchmark, manifest verification, wheel and
sdist creation, Twine metadata, and a source-independent install/run of the
newly built wheel. A successful full run writes an ignored
build/release-preflight/release_acceptance.json receipt bound to the exact
current tracked and non-ignored source fingerprint (including intended new
files, but excluding ignored build/workflow artifacts). The standalone
--stage release --strict gate is
fail-closed until that receipt exists and still matches; a run using
--skip-tests or --skip-wheel never writes it.
The preflight fingerprints the source both before and after execution and
refuses a receipt if source changed while tests were running. In GitHub tag
workflows the strict gate additionally requires a clean checkout; a local
uncommitted release candidate may be tested, but it cannot be mistaken for a
taggable commit.
Retain scientific and JOSS preflight artifacts:
diffractscout benchmark -o build/release-benchmark
python scripts/joss_readiness.py --stage release --strict --output build/joss-readinessThe release stage deliberately ignores public-age, impact, external-engagement, and archive-DOI gates while still rejecting release-structure, version, license, ledger, repository, and paper-structure defects. Run the submission stage separately to retain the honest JOSS blocker snapshot.
Additional GUI smoke check on Linux:
xvfb-run -a python -c \
"from diffractscout.gui import create_app; app=create_app(); app.update(); app.destroy()"Inspect the generated wheel:
python -m zipfile -l dist/diffractscout-*.whlCreate a new environment outside the repository and install the wheel:
python -m venv /tmp/diffractscout-wheel-test
/tmp/diffractscout-wheel-test/bin/python -m pip install dist/diffractscout-*.whl
/tmp/diffractscout-wheel-test/bin/python -m diffractscout demo -o /tmp/diffractscout-demo
/tmp/diffractscout-wheel-test/bin/python -m diffractscout verify /tmp/diffractscout-demoWindows / PowerShell equivalent, using the wheel produced in $releaseDist:
$wheelTestRoot = Join-Path $env:TEMP ("diffractscout-wheel-" + [guid]::NewGuid().ToString("N"))
.\.venv\Scripts\python.exe -m venv $wheelTestRoot
$wheelPython = Join-Path $wheelTestRoot "Scripts/python.exe"
$wheelFiles = @(Get-ChildItem -LiteralPath $releaseDist -Filter "diffractscout-*.whl" -File)
if ($wheelFiles.Count -ne 1) { throw "Expected exactly one DiffractScout wheel." }
& $wheelPython -m pip install $wheelFiles[0].FullName
Push-Location $wheelTestRoot
try {
& $wheelPython -m diffractscout demo -o demo
& $wheelPython -m diffractscout verify demo
} finally {
Pop-Location
}For a local preflight whose normal temporary directory is inaccessible or lies
inside the checkout, set DIFFRACTSCOUT_CLEAN_WHEEL_ROOT to a writable directory
outside the repository. The smoke test rejects an import that resolves outside
the newly created environment. CI installs dependencies into that new
environment. An offline local audit may additionally set
DIFFRACTSCOUT_CLEAN_WHEEL_SYSTEM_SITE_PACKAGES=1; this reuses the already
validated dependency environment but still installs the wheel with no source
path and runs pip-check, demo, verify, benchmark, and quick-export outside the
checkout. The receipt records which mode was used.
Review:
docs/SCIENTIFIC_CONTRACTS.md;docs/SOURCE_LINEAGE.md;docs/VALIDATION.md;- dependency licenses;
NOTICE.md;AUTHORS.md;paper/paper.mdand its AI disclosure;- author, affiliation, funding, acknowledgements, and archive metadata.
The human release owner should inspect representative CSV/XLSX/provenance output and confirm that diagnostic errors are visible and scientific boundaries remain accurate.
The exact JOSS draft is generated by .github/workflows/draft-pdf.yml. When Docker is available locally:
bash scripts/build_paper.shInspect every PDF page using a local viewer or renderer as described in PDF verification. Check equations, figures, references and missing glyphs, and record any unverified pages.
The bundled publication helpers create the repository when it is absent, replace a local bundle/file origin with the intended GitHub URL, push the current branch, and configure repository topics:
DIFFRACTSCOUT_PUBLISH_DRY_RUN=1 bash scripts/publish_github.sh
bash scripts/publish_github.sh D-sudoasd DiffractScout --private$env:DIFFRACTSCOUT_PUBLISH_DRY_RUN = "1"
.\scripts\publish_github.ps1
Remove-Item Env:DIFFRACTSCOUT_PUBLISH_DRY_RUN
.\scripts\publish_github.ps1 -Owner D-sudoasd -Repository DiffractScout -Visibility privateThe dry run still checks GitHub CLI authentication and repository visibility, then prints the planned create/remote/push/edit commands without changing local or remote state.
Pushing a version tag matching the package version triggers .github/workflows/release.yml. The workflow repeats the release preflight, which builds the wheel and source distribution, validates distribution metadata with Twine, and smoke-tests the built wheel; it then packages the analytic benchmark, offline demo, and readiness report with scripts/archive_tree.py, computes SHA256SUMS.txt, and creates the GitHub Release. The archive helper uses sorted paths, a timestamp fixed by SOURCE_DATE_EPOCH, a single safe root, symbolic-link rejection, and atomic replacement so the scientific evidence archives are reproducible for a fixed source and runtime stack.
When a release is created for the first time, the workflow passes
--generate-notes to GitHub, so GitHub automatically generates the release
notes. The workflow does not use CHANGELOG.md as a notes file. Maintainers
must review and curate the generated notes against the current-version
CHANGELOG.md, before publication when practical or immediately after
publication when that is the available review point. CHANGELOG.md remains
the project's versioned history and review reference.
.github/workflows/monthly-audit.yml reruns the release and scientific checks on the first day of each month and retains deterministic evidence archives for 90 days. .github/dependabot.yml proposes monthly Python and GitHub Actions updates. Timer-triggered audit runs are maintenance evidence; they do not substitute for substantive public commits, validation, support records, issues, pull requests, or releases during the six-month period.
After CI passes on the release commit:
- merge the release pull request;
- delete the merged feature branch after confirming the PR head SHA is represented in the default branch;
- create a new annotated tag matching the package version, for example
v0.4.0; never move an existing tag; - push the tag;
- review and curate GitHub's automatically generated release notes against the
current-version
CHANGELOG.mdbefore publication when practical, or immediately after publication; - attach distribution files and checksums when appropriate;
- for ordinary pre-submission releases, retain the verified GitHub Release artifacts and public CI URL;
- after successful JOSS review, create the final tag if needed and archive that exact repository state with Zenodo or an equivalent service;
- add the post-review software archive DOI to
CITATION.cff, the bibliography, GitHub Release, and the JOSS review issue; - rebuild the JOSS draft and run
--stage publication --strictafter archive metadata changes.
Remote branch deletion is a separate destructive action. Delete only branches that are merged, have no unique unreviewed commits, are not protected/default/current branches, and have been explicitly selected for cleanup.