diff --git a/backend/src/services/dockerRunner.ts b/backend/src/services/dockerRunner.ts index 2aeb50d..19e4c9c 100644 --- a/backend/src/services/dockerRunner.ts +++ b/backend/src/services/dockerRunner.ts @@ -89,33 +89,6 @@ export async function pullRunnerImage( ): Promise { const d = initDocker(); const platform = `linux/${architecture}`; - - const splitImageRef = (ref: string): { repo: string; tag: string } => { - const lastColon = ref.lastIndexOf(':'); - const lastSlash = ref.lastIndexOf('/'); - if (lastColon > lastSlash) { - return { repo: ref.slice(0, lastColon), tag: ref.slice(lastColon + 1) }; - } - return { repo: ref, tag: 'latest' }; - }; - - const { repo, tag: originalTag } = splitImageRef(RUNNER_IMAGE); - const archTag = `${originalTag}-${architecture}`; - const platformTag = `${repo}:${archTag}`; - - // Check if we already have the platform-specific tag with correct architecture - try { - const existingImage = await d.getImage(platformTag).inspect(); - const imageArch = normalizeImageArchitecture(existingImage.Architecture); - - if (imageArch === architecture) { - console.log(`Image ${platformTag} already exists with correct architecture (${existingImage.Architecture})`); - return platformTag; - } - console.log(`Image ${platformTag} exists but has wrong architecture (${existingImage.Architecture}), re-pulling...`); - } catch { - // Image doesn't exist, need to pull - } console.log(`Pulling image ${RUNNER_IMAGE} for ${platform}...`); @@ -140,23 +113,8 @@ export async function pullRunnerImage( }); }); }); - - // Verify the pulled image has the correct architecture - const pulledImage = await d.getImage(RUNNER_IMAGE).inspect(); - console.log(`Pulled image architecture: ${pulledImage.Architecture}`); - assertImageArchitecture(RUNNER_IMAGE, pulledImage.Architecture, architecture); - - // Tag the pulled image with architecture-specific tag - console.log(`Tagging image as ${platformTag}...`); - const image = d.getImage(RUNNER_IMAGE); - await image.tag({ repo, tag: archTag }); - - // Verify the tagged image - const taggedImage = await d.getImage(platformTag).inspect(); - console.log(`Tagged image ${platformTag} architecture: ${taggedImage.Architecture}`); - assertImageArchitecture(platformTag, taggedImage.Architecture, architecture); - - return platformTag; + + return RUNNER_IMAGE; } /** diff --git a/backend/src/services/reconciler.ts b/backend/src/services/reconciler.ts index efb84a5..2518ed2 100644 --- a/backend/src/services/reconciler.ts +++ b/backend/src/services/reconciler.ts @@ -55,6 +55,10 @@ const getAllEnabledPools = db.prepare('SELECT * FROM runner_pools WHERE enabled const deleteRunner = db.prepare('DELETE FROM runners WHERE id = ?'); const updateRunnerStatus = db.prepare('UPDATE runners SET status = ?, updated_at = datetime(\'now\') WHERE id = ?'); +export function shouldSkipGitHubExistenceCheck(status: string): boolean { + return status === 'pending' || status === 'configuring'; +} + /** * Start the periodic reconciliation service */ @@ -157,6 +161,11 @@ async function reconcileRunnersInternal(): Promise { for (const runner of localRunners) { stats.checked++; + // Newly provisioning runners are expected to not exist in GitHub yet. + if (shouldSkipGitHubExistenceCheck(runner.status)) { + continue; + } + // Check if runner exists in GitHub (by ID or name) const existsInGitHub = (runner.github_runner_id && ghRunnerIds.has(runner.github_runner_id)) || diff --git a/backend/src/services/runnerManager.ts b/backend/src/services/runnerManager.ts index 6d7bbbd..f533675 100644 --- a/backend/src/services/runnerManager.ts +++ b/backend/src/services/runnerManager.ts @@ -441,7 +441,12 @@ export async function downloadRunner( } // Cleanup archive - await fs.unlink(archivePath); + await fs.unlink(archivePath).catch((error: unknown) => { + const code = (error as NodeJS.ErrnoException).code; + if (code !== 'ENOENT') { + throw error; + } + }); // Update runner directory in database updateRunnerDir.run(runnerDir, runnerId); diff --git a/backend/tests/reconciler.test.ts b/backend/tests/reconciler.test.ts index 5b507f1..7b42df6 100644 --- a/backend/tests/reconciler.test.ts +++ b/backend/tests/reconciler.test.ts @@ -7,6 +7,7 @@ import fs from 'fs/promises'; import path from 'path'; import os from 'os'; import { db } from '../src/db/index.js'; +import { shouldSkipGitHubExistenceCheck } from '../src/services/reconciler.js'; // Test directory for runner cleanup tests const TEST_RUNNERS_DIR = path.join(os.tmpdir(), 'action-packer-test-runners'); @@ -16,6 +17,18 @@ let cleanupOrphanedDirectories: () => Promise; let withTimeout: (promise: Promise, ms: number, operation: string) => Promise; describe('Reconciler', () => { + describe('shouldSkipGitHubExistenceCheck', () => { + it('skips pending and configuring runners', () => { + expect(shouldSkipGitHubExistenceCheck('pending')).toBe(true); + expect(shouldSkipGitHubExistenceCheck('configuring')).toBe(true); + }); + + it('does not skip online/offline runners', () => { + expect(shouldSkipGitHubExistenceCheck('online')).toBe(false); + expect(shouldSkipGitHubExistenceCheck('offline')).toBe(false); + }); + }); + describe('withTimeout', () => { beforeEach(async () => { // Dynamically import to get fresh module