@soleil-se/run
Version:
Run various utility scripts for creating apps, configs or migration tasks.
151 lines (139 loc) • 6.08 kB
JavaScript
import { readFileSync, readdirSync, statSync } from 'fs';
import chalk from 'chalk';
import path from 'path';
import config from '@soleil-se/build-config';
import getAddonVersions from './install-check/getAddonVersions.js';
// Config files project.config/user.config can include settings to exclude apps from install checks
// This array of strings is provided in property `installCheckExcludedApps`.
const { installCheckExcludedApps } = config.env;
const MAINPATH = '.';
/**
* Recursively traverses a directory and collects all file paths.
* @param {string} dir - The directory to traverse.
* @param {string[]} fileList - The list of collected file paths this far.
* @returns The list of collected file paths.
*/
function traverseDirectory(dir, fileList = []) {
const files = readdirSync(dir);
files.forEach((file) => {
const filePath = path.join(dir, file);
const stats = statSync(filePath);
if (stats.isDirectory()) {
// Skip dist and node_modules directories
if (file === 'dist' || file === 'node_modules') {
return;
}
traverseDirectory(filePath, fileList);
} else {
fileList.push(filePath);
}
});
return fileList;
}
/**
* Retrieves the manifest files of repository applications by traversing the main path,
* filtering for files ending with 'manifest.json', and checking if their type is
* 'WebApp', 'RESTApp', or 'Widget'.
*
* @returns {string[]} An array of manifest file paths containing valid application manifests.
*/
function getRepoApps() {
const files = traverseDirectory(MAINPATH);
return files
.filter((file) => {
if (file.endsWith('manifest.json')) {
const { type } = JSON.parse(readFileSync(file));
return type === 'WebApp' || type === 'RESTApp' || type === 'Widget';
}
return false;
});
}
/**
* Compares the versions of repository apps with the installed addons and categorizes them.
*
* @param {Array<Object>} repositoryApps - List of repository app objects.
* @param {Array<Object>} installedAddons - List of installed addon objects.
* @returns {Object} An object containing:
* - {Array<Object>} notInstalled - Repository apps that are not installed.
* - {Array<Object>} versionMismatches - Repository apps whose installed version does not match
* the repository version. Each object includes `installedVersion`.
* - {Array<Object>} excluded - Repository apps that are excluded from installation checks.
*/
function compareVersions(repositoryApps, installedAddons) {
// Compare the versions of the repository apps with the installed addons
// and collect them into the three lists of not installed, version mismatches, and excluded.
return repositoryApps.reduce(
({ notInstalled, versionMismatches, excluded }, repoApp) => {
const installedAddon = installedAddons.find((addon) => addon.appIdentifier === repoApp.id);
// Add the app to the appropriate list based on its status
if (installedAddon) {
if (repoApp.version !== installedAddon.version) {
versionMismatches.push({ ...repoApp, installedVersion: installedAddon.version });
}
} else if (!installCheckExcludedApps || !installCheckExcludedApps.includes(repoApp.id)) {
notInstalled.push(repoApp);
} else {
excluded.push(repoApp);
}
return { notInstalled, versionMismatches, excluded };
},
// The starting empty buckets
{ notInstalled: [], versionMismatches: [], excluded: [] },
);
}
/**
* Checks the repository for available apps, compares them with installed addons,
* and logs the status of each app (installed, version mismatches, not installed, or excluded).
*
* - Lists the number of apps by type found in the repository.
* - Compares repository apps with installed addon versions.
* - Logs details about version mismatches, missing installations, and excluded apps.
*
* @returns {Promise<void>} Resolves when the check is complete and all results are logged.
*/
export default async function installCheck() {
console.log('Checking for apps in repository...');
const apps = getRepoApps().map((filePath) => {
const appInfo = JSON.parse(readFileSync(filePath, 'utf-8'));
return {
id: appInfo.id,
version: appInfo.version,
name: appInfo.name.sv || appInfo.name.en || appInfo.name,
type: appInfo.type,
};
});
// Get the count of each type of apps
const appCounts = apps.reduce((acc, curr) => {
acc[curr.type] = acc[curr.type] ? acc[curr.type] + 1 : 1;
return acc;
}, {});
Object.keys(appCounts).forEach((key) => {
console.log(`${chalk.blue(`Found ${appCounts[key]} ${key}s in the repository.`)}`);
});
console.log('\nChecking for installed apps...');
const addonVersions = await getAddonVersions();
const diff = compareVersions(apps, addonVersions);
if (diff.versionMismatches.length === 0 && diff.notInstalled.length === 0) {
console.log(chalk.green('All apps are installed and up to date as addons.'));
}
if (diff.versionMismatches.length > 0) {
const logText = `${diff.versionMismatches.length} app${diff.versionMismatches.length > 1 ? 's' : ''} have version mismatches.`;
console.log(`\n${chalk.redBright(logText)}`);
console.log('-'.repeat(logText.length));
diff.versionMismatches.forEach((app) => {
console.log(`${app.name} (${app.id})\n${chalk.redBright(`Available: ${app.version}, Installed: ${app.installedVersion}`)}`);
});
}
if (diff.notInstalled.length > 0) {
const logText = `${diff.notInstalled.length} app${diff.notInstalled.length > 1 ? 's' : ''} are not installed as addons.`;
console.log(`\n${chalk.yellow(logText)}`);
console.log('-'.repeat(logText.length));
diff.notInstalled.forEach((app) => {
console.log(`${app.name} (${app.id})`);
});
}
if (diff.excluded.length > 0) {
const logText = `!Note that ${diff.excluded.length} app${diff.excluded.length > 1 ? 's' : ''} are excluded from the install check.`;
console.log(`\n${chalk.blue(logText)}`);
}
}