Runner image has nvm; release asset imports retry GitHub's server errors (#123)
nvm 0.40.7 is in the hosted image (pinned to its commit, loaded from /etc/profile.d in login shells), and an imported release asset whose download gets a 5xx or 429 is tried up to three times. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AizKXSsengXZRegJxRATkL
Canberk Demirci authored · GitHub committed signed1 parent 1d1a71fcommit e16231071490bfc95cb73841d70344d27853f2c7Browse files
4 files changed+37−16
Files (4)
modifiedapps/diffinery/docs/actions/compatibility.md+1−1View file
| @@ -87,7 +87,7 @@ | ||
| 87 | 87 | |
| 88 | 88 | What's different about the hosted image: |
| 89 | 89 | |
| 90 | -- It's much smaller than GitHub's image. Git, Git LFS, curl, jq, zstd, build-essential, Python 3.12, Node.js 22 with npm, pnpm and yarn, Docker and the GitHub CLI (`gh`) are preinstalled. `ImageOS` is `ubuntu24`, as on GitHub's `ubuntu-24.04` image. Go, Java, Ruby, Rust, .NET, PowerShell, browsers and cloud CLIs aren't. See [The Ubuntu 24.04 image](/docs/actions/runners#the-ubuntu-2404-image). | |
| 90 | +- It's much smaller than GitHub's image. Git, Git LFS, curl, jq, zstd, build-essential, Python 3.12, Node.js 22 with npm, pnpm and yarn, nvm, Docker and the GitHub CLI (`gh`) are preinstalled. `ImageOS` is `ubuntu24`, as on GitHub's `ubuntu-24.04` image. Go, Java, Ruby, Rust, .NET, PowerShell, browsers and cloud CLIs aren't. See [The Ubuntu 24.04 image](/docs/actions/runners#the-ubuntu-2404-image). | |
| 91 | 91 | - Jobs run as `root` and `sudo` isn't installed. Drop `sudo` from commands. |
| 92 | 92 | - The apt package lists are removed. Run `apt-get update` before `apt-get install`. |
| 93 | 93 | - Every job starts in a fresh container. Nothing carries over between jobs except caches and artifacts. |
modifiedapps/diffinery/docs/actions/runners.md+1−1View file
| @@ -83,7 +83,7 @@ | ||
| 83 | 83 | | GitHub CLI | `gh`, from GitHub's apt repository | |
| 84 | 84 | | Build tools | `build-essential` (gcc, g++, make and the C library headers) | |
| 85 | 85 | | Python | Ubuntu's `python3` (3.12), `python3-pip`, `python3-venv`, `python-is-python3` (so `python` runs `python3`) | |
| 86 | -| Node.js | Node.js 22.22.0 with npm, plus corepack shims activated for pnpm 10.28.0 and yarn 1.22.22 | | |
| 86 | +| Node.js | Node.js 22.22.0 with npm, plus corepack shims activated for pnpm 10.28.0 and yarn 1.22.22; nvm 0.40.7 in `/usr/local/nvm` (`NVM_DIR`), loaded in login shells such as `shell: bash -l {0}`, as on GitHub | | |
| 87 | 87 | | Docker | Docker Engine (`docker-ce`), the Docker CLI, `containerd.io`, the Buildx plugin, the Compose plugin, `iptables` | |
| 88 | 88 | |
| 89 | 89 | Image settings that affect your steps: |
modifiedapps/diffinery/src/repo/releases.ts+23−14View file
| @@ -112,6 +112,8 @@ | ||
| 112 | 112 | }; |
| 113 | 113 | |
| 114 | 114 | const MAX_IMPORT_BYTES = 2 * 1024 ** 3; |
| 115 | +/** Downloads of an imported release asset, when GitHub answers with a server error. */ | |
| 116 | +const ASSET_DOWNLOAD_TRIES = 3; | |
| 115 | 117 | |
| 116 | 118 | /** GitHub renames asset files with unusual characters; do the same. */ |
| 117 | 119 | export function assetName(raw: string): string { |
| @@ -663,22 +665,29 @@ | ||
| 663 | 665 | return ""; |
| 664 | 666 | } |
| 665 | 667 | })(); |
| 666 | - let url = source.url; | |
| 667 | - let headers = { ...(source.headers ?? {}) }; | |
| 668 | 668 | let res: Response | null = null; |
| 669 | - for (let hop = 0; hop < 5; hop++) { | |
| 670 | - if (!allowed(url)) throw new HttpError(422, "asset URL must be an https URL on github.com, its asset hosts, or the configured GitHub Enterprise host"); | |
| 671 | - res = await fetch(url, { headers, redirect: "manual" }); | |
| 672 | - const loc = res.headers.get("location"); | |
| 673 | - if (res.status >= 300 && res.status < 400 && loc) { | |
| 674 | - await res.body?.cancel(); | |
| 675 | - const next = new URL(loc, url); | |
| 676 | - if (next.host !== startHost) headers = withoutCredentials(headers); | |
| 677 | - url = next.toString(); | |
| 678 | - res = null; | |
| 679 | - continue; | |
| 669 | + // GitHub's asset host answers some downloads in a large release with a 5xx (or 429): try those again. | |
| 670 | + for (let attempt = 0; attempt < ASSET_DOWNLOAD_TRIES; attempt++) { | |
| 671 | + if (attempt) await new Promise((r) => setTimeout(r, 2000 * 3 ** (attempt - 1))); | |
| 672 | + let url = source.url; | |
| 673 | + let headers = { ...(source.headers ?? {}) }; | |
| 674 | + res = null; | |
| 675 | + for (let hop = 0; hop < 5; hop++) { | |
| 676 | + if (!allowed(url)) throw new HttpError(422, "asset URL must be an https URL on github.com, its asset hosts, or the configured GitHub Enterprise host"); | |
| 677 | + res = await fetch(url, { headers, redirect: "manual" }); | |
| 678 | + const loc = res.headers.get("location"); | |
| 679 | + if (res.status >= 300 && res.status < 400 && loc) { | |
| 680 | + await res.body?.cancel(); | |
| 681 | + const next = new URL(loc, url); | |
| 682 | + if (next.host !== startHost) headers = withoutCredentials(headers); | |
| 683 | + url = next.toString(); | |
| 684 | + res = null; | |
| 685 | + continue; | |
| 686 | + } | |
| 687 | + break; | |
| 680 | 688 | } |
| 681 | - break; | |
| 689 | + if (!res || !(res.status >= 500 || res.status === 429) || attempt === ASSET_DOWNLOAD_TRIES - 1) break; | |
| 690 | + await res.body?.cancel(); | |
| 682 | 691 | } |
| 683 | 692 | if (!res || !res.ok || !res.body) { |
| 684 | 693 | await res?.body?.cancel(); |
modifiedcontainers/runner/Dockerfile+12−0View file
| @@ -47,6 +47,18 @@ | ||
| 47 | 47 | npm cache clean --force; \ |
| 48 | 48 | rm -rf /root/.npm /tmp/* |
| 49 | 49 | |
| 50 | +# nvm, as on hosted runners: login shells (`shell: bash -l {0}`) can `nvm install` other Node.js versions. | |
| 51 | +# A fixed tag, checked against its commit. | |
| 52 | +ARG NVM_VERSION=v0.40.7 | |
| 53 | +ARG NVM_COMMIT=f0b0c6bb0b281ceeb106c8cf9ab8fde141215092 | |
| 54 | +ENV NVM_DIR=/usr/local/nvm | |
| 55 | +RUN set -eux; \ | |
| 56 | + git clone -q --depth 1 --branch "${NVM_VERSION}" https://github.com/nvm-sh/nvm.git "${NVM_DIR}"; \ | |
| 57 | + test "$(git -C "${NVM_DIR}" rev-parse HEAD)" = "${NVM_COMMIT}"; \ | |
| 58 | + rm -rf "${NVM_DIR}/.git"; \ | |
| 59 | + printf '%s\n' 'export NVM_DIR=/usr/local/nvm' '[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"' > /etc/profile.d/nvm.sh; \ | |
| 60 | + bash -lc 'nvm --version' | |
| 61 | + | |
| 50 | 62 | # GitHub CLI (preinstalled on hosted runners; workflows call `gh` with GH_TOKEN). |
| 51 | 63 | RUN set -eux; \ |
| 52 | 64 | install -m 0755 -d /etc/apt/keyrings; \ |