Files
galaxy/client/gulpfile.js
T

212 lines
7.9 KiB
JavaScript

const path = require("path");
const fs = require("fs-extra");
const del = require("del");
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)) {
del.sync(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 stagePlugins(callback) {
if (SKIP_VIZ) {
console.log("Skipping plugin staging (SKIP_VIZ is set)");
return callback();
}
fs.ensureDirSync(path.join(staticPluginDir));
// Get visualization directories
const visualizationDirs = [
path.join(PATHS.pluginBaseDir, "visualizations/*/static"),
path.join(PATHS.pluginBaseDir, "visualizations/*/*/static"),
];
// Flatten the glob patterns to actual directory paths
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, stagePlugins);
const pluginsRebuild = series(forceInstallVisualizations, stagePlugins);
module.exports.client = client;
module.exports.plugins = plugins;
module.exports.default = parallel(client, plugins);
module.exports.installVisualizations = installVisualizations;
module.exports.pluginsRebuild = pluginsRebuild;