2019-09-06 13:43:53 +00:00
|
|
|
/**
|
|
|
|
* @license
|
2020-01-24 01:21:51 +00:00
|
|
|
* Copyright 2019-2020 Balena Ltd.
|
2019-09-06 13:43:53 +00:00
|
|
|
*
|
|
|
|
* Licensed 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 CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2020-01-24 01:21:51 +00:00
|
|
|
// tslint:disable-next-line:no-var-requires
|
|
|
|
require('./config-tests'); // required for side effects
|
|
|
|
|
2019-09-06 13:43:53 +00:00
|
|
|
import intercept = require('intercept-stdout');
|
2019-11-14 09:53:38 +00:00
|
|
|
import * as _ from 'lodash';
|
2019-08-15 12:40:55 +00:00
|
|
|
import * as nock from 'nock';
|
2019-08-09 17:36:52 +00:00
|
|
|
import * as path from 'path';
|
2019-09-06 13:43:53 +00:00
|
|
|
|
2019-08-09 17:36:52 +00:00
|
|
|
import * as balenaCLI from '../build/app';
|
2020-05-21 13:51:10 +00:00
|
|
|
import { setupSentry } from '../build/app-common';
|
2019-08-09 17:36:52 +00:00
|
|
|
|
|
|
|
export const runCommand = async (cmd: string) => {
|
|
|
|
const preArgs = [process.argv[0], path.join(process.cwd(), 'bin', 'balena')];
|
|
|
|
|
|
|
|
const err: string[] = [];
|
|
|
|
const out: string[] = [];
|
|
|
|
|
2019-09-06 13:43:53 +00:00
|
|
|
const stdoutHook = (log: string | Buffer) => {
|
2019-08-09 17:36:52 +00:00
|
|
|
// Skip over debug messages
|
2019-09-06 13:43:53 +00:00
|
|
|
if (typeof log === 'string' && !log.startsWith('[debug]')) {
|
2019-08-09 17:36:52 +00:00
|
|
|
out.push(log);
|
|
|
|
}
|
|
|
|
};
|
2019-09-06 13:43:53 +00:00
|
|
|
const stderrHook = (log: string | Buffer) => {
|
2019-08-09 17:36:52 +00:00
|
|
|
// Skip over debug messages
|
2019-08-15 12:40:55 +00:00
|
|
|
if (
|
2019-09-06 13:43:53 +00:00
|
|
|
typeof log === 'string' &&
|
2020-01-15 19:41:47 +00:00
|
|
|
!log.match(/\[debug\]/i) &&
|
2019-08-15 12:40:55 +00:00
|
|
|
// TODO stop this warning message from appearing when running
|
|
|
|
// sdk.setSharedOptions multiple times in the same process
|
2019-10-09 21:32:26 +00:00
|
|
|
!log.startsWith('Shared SDK options') &&
|
|
|
|
// Node 12: '[DEP0066] DeprecationWarning: OutgoingMessage.prototype._headers is deprecated'
|
|
|
|
!log.includes('[DEP0066]')
|
2019-08-15 12:40:55 +00:00
|
|
|
) {
|
2019-08-09 17:36:52 +00:00
|
|
|
err.push(log);
|
|
|
|
}
|
|
|
|
};
|
2019-09-06 13:43:53 +00:00
|
|
|
const unhookIntercept = intercept(stdoutHook, stderrHook);
|
2019-08-09 17:36:52 +00:00
|
|
|
|
|
|
|
try {
|
|
|
|
await balenaCLI.run(preArgs.concat(cmd.split(' ')), {
|
|
|
|
noFlush: true,
|
|
|
|
});
|
|
|
|
return {
|
|
|
|
err,
|
|
|
|
out,
|
|
|
|
};
|
2019-09-06 13:43:53 +00:00
|
|
|
} finally {
|
|
|
|
unhookIntercept();
|
2019-08-09 17:36:52 +00:00
|
|
|
}
|
|
|
|
};
|
2019-08-15 12:40:55 +00:00
|
|
|
|
|
|
|
export const balenaAPIMock = () => {
|
2019-12-05 08:55:17 +00:00
|
|
|
if (!nock.isActive()) {
|
|
|
|
nock.activate();
|
|
|
|
}
|
|
|
|
|
2019-08-15 12:40:55 +00:00
|
|
|
return nock(/./)
|
|
|
|
.get('/config/vars')
|
|
|
|
.reply(200, {
|
|
|
|
reservedNames: [],
|
|
|
|
reservedNamespaces: [],
|
|
|
|
invalidRegex: '/^d|W/',
|
|
|
|
whiteListedNames: [],
|
|
|
|
whiteListedNamespaces: [],
|
|
|
|
blackListedNames: [],
|
|
|
|
configVarSchema: [],
|
|
|
|
});
|
|
|
|
};
|
2019-11-14 09:53:38 +00:00
|
|
|
|
2020-01-15 19:41:47 +00:00
|
|
|
export function cleanOutput(output: string[] | string): string[] {
|
2019-11-14 09:53:38 +00:00
|
|
|
return _(_.castArray(output))
|
2020-01-15 19:41:47 +00:00
|
|
|
.map((log: string) => {
|
2019-11-14 09:53:38 +00:00
|
|
|
return log.split('\n').map(line => {
|
2020-01-15 19:41:47 +00:00
|
|
|
return monochrome(line.trim());
|
2019-11-14 09:53:38 +00:00
|
|
|
});
|
|
|
|
})
|
|
|
|
.flatten()
|
|
|
|
.compact()
|
|
|
|
.value();
|
|
|
|
}
|
2020-01-15 19:41:47 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Remove text colors (ASCII escape sequences). Example:
|
|
|
|
* Input: '\u001b[2K\r\u001b[34m[Build]\u001b[39m \u001b[1mmain\u001b[22m Image size: 1.14 MB'
|
|
|
|
* Output: '[Build] main Image size: 1.14 MB'
|
|
|
|
*
|
|
|
|
* TODO: check this function against a spec (ASCII escape sequences). It was
|
|
|
|
* coded from observation of a few samples only, and may not cover all cases.
|
|
|
|
*/
|
|
|
|
export function monochrome(text: string): string {
|
|
|
|
return text.replace(/\u001b\[\??\d+?[a-zA-Z]\r?/g, '');
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-02-15 20:18:00 +00:00
|
|
|
* Dynamic template string resolution.
|
|
|
|
* Usage example:
|
|
|
|
* const templateString = 'hello ${name}!';
|
|
|
|
* const templateVars = { name: 'world' };
|
|
|
|
* console.log( fillTemplate(templateString, templateVars) );
|
|
|
|
* // hello world!
|
2020-01-15 19:41:47 +00:00
|
|
|
*/
|
2020-02-15 20:18:00 +00:00
|
|
|
export function fillTemplate(
|
|
|
|
templateString: string,
|
|
|
|
templateVars: object,
|
|
|
|
): string {
|
|
|
|
const escaped = templateString.replace(/\\/g, '\\\\').replace(/`/g, '\\`');
|
|
|
|
const resolved = new Function(
|
|
|
|
...Object.keys(templateVars),
|
|
|
|
`return \`${escaped}\`;`,
|
|
|
|
).call(null, ...Object.values(templateVars));
|
|
|
|
const unescaped = resolved.replace(/\\`/g, '`').replace(/\\\\/g, '\\');
|
|
|
|
return unescaped;
|
2020-02-01 00:29:51 +00:00
|
|
|
}
|
|
|
|
|
2020-03-10 16:56:42 +00:00
|
|
|
export function fillTemplateArray(
|
|
|
|
templateStringArray: string[],
|
|
|
|
templateVars: object,
|
|
|
|
): string[];
|
|
|
|
export function fillTemplateArray(
|
|
|
|
templateStringArray: Array<string | string[]>,
|
|
|
|
templateVars: object,
|
|
|
|
): Array<string | string[]>;
|
2020-02-15 20:18:00 +00:00
|
|
|
export function fillTemplateArray(
|
|
|
|
templateStringArray: Array<string | string[]>,
|
|
|
|
templateVars: object,
|
2020-03-10 16:56:42 +00:00
|
|
|
): Array<string | string[]> {
|
2020-02-15 20:18:00 +00:00
|
|
|
return templateStringArray.map(i =>
|
|
|
|
Array.isArray(i)
|
|
|
|
? fillTemplateArray(i, templateVars)
|
|
|
|
: fillTemplate(i, templateVars),
|
|
|
|
);
|
2020-01-15 19:41:47 +00:00
|
|
|
}
|
2020-05-21 13:51:10 +00:00
|
|
|
|
|
|
|
export async function switchSentry(
|
|
|
|
enabled: boolean | undefined,
|
|
|
|
): Promise<boolean | undefined> {
|
|
|
|
const sentryOpts = (await setupSentry()).getClient()?.getOptions();
|
|
|
|
if (sentryOpts) {
|
|
|
|
const sentryStatus = sentryOpts.enabled;
|
|
|
|
sentryOpts.enabled = enabled;
|
|
|
|
return sentryStatus;
|
|
|
|
}
|
|
|
|
}
|