124 lines
3.6 KiB
JavaScript
124 lines
3.6 KiB
JavaScript
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
class ProjectScanner {
|
|
constructor(rootPath) {
|
|
this.rootPath = rootPath;
|
|
}
|
|
|
|
async scan() {
|
|
const projects = [];
|
|
|
|
try {
|
|
const entries = fs.readdirSync(this.rootPath, { withFileTypes: true });
|
|
|
|
for (const entry of entries) {
|
|
if (!entry.isDirectory()) continue;
|
|
|
|
const projectPath = path.join(this.rootPath, entry.name);
|
|
const projectInfo = this.analyzeProject(projectPath, entry.name);
|
|
|
|
if (projectInfo) {
|
|
projects.push(projectInfo);
|
|
}
|
|
}
|
|
} catch (err) {
|
|
console.error('Failed to scan projects:', err);
|
|
}
|
|
|
|
return projects;
|
|
}
|
|
|
|
analyzeProject(projectPath, name) {
|
|
const hasDockerfile = fs.existsSync(path.join(projectPath, 'Dockerfile'));
|
|
const hasDockerCompose = fs.existsSync(path.join(projectPath, 'docker-compose.yml'));
|
|
const hasBuildScript = fs.existsSync(path.join(projectPath, 'build-image-tar.ps1'));
|
|
const hasDeployScript = fs.existsSync(path.join(projectPath, 'deploy-docker-auto.ps1'));
|
|
const hasDeploymentConfig = fs.existsSync(path.join(projectPath, 'docker-deployment.json'));
|
|
const hasDockerIgnore = fs.existsSync(path.join(projectPath, '.dockerignore'));
|
|
|
|
// Find tar files
|
|
let tarFile = null;
|
|
try {
|
|
const files = fs.readdirSync(projectPath);
|
|
tarFile = files.find(f => f.endsWith('.tar'));
|
|
} catch (err) {
|
|
// ignore
|
|
}
|
|
|
|
// Read deployment config if exists
|
|
let deploymentConfig = null;
|
|
if (hasDeploymentConfig) {
|
|
try {
|
|
deploymentConfig = JSON.parse(fs.readFileSync(path.join(projectPath, 'docker-deployment.json'), 'utf-8'));
|
|
} catch (err) {
|
|
// ignore
|
|
}
|
|
}
|
|
|
|
// Determine docker status
|
|
let dockerStatus = 'none';
|
|
if (hasDockerfile && hasDockerCompose) {
|
|
dockerStatus = 'configured';
|
|
} else if (hasDockerfile || hasDockerCompose) {
|
|
dockerStatus = 'partial';
|
|
}
|
|
|
|
return {
|
|
name,
|
|
path: projectPath,
|
|
hasDockerfile,
|
|
hasDockerCompose,
|
|
hasBuildScript,
|
|
hasDeployScript,
|
|
hasDeploymentConfig,
|
|
hasDockerIgnore,
|
|
tarFile,
|
|
deploymentConfig,
|
|
dockerStatus,
|
|
serverId: deploymentConfig?.deployment?.serverId || null,
|
|
remotePath: deploymentConfig?.deployment?.targetPath || `~/containers/${name}`
|
|
};
|
|
}
|
|
|
|
// Scan subdirectories too (for monorepos like AudioSphere)
|
|
async scanDeep(maxDepth = 2) {
|
|
const projects = [];
|
|
await this._scanRecursive(this.rootPath, projects, 0, maxDepth);
|
|
return projects;
|
|
}
|
|
|
|
async _scanRecursive(currentPath, projects, depth, maxDepth) {
|
|
if (depth > maxDepth) return;
|
|
|
|
try {
|
|
const entries = fs.readdirSync(currentPath, { withFileTypes: true });
|
|
|
|
// Check if this directory has a Dockerfile
|
|
const hasDockerfile = entries.some(e => e.name === 'Dockerfile');
|
|
if (hasDockerfile) {
|
|
const name = path.relative(this.rootPath, currentPath).replace(/\\/g, '/');
|
|
const projectInfo = this.analyzeProject(currentPath, name);
|
|
if (projectInfo) {
|
|
projects.push(projectInfo);
|
|
}
|
|
}
|
|
|
|
// Recurse into subdirectories
|
|
for (const entry of entries) {
|
|
if (!entry.isDirectory()) continue;
|
|
if (entry.name.startsWith('.')) continue;
|
|
if (entry.name === 'node_modules') continue;
|
|
if (entry.name === 'dist') continue;
|
|
if (entry.name === 'build') continue;
|
|
|
|
await this._scanRecursive(path.join(currentPath, entry.name), projects, depth + 1, maxDepth);
|
|
}
|
|
} catch (err) {
|
|
// ignore permission errors etc
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = { ProjectScanner };
|