From 7d9b3b5cf395658fc7ad131a7ec596844b0c6614 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 14 Nov 2025 19:56:44 +0200 Subject: [PATCH 1/3] cache mirror list --- src/zigSetup.ts | 46 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/src/zigSetup.ts b/src/zigSetup.ts index b44f6f4..6c6e7ad 100644 --- a/src/zigSetup.ts +++ b/src/zigSetup.ts @@ -577,6 +577,37 @@ async function updateStatus(context: vscode.ExtensionContext): Promise { }); } +async function getMirrors(context: vscode.ExtensionContext): Promise { + let cached = { timestamp: 0, mirrors: "" }; + const key = "mirror-cache"; + + const cachedStr = context.globalState.get(key); + if (cachedStr !== undefined) { + cached = JSON.parse(cachedStr) as typeof cached; + } + + const millisecondsInDay = 24 * 60 * 60 * 1000; + if (new Date().getTime() - cached.timestamp > millisecondsInDay) { + try { + const response = await fetch("https://ziglang.org/download/community-mirrors.txt"); + if (response.status !== 200) throw Error("invalid mirrors"); + const mirrorList = await response.text(); + cached = { + timestamp: new Date().getTime(), + mirrors: mirrorList, + }; + context.globalState.update(key, JSON.stringify(cached)); + } catch { + // Cannot fetch mirrors, rely on cache. + } + } + + return cached.mirrors + .trim() + .split("\n") + .map((u) => vscode.Uri.parse(u)); +} + export async function setupZig(context: vscode.ExtensionContext) { { // This check can be removed once enough time has passed so that most users switched to the new value @@ -674,19 +705,6 @@ export async function setupZig(context: vscode.ExtensionContext) { break; } - let mirrors: vscode.Uri[] = []; - try { - const response = await fetch("https://ziglang.org/download/community-mirrors.txt"); - if (response.status !== 200) throw Error("invalid mirrors"); - const mirrorList = await response.text(); - mirrors = mirrorList - .trim() - .split("\n") - .map((u) => vscode.Uri.parse(u)); - } catch { - // Cannot fetch mirrors, attempt downloading from canonical source. - } - versionManagerConfig = { context: context, title: "Zig", @@ -695,7 +713,7 @@ export async function setupZig(context: vscode.ExtensionContext) { /** https://ziglang.org/download */ minisignKey: minisign.parseKey("RWSGOq2NVecA2UPNdBUZykf1CCb147pkmdtYxgb3Ti+JO/wCYvhbAb/U"), versionArg: "version", - mirrorUrls: mirrors, + mirrorUrls: await getMirrors(context), canonicalUrl: { release: vscode.Uri.parse("https://ziglang.org/download"), nightly: vscode.Uri.parse("https://ziglang.org/builds"), From a7e22b0e0dd012802de5a725159dc96ac4c5c6fc Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sat, 15 Nov 2025 15:06:23 +0200 Subject: [PATCH 2/3] fetch mirrors lazily Co-authored-by: Techatrix --- src/versionManager.ts | 4 ++-- src/zigSetup.ts | 16 +++++++--------- src/zls.ts | 4 +++- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/versionManager.ts b/src/versionManager.ts index 3e595e7..14d1643 100644 --- a/src/versionManager.ts +++ b/src/versionManager.ts @@ -36,7 +36,7 @@ export interface Config { * `"version"` for Zig, `"--version"` for ZLS */ versionArg: string; - mirrorUrls: vscode.Uri[]; + getMirrorUrls: () => Promise; canonicalUrl: { release: vscode.Uri; nightly: vscode.Uri; @@ -93,7 +93,7 @@ async function installGuarded(config: Config, version: semver.SemVer): Promise ({ mirror, sort: Math.random() })) .sort((a, b) => a.sort - b.sort) .map(({ mirror }) => mirror); diff --git a/src/zigSetup.ts b/src/zigSetup.ts index 6c6e7ad..0bb1c97 100644 --- a/src/zigSetup.ts +++ b/src/zigSetup.ts @@ -578,13 +578,8 @@ async function updateStatus(context: vscode.ExtensionContext): Promise { } async function getMirrors(context: vscode.ExtensionContext): Promise { - let cached = { timestamp: 0, mirrors: "" }; - const key = "mirror-cache"; - - const cachedStr = context.globalState.get(key); - if (cachedStr !== undefined) { - cached = JSON.parse(cachedStr) as typeof cached; - } + const key = "zig-mirror-cache"; + let cached = context.globalState.get(key, { timestamp: 0, mirrors: "" }); const millisecondsInDay = 24 * 60 * 60 * 1000; if (new Date().getTime() - cached.timestamp > millisecondsInDay) { @@ -596,7 +591,7 @@ async function getMirrors(context: vscode.ExtensionContext): Promise !!u) .map((u) => vscode.Uri.parse(u)); } @@ -713,7 +709,9 @@ export async function setupZig(context: vscode.ExtensionContext) { /** https://ziglang.org/download */ minisignKey: minisign.parseKey("RWSGOq2NVecA2UPNdBUZykf1CCb147pkmdtYxgb3Ti+JO/wCYvhbAb/U"), versionArg: "version", - mirrorUrls: await getMirrors(context), + getMirrorUrls() { + return getMirrors(context); + }, canonicalUrl: { release: vscode.Uri.parse("https://ziglang.org/download"), nightly: vscode.Uri.parse("https://ziglang.org/builds"), diff --git a/src/zls.ts b/src/zls.ts index 0a72eb2..0bdc3b6 100644 --- a/src/zls.ts +++ b/src/zls.ts @@ -511,7 +511,9 @@ export async function activate(context: vscode.ExtensionContext) { /** https://github.com/zigtools/release-worker */ minisignKey: minisign.parseKey("RWR+9B91GBZ0zOjh6Lr17+zKf5BoSuFvrx2xSeDE57uIYvnKBGmMjOex"), versionArg: "--version", - mirrorUrls: [], + getMirrorUrls() { + return Promise.resolve([]); + }, canonicalUrl: { release: vscode.Uri.parse("https://builds.zigtools.org"), nightly: vscode.Uri.parse("https://builds.zigtools.org"), From 2d3b3718f3c09f7de61deb752ffe2ba351d3f46b Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sat, 15 Nov 2025 22:11:37 +0200 Subject: [PATCH 3/3] cache result of getVersions This enables zig.install to work even if ziglang.org is down. --- src/zigSetup.ts | 54 ++++++++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/src/zigSetup.ts b/src/zigSetup.ts index 0bb1c97..a8d406c 100644 --- a/src/zigSetup.ts +++ b/src/zigSetup.ts @@ -71,33 +71,24 @@ async function findClosestSatisfyingZigVersion( version: semver.SemVer, ): Promise { if (version.prerelease.length !== 0) return version; - const cacheKey = `zig-satisfying-version-${version.raw}`; try { // We can't just return `version` because `0.12.0` should return `0.12.1`. - const availableVersions = (await getVersions()).map((item) => item.version); + const availableVersions = (await getVersions(context)).map((item) => item.version); const selectedVersion = semver.maxSatisfying(availableVersions, `^${version.toString()}`); - await context.globalState.update(cacheKey, selectedVersion ? selectedVersion.raw : undefined); return selectedVersion ?? version; } catch { - const selectedVersion = context.globalState.get(cacheKey, null); - return selectedVersion ? new semver.SemVer(selectedVersion) : version; + return version; } } async function getLatestTaggedZigVersion(context: vscode.ExtensionContext): Promise { - const cacheKey = "zig-latest-tagged"; try { - const zigVersion = await getVersions(); + const zigVersion = await getVersions(context); const latestTagged = zigVersion.find((item) => item.version.prerelease.length === 0); const result = latestTagged?.version ?? null; - await context.globalState.update(cacheKey, latestTagged?.version.raw); return result; } catch { - const latestTagged = context.globalState.get(cacheKey, null); - if (latestTagged) { - return new semver.SemVer(latestTagged); - } return null; } } @@ -108,13 +99,29 @@ async function getLatestTaggedZigVersion(context: vscode.ExtensionContext): Prom * * Throws an exception when no network connection is available. */ -async function getVersions(): Promise { - const [zigIndexJson, machIndexJson] = await Promise.all( - ["https://ziglang.org/download/index.json", "https://pkg.machengine.org/zig/index.json"].map(async (url) => { - const response = await fetch(url); - return response.json() as Promise; - }), - ); +async function getVersions(context: vscode.ExtensionContext): Promise { + const cacheKey = "zig-version-list"; + let zigIndexJson, machIndexJson; + try { + [zigIndexJson, machIndexJson] = await Promise.all( + ["https://ziglang.org/download/index.json", "https://pkg.machengine.org/zig/index.json"].map( + async (url) => { + const response = await fetch(url); + return response.json() as Promise; + }, + ), + ); + } catch (error) { + const cached = context.globalState.get(cacheKey); + if (cached !== undefined) { + for (const version of cached) { + // Must be instanceof SemVer + version.version = new semver.SemVer(version.version.raw); + } + return cached; + } + throw error; + } const indexJson = { ...machIndexJson, ...zigIndexJson }; const result: zigUtil.ZigVersion[] = []; @@ -140,6 +147,7 @@ async function getVersions(): Promise { ); } sortVersions(result); + await context.globalState.update(cacheKey, result); return result; } @@ -182,7 +190,7 @@ async function selectVersionAndInstall(context: vscode.ExtensionContext) { })); try { - const onlineVersions = await getVersions(); + const onlineVersions = await getVersions(context); outer: for (const onlineVersion of onlineVersions) { for (const version of versions) { if (semver.eq(version.version, onlineVersion.version)) { @@ -578,8 +586,8 @@ async function updateStatus(context: vscode.ExtensionContext): Promise { } async function getMirrors(context: vscode.ExtensionContext): Promise { - const key = "zig-mirror-cache"; - let cached = context.globalState.get(key, { timestamp: 0, mirrors: "" }); + const cacheKey = "zig-mirror-list"; + let cached = context.globalState.get(cacheKey, { timestamp: 0, mirrors: "" }); const millisecondsInDay = 24 * 60 * 60 * 1000; if (new Date().getTime() - cached.timestamp > millisecondsInDay) { @@ -591,7 +599,7 @@ async function getMirrors(context: vscode.ExtensionContext): Promise