@eclipse-scout/releng
Version:
Release engineering module for Eclipse Scout
146 lines (122 loc) • 4.15 kB
JavaScript
/*
* Copyright (c) 2010, 2023 BSI Business Systems Integration AG
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
const axios = require('axios');
// Makes the script crash on unhandled rejections instead of silently
// ignoring them. In the future, promise rejections that are not handled will
// terminate the Node.js process with a non-zero exit code.
process.on('unhandledRejection', err => {
throw err;
});
const constructPath = (artifactoryUrl, item) => {
if (item.path === '.') {
return `${artifactoryUrl}/${item.repo}/${item.filename}`;
}
return `${artifactoryUrl}/${item.repo}/${item.path}/${item.filename}`;
};
const getScopeName = path => {
const SCOPE_REGEX = /^@[\w-]+/g;
return path.match(SCOPE_REGEX);
};
const getPackageName = itemName => {
const NAME_REGEX = /-snapshot.*/gi;
return itemName.replace(NAME_REGEX, '');
};
const getSnapshots = async (artifactoryUrl, repoName, config, verbose) => {
const AQL_API = 'api/search/aql'; // use aql for search
const searchUrl = `${artifactoryUrl}${AQL_API}`;
const query = `items.find({"repo":"${repoName}","name":{"$match":"*snapshot.*tgz"}})`;
const snapshotMap = new Map();
const response = await axios.post(searchUrl, query, config);
const data = response.data;
if (verbose) {
console.log('found items:\n' + JSON.stringify(data, null, 2));
}
if (data && data.results) {
for (const item of data.results) {
// group the snapshots by package and version to ensure that old versions won't be deleted e.g. groupName = @scout/cli-10.0.0
const groupName = `${getScopeName(item.path)}/${getPackageName(item.name)}`;
let groupSet = snapshotMap.get(groupName);
if (!groupSet) {
groupSet = new Set();
snapshotMap.set(groupName, groupSet);
}
groupSet.add({
groupName,
filename: item.name,
path: item.path,
created: new Date(item.created),
repo: item.repo
});
}
}
return snapshotMap;
};
const calculateItemsToDelete = async (itemMap, noToKeep) => {
const toDelete = [];
for (const itemSet of itemMap.values()) {
// order the snapshots of each package by date and select the oldest items to delete
const deleteItems = Array.from(itemSet)
.sort((a, b) => b.created - a.created)
.slice(noToKeep);
toDelete.push(...deleteItems);
}
return toDelete;
};
const deleteItems = async (artifactoryUrl, items, config, dryrun) => {
let success = true;
if (!items || items.length === 0) {
console.log('Nothing to cleanup');
}
for (const item of items) {
const itemUrl = constructPath(artifactoryUrl, item);
console.log(`delete: ${itemUrl}; ${dryrun ? 'dryrun=true' : ''}`);
if (dryrun) {
continue;
}
try {
const response = await axios.delete(itemUrl, config);
console.log(response.status);
} catch (error) {
success = false;
console.error(`couldn't delete item: ${itemUrl}`);
console.error(error);
}
}
if (!success) {
throw Error('Not every item could be deleted');
}
};
const doCleanup = async ({url, apikey, user, pwd, reponame, keep = 5, dryrun = false, verbose = false}) => {
console.log(`Input arguments: url=${url}; repo-name=${reponame}; number of artifacts to keep=${keep}; dry-run=${dryrun}; verbose=${verbose}`);
if (!reponame || !url) {
throw new Error('Please provide arguments for --url and --repo-name');
}
const headers = {
'Content-Type': 'text/plain'
};
if (apikey) {
headers['X-JFrog-Art-Api'] = apikey;
}
const config = {
headers: headers
};
if (!apikey && user && pwd) {
config.auth = {
username: user,
password: pwd
};
}
const foundItems = await getSnapshots(url, reponame, config, verbose);
const itemsToDelete = await calculateItemsToDelete(foundItems, keep);
await deleteItems(url, itemsToDelete, config, dryrun);
};
module.exports = {
doCleanup
};