Every download tries again, the checks cache what they download, and the CI skips no check for an unreachable server¶
- Status: accepted
- Date: 2026-10-13
Context¶
The workflows download tools that no package manager brings: krankerl, which packages a Nextcloud app, Infection, gitsign, the certificate of an app and the server of Nextcloud that signs it. They used curl --fail without trying again. On 2026-10-12, GitHub answered a download of krankerl with a 504 for a moment, the checks of a Nextcloud app failed on main, and the flaky-test bot reported a flaky test that wasn't one (Dennis-Otto/paperless-unified-search#94); the second attempt passed unchanged. The same error in the isolated build of a release leaves a draft that a maintainer has to finish by hand. Scorecard and release-please met other 5xx answers of GitHub the same day.
The opposite also happened: when the schema of the App Store was out of reach, scripts/check.sh passed its check of appinfo/info.xml with "the CI checks it", and in the CI, which runs the same script, nothing else did.
Options¶
- Run the failed jobs again, by hand or by the flaky-test bot. It costs a full run, and a release can't simply run again.
- One helper script for every download. A workflow would need a checkout of its own for it, and a release would run code from the repository in the job that holds the signing key.
- The options of curl that try again, in every download, with a test that keeps them there; a cache for the tools of the checks; and a CI that fails a check it can't make.
Decision¶
Option 3. Every download in the workflows and scripts runs curl --fail --retry 5 --retry-all-errors --connect-timeout 15 --max-time 300 --output FILE: every error, a reset connection too, brings another try, after a pause that doubles each time (1, 2, 4, 8 and 16 seconds, or what the server asks for); a connection that hangs fails and tries again instead of holding the job until its time limit; and the download goes into a file, which curl empties before it tries again, where in a pipe the start of a broken try would stay in front of the next. Every tool is still checked against its pinned checksum. Uploads, such as a release in the App Store, try again only where the API takes a second one without harm. test_every_download_tries_again_after_a_passing_error holds the blueprint and every kind of project to this.
The checks of a Nextcloud app keep krankerl in the cache of GitHub Actions, under a key of its version and checksum, and check the checksum of the cached package too. GitHub saves the cache only after a run that passed. A release builds without any cache, so that nothing a pull request saved can reach a signed package.
---
config:
flowchart:
wrappingWidth: 400
---
flowchart TB
accTitle: How the checks of a Nextcloud app get krankerl
accDescr: The checks restore krankerl from the cache under a key of its version and checksum. Without a cached package they download it, trying again after a passing error. Either way the checksum is checked before krankerl is installed, and GitHub saves the cache after a run that passed. A release always downloads krankerl and uses no cache.
restore["Restore from the cache<br>key: version + checksum"]
hit{"In the cache?"}
download["Download<br>tries again after a passing error"]
verify["Check the checksum"]
install["Install krankerl"]
save["GitHub saves the cache<br>after a run that passed"]
release["A release: always the download<br>never a cache"]
restore --> hit
hit -- "yes" --> verify
hit -- "no" --> download --> verify
verify --> install --> save
release --> download
classDef bot fill:#526cfe2e,stroke:#526cfe,stroke-width:2px
classDef done fill:#16a34a2e,stroke:#16a34a,stroke-width:2px
class restore,download,verify bot
class install,save done
scripts/check.sh still passes a check that needs a server out of reach on a computer of a developer, and says that the CI makes it. In the CI, where CI is set, such a check fails and says to run it again later, because no other check would follow.
Consequences¶
A passing error of a server costs a few seconds instead of a failed run, and most runs of the checks of a Nextcloud app no longer download krankerl at all. A download that fails for good, such as a wrong address, now fails after about half a minute of tries instead of at once. A check of the CI can fail while the App Store is down; it passes when it runs again. A new download in a workflow or a script needs the same options, which the test asks for.