@adobe/helix-deploy
Version:
Library and Commandline Tools to build and deploy OpenWhisk Actions
179 lines (160 loc) • 5.98 kB
JavaScript
/*
* Copyright 2021 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
import { fileURLToPath } from 'url';
import { fork } from 'child_process';
import path from 'path';
// eslint-disable-next-line no-underscore-dangle
const __dirname = path.resolve(fileURLToPath(import.meta.url), '..');
/**
* @typedef {object} VersionCoordinates
* @property {number} ci - CI build number
* @property {number} patch - semver patch version
* @property {number} minor - semver minor version
* @property {number} major - semver major version
*/
/**
* @typedef {object} VersionSpec
* @property {number} patchVersion - semver patch version
* @property {number} minorVersion - semver minor version
* @property {number} majorVersion - semver major version
*/
/**
* @typedef {object} RangeSpec
* @property {number} ciAge - number of seconds to keep outdated CI builds
* @property {number} patchAge - number of seconds to keep outdated patch versions
* @property {number} minorAge - number of seconds to keep outdated minor versions
* @property {number} majorAge - number of seconds to keep outdated major versions
* @property {number} ciAge - number of outdated CI builds to keep
* @property {number} patchAge - number of outdated patch versions to keep
* @property {number} minorAge - number of outdated minor versions to keep
* @property {number} majorAge - number of outdated major versions to keep
*/
/**
* @typedef {object} NamedAction
* @property {string} name - short name of the action, e.g. `embed`
* @property {string} fqName - fully qualified name of the action, dependent on deployer, e.g.
* `projects/helix-225321/locations/us-central1/functions/helix-services--data-embed_2_4_0`
* @property {VersionCoordinates} version - version of the action
* @property {Date} updated - timestamp of the update of the action
*/
/**
* Filters a list of named actions according
* to maximum age or number of versions.
* @param {NamedAction[]} fns - a list of actions, all with the same name
* @param {Date} now - date time to use as reference for age comparisons
* @param {RangeSpec} rangespec - which versions to keep
* @param {VersionSpec} versionspec - which version is current
* @returns {NamedAction[]} - a list of actions that can safely be deleted
*/
export function filterActions(fns, now, {
ciAge, patchAge, minorAge, majorAge, ciNum, patchNum, minorNum, majorNum,
} = {}, { patchVersion, minorVersion, majorVersion } = {}) {
// sort by updated date
const namedfns = fns.sort((a, b) => {
if (a.updated && b.updated) {
return a.updated - b.updated;
}
return 0;
});
function bycount(max) {
return (_, index, { length }) => index + max < length;
}
function bydate(age) {
return ({ updated }) => updated < new Date(now - (1000 * age));
}
function has(prop) {
return () => !!prop;
}
function hasVersion(prop) {
return (fn) => !!fn.version[prop];
}
function matchVersion(name) {
return (fn) => {
if (name === 'patch') {
return fn.version.patch < patchVersion
&& fn.version.minor === minorVersion
&& fn.version.major === majorVersion;
}
if (name === 'minor') {
return fn.version.minor < minorVersion
&& fn.version.major === majorVersion;
}
if (name === 'major') {
return fn.version.major < majorVersion;
}
return false;
};
}
const cleancibyage = namedfns
.filter(has(ciAge))
.filter(hasVersion('ci'))
.filter(bydate(ciAge));
const cleancibycount = namedfns
.filter(has(ciNum))
.filter(hasVersion('ci'))
.filter(bycount(ciNum));
const cleanpatchbyage = namedfns
.filter(has(patchAge))
.filter(hasVersion('patch'))
.filter(matchVersion('patch'))
.filter(bydate(patchAge));
const cleanpatchbycount = namedfns
.filter(has(patchNum))
.filter(hasVersion('patch'))
.filter(matchVersion('patch'))
.filter(bycount(patchNum));
const cleanminorbyage = namedfns
.filter(has(minorAge))
.filter(hasVersion('minor'))
.filter(matchVersion('minor'))
.filter(bydate(minorAge));
const cleanminorbycount = namedfns
.filter(has(minorNum))
.filter(hasVersion('minor'))
.filter(matchVersion('minor'))
.filter(bycount(minorNum));
const cleanmajorbyage = namedfns
.filter(has(majorAge))
.filter(hasVersion('major'))
.filter(matchVersion('major'))
.filter(bydate(majorAge));
const cleanmajorbycount = namedfns
.filter(has(majorNum))
.filter(hasVersion('major'))
.filter(matchVersion('major'))
.filter(bycount(majorNum));
return [...cleancibyage, ...cleancibycount,
...cleanpatchbyage, ...cleanpatchbycount,
...cleanminorbyage, ...cleanminorbycount,
...cleanmajorbyage, ...cleanmajorbycount];
}
export async function validateBundle(bundlePath, cfg) {
try {
const child = fork(path.resolve(__dirname, 'template', 'validate-bundle.js'), [bundlePath, JSON.stringify(cfg)]);
const ret = await new Promise((resolve, reject) => {
child.on('message', resolve);
child.on('error', reject);
child.on('exit', (code) => {
resolve(JSON.stringify({
status: 'error',
error: `Child process stopped with exit code ${code}`,
}));
});
});
return JSON.parse(ret);
} catch (e) {
return {
status: 'error',
error: e.message,
};
}
}