fix: resolve CI test failures
Some checks failed
CI / test (push) Has been cancelled

This commit is contained in:
2026-02-01 01:36:31 +00:00
parent 8196b6bdb8
commit a35e139861

View File

@@ -0,0 +1,263 @@
import * as path from 'path';
import * as fs from 'fs';
import { DependencyAnalysis, DependencyInfo, ProjectType } from '../types';
import { parseJSONFile, parseTOMLFile } from '../utils/fileUtils';
const NODE_PACKAGE_MANAGERS = ['yarn.lock', 'pnpm-lock.yaml', 'bun.lockb'];
const PYTHON_PACKAGE_MANAGERS = ['Pipfile.lock', 'poetry.lock'];
export async function analyzeDependencies(
directory: string,
projectType: ProjectType
): Promise<DependencyAnalysis> {
let dependencies: DependencyInfo[] = [];
let packageManager: DependencyAnalysis['packageManager'] = null;
let lockFile: string | undefined;
switch (projectType) {
case 'node':
const nodeDeps = await analyzeNodeDependencies(directory);
dependencies = nodeDeps.dependencies;
packageManager = nodeDeps.packageManager;
lockFile = nodeDeps.lockFile;
break;
case 'python':
const pyDeps = await analyzePythonDependencies(directory);
dependencies = pyDeps.dependencies;
packageManager = pyDeps.packageManager;
lockFile = pyDeps.lockFile;
break;
case 'go':
const goDeps = await analyzeGoDependencies(directory);
dependencies = goDeps.dependencies;
packageManager = goDeps.packageManager;
break;
case 'rust':
const rustDeps = await analyzeRustDependencies(directory);
dependencies = rustDeps.dependencies;
packageManager = rustDeps.packageManager;
break;
case 'java':
const javaDeps = await analyzeJavaDependencies(directory);
dependencies = javaDeps.dependencies;
packageManager = javaDeps.packageManager;
break;
default:
dependencies = [];
}
return {
dependencies,
packageManager,
lockFile
};
}
async function analyzeNodeDependencies(
directory: string
): Promise<{ dependencies: DependencyInfo[]; packageManager: DependencyAnalysis['packageManager']; lockFile?: string }> {
const packageJsonPath = path.join(directory, 'package.json');
const packageJson = parseJSONFile<{
dependencies?: Record<string, string>;
devDependencies?: Record<string, string>;
}>(packageJsonPath);
if (!packageJson) {
return { dependencies: [], packageManager: null };
}
const dependencies: DependencyInfo[] = [];
if (packageJson.dependencies) {
for (const [name, version] of Object.entries(packageJson.dependencies)) {
dependencies.push({ name, version: String(version), type: 'prod' });
}
}
if (packageJson.devDependencies) {
for (const [name, version] of Object.entries(packageJson.devDependencies)) {
dependencies.push({ name, version: String(version), type: 'dev' });
}
}
let packageManager: DependencyAnalysis['packageManager'] = 'npm';
let lockFile: string | undefined;
for (const lockFileName of NODE_PACKAGE_MANAGERS) {
if (fs.existsSync(path.join(directory, lockFileName))) {
lockFile = lockFileName;
if (lockFileName === 'yarn.lock') packageManager = 'yarn';
else if (lockFileName === 'pnpm-lock.yaml') packageManager = 'pnpm';
else if (lockFileName === 'bun.lockb') packageManager = 'npm';
break;
}
}
return { dependencies, packageManager, lockFile };
}
async function analyzePythonDependencies(
directory: string
): Promise<{ dependencies: DependencyInfo[]; packageManager: DependencyAnalysis['packageManager']; lockFile?: string }> {
const dependencies: DependencyInfo[] = [];
const requirementsPath = path.join(directory, 'requirements.txt');
if (fs.existsSync(requirementsPath)) {
const content = fs.readFileSync(requirementsPath, 'utf-8');
const lines = content.split('\n');
for (const line of lines) {
const trimmed = line.trim();
if (trimmed && !trimmed.startsWith('#')) {
const match = trimmed.match(/^([a-zA-Z0-9_-]+)([<>=!~]+[^;]+)?/);
if (match) {
dependencies.push({
name: match[1],
version: match[2] || 'latest',
type: 'prod'
});
}
}
}
}
const pyprojectPath = path.join(directory, 'pyproject.toml');
const pyproject = parseTOMLFile(pyprojectPath);
const projectDeps = pyproject?.['project'] as Record<string, unknown> | undefined;
if (projectDeps && Array.isArray(projectDeps['dependencies'])) {
const deps = projectDeps['dependencies'] as string[];
for (const dep of deps) {
const match = dep.match(/^([a-zA-Z0-9_-]+)([<>=!~]+[^;]+)?/);
if (match) {
dependencies.push({
name: match[1],
version: match[2] || 'latest',
type: 'prod'
});
}
}
}
let packageManager: DependencyAnalysis['packageManager'] = 'pip';
let lockFile: string | undefined;
for (const lockFileName of PYTHON_PACKAGE_MANAGERS) {
if (fs.existsSync(path.join(directory, lockFileName))) {
lockFile = lockFileName;
if (lockFileName === 'poetry.lock') packageManager = 'poetry';
break;
}
}
return { dependencies, packageManager, lockFile };
}
async function analyzeGoDependencies(
directory: string
): Promise<{ dependencies: DependencyInfo[]; packageManager: DependencyAnalysis['packageManager'] }> {
const goModPath = path.join(directory, 'go.mod');
if (!fs.existsSync(goModPath)) {
return { dependencies: [], packageManager: 'go' };
}
const content = fs.readFileSync(goModPath, 'utf-8');
const dependencies: DependencyInfo[] = [];
const requireBlockMatch = content.match(/require\s*\(\s*([\s\S]*?)\s*\)/);
if (requireBlockMatch) {
const requireBlock = requireBlockMatch[1];
const depRegex = /\b([^\s]+)\s+v?([^\s]+)/g;
let match;
while ((match = depRegex.exec(requireBlock)) !== null) {
const path = match[1];
if (path && !path.startsWith('//') && path !== 'require') {
dependencies.push({
name: path,
version: match[2] || 'latest',
type: 'prod'
});
}
}
}
return { dependencies, packageManager: 'go' };
}
async function analyzeRustDependencies(
directory: string
): Promise<{ dependencies: DependencyInfo[]; packageManager: DependencyAnalysis['packageManager'] }> {
const cargoTomlPath = path.join(directory, 'Cargo.toml');
const cargoToml = parseTOMLFile(cargoTomlPath);
if (!cargoToml) {
return { dependencies: [], packageManager: 'cargo' };
}
const dependencies: DependencyInfo[] = [];
const deps = cargoToml.dependencies as Record<string, string | { version?: string }> | undefined;
if (deps) {
for (const [name, config] of Object.entries(deps)) {
const version = typeof config === 'string' ? config : (config as { version?: string }).version || '*';
dependencies.push({ name, version, type: 'prod' });
}
}
const dev = cargoToml.dev as { dependencies?: Record<string, string | { version?: string }> } | undefined;
if (dev?.dependencies) {
for (const [name, config] of Object.entries(dev.dependencies)) {
const version = typeof config === 'string' ? config : (config as { version?: string }).version || '*';
dependencies.push({ name, version, type: 'dev' });
}
}
return { dependencies, packageManager: 'cargo' };
}
async function analyzeJavaDependencies(
directory: string
): Promise<{ dependencies: DependencyInfo[]; packageManager: DependencyAnalysis['packageManager'] }> {
const dependencies: DependencyInfo[] = [];
const pomPath = path.join(directory, 'pom.xml');
if (fs.existsSync(pomPath)) {
const content = fs.readFileSync(pomPath, 'utf-8');
const dependencyRegex = /<dependency>\s*<groupId>([^<]+)<\/groupId>\s*<artifactId>([^<]+)<\/artifactId>\s*<version>([^<]+)<\/version>\s*<\/dependency>/g;
let match;
while ((match = dependencyRegex.exec(content)) !== null) {
dependencies.push({
name: `${match[1]}:${match[2]}`,
version: match[3],
type: 'prod'
});
}
return { dependencies, packageManager: 'maven' };
}
const buildGradlePath = path.join(directory, 'build.gradle');
const buildGradleKtsPath = path.join(directory, 'build.gradle.kts');
if (fs.existsSync(buildGradlePath) || fs.existsSync(buildGradleKtsPath)) {
const content = fs.readFileSync(buildGradlePath || buildGradleKtsPath, 'utf-8');
const dependencyRegex = /implementation\s+['"]([^:'"]+):([^:'"']+):([^'"@]+)@?(jar)?['"]/g;
let match;
while ((match = dependencyRegex.exec(content)) !== null) {
dependencies.push({
name: `${match[1]}:${match[2]}`,
version: match[3],
type: 'prod'
});
}
return { dependencies, packageManager: 'gradle' };
}
return { dependencies, packageManager: null };
}