Releasing an official USGS software information product#
gdptools is a public open-source software project. That is not the same thing
as an official USGS software information product — only the latter is citable by
other USGS information products, and only the latter carries a DOI.
This page is the procedure for producing one. For an ordinary engineering release (version bump, tag, PyPI, conda-forge) see RELEASING.md instead — that process stops short of everything below.
Open a tracking issue from the USGS Official Release issue template before you start; it carries this procedure as a checklist you can tick through, and doubles as the review artifact for IPDS.
What policy requires#
Five things, per the USGS Git and Software Release course:
Proper license, disclaimer, and metadata (
code.json)Appropriate review(s) and approval as defined by current policy
An approved Information Product Data System (IPDS) record
A Git tag, and a GitLab Release corresponding to that tag
A digital object identifier (DOI)
Local policies set by the mission area, science center, or equivalent unit may add more. Confirm with your supervisor rather than assuming.
Two files carry two states#
This is the part most easily got wrong, because it looks like a mistake when you see it in a diff.
File |
On |
On the release tag |
|---|---|---|
|
provisional wording (FSP §11) |
approved wording (FSP §5) |
|
|
|
The approved disclaimer is introduced on the release-candidate branch, captured
permanently in the tag, and never merged back to develop. CI enforces the trunk
half of this: tests/test_release_metadata.py fails if approved wording reaches
develop.
Both wordings come from the FSP Guidance on Disclaimer Statements, and each section is linked directly — copy from these rather than from anywhere else, including from this page:
§5 — approved software, for the release tag
§11 — provisional software, for
develop
A note on why develop reverts to provisional even though §5 says the USGS “reserves
the right to update the software as needed pursuant to further analysis and review”:
that clause scopes the warranty on the approved snapshot and makes future revisions
subject to further review — it does not extend the approval to later commits. The
first merge to develop after a release is unreviewed, so trunk must not claim an
approval it does not hold.
Before you start#
Reserve a DOI through the USGS Asset Identifier Service. Do not activate it yet — activation happens after the GitLab Release exists, because the DOI must resolve to the Release. Some units have a designated person who creates DOIs; check first.
Create
CITATION.mdondevelopcarrying the reserved DOI. Do this only once the DOI is reserved — a file with a placeholder DOI is worse than no file.Create the IPDS record.
Identify your administrative, code, and domain reviewers, and your approving official.
Confirm the engineering release this product is based on is already merged and green.
Confirm the
tests-fulljob passed on the latestdeveloppipeline. It runs automatically there and its failure fails the pipeline, so this is a check rather than an action. On a merge request it is still a manual play button that never blocks. It runs theslowtests, which reach the NHGF STAC catalog, a THREDDS OPeNDAP endpoint and a parquet file on GitHub; those are deselected from every merge gate on purpose, because a third-party outage should not redden unrelated merge requests.Write the release’s
HISTORY.mdsection ondevelop, describing every user-visible change since that engineering release and labelling anything breaking. Do this ondevelop, not on the release-candidate branch: the candidate is never merged back, so an entry written there is lost. The published changelog{include}s this file, so an empty section means the documentation shows the previous release as the newest while PyPI serves this one.
The release-candidate branch#
The release-candidate branch is named exactly after the tag it will become —
0.4.1, not v0.4.1, not rc-0.4.1. It is created as an orphan branch so the
review merge request shows the whole project as a reviewable diff.
git switch develop
git pull --ff-only origin develop
git switch --orphan 0.4.1
# If git asks "Deletion of directory 'NAME' failed. Should I try again?", answer n
git commit --allow-empty -m 'Create release candidate branch'
git push -u origin 0.4.1
# The orphan branch leaves you standing on an empty working tree -- go back.
git switch develop
Then open a merge request from develop into 0.4.1. This is the opposite
direction from normal development — do not use the “Create merge request” button
GitLab offers after the push, which would propose 0.4.1 → develop. Create it
manually and set the source and target explicitly.
Select the USGS_Release_Review description template. It is the course template already trimmed to this project, with separate sections for the administrative, code and domain reviewers, so nobody has to trim one under time pressure. Delete anything that genuinely does not apply to this release and say in its Notes what you deleted.
The upstream originals, if you need to compare:
Review and reconciliation#
The review must cover administrative, code, and domain aspects. The review template scopes a section to each, so a reviewer reads only their own.
Tell the administrative reviewer this explicitly, because a green pipeline
invites the opposite conclusion: the trunk-state guards in
tests/test_release_metadata.py stand themselves down on any ref named
X.Y.Z — that is, on the release-candidate branch. That is deliberate, since
the branch carries product state on purpose. But it means code.json’s pinned
URLs and Release status, and the approved FSP §5 disclaimer, are not
machine-checked where the reviewers are looking. Those are precisely the items
that must be verified by hand. The template says so at the top of that section.
AI-assisted review is permitted provided a human reads the review and signs off on it. If you use it, record the sign-off explicitly on the checklist — who read it, and when. An unattributed AI review is not a review.
To reconcile:
Make fixes on
develop, not on the release-candidate branch.Push them. The merge request updates automatically, because
developis its source branch. Do not merge into the release-candidate branch to pick fixes up — that branch is the merge request’s target, and merging into it ends the review.Reply to each review thread with the commit hash that addressed it, then resolve the thread. Push the commit before posting the comment, or GitLab will not render the hash as a link.
When every thread is resolved, expand them all and print the merge request to PDF. That PDF is the review and reconciliation artifact for IPDS.
Merging the review merge request#
Once the review is approved and every thread is resolved, merge the merge request into the release-candidate branch. The RC branch was created as an orphan branch holding a single empty commit; until this merge it has no project content at all, and a tag cut from it would archive an empty tree.
Everything in the sections below happens on the release-candidate branch after this merge.
Switching the branch to product state#
On the release-candidate branch only:
uv run bump2version <part>to set the product version, choosing whichever bump produces exactly the release-candidate branch name. The product version is normally a patch off the engineering release it is based on (e.g.0.4.1off0.4.0), sopatchis normally the right part. The branch is named after the tag it will become, so a bump that lands on any other number breaks that invariant.bump2versionmoves.bumpversion.cfg,src/gdptools/__init__.py, anddocs/_config.ymltogether — do not hand-edit any of them.Bump the
gdptools==X.Y.Zpin inenvironment-examples.ymlto the version being released.bump2versiondoes not touch this file, and it does not track the package version automatically: it pins whatever release readers should install to run the example notebooks. It sat at0.3.2from 0.3.10 through 0.4.0, so anyone building that environment got a package predating the removal of the dask engine and the requiredcalc_crs, while the notebooks in the repository assumed the current one. Pin the version being released even though conda-forge will not have it until the feedstock’s autotick bot lands — the file describes the release, and the environment is built after that package is available.In
code.json, setversionto the release version andstatusto"Release"In
code.json, pin only the URLs that carry a version segment to the release version instead ofdevelop:disclaimerURL, the license URL underpermissions.licenses, anddownloadURL. LeavehomepageURLandrepositoryURLexactly as they are — they are the bare repository URL and its.gitform, with no version segment to pin. Mangling these two is the release validator’s most common rejection.In
code.json, replace thelaborHours: 0placeholder with the real figure — it ships into the permanent, citable product recordIn
code.json, refreshdate.metadataLastUpdatedto the date you are preparing the releaseReplace
DISCLAIMER.mdwith the approved FSP §5 wordingRe-confirm
CITATION.mdstill carries the reserved DOI
The trunk-state guards in tests/test_release_metadata.py stand down automatically
on a ref whose name is exactly a version number — which the release-candidate
branch and its tag are — so a green pipeline is still expected here. They key
on the ref, deliberately never on the file contents they check, so that drift on
trunk cannot switch off the very guards that catch it. If they go red on the RC
branch, the branch name and the product state disagree — fix the state, do not
delete the guard.
Requesting publication#
Open an issue on the USGS GitLab Software Management queue and select the GitLab Official Release description template.
Replace the examples between backticks with your values.
Change
[ ]to[x]to acknowledge each item.Leave the
/labellines alone — editing them delays processing.Tag the approving official with
@username, or give their email if they have no GitLab account.
An administrator runs an automated validation tool against the request. Its most
common finding is incorrect URLs in code.json — which is why the trunk copy is
tested in CI and the product copy is pinned deliberately.
Publishing#
Once the request is approved:
Create the tag from the release-candidate branch, named identically to it.
Create a GitLab Release from that tag. Put the recommended citation in the release notes. It lives in CITATION.md; copy it from there rather than retyping it. The form is:
Author, A.B., Author, C.D., and Author, E.F., YEAR, gdptools, version X.Y.Z: U.S. Geological Survey software release, https://doi.org/10.5066/XXXXXXXX
Note the punctuation: a comma before
version, a colon after it. The year is the year the DOI is activated, which may not be the year the work was done. The Release, not the tag, is what the DOI points at — release notes can be edited later to redirect users to a newer version, and a tag cannot.Delete the release-candidate branch. A branch and a tag of the same name share a URL, so leaving both is confusing.
Activate the DOI at the Asset Identifier Service, pointing it at
https://code.usgs.gov/wma/nhgf/toolsteam/gdptools/-/releases/<version>. You will need the creators, publication year, that URL, the IPDS number, and the related publication if there is one. A description is required for the product to appear on the USGS website.Disseminate the record in IPDS, following your center’s policy.
Publish to PyPI from the approved tag, so the citable product and the distributed artifact are the same code.
Confirm the conda-forge feedstock updates. The feedstock’s bot notices the new sdist on PyPI and opens a pull request against
conda-forge/gdptools-feedstockon its own; check that it appeared, that its dependency changes are right, and that it is merged. No manual upload is needed, but nobody is told if the bot’s pull request sits unmerged.Check the feedstock’s open issues before merging that pull request. Recipe cleanup is deliberately deferred to ride along with a version bump, rather than spending a build-number bump and a separate review on a test-only change — so the bot’s pull request is where those changes are meant to land. As of 0.4.0 there is one such issue, gdptools-feedstock#78, carrying five items. It lives on GitHub, in another repository, on a queue nobody on this side watches; this step is the only place it reliably resurfaces.
Bring
developforward past the released version. The version bump happened on the release-candidate branch, which is never merged back, sodevelopand.bumpversion.cfgstill read the previous version — and the nextuv run bump2version patchondevelopwould regenerate a number already on PyPI, whichuv publishrejects. Runuv run bump2versionondevelopto move it past the released version — that moves.bumpversion.cfg,src/gdptools/__init__.py, anddocs/_config.ymltogether, and hand-editing any of them instead lands a red pipeline, becausetests/test_version_consistency.pyfails when they drift. Merge that through the normal process. The version-consistency tests cannot catch this: both files agree with each other, they just disagree with PyPI.Verify it rather than trusting the checkbox:
uv run nox -s check-pypi-version
That fails if PyPI holds a release newer than the version on the branch, and names the version and the fix. It skips rather than fails when PyPI is unreachable, and stands down on release tags and release-candidate branches, where the version is published on purpose. It is not part of the default test suite, because it needs the network. See issue #108.
References#
Resource |
Link |
|---|---|
USGS Git and Software Release course |
https://cdi.code-pages.usgs.gov/usgs-git-and-software-release-course/ |
FSP Guidance on Disclaimer Statements |
|
USGS Asset Identifier Service |
|
Software Management publication queue |
https://code.chs.usgs.gov/software/software-management/-/issues |
Administrative templates |
https://code.chs.usgs.gov/software/software-management/-/tree/main/administrative_templates |
USGS Thesaurus (for |
|
IM OSQI 2019-01 — Review and Approval of Scientific Software for Release |
https://www.usgs.gov/survey-manual/im-osqi-2019-01-review-and-approval-scientific-software-release |
SM 502.5 — Safeguarding Unpublished USGS Scientific Information |