mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-10-31 02:28:09 +08:00
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.
219 lines
8.1 KiB
JavaScript
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;
|