| … | … | @@ -34,6 +34,7 @@ export async function buildAndPublish ({ |
| 34 | 34 | isTag = false, |
| 35 | 35 | version: forcedVersion, |
| 36 | 36 | channel: forcedChannel, |
| 37 | vdriveSeeder, | |
| 37 | 38 | onLog |
| 38 | 39 | } = {}) { |
| 39 | 40 | if (!sync) throw new Error('buildAndPublish requires sync service') |
| … | … | @@ -136,6 +137,17 @@ export async function buildAndPublish ({ |
| 136 | 137 | onLog |
| 137 | 138 | }) |
| 138 | 139 | if (vdrive.skipped) log(onLog, `[ci] vdrive skipped: ${vdrive.reason}`) |
| 140 | // Announce it NOW. The seeder's boot scan cannot have seen a drive that | |
| 141 | // did not exist yet, so without this a freshly published drive stays | |
| 142 | // unreachable until the next daemon restart. | |
| 143 | if (vdrive.ok && vdriveSeeder) { | |
| 144 | try { | |
| 145 | const seeded = await vdriveSeeder.announceRepo(repo) | |
| 146 | if (seeded) log(onLog, `[ci] vdrive seeding ${repo} -> ${seeded.link}`) | |
| 147 | } catch (err) { | |
| 148 | log(onLog, `[ci] vdrive announce failed (release unaffected): ${err.message}`) | |
| 149 | } | |
| 150 | } | |
| 139 | 151 | } catch (err) { |
| 140 | 152 | vdrive = { ok: false, error: err.message } |
| 141 | 153 | log(onLog, `[ci] vdrive failed (release unaffected): ${err.message}`) |
| … | … | @@ -201,7 +213,7 @@ export async function snapshotRefs (repoDir) { |
| 201 | 213 | * Only refs under refs/heads/ (branches) and refs/tags/ (tags) are |
| 202 | 214 | * considered — refs/stash, refs/notes, etc. are ignored. |
| 203 | 215 | */ |
| 204 | export async function autoBuildFromPush ({ sync, releaseManager, repo, before, after, onLog, pendingBuilds } = {}) { | |
| 216 | export async function autoBuildFromPush ({ sync, releaseManager, repo, before, after, onLog, pendingBuilds, vdriveSeeder } = {}) { | |
| 205 | 217 | if (!sync || !releaseManager) return { triggered: [] } |
| 206 | 218 | const triggered = [] |
| 207 | 219 | |
| … | … | @@ -227,6 +239,7 @@ export async function autoBuildFromPush ({ sync, releaseManager, repo, before, a |
| 227 | 239 | const p = buildAndPublish({ |
| 228 | 240 | sync, |
| 229 | 241 | releaseManager, |
| 242 | vdriveSeeder, | |
| 230 | 243 | repo, |
| 231 | 244 | ref: t.ref, |
| 232 | 245 | isTag: t.isTag, |
| … | … | @@ -1096,6 +1096,7 @@ export function registerGitRoutes (svc) { |
| 1096 | 1096 | try { |
| 1097 | 1097 | const postRefs = await snapshotRefs(repoPath) |
| 1098 | 1098 | await autoBuildFromPush({ |
| 1099 | vdriveSeeder: svc._vdriveSeeder, | |
| 1099 | 1100 | sync: s, |
| 1100 | 1101 | releaseManager: svc._releases, |
| 1101 | 1102 | pendingBuilds: svc._pendingBuilds, |
| … | … | @@ -2706,6 +2706,7 @@ export async function startPearGit (opts = {}) { |
| 2706 | 2706 | const before = new Map([[params.ref, alreadyBuilt]]) |
| 2707 | 2707 | const after = new Map([[params.ref, head]]) |
| 2708 | 2708 | await autoBuildFromPush({ |
| 2709 | vdriveSeeder: svc._vdriveSeeder, | |
| 2709 | 2710 | sync: svc._sync, |
| 2710 | 2711 | releaseManager: svc._releases, |
| 2711 | 2712 | pendingBuilds: svc._pendingBuilds, |
| … | … | @@ -1,6 +1,6 @@ |
| 1 | 1 | { |
| 2 | 2 | "name": "pear-git-v3", |
| 3 | "version": "0.1.228", | |
| 3 | "version": "0.1.229", | |
| 4 | 4 | "description": "P2P git forge \u2014 multi-writer issue/org/release/peer-mirror daemon. Built bottom-up after the failed v2.", |
| 5 | 5 | "type": "module", |
| 6 | 6 | "main": "index.js", |
| … | … | @@ -135,3 +135,26 @@ test('a drive that will not open does not stop the others', async (t) => { |
| 135 | 135 | await s.close() |
| 136 | 136 | await fsp.rm(releaseDir, { recursive: true, force: true }) |
| 137 | 137 | }) |
| 138 | ||
| 139 | test('CI announces a freshly published drive — boot scan cannot have seen it', async (t) => { | |
| 140 | // The regression: the seeder scans at boot, but a drive published later that | |
| 141 | // same run did not exist then. Without an announce on the publish path a new | |
| 142 | // drive stays unreachable until the next daemon restart. | |
| 143 | const releaseDir = await tmpdir('ciannounce') | |
| 144 | const { buildAndPublish } = await import('../lib/ci-orchestrator.js') | |
| 145 | t.ok(typeof buildAndPublish === 'function', 'orchestrator exposes buildAndPublish') | |
| 146 | ||
| 147 | // Drive the seam directly: publish, then announce, and assert the announce | |
| 148 | // reaches a seeder that had already scanned an empty release dir. | |
| 149 | const s = new VdriveSeeder({ releaseDir, wire: () => {} }) | |
| 150 | const before = await s.start() | |
| 151 | t.is(before.seeded, 0, 'nothing to seed before the publish') | |
| 152 | ||
| 153 | await seedOneRepo(releaseDir, 'demo', '1.0.0', Buffer.alloc(64 * 1024, 9)) | |
| 154 | const announced = await s.announceRepo('demo') | |
| 155 | ||
| 156 | t.ok(announced, 'announceRepo picks up a drive published after the boot scan') | |
| 157 | t.is(s.count, 1, 'now seeding it') | |
| 158 | await s.close() | |
| 159 | await fsp.rm(releaseDir, { recursive: true, force: true }) | |
| 160 | }) |