Dependency audit / audit (push) Canceled after 0s
CI / plan (push) Canceled after 0s
Gitoxide helper admission / macos-latest (push) Canceled after 0s
Gitoxide helper admission / ubuntu-latest (push) Canceled after 0s
Gitoxide helper admission / windows-latest (push) Canceled after 0s
Runtime Host peer admission / quality (push) Canceled after 0s
Windows recovery / windows_recovery (push) Canceled after 0s
CI / heavy (push) Canceled after 0s
CI / test (push) Canceled after 0s
151 lines
5.6 KiB
JavaScript
151 lines
5.6 KiB
JavaScript
/*
|
|
* 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 { spawn } from 'node:child_process';
|
|
import { access, copyFile, mkdir, readFile, rm } from 'node:fs/promises';
|
|
import { createRequire } from 'node:module';
|
|
import { dirname, join } from 'node:path';
|
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
import { resolveDesktopBuildVersion } from './desktop-nightly.mjs';
|
|
import { npmSpawnOptions } from './npm-spawn.mjs';
|
|
|
|
const repoRoot = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
const desktopRoot = join(repoRoot, 'apps', 'desktop');
|
|
const releaseDirectory = join(desktopRoot, 'release');
|
|
// electron is declared by apps/desktop, so resolve its install directory from
|
|
// there rather than assuming node_modules hoisted it to the repo root. This
|
|
// pre-flight guard exists to catch a missing electron dist before packaging;
|
|
// pinning it to the hoisted path would make it fail at the wrong location the
|
|
// moment an installer nests electron under apps/desktop.
|
|
const require = createRequire(join(desktopRoot, 'package.json'));
|
|
const electronDistributionDirectory = join(
|
|
dirname(require.resolve('electron/package.json')),
|
|
'dist',
|
|
);
|
|
const sandboxManifestPath = join(
|
|
repoRoot,
|
|
'experiments',
|
|
'windows-sandbox',
|
|
'launcher',
|
|
'Cargo.toml',
|
|
);
|
|
const sandboxBinaryPath = join(
|
|
repoRoot,
|
|
'experiments',
|
|
'windows-sandbox',
|
|
'launcher',
|
|
'target',
|
|
'release',
|
|
'maka-windows-sandbox.exe',
|
|
);
|
|
const sandboxResourceDirectory = join(desktopRoot, 'resources', 'windows-sandbox');
|
|
const sandboxResourcePath = join(sandboxResourceDirectory, 'maka-windows-sandbox.exe');
|
|
const requiredElectronLicensePaths = [
|
|
join(electronDistributionDirectory, 'LICENSE'),
|
|
join(electronDistributionDirectory, 'LICENSES.chromium.html'),
|
|
];
|
|
|
|
export function runCommand(
|
|
command,
|
|
args,
|
|
{ spawnProcess = spawn, platform = process.platform } = {},
|
|
) {
|
|
return new Promise((resolve, reject) => {
|
|
// Every command here is a repository constant, so the shell that Windows
|
|
// needs to reach npm.cmd introduces no quoting concern.
|
|
const child = spawnProcess(
|
|
command,
|
|
args,
|
|
npmSpawnOptions({ cwd: repoRoot, env: process.env, stdio: 'inherit' }, platform),
|
|
);
|
|
child.once('error', reject);
|
|
child.once('exit', (code, signal) => {
|
|
if (code === 0) {
|
|
resolve();
|
|
return;
|
|
}
|
|
reject(
|
|
new Error(
|
|
`${command} ${args.join(' ')} failed with ${
|
|
signal ? `signal ${signal}` : `exit code ${code}`
|
|
}`,
|
|
),
|
|
);
|
|
});
|
|
});
|
|
}
|
|
|
|
export async function packageWindowsX64({
|
|
platform = process.platform,
|
|
arch = process.arch,
|
|
run = runCommand,
|
|
env = process.env,
|
|
remove = rm,
|
|
assertFile = access,
|
|
makeDirectory = mkdir,
|
|
copy = copyFile,
|
|
} = {}) {
|
|
if (platform !== 'win32' || arch !== 'x64') {
|
|
throw new Error('Release packaging requires a Windows x64 host.');
|
|
}
|
|
|
|
const manifest = JSON.parse(await readFile(join(desktopRoot, 'package.json'), 'utf8'));
|
|
const buildVersion = resolveDesktopBuildVersion(manifest.version, env);
|
|
const exePath = join(releaseDirectory, `Maka-${buildVersion}-win-x64.exe`);
|
|
const zipPath = join(releaseDirectory, `Maka-${buildVersion}-win-x64.zip`);
|
|
const updateMetadataPath = join(
|
|
releaseDirectory,
|
|
buildVersion === manifest.version ? 'latest.yml' : 'dev.yml',
|
|
);
|
|
const unpackedDirectory = join(releaseDirectory, 'win-unpacked');
|
|
|
|
for (const path of requiredElectronLicensePaths) {
|
|
await assertFile(path);
|
|
}
|
|
|
|
await run('npm', ['run', 'clean']);
|
|
await run('npm', ['run', 'build']);
|
|
await run('npm', ['run', 'build:runtime-host-peer']);
|
|
await run('npm', ['run', 'check:runtime-host-peer-notices']);
|
|
await run('cargo', ['build', '--manifest-path', sandboxManifestPath, '--release', '--locked']);
|
|
await run('npm', ['run', 'check:windows-cargo-notices']);
|
|
await makeDirectory(sandboxResourceDirectory, { recursive: true });
|
|
await copy(sandboxBinaryPath, sandboxResourcePath);
|
|
await run('npm', ['run', 'check:release']);
|
|
await remove(releaseDirectory, { recursive: true, force: true });
|
|
await run('npm', ['--workspace', '@maka/desktop', 'run', 'package:windows-x64']);
|
|
await assertFile(exePath);
|
|
await assertFile(zipPath);
|
|
await assertFile(updateMetadataPath);
|
|
// win-unpacked stays: the ZIP is an archive of exactly this directory, so it
|
|
// is what the verifier inspects. Extracting the ZIP would only rebuild a copy
|
|
// of it, and writing tens of thousands of small files on Windows costs more
|
|
// than the entire packaging step. It is not a release asset — the upload
|
|
// globs match artifacts by name — and the release directory is rebuilt from
|
|
// scratch on the next run.
|
|
await assertFile(unpackedDirectory);
|
|
|
|
return { exePath, zipPath, unpackedDirectory };
|
|
}
|
|
|
|
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
const { exePath } = await packageWindowsX64();
|
|
console.log(`Created ${exePath}`);
|
|
}
|