/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ import { createHash } from 'node:crypto'; import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { basename, join, resolve } from 'node:path'; import { createTaskFixture, findEvalReleaseTarball, findJsonDiagnostics, isolatedEnvironment, readFrameworkOutputs, run, runAllowingFailure, } from './release-cli-eval-support.mjs'; const HARBOR_VERSION = '0.20.0'; const PIER_VERSION = '0.3.0'; const TASK_IMAGE = 'python:3.12-slim@sha256:dd29372629eeba2dd003fd9e9d35a5b8236c44727875a0364254b5127af88e65'; const releaseDirectory = resolve('packages/cli/release'); const tarballPath = findEvalReleaseTarball(releaseDirectory); const root = mkdtempSync(join(tmpdir(), 'maka-cli-eval-validation-')); let primaryError; try { validateChecksum(); const prefix = join(root, 'prefix'); const environment = isolatedEnvironment(join(root, 'home')); logStep('installing the immutable tarball with an empty offline npm cache'); run( 'npm', [ 'install', '--global', '--ignore-scripts', '--offline', '--no-audit', '--no-fund', '--cache', join(root, 'npm-cache'), '--prefix', prefix, tarballPath, ], environment, root, ); const maka = join(prefix, 'bin/maka'); const packageRoot = join(prefix, 'lib/node_modules/maka-agent'); if ( !existsSync(maka) || !existsSync(join(packageRoot, 'node_modules/@maka/eval/harbor/run_trial.py')) ) { throw new Error('The installed candidate is missing its CLI or bundled Eval runtime'); } const fixture = createTaskFixture(join(root, 'fixture'), environment, TASK_IMAGE); for (const framework of ['harbor', 'pier']) { logStep(`running a real ${framework} Docker cell from the installed candidate`); validateFramework({ environment, fixture, framework, maka, root: join(root, framework), }); } logStep(`OK — installed ${basename(tarballPath)} completed real Harbor and Pier cells`); } catch (error) { primaryError = error; } finally { let cleanupError; try { rmSync(root, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 }); } catch (error) { cleanupError = error; } if (primaryError && cleanupError) { throw new AggregateError([primaryError, cleanupError], 'Eval validation and cleanup failed'); } if (primaryError) throw primaryError; if (cleanupError) throw cleanupError; } function validateFramework({ environment, fixture, framework, maka, root: frameworkRoot }) { mkdirSync(frameworkRoot, { recursive: true, mode: 0o700 }); const outputRoot = join(frameworkRoot, 'output'); const trialsRoot = join(frameworkRoot, 'trials'); const pythonVariable = framework === 'harbor' ? 'MAKA_RELEASE_HARBOR_PYTHON' : 'MAKA_RELEASE_PIER_PYTHON'; const pythonPath = process.env[pythonVariable]; if (!pythonPath) throw new Error(`${pythonVariable} is required`); const pythonPathVariable = `MAKA_RELEASE_${framework.toUpperCase()}_PYTHON_PATH`; const trialsVariable = `MAKA_RELEASE_${framework.toUpperCase()}_TRIALS`; const tasksVariable = 'MAKA_RELEASE_PIER_TASKS'; const specPath = join(frameworkRoot, 'experiment.json'); const spec = experimentSpec({ fixture, framework, pythonPathVariable, tasksVariable, trialsVariable, }); writeFileSync(specPath, `${JSON.stringify(spec)}\n`, { mode: 0o600 }); const childEnvironment = { ...environment, [pythonPathVariable]: pythonPath, [trialsVariable]: trialsRoot, ...(framework === 'pier' ? { [tasksVariable]: fixture.tasksRoot } : {}), }; const invocation = runAllowingFailure( maka, ['eval', 'run', specPath, '--out', outputRoot], childEnvironment, frameworkRoot, ); const { summary, attempt } = readFrameworkOutputs({ framework, invocation, outputRoot, trialsRoot, }); const result = attempt.result; if ( summary.experimentId !== `release-${framework}` || summary.cells !== 1 || summary.incomplete !== 0 || attempt.cellId !== 'task::1::subject' || attempt.sequence !== 1 || result?.status !== 'completed' || result?.score !== 1 || result?.usage !== null || result?.costUsd !== null || result?.failureReason !== null ) { throw new Error( `${framework} produced an invalid completed attempt: ${JSON.stringify({ invocation, summary, attempt, frameworkDiagnostics: findJsonDiagnostics(trialsRoot), })}`, ); } const trialArtifact = result.artifacts.find( (artifact) => artifact.kind === 'trial' && artifact.framework === framework, ); const processArtifact = result.artifacts.find( (artifact) => artifact.kind === 'external_process' && artifact.exitCode === 0, ); const collected = result.artifacts.filter((artifact) => artifact.kind === 'collected-artifact'); if (!trialArtifact || !processArtifact || collected.length < 2) { throw new Error(`${framework} did not preserve the expected trial and subject artifacts`); } const containers = run( 'docker', ['ps', '--all', '--quiet', '--filter', `name=${trialArtifact.trialName}`], childEnvironment, frameworkRoot, ).trim(); if (containers) throw new Error(`${framework} left trial containers behind: ${containers}`); } function experimentSpec({ fixture, framework, pythonPathVariable, tasksVariable, trialsVariable }) { return { schemaVersion: 'maka.eval.v1', id: `release-${framework}`, benchmark: { id: 'release-validation', version: fixture.commit, config: { repository: fixture.repository }, }, executor: { kind: framework, config: { frameworkVersion: framework === 'harbor' ? HARBOR_VERSION : PIER_VERSION, pythonPathEnv: pythonPathVariable, trialsRootEnv: trialsVariable, ...(framework === 'pier' ? { tasksRootEnv: tasksVariable } : {}), environment: { type: 'docker', delete: true }, preparationEnvironment: [], mounts: [], }, }, subjects: [ { id: 'subject', kind: 'external', credentials: [], config: { command: '/bin/true', args: [], result: 'exit-code' }, }, ], tasks: [ { id: 'task', input: 'Exit successfully without modifying the task.', config: framework === 'harbor' ? { harbor: { path: 'task' } } : { pier: { path: 'task' } }, }, ], repetitions: 1, budget: { timeoutMultiplier: 1 }, verifier: { reward: 'reward' }, }; } function validateChecksum() { const checksumPath = `${tarballPath}.sha256`; const [expected, name, extra] = readFileSync(checksumPath, 'utf8').trim().split(/\s+/u); if (!expected || name !== basename(tarballPath) || extra !== undefined) { throw new Error(`Invalid checksum file ${checksumPath}`); } const actual = createHash('sha256').update(readFileSync(tarballPath)).digest('hex'); if (actual !== expected) throw new Error(`Checksum mismatch for ${tarballPath}`); } function logStep(message) { console.log(`[release-cli-eval-validation] ${message}`); }