Checked for duplicates
Yes - I've already checked
🗒 User Story
As a roundup user, I want builds to automatically retry calls to external network services with exponential backoff so that transient network errors do not cause permanent build failures.
💪 Motivation
A stable release of doi-service failed during artifact publication due to a 400 Bad Request from PyPI during a twine upload. The error was a transient network issue, but the build had no retry logic and failed permanently — requiring a manual re-run.
The following external network calls in roundup have no retry logic today:
- PyPI —
twine upload (_python.py _ArtifactPublicationStep)
- GitHub releases —
python-release, maven-release, nodejs-release (_GitHubReleaseStep in each context)
- Maven Central —
mvn deploy (_maven.py _ArtifactPublicationStep)
- npmjs.com —
npm publish (_nodejs.py _ArtifactPublicationStep)
- GitHub API —
github_changelog_generator, requirement-report, upload_asset (step.py)
- git push/pull/fetch — across
util.py, _maven.py, and all context-specific steps
- gh-pages —
deploy.sh (DocPublicationStep)
📋 Acceptance Criteria
🩺 Additional context
The invoke_with_retry function should be placed in util.py and imported where needed. The retry count and initial delay should have sensible defaults but ideally be configurable. The github3 upload_asset() call in DocPublicationStep is a Python API call (not a subprocess) and will need its own inline retry loop.
Checked for duplicates
Yes - I've already checked
🗒 User Story
As a roundup user, I want builds to automatically retry calls to external network services with exponential backoff so that transient network errors do not cause permanent build failures.
💪 Motivation
A stable release of
doi-servicefailed during artifact publication due to a400 Bad Requestfrom PyPI during atwine upload. The error was a transient network issue, but the build had no retry logic and failed permanently — requiring a manual re-run.The following external network calls in roundup have no retry logic today:
twine upload(_python.py_ArtifactPublicationStep)python-release,maven-release,nodejs-release(_GitHubReleaseStepin each context)mvn deploy(_maven.py_ArtifactPublicationStep)npm publish(_nodejs.py_ArtifactPublicationStep)github_changelog_generator,requirement-report,upload_asset(step.py)util.py,_maven.py, and all context-specific stepsdeploy.sh(DocPublicationStep)📋 Acceptance Criteria
invoke_with_retry(argv, retries=3, delay=30)is added toutil.pythat wrapsinvoke()with exponential backoff (e.g. 30s, 60s, 120s between attempts)invoke_with_retryis used for all external network service calls listed above🩺 Additional context
The
invoke_with_retryfunction should be placed inutil.pyand imported where needed. The retry count and initial delay should have sensible defaults but ideally be configurable. Thegithub3upload_asset()call inDocPublicationStepis a Python API call (not a subprocess) and will need its own inline retry loop.