mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-09-17 23:52:36 +00:00
278 lines
No EOL
8.6 KiB
JavaScript
278 lines
No EOL
8.6 KiB
JavaScript
import { execSync } from 'node:child_process';
|
|
import fs from 'node:fs';
|
|
import os from 'node:os';
|
|
import path from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = path.dirname(__filename);
|
|
const packageRoot = path.resolve(__dirname, '..');
|
|
const workspaceRoot = path.resolve(packageRoot, '..');
|
|
const gitnexusRoot = path.join(workspaceRoot, 'gitnexus');
|
|
const gitnexusWebRoot = path.join(workspaceRoot, 'gitnexus-web');
|
|
const releaseRoot = path.join(packageRoot, 'release');
|
|
const desktopPackageJson = JSON.parse(fs.readFileSync(path.join(packageRoot, 'package.json'), 'utf8'));
|
|
const desktopPackageLock = JSON.parse(fs.readFileSync(path.join(packageRoot, 'package-lock.json'), 'utf8'));
|
|
const electronVersion = desktopPackageJson.devDependencies?.electron?.replace(/^[^\d]*/, '') ?? '41.2.1';
|
|
const electronBuilderVersion = desktopPackageJson.devDependencies?.['electron-builder']?.replace(/^[^\d]*/, '') ?? '26.8.1';
|
|
const appBuilderLibVersion =
|
|
desktopPackageLock.packages?.['node_modules/app-builder-lib']?.version ?? electronBuilderVersion.replace(/^[^\d]*/, '');
|
|
const stamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
const outputDir = path.join(releaseRoot, stamp);
|
|
const generatedBuilderConfigPath = path.join(outputDir, 'electron-builder.generated.json');
|
|
const requiredNsisTemplateFiles = [
|
|
path.join(packageRoot, 'node_modules', 'app-builder-lib', 'templates', 'nsis', 'messages.yml'),
|
|
path.join(packageRoot, 'node_modules', 'app-builder-lib', 'templates', 'nsis', 'assistedMessages.yml'),
|
|
];
|
|
const supportedTargetHosts = {
|
|
'--linux': 'linux',
|
|
'--mac': 'darwin',
|
|
'--win': 'win32',
|
|
};
|
|
const artifactFileExtensions = new Set(['.AppImage', '.dmg', '.exe', '.msi', '.zip']);
|
|
|
|
const builderArgs = process.argv.slice(2);
|
|
const requestedTargets = builderArgs.filter((argument) => argument in supportedTargetHosts);
|
|
|
|
const createBuilderConfig = () => ({
|
|
appId: 'io.github.abhigyanpatwari.gitnexus.desktop',
|
|
electronVersion,
|
|
productName: 'GitNexus Desktop',
|
|
directories: {
|
|
output: outputDir,
|
|
buildResources: path.join(packageRoot, 'build'),
|
|
},
|
|
files: ['dist/**/*', 'package.json'],
|
|
extraResources: [
|
|
{
|
|
from: path.join(gitnexusWebRoot, 'dist'),
|
|
to: 'gitnexus-web',
|
|
filter: ['**/*'],
|
|
},
|
|
{
|
|
from: path.join(gitnexusRoot, 'dist'),
|
|
to: 'gitnexus/dist',
|
|
filter: ['**/*'],
|
|
},
|
|
{
|
|
from: path.join(gitnexusRoot, 'node_modules'),
|
|
to: 'gitnexus/node_modules',
|
|
filter: ['**/*'],
|
|
},
|
|
{
|
|
from: path.join(gitnexusRoot, 'hooks'),
|
|
to: 'gitnexus/hooks',
|
|
filter: ['**/*'],
|
|
},
|
|
{
|
|
from: path.join(gitnexusRoot, 'skills'),
|
|
to: 'gitnexus/skills',
|
|
filter: ['**/*'],
|
|
},
|
|
{
|
|
from: path.join(gitnexusRoot, 'vendor'),
|
|
to: 'gitnexus/vendor',
|
|
filter: ['**/*'],
|
|
},
|
|
{
|
|
from: path.join(gitnexusRoot, 'package.json'),
|
|
to: 'gitnexus/package.json',
|
|
},
|
|
],
|
|
asar: false,
|
|
npmRebuild: false,
|
|
win: {
|
|
signAndEditExecutable: false,
|
|
icon: path.join(packageRoot, 'build', 'icon.png'),
|
|
target: ['nsis'],
|
|
},
|
|
mac: {
|
|
category: 'public.app-category.developer-tools',
|
|
icon: path.join(packageRoot, 'build', 'icon.png'),
|
|
target: ['dmg'],
|
|
},
|
|
linux: {
|
|
category: 'Development',
|
|
icon: path.join(packageRoot, 'build', 'icon.png'),
|
|
target: ['AppImage'],
|
|
},
|
|
nsis: {
|
|
oneClick: false,
|
|
allowToChangeInstallationDirectory: true,
|
|
},
|
|
});
|
|
|
|
const builderCommand = [
|
|
`npx --yes -p electron@${electronVersion} -p electron-builder@${electronBuilderVersion} electron-builder`,
|
|
`--config "${generatedBuilderConfigPath}"`,
|
|
`-c.electronVersion=${electronVersion}`,
|
|
'--publish never',
|
|
...builderArgs,
|
|
].join(' ');
|
|
|
|
const runCommand = (command, cwd, extraEnv = {}) => {
|
|
execSync(command, {
|
|
cwd,
|
|
env: {
|
|
...process.env,
|
|
...extraEnv,
|
|
},
|
|
stdio: 'inherit',
|
|
});
|
|
};
|
|
|
|
const tryRunCommand = (command, cwd, extraEnv = {}) => {
|
|
try {
|
|
runCommand(command, cwd, extraEnv);
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
};
|
|
|
|
const overlayDirectoryContents = (sourceDirectory, destinationDirectory) => {
|
|
fs.mkdirSync(destinationDirectory, { recursive: true });
|
|
|
|
for (const entry of fs.readdirSync(sourceDirectory, { withFileTypes: true })) {
|
|
fs.cpSync(path.join(sourceDirectory, entry.name), path.join(destinationDirectory, entry.name), {
|
|
force: true,
|
|
recursive: true,
|
|
});
|
|
}
|
|
};
|
|
|
|
const repairAppBuilderLibPackage = () => {
|
|
const repairDirectory = fs.mkdtempSync(path.join(os.tmpdir(), 'gitnexus-desktop-app-builder-lib-'));
|
|
|
|
try {
|
|
const tarballName = `app-builder-lib-${appBuilderLibVersion}.tgz`;
|
|
const tarballPath = path.join(repairDirectory, tarballName);
|
|
const extractedPackageRoot = path.join(repairDirectory, 'package');
|
|
const installedPackageRoot = path.join(packageRoot, 'node_modules', 'app-builder-lib');
|
|
|
|
runCommand(`npm pack app-builder-lib@${appBuilderLibVersion} --pack-destination "${repairDirectory}"`, packageRoot);
|
|
runCommand(`tar -xzf "${tarballPath}" -C "${repairDirectory}"`, packageRoot);
|
|
overlayDirectoryContents(extractedPackageRoot, installedPackageRoot);
|
|
} finally {
|
|
fs.rmSync(repairDirectory, { force: true, recursive: true });
|
|
}
|
|
};
|
|
|
|
const assertSupportedHostForRequestedTargets = () => {
|
|
const unsupportedTargets = requestedTargets.filter((target) => supportedTargetHosts[target] !== process.platform);
|
|
|
|
if (unsupportedTargets.length === 0) {
|
|
return;
|
|
}
|
|
|
|
const messages = unsupportedTargets.map((target) => {
|
|
if (target === '--mac') {
|
|
return 'macOS DMG builds must run on a macOS host. Use the Desktop Packaging GitHub Actions workflow for .dmg artifacts.';
|
|
}
|
|
|
|
if (target === '--linux') {
|
|
return 'Linux AppImage builds should run on a Linux host. Use the Desktop Packaging GitHub Actions workflow for Linux installers.';
|
|
}
|
|
|
|
return 'Windows NSIS builds must run on a Windows host.';
|
|
});
|
|
|
|
throw new Error(messages.join('\n'));
|
|
};
|
|
|
|
const ensureDesktopToolchainHealthy = () => {
|
|
if (!requestedTargets.includes('--win')) {
|
|
return;
|
|
}
|
|
|
|
const missingNsisTemplates = requiredNsisTemplateFiles.filter((filePath) => !fs.existsSync(filePath));
|
|
|
|
if (missingNsisTemplates.length === 0) {
|
|
return;
|
|
}
|
|
|
|
console.warn('[build] electron-builder NSIS templates are missing. Restoring app-builder-lib package contents first...');
|
|
|
|
try {
|
|
repairAppBuilderLibPackage();
|
|
} catch {
|
|
// Fall through to install-based repair paths below.
|
|
}
|
|
|
|
if (requiredNsisTemplateFiles.every((filePath) => fs.existsSync(filePath))) {
|
|
return;
|
|
}
|
|
|
|
console.warn('[build] Package overlay repair was not enough. Reinstalling electron-builder next...');
|
|
|
|
const targetedRepairWorked = tryRunCommand(
|
|
`npm install --no-save --package-lock=false electron-builder@${electronBuilderVersion}`,
|
|
packageRoot,
|
|
);
|
|
|
|
const targetedRepairResolvedTemplates =
|
|
targetedRepairWorked && requiredNsisTemplateFiles.every((filePath) => fs.existsSync(filePath));
|
|
|
|
if (!targetedRepairResolvedTemplates) {
|
|
console.warn('[build] Targeted repair was not enough. Falling back to npm ci...');
|
|
runCommand('npm ci', packageRoot);
|
|
}
|
|
|
|
const unresolvedTemplates = requiredNsisTemplateFiles.filter((filePath) => !fs.existsSync(filePath));
|
|
|
|
if (unresolvedTemplates.length > 0) {
|
|
throw new Error(
|
|
`electron-builder is missing required NSIS templates after reinstall:\n${unresolvedTemplates.join('\n')}`,
|
|
);
|
|
}
|
|
};
|
|
|
|
const listArtifacts = (directoryPath) => {
|
|
const results = [];
|
|
const entries = fs.readdirSync(directoryPath, { withFileTypes: true });
|
|
|
|
for (const entry of entries) {
|
|
const fullPath = path.join(directoryPath, entry.name);
|
|
|
|
if (entry.isDirectory()) {
|
|
if (entry.name === 'win-unpacked') {
|
|
results.push(fullPath);
|
|
continue;
|
|
}
|
|
|
|
results.push(...listArtifacts(fullPath));
|
|
continue;
|
|
}
|
|
|
|
if (artifactFileExtensions.has(path.extname(entry.name))) {
|
|
results.push(fullPath);
|
|
}
|
|
}
|
|
|
|
return results;
|
|
};
|
|
|
|
fs.mkdirSync(outputDir, { recursive: true });
|
|
fs.writeFileSync(generatedBuilderConfigPath, JSON.stringify(createBuilderConfig(), null, 2));
|
|
|
|
assertSupportedHostForRequestedTargets();
|
|
ensureDesktopToolchainHealthy();
|
|
|
|
runCommand('node scripts/ensure-gitnexus-runtime.mjs', packageRoot);
|
|
|
|
runCommand('npm run bundle', packageRoot);
|
|
|
|
runCommand('npm run build', gitnexusWebRoot);
|
|
|
|
runCommand(builderCommand, packageRoot);
|
|
|
|
const artifacts = listArtifacts(outputDir);
|
|
|
|
console.log(`[build] artifacts written to release/${stamp}`);
|
|
|
|
if (artifacts.length > 0) {
|
|
console.log('[build] artifacts:');
|
|
for (const artifact of artifacts) {
|
|
console.log(` - ${artifact}`);
|
|
}
|
|
} |