mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-27 06:12:20 +00:00
Some checks failed
Devcontainer Smoke / Config-transform unit tests (push) Has been cancelled
Devcontainer Smoke / Build devcontainer image (default) (push) Has been cancelled
Publish Development Images / publish (core) (push) Has been cancelled
Publish Development Images / publish (ui) (push) Has been cancelled
Devcontainer Smoke / Build devcontainer image (ui) (push) Has been cancelled
CodeQL / Analyze (python) (push) Waiting to run
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
Gitleaks / gitleaks (push) Waiting to run
Publish / ci (push) Blocked by required conditions
Publish / Classify release event (push) Waiting to run
Publish / RC guard (marker + release-PR skip) (push) Blocked by required conditions
Publish / Publish to npm (push) Blocked by required conditions
Publish / Build & Push RC Docker images (push) Blocked by required conditions
Scorecard / Scorecard analysis (push) Waiting to run
Skill copy sync / shipped skills drift guard (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-cli) (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-web) (push) Waiting to run
139 lines
4.9 KiB
JavaScript
139 lines
4.9 KiB
JavaScript
'use strict';
|
|
|
|
const fs = require('node:fs');
|
|
const path = require('node:path');
|
|
const crypto = require('node:crypto');
|
|
const { execFileSync } = require('node:child_process');
|
|
|
|
const markerName = '.gitnexus-devcontainer-install.json';
|
|
const nativeInputs = [
|
|
'vendor',
|
|
'prebuilds/bundled',
|
|
'scripts/build-tree-sitter-grammars.cjs',
|
|
'scripts/prepare-tree-sitter-bundle.cjs',
|
|
'scripts/arm64-prebuild.cjs',
|
|
];
|
|
|
|
function fingerprint(directory, runtime = process) {
|
|
const hash = crypto.createHash('sha256');
|
|
hash.update(
|
|
JSON.stringify([
|
|
3,
|
|
runtime.version,
|
|
runtime.versions.modules,
|
|
runtime.platform,
|
|
runtime.arch,
|
|
runtime.npmVersion,
|
|
]),
|
|
);
|
|
for (const file of ['package.json', 'package-lock.json']) {
|
|
hash.update(file);
|
|
hash.update(fs.readFileSync(path.join(directory, file)));
|
|
}
|
|
const manifest = JSON.parse(fs.readFileSync(path.join(directory, 'package.json')));
|
|
for (const dependency of Object.values({
|
|
...manifest.dependencies,
|
|
...manifest.devDependencies,
|
|
...manifest.optionalDependencies,
|
|
})) {
|
|
if (typeof dependency !== 'string' || !dependency.startsWith('file:')) continue;
|
|
const sibling = path.resolve(directory, dependency.slice(5), 'package.json');
|
|
hash.update(fs.readFileSync(sibling));
|
|
}
|
|
if (fs.existsSync(path.join(directory, 'scripts/build-tree-sitter-grammars.cjs'))) {
|
|
hash.update(
|
|
JSON.stringify({ skipOptionalGrammars: process.env.GITNEXUS_SKIP_OPTIONAL_GRAMMARS === '1' }),
|
|
);
|
|
// Blob IDs cover committed native inputs; the diff covers local tracked
|
|
// edits. Do not read npm credential files or hash large unchanged prebuilds.
|
|
for (const args of [
|
|
['ls-files', '-s', '--', ...nativeInputs],
|
|
['diff', 'HEAD', '--', ...nativeInputs],
|
|
]) {
|
|
hash.update(execFileSync('git', args, { cwd: directory, maxBuffer: 32 * 1024 * 1024 }));
|
|
}
|
|
}
|
|
return hash.digest('hex');
|
|
}
|
|
|
|
function ensureDependencies(directory, { run = runNpm, force = false, runtime = process } = {}) {
|
|
const expected = fingerprint(directory, runtime);
|
|
const marker = path.join(directory, 'node_modules', markerName);
|
|
const manifest = JSON.parse(fs.readFileSync(path.join(directory, 'package.json')));
|
|
const packages = Object.keys({ ...manifest.dependencies, ...manifest.devDependencies });
|
|
let saved;
|
|
try {
|
|
saved = JSON.parse(fs.readFileSync(marker, 'utf8'));
|
|
} catch {
|
|
/* First or interrupted install. */
|
|
}
|
|
const present = packages.every((name) =>
|
|
fs.existsSync(path.join(directory, 'node_modules', name, 'package.json')),
|
|
);
|
|
if (!force && present && saved?.fingerprint === expected) {
|
|
console.log(
|
|
`[install-deps] ${path.basename(directory)} dependencies unchanged; reusing volume`,
|
|
);
|
|
return false;
|
|
}
|
|
// Invalidate before installing: a failed attempt must never leave a reusable receipt.
|
|
fs.rmSync(marker, { force: true });
|
|
run(directory, ['ci']);
|
|
fs.mkdirSync(path.dirname(marker), { recursive: true });
|
|
const temporary = `${marker}.tmp`;
|
|
fs.writeFileSync(temporary, JSON.stringify({ fingerprint: fingerprint(directory, runtime) }));
|
|
fs.renameSync(temporary, marker);
|
|
return true;
|
|
}
|
|
|
|
function runNpm(directory, args) {
|
|
execFileSync('npm', args, { cwd: directory, stdio: 'inherit' });
|
|
}
|
|
|
|
function timed(label, action) {
|
|
const start = performance.now();
|
|
try {
|
|
return action();
|
|
} finally {
|
|
console.log(`[install-deps] ${label}: ${((performance.now() - start) / 1000).toFixed(2)}s`);
|
|
}
|
|
}
|
|
|
|
module.exports = { fingerprint, ensureDependencies, markerName };
|
|
|
|
if (require.main === module) {
|
|
const root = path.resolve(process.argv[2] || '/workspace');
|
|
const runtime = {
|
|
version: process.version,
|
|
versions: process.versions,
|
|
platform: process.platform,
|
|
arch: process.arch,
|
|
npmVersion: execFileSync('npm', ['--version'], { encoding: 'utf8' }).trim(),
|
|
};
|
|
const ensure = (directory) =>
|
|
timed(`dependencies ${path.basename(directory)}`, () =>
|
|
ensureDependencies(directory, { force: process.env.GITNEXUS_FORCE_DEPS === '1', runtime }),
|
|
);
|
|
ensure(root);
|
|
// Always restore and verify hooks, including the cached-install path.
|
|
runNpm(root, ['run', 'prepare']);
|
|
require('./verify-git-hooks.cjs').verifyGitHooks(root);
|
|
console.log('[install-deps] Husky hooks verified');
|
|
const shared = path.join(root, 'gitnexus-shared');
|
|
ensure(shared);
|
|
timed('shared build', () => runNpm(shared, ['run', 'build']));
|
|
const web = path.join(root, 'gitnexus-web');
|
|
ensure(web);
|
|
const core = path.join(root, 'gitnexus');
|
|
// A fresh npm ci already builds via prepare. A reused install still needs
|
|
// source compilation, even when manifests and lockfiles have not changed.
|
|
if (!ensure(core)) timed('CLI source build', () => runNpm(core, ['run', 'build']));
|
|
if (process.env.GITNEXUS_DEV_UI === '1') {
|
|
timed('Chromium provisioning', () =>
|
|
execFileSync(path.join(web, 'node_modules/.bin/playwright'), ['install', 'chromium'], {
|
|
cwd: web,
|
|
stdio: 'inherit',
|
|
}),
|
|
);
|
|
}
|
|
}
|