Diffinery

codician/diffinerypublic

Open-source, agent-first GitHub and GitHub Actions alternative that runs on your own Cloudflare account: git hosting, pull requests, code review and drop-in GitHub Actions YAML on Workers, Durable Objects, R2 and Containers.

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 @@
8787
8888 What's different about the hosted image:
8989
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).
9191 - Jobs run as `root` and `sudo` isn't installed. Drop `sudo` from commands.
9292 - The apt package lists are removed. Run `apt-get update` before `apt-get install`.
9393 - 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 @@
8383 | GitHub CLI | `gh`, from GitHub's apt repository |
8484 | Build tools | `build-essential` (gcc, g++, make and the C library headers) |
8585 | 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 |
8787 | Docker | Docker Engine (`docker-ce`), the Docker CLI, `containerd.io`, the Buildx plugin, the Compose plugin, `iptables` |
8888
8989 Image settings that affect your steps:
modifiedapps/diffinery/src/repo/releases.ts+23−14View file
@@ -112,6 +112,8 @@
112112 };
113113
114114 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;
115117
116118 /** GitHub renames asset files with unusual characters; do the same. */
117119 export function assetName(raw: string): string {
@@ -663,22 +665,29 @@
663665 return "";
664666 }
665667 })();
666- let url = source.url;
667- let headers = { ...(source.headers ?? {}) };
668668 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;
680688 }
681- break;
689+ if (!res || !(res.status >= 500 || res.status === 429) || attempt === ASSET_DOWNLOAD_TRIES - 1) break;
690+ await res.body?.cancel();
682691 }
683692 if (!res || !res.ok || !res.body) {
684693 await res?.body?.cancel();
modifiedcontainers/runner/Dockerfile+12−0View file
@@ -47,6 +47,18 @@
4747 npm cache clean --force; \
4848 rm -rf /root/.npm /tmp/*
4949
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+
5062 # GitHub CLI (preinstalled on hosted runners; workflows call `gh` with GH_TOKEN).
5163 RUN set -eux; \
5264 install -m 0755 -d /etc/apt/keyrings; \