Files
galaxy/client/gulpfile.js
T
Dannon Baker e723ffb858 Remove unused del dependency
Replaced del.sync with fs.removeSync from fs-extra which we already use.
No need for a separate dependency when fs-extra provides the same functionality.
2025-09-30 14:11:58 -04:00

219 lines
8.1 KiB
JavaScript

const path = require("path");
const fs = require("fs-extra");
const { parallel, series } = require("gulp");
const child_process = require("child_process");
const { globSync } = require("glob");
const buildIcons = require("./icons/build_icons");
const os = require("os");
const yaml = require("yaml");
const PATHS = {
nodeModules: "./node_modules",
stagedLibraries: {
backbone: ["backbone.js", "backbone.js"],
jquery: ["dist/jquery.js", "jquery/jquery.js"],
"jquery-migrate": ["dist/jquery-migrate.js", "jquery/jquery.migrate.js"],
"jquery-mousewheel": ["jquery.mousewheel.js", "jquery/jquery.mousewheel.js"],
requirejs: ["require.js", "require.js"],
underscore: ["underscore.js", "underscore.js"],
},
};
// Check if visualization steps should be skipped
const SKIP_VIZ = process.env.SKIP_VIZ === "true" || process.env.SKIP_VIZ === "1";
PATHS.pluginBaseDir =
(process.env.GALAXY_PLUGIN_PATH && process.env.GALAXY_PLUGIN_PATH !== "None"
? process.env.GALAXY_PLUGIN_PATH
: undefined) || "../config/plugins/";
const staticPluginDir = "../static/plugins";
const visualizationsConfig = "./visualizations.yml";
// Load and validate visualization config
let VISUALIZATION_PLUGINS = {};
try {
if (fs.existsSync(visualizationsConfig)) {
const file = fs.readFileSync(visualizationsConfig, "utf8");
VISUALIZATION_PLUGINS = yaml.parse(file) || {};
} else {
console.warn(`Visualization config file not found: ${visualizationsConfig}`);
}
} catch (err) {
console.error(`Error loading visualization config: ${err.message}`);
VISUALIZATION_PLUGINS = {};
}
function stageLibs(callback) {
try {
Object.keys(PATHS.stagedLibraries).forEach((lib) => {
const p1 = path.resolve(path.join(PATHS.nodeModules, lib, PATHS.stagedLibraries[lib][0]));
const p2 = path.resolve(path.join("src", "libs", PATHS.stagedLibraries[lib][1]));
if (fs.existsSync(p1)) {
fs.removeSync(p2);
fs.createReadStream(p1).pipe(fs.createWriteStream(p2));
} else {
throw new Error(
`${p1} does not exist, yet it is a required library. Check that the package in question exists in node_modules.`,
);
}
});
callback();
} catch (err) {
callback(err);
}
}
async function icons() {
await buildIcons("./src/assets/icons.json");
}
function cleanPlugins(callback) {
if (SKIP_VIZ) {
console.log("Skipping plugin cleanup (SKIP_VIZ is set)");
return callback();
}
const vizStagingDir = path.join(staticPluginDir, "visualizations");
console.log(`Cleaning visualization staging directory: ${vizStagingDir}`);
fs.removeSync(vizStagingDir);
callback();
}
function stagePlugins(callback) {
if (SKIP_VIZ) {
console.log("Skipping plugin staging (SKIP_VIZ is set)");
return callback();
}
fs.ensureDirSync(path.join(staticPluginDir, "visualizations"));
// Get visualization directories
const visualizationDirs = path.join(PATHS.pluginBaseDir, "visualizations/*/static");
const dirs = globSync(visualizationDirs);
// Process each directory
const copyPromises = dirs.map((sourceDir) => {
// Get the relative path from the plugin base dir
const relativeDir = path.relative(PATHS.pluginBaseDir, sourceDir);
// Extract the plugin name from the path for logging
const pathParts = relativeDir.split(path.sep);
const pluginName = pathParts[1]; // visualizations/[pluginName]/static
console.log(`Staging plugin '${pluginName}' from ${sourceDir}`);
// The target should preserve the full path including 'static'
const targetDir = path.join(staticPluginDir, relativeDir);
// Skip node_modules/.bin directories
if (sourceDir.includes("node_modules/.bin")) {
return Promise.resolve();
}
return fs
.copy(sourceDir, targetDir, {
filter: (src) => !src.includes("node_modules/.bin"),
})
.catch((err) => {
console.error(`Error copying ${sourceDir}:`, err);
// Don't fail the entire build for a single plugin
return Promise.resolve();
});
});
Promise.all(copyPromises)
.then(() => {
console.log("Plugin staging completed successfully");
callback();
})
.catch((err) => {
console.error("Error during plugin staging:", err);
callback(err);
});
}
/**
* Produce plugins from fully self-contained npm packages
*/
async function installVisualizations(callback, forceReinstall = false) {
if (SKIP_VIZ) {
console.log("Skipping visualization installation (SKIP_VIZ is set)");
return callback();
}
const visualizationsDir = path.join(PATHS.pluginBaseDir, "visualizations");
fs.ensureDirSync(visualizationsDir);
for (const pluginName of Object.keys(VISUALIZATION_PLUGINS)) {
const pluginConfig = VISUALIZATION_PLUGINS[pluginName];
// Validate plugin configuration
if (!pluginConfig.package || !pluginConfig.version) {
console.error(`Invalid configuration for plugin '${pluginName}': missing package or version`);
continue;
}
const { package: pluginPackage, version } = pluginConfig;
const pluginDir = path.join(visualizationsDir, pluginName);
const staticDir = path.join(pluginDir, "static");
const xmlPath = path.join(staticDir, `${pluginName}.xml`);
const hashFilePath = path.join(staticDir, "plugin_build_hash.txt");
const currentHash = `${pluginPackage}@${version}`;
let tempDir = null;
try {
if (!forceReinstall && fs.existsSync(hashFilePath)) {
const storedHash = fs.readFileSync(hashFilePath, "utf8").trim();
if (storedHash === currentHash) {
console.log(`Package ${currentHash} already installed for ${pluginName}, skipping.`);
continue;
}
}
console.log(`Installing ${currentHash} into ${pluginDir}...`);
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), `${pluginName}-`));
fs.writeFileSync(path.join(tempDir, "package.json"), JSON.stringify({ private: true }, null, 2));
child_process.execSync(`npm install ${currentHash}`, {
cwd: tempDir,
stdio: "pipe",
shell: true,
});
const pkgDir = path.join(tempDir, "node_modules", pluginPackage);
if (!fs.existsSync(pkgDir)) {
throw new Error(`Package directory not found: ${pkgDir}`);
}
fs.emptyDirSync(pluginDir);
fs.copySync(pkgDir, pluginDir, { overwrite: true });
if (!fs.existsSync(xmlPath)) {
throw new Error(`Expected XML file not found after install: ${xmlPath}`);
}
fs.writeFileSync(hashFilePath, currentHash);
console.log(`Installed ${currentHash} into ${pluginDir}`);
} catch (err) {
console.error(`Failed to install ${pluginPackage}@${version} for ${pluginName}:`, err);
} finally {
if (tempDir && fs.existsSync(tempDir)) {
try {
fs.removeSync(tempDir);
} catch (cleanupErr) {
console.warn(`Warning: Failed to clean temp dir ${tempDir}:`, cleanupErr);
}
}
}
}
return callback();
}
function forceInstallVisualizations(callback) {
return installVisualizations(callback, true);
}
const client = parallel(stageLibs, icons);
const plugins = series(installVisualizations, cleanPlugins, stagePlugins);
const pluginsRebuild = series(forceInstallVisualizations, cleanPlugins, stagePlugins);
module.exports.client = client;
module.exports.plugins = plugins;
module.exports.default = parallel(client, plugins);
module.exports.installVisualizations = installVisualizations;
module.exports.cleanPlugins = cleanPlugins;
module.exports.pluginsRebuild = pluginsRebuild;