/* * 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 assert from 'node:assert/strict'; import { execFile } from 'node:child_process'; import { access, chmod, mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { isAbsolute, join } from 'node:path'; import test from 'node:test'; import { promisify } from 'node:util'; import { parse as parseYaml } from 'yaml'; import { resolveDesktopBuilderConfig } from '../apps/desktop/electron-builder.config.mjs'; import { parseAsfSourceReferenceTag, resolveProductManifestIdentity, resolveProductReleaseIdentity, } from './product-release-identity.mjs'; import { assertExpectedAppleTeam, createSigningKeychain, decodeSigningCertificate, DISTRIBUTION_NODE_RUNTIME_ENTITLEMENTS, distributionNodeEntitlements, extractDistributionNodeEntitlements, extractOfficialNodeEntitlements, macosArm64MachOAction, macosArm64CliWrapper, parseDeveloperIdApplicationIdentity, pruneThirdPartyDevelopmentArtifacts, resolveCliWorkspacePackages, runCommand, stageWorkspacePackages, standaloneInstallEnvironment, standaloneInstallRootManifest, } from './package-macos-arm64-cli.mjs'; import { resolveWorkspaceReleaseFiles } from './release-cli-file-policy.mjs'; import { isTuiReadyOutput, verifyQuarantinedExecution } from './verify-macos-arm64-cli.mjs'; import { makePtyProbe } from './verify-packaged-app.mjs'; import { ensureProductTag } from './product-release-tag.mjs'; const execFileAsync = promisify(execFile); const repoRoot = join(import.meta.dirname, '..'); const desktopBuilderConfig = resolveDesktopBuilderConfig({}); const rootManifest = { version: '1.2.3', packageManager: 'npm@11.19.0', releaseToolchain: { appleTeamIdentifier: 'FABM2QUA8Q', node: '24.18.1', nodeDarwinArm64Sha256: '1'.repeat(64), }, }; test('one root version defines every product artifact from one source commit', () => { const identity = resolveProductReleaseIdentity({ rootManifest, desktopManifest: { version: '1.2.3' }, cliManifest: { version: '1.2.3', bin: { maka: './dist/cli.js' } }, sha: 'a'.repeat(40), sourceReferenceTag: 'v1.2.3-incubating-rc2', }); assert.equal(identity.version, '1.2.3'); assert.equal(identity.tag, 'v1.2.3'); assert.equal(identity.sourceCommit, 'a'.repeat(40)); assert.equal(identity.sourceReferenceTag, 'v1.2.3-incubating-rc2'); assert.equal(identity.runtimeHostSetupPackage, 'maka-agent@1.2.3'); assert.deepEqual(identity.publicCommands, ['maka']); assert.equal(identity.appleTeamIdentifier, 'FABM2QUA8Q'); assert.equal(identity.nodeVersion, '24.18.1'); assert.equal(identity.nodeArchive, 'node-v24.18.1-darwin-arm64.tar.xz'); assert.equal(identity.nodeArchiveSha256, '1'.repeat(64)); assert.equal( identity.nodeSourceUrl, 'https://nodejs.org/download/release/v24.18.1/node-v24.18.1-darwin-arm64.tar.xz', ); assert.equal(identity.npmVersion, '11.19.0'); assert.equal(identity.dmg, 'Maka-1.2.3-mac-arm64.dmg'); assert.equal(identity.exe, 'Maka-1.2.3-win-x64.exe'); assert.equal(identity.cliArchive, 'Maka-1.2.3-cli-mac-arm64.zip'); assert.equal(Object.hasOwn(identity, 'sourceArchive'), false); assert.deepEqual(identity.artifacts, { 'desktop-macos': [ 'Maka-1.2.3-mac-arm64.dmg', 'Maka-1.2.3-mac-arm64.dmg.sha256', 'Maka-1.2.3-mac-arm64.zip', 'Maka-1.2.3-mac-arm64.zip.blockmap', 'latest-mac.yml', ], 'desktop-windows': [ 'Maka-1.2.3-win-x64.exe', 'Maka-1.2.3-win-x64.exe.blockmap', 'Maka-1.2.3-win-x64.exe.sha256', 'Maka-1.2.3-win-x64.zip', 'Maka-1.2.3-win-x64.zip.sha256', 'latest.yml', ], 'cli-macos-arm64': ['Maka-1.2.3-cli-mac-arm64.zip', 'Maka-1.2.3-cli-mac-arm64.zip.sha256'], }); }); test('the standalone launcher identifies its installed Eval bundle root', async (t) => { const archiveRoot = await mkdtemp(join(tmpdir(), 'maka-standalone-launcher-')); t.after(() => rm(archiveRoot, { recursive: true, force: true })); const launcher = join(archiveRoot, 'bin', 'maka'); const node = join(archiveRoot, 'libexec', 'node', 'bin', 'node'); await Promise.all([ mkdir(join(archiveRoot, 'bin'), { recursive: true }), mkdir(join(archiveRoot, 'libexec', 'node', 'bin'), { recursive: true }), ]); await Promise.all([ writeFile(launcher, macosArm64CliWrapper()), writeFile( node, `#!/bin/sh expected=$(CDPATH= cd -P "$(dirname "$0")/../.." && pwd) [ "$MAKA_EVAL_MAKA_BUNDLE_PATH" = "$expected" ] printf verified `, ), ]); await Promise.all([chmod(launcher, 0o755), chmod(node, 0o755)]); const { stdout } = await execFileAsync('bash', [launcher]); assert.equal(stdout, 'verified'); }); test('formal product identity rejects every prerelease channel', () => { for (const version of ['1.2.3-alpha.1', '1.2.3-beta.2', '1.2.3-rc.1', '1.2.3-dev.1']) { assert.throws( () => resolveProductManifestIdentity({ rootManifest: { ...rootManifest, version }, desktopManifest: { version }, cliManifest: { version, bin: { maka: './dist/cli.js' } }, }), /require a stable version/u, ); } }); test('Desktop packaging derives the Runtime Host setup package from product manifests', async () => { const manifestIdentity = resolveProductManifestIdentity({ rootManifest, desktopManifest: { version: '1.2.3' }, cliManifest: { version: '1.2.3', bin: { maka: './dist/cli.js' } }, }); assert.equal(manifestIdentity.runtimeHostSetupPackage, 'maka-agent@1.2.3'); const checkedRootManifest = JSON.parse(await readFile(join(repoRoot, 'package.json'), 'utf8')); assert.deepEqual(desktopBuilderConfig.extraMetadata, { runtimeHostSetupPackage: `maka-agent@${checkedRootManifest.version}`, makaUpdateChannel: 'release', }); assert.deepEqual(desktopBuilderConfig.publish, [ { provider: 'github', owner: 'apache', repo: 'maka' }, ]); }); test('Desktop stages release manifests before electron-builder builds the archive', async (t) => { const stage = await mkdtemp(join(tmpdir(), 'maka-desktop-release-manifests-')); t.after(() => rm(stage, { recursive: true, force: true })); const files = [{ filter: [...desktopBuilderConfig.files] }]; await desktopBuilderConfig.beforePack({ packager: { config: { files }, info: { tempDirManager: { createTempDir: async () => stage } }, }, }); assert.equal(files.length, 2); assert.ok( files[0].filter.includes('!node_modules/@maka/{mcp,runtime,runtime-host}/package.json'), ); assert.deepEqual(files.at(-1), { from: stage, to: 'node_modules/@maka' }); for (const name of ['mcp', 'runtime', 'runtime-host']) { const projected = JSON.parse(await readFile(join(stage, name, 'package.json'))); assert.equal( Object.keys(projected.exports).some((subpath) => subpath.startsWith('./test-only/')), false, ); } }); test('Desktop packaging does not distribute the retired bundled Git runtime', () => { const resources = desktopBuilderConfig.extraResources.map(({ from, to }) => ({ from, to })); assert.equal( resources.some(({ from }) => from.includes('dugite')), false, ); assert.equal( resources.some(({ to }) => to === 'git' || to.startsWith('licenses/git')), false, ); assert.equal( resources.some(({ to }) => to === 'bundled-git.json'), false, ); assert.equal( resources.some(({ to }) => to.startsWith('licenses/dugite')), false, ); }); test('packaged third-party license sources are resolved, not assumed hoisted', () => { // electron and @fontsource-variable/geist* are declared by apps/desktop, so // `../../node_modules/` only resolves when the installer hoists them to // the workspace root. electron-builder logs a warning and still exits 0 on a // missing `extraResources` source, so a non-hoisting layout would silently // drop these notices. The config resolves each package instead; assert the // sources are absolute paths that end at the intended in-package file. const expectedSuffixes = new Map([ ['licenses/electron/LICENSE', join('electron', 'dist', 'LICENSE')], [ 'licenses/electron/LICENSES.chromium.html', join('electron', 'dist', 'LICENSES.chromium.html'), ], ['licenses/renderer/GEIST_LICENSE.txt', join('@fontsource-variable', 'geist', 'LICENSE')], [ 'licenses/renderer/GEIST_MONO_LICENSE.txt', join('@fontsource-variable', 'geist-mono', 'LICENSE'), ], ]); const byTarget = new Map(desktopBuilderConfig.extraResources.map(({ from, to }) => [to, from])); for (const [to, suffix] of expectedSuffixes) { const from = byTarget.get(to); assert.ok(from, `missing extraResources entry for ${to}`); assert.ok( isAbsolute(from), `${to} source must be a resolved absolute path, not a '../../node_modules' hoisting assumption`, ); assert.ok(from.endsWith(suffix), `${to} source must resolve to ${suffix}, got ${from}`); } }); test('macOS DMG ships correctly sized background assets', async () => { const backgroundDirectory = join(repoRoot, 'apps', 'desktop', 'build'); const pngSignature = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]); for (const [name, dimensions] of [ ['background.png', { width: 540, height: 380 }], ['background@2x.png', { width: 1080, height: 760 }], ]) { const data = await readFile(join(backgroundDirectory, name)); assert.deepEqual(data.subarray(0, pngSignature.length), pngSignature, `${name} must be a PNG`); assert.deepEqual({ width: data.readUInt32BE(16), height: data.readUInt32BE(20) }, dimensions); } }); test('a successful Windows upgrade invalidates stale backup authority before best-effort cleanup', async () => { const source = await readFile( join(repoRoot, 'apps', 'desktop', 'build', 'installer.nsh'), 'utf8', ); const macroStart = source.indexOf('!macro customInstall'); const macroEnd = source.indexOf('!macroend', macroStart); assert.ok(macroStart >= 0 && macroEnd > macroStart); const cleanup = source.slice(macroStart, macroEnd); const markerInvalidation = cleanup.indexOf('Delete "$makaBackupDir\\${MAKA_BACKUP_MARKER}"'); const backupRemoval = cleanup.indexOf('RMDir /r "$makaBackupDir"'); const snapshotRemoval = cleanup.indexOf('DeleteRegKey SHELL_CONTEXT "${MAKA_SNAPSHOT_REG_KEY}"'); assert.ok(markerInvalidation >= 0); assert.ok(backupRemoval > markerInvalidation); assert.ok(snapshotRemoval > markerInvalidation); }); test('platform package verifiers keep Git checks out of every artifact', async () => { const windowsSource = await readFile(join(repoRoot, 'scripts', 'verify-windows-x64.mjs'), 'utf8'); assert.doesNotMatch(windowsSource, /bundledGitContract/u); assert.doesNotMatch(windowsSource, /requirePath\(join\(resources, ['"]git['"]/u); assert.match( windowsSource, /if \(requiresCurrentContract\) \{\s*await assertPackagedUpdateConfiguration\(resources, \{\s*channel: environment\.MAKA_DESKTOP_NIGHTLY_VERSION \? ['"]nightly['"] : ['"]release['"],\s*\}\);\s*await assertPackagedDependencyClosure\(resources\);\s*\}/u, ); const macosSource = await readFile( join(repoRoot, 'scripts', 'verify-macos-arm64-dmg.mjs'), 'utf8', ); assert.doesNotMatch(macosSource, /requirePath\(join\(resources, ['"]git['"]/u); }); test('the packaged-app probe rejects a mismatched Runtime Host setup package', async () => { const fixture = await mkdtemp(join(tmpdir(), 'maka-packaged-manifest-')); try { const manifestPath = join(fixture, 'package.json'); const ptyDirectory = join(fixture, 'node_modules', 'node-pty'); await mkdir(ptyDirectory, { recursive: true }); await Promise.all([ writeFile(manifestPath, JSON.stringify({ runtimeHostSetupPackage: 'maka-agent@1.2.3' })), writeFile( join(ptyDirectory, 'index.js'), `module.exports = { spawn() { return { onData(listener) { queueMicrotask(() => listener('maka-node-pty-ok')); }, onExit(listener) { setImmediate(() => listener({ exitCode: 0 })); }, }; } };\n`, ), ]); await execFileAsync(process.execPath, [ '-e', makePtyProbe('/bin/echo', ['maka-node-pty-ok'], 'maka-agent@1.2.3'), manifestPath, ]); await assert.rejects( execFileAsync(process.execPath, [ '-e', makePtyProbe('/bin/echo', ['maka-node-pty-ok'], 'maka-agent@1.2.4'), manifestPath, ]), /Packaged Runtime Host setup package mismatch/u, ); } finally { await rm(fixture, { recursive: true, force: true }); } }); test('product releases accept only an exact same-version ASF source reference', () => { assert.deepEqual(parseAsfSourceReferenceTag('v1.2.3-incubating-rc2'), { rcNumber: '2', tag: 'v1.2.3-incubating-rc2', version: '1.2.3', }); for (const tag of ['v1.2.3-incubating-rc0', 'v1.2.3-incubating-rc01', 'v1.2.3-rc1']) { assert.throws(() => parseAsfSourceReferenceTag(tag), /ASF source reference must match/u); } assert.throws( () => resolveProductReleaseIdentity({ rootManifest, desktopManifest: { version: '1.2.3' }, cliManifest: { version: '1.2.3', bin: { maka: './dist/cli.js' } }, sha: 'a'.repeat(40), sourceReferenceTag: 'v1.2.4-incubating-rc1', }), /source reference version 1\.2\.4 does not match product 1\.2\.3/u, ); }); test('the product identity CLI uses the checked-out commit outside GitHub Actions', async () => { const env = { ...process.env }; delete env.GITHUB_SHA; delete env.GITHUB_OUTPUT; const [{ stdout }, { stdout: head }, manifest] = await Promise.all([ execFileAsync(process.execPath, [join(repoRoot, 'scripts/product-release-identity.mjs')], { cwd: repoRoot, env, }), execFileAsync('git', ['rev-parse', 'HEAD'], { cwd: repoRoot }), readFile(join(repoRoot, 'package.json'), 'utf8').then(JSON.parse), ]); assert.equal(stdout.trim(), `Product release v${manifest.version} from ${head.trim()}`); await assert.rejects( execFileAsync(process.execPath, [join(repoRoot, 'scripts/product-release-identity.mjs')], { cwd: repoRoot, env: { ...env, EXPECTED_PRODUCT_VERSION: '9.9.9' }, }), /does not match requested release/u, ); }); test('product release identity rejects a non-canonical version at its boundary', () => { const version = '01.2.3'; assert.throws( () => resolveProductReleaseIdentity({ rootManifest: { ...rootManifest, version }, desktopManifest: { version }, cliManifest: { version, bin: { maka: './dist/cli.js' } }, sha: 'a'.repeat(40), }), /valid product release version/u, ); }); test('product tag creation is exact and idempotent but rejects a conflicting commit', async () => { const root = await mkdtemp(join(tmpdir(), 'maka-product-tag-')); const remote = join(root, 'remote.git'); const source = join(root, 'source'); try { await execFileAsync('git', ['init', '--bare', remote]); await mkdir(source); await execFileAsync('git', ['init'], { cwd: source }); await execFileAsync('git', ['config', 'user.name', 'Maka release test'], { cwd: source }); await execFileAsync('git', ['config', 'user.email', 'release-test@example.invalid'], { cwd: source, }); await writeFile(join(source, 'source.txt'), 'one\n'); await execFileAsync('git', ['add', 'source.txt'], { cwd: source }); await execFileAsync('git', ['commit', '-m', 'first'], { cwd: source }); const first = ( await execFileAsync('git', ['rev-parse', 'HEAD'], { cwd: source }) ).stdout.trim(); assert.equal( await ensureProductTag({ cwd: source, remote, tag: 'v1.2.3', source: first }), 'created', ); assert.equal( await ensureProductTag({ cwd: source, remote, tag: 'v1.2.3', source: first }), 'existing', ); await writeFile(join(source, 'source.txt'), 'two\n'); await execFileAsync('git', ['commit', '-am', 'second'], { cwd: source }); const second = ( await execFileAsync('git', ['rev-parse', 'HEAD'], { cwd: source }) ).stdout.trim(); await assert.rejects( ensureProductTag({ cwd: source, remote, tag: 'v1.2.3', source: second }), /points to .* instead of/u, ); } finally { await rm(root, { recursive: true, force: true }); } }); test('standalone verification recognizes the current TUI status line through ANSI output', () => { assert.equal( isTuiReadyOutput( '\u001b[1mMaka\u001b[22m\u001b[2m · \u001b[22m\u001b[2mAuto\u001b[22m\u001b[2m · model · provider\u001b[0m', ), true, ); }); test('standalone verification assesses the downloaded quarantine state', async () => { const commands = []; await verifyQuarantinedExecution( '/artifact/Maka', '/artifact/Maka/libexec/node/bin/node', async (command) => { commands.push(command); }, ); assert.deepEqual(commands, ['xattr', 'spctl']); }); test('standalone packaging keeps or thins arm64 Mach-O files instead of deleting them', () => { const macos = 'platform MACOS\n'; assert.equal(macosArm64MachOAction('arm64', macos), 'keep'); assert.equal(macosArm64MachOAction('x86_64 arm64', macos), 'thin'); assert.equal(macosArm64MachOAction('x86_64', macos), 'remove'); assert.equal(macosArm64MachOAction('arm64', 'platform IOS\n'), 'remove'); }); test('CLI signing accepts one base64 PKCS12 and one isolated Developer ID identity', () => { const certificate = Buffer.from('pkcs12'); const encodedCertificate = certificate.toString('base64'); assert.deepEqual(decodeSigningCertificate(encodedCertificate), { bytes: Buffer.from('pkcs12'), }); assert.deepEqual( decodeSigningCertificate(`${encodedCertificate.slice(0, 4)}\n${encodedCertificate.slice(4)}`), { bytes: certificate, }, ); assert.throws(() => decodeSigningCertificate('not base64!'), /base64-encoded/u); assert.deepEqual( parseDeveloperIdApplicationIdentity( ' 1) ABCDEF0123456789ABCDEF0123456789ABCDEF01 "Developer ID Application: Maka Test (FABM2QUA8Q)"\n 1 valid identities found\n', ), { hash: 'ABCDEF0123456789ABCDEF0123456789ABCDEF01', name: 'Developer ID Application: Maka Test (FABM2QUA8Q)', teamIdentifier: 'FABM2QUA8Q', }, ); assert.throws( () => parseDeveloperIdApplicationIdentity( ' 1) ABCDEF0123456789ABCDEF0123456789ABCDEF01 "Apple Development: Test"\n', ), /one Developer ID Application/u, ); assert.throws( () => assertExpectedAppleTeam( { teamIdentifier: 'ABCDEFGHIJ' }, rootManifest.releaseToolchain.appleTeamIdentifier, ), /belongs to Apple team ABCDEFGHIJ, expected FABM2QUA8Q/u, ); }); test('CLI signing removes temporary credentials when keychain deletion fails', async () => { const signing = await createSigningKeychain({ env: { CSC_LINK: Buffer.from('pkcs12').toString('base64') }, expectedTeamIdentifier: 'FABM2QUA8Q', run: async (command, args) => { if (command === 'openssl') { await writeFile(args[args.indexOf('-out') + 1], 'temporary private key'); } if (command === 'security' && args[0] === 'delete-keychain') { throw new Error('simulated keychain deletion failure'); } }, inspect: async () => ({ stdout: ' 1) ABCDEF0123456789ABCDEF0123456789ABCDEF01 "Developer ID Application: Maka Test (FABM2QUA8Q)"\n 1 valid identities found\n', }), }); await assert.rejects(signing.cleanup(), /simulated keychain deletion failure/u); await assert.rejects(access(signing.directory), { code: 'ENOENT' }); }); test('CLI signing command failures do not disclose credential arguments', async () => { const secret = 'temporary-keychain-password'; await assert.rejects( runCommand(process.execPath, ['-e', 'process.exit(9)', secret], { displayArgs: ['-e', '