chore: convert core logger to global logger

pull/413/head
Webber 2022-08-06 22:43:27 +02:00
parent 0440a33b65
commit 38285d014c
20 changed files with 104 additions and 69 deletions

View File

@ -55,6 +55,8 @@
"no-continue": "off", "no-continue": "off",
// From experience, named exports are almost always desired. I got tired of this rule // From experience, named exports are almost always desired. I got tired of this rule
"import/prefer-default-export": "off", "import/prefer-default-export": "off",
// Disable in favour of TS equivalent
"no-unused-vars": "off",
// Unused vars are useful to keep method signatures consistent and documented // Unused vars are useful to keep method signatures consistent and documented
"@typescript-eslint/no-unused-vars": "off", "@typescript-eslint/no-unused-vars": "off",
// For this project only use kebab-case // For this project only use kebab-case
@ -65,9 +67,13 @@
"unicorn/prevent-abbreviations": "off", "unicorn/prevent-abbreviations": "off",
// Allow disabling eslint per file // Allow disabling eslint per file
"eslint-comments/no-use": "off", "eslint-comments/no-use": "off",
// Deno import style // Deno import style not supported (enable after upgrading)
"import/extensions": "off", "import/extensions": "off",
// Deno import style // Deno import style not supported (enable after upgrading)
"import/no-unresolved": "off" "import/no-unresolved": "off",
// App it not yet internationalised
"i18n-text/no-en": "off",
// Showing false positives (enable after upgrading)
"no-shadow": "off"
} }
} }

View File

@ -11,7 +11,13 @@ export const createFormatter = ({
showBrackets = true, showBrackets = true,
depth = 3, depth = 3,
} = {}): FormatterFunction => { } = {}): FormatterFunction => {
const column = (value: string) => (showBrackets ? `[${value}]` : ` ${value}`); const column = (input: string, { width = 0, align = 'left' } = {}) => {
const totalWidth = showBrackets ? width + 2 : width;
const value = showBrackets ? `[${input}]` : ` ${input}`;
const paddingOptions = { side: align === 'left' ? 'right' : 'left' };
return pad(value, totalWidth, paddingOptions);
};
return ({ level, levelName, msg, args, loggerName }: LogRecord) => { return ({ level, levelName, msg, args, loggerName }: LogRecord) => {
let line = ''; let line = '';
@ -31,7 +37,7 @@ export const createFormatter = ({
if (showLevelName) { if (showLevelName) {
const shortName = levelName.length <= 5 ? levelName : levelName.slice(0, 4); const shortName = levelName.length <= 5 ? levelName : levelName.slice(0, 4);
line += column(`${pad(shortName, 5, { side: 'left' })}`); line += column(shortName, { width: 5, align: 'right' });
} }
if (showLevel) { if (showLevel) {

16
src/global.d.ts vendored
View File

@ -1,10 +1,10 @@
/* eslint-disable no-unused-vars */ let log: {
export type Level = 'debug' | 'info' | 'warn' | 'error' | 'critical'; debug: (msg: any, ...args: any[]) => void;
info: (msg: any, ...args: any[]) => void;
warning: (msg: any, ...args: any[]) => void;
error: (msg: any, ...args: any[]) => void;
};
declare global { interface Window {
const log: (level: Level, ...args: any[]) => void; log: any;
interface Window {
log: any;
}
} }

View File

@ -8,7 +8,8 @@ import PlatformSetup from './model/platform-setup.ts';
async function runMain() { async function runMain() {
try { try {
if (Cli.InitCliMode()) { if (Cli.InitCliMode()) {
log.debug('CloudBuilder CLI mode'); // Todo - this is only here for testing the entire flow in deno and making sure I'm hitting the right path
log.error('CloudBuilder CLI mode');
await Cli.RunCli(); await Cli.RunCli();
return; return;
@ -28,7 +29,7 @@ async function runMain() {
if (buildParameters.cloudRunnerCluster !== 'local') { if (buildParameters.cloudRunnerCluster !== 'local') {
await CloudRunner.run(buildParameters, baseImage.toString()); await CloudRunner.run(buildParameters, baseImage.toString());
} else { } else {
core.info('Building locally'); log.info('Building locally');
await PlatformSetup.setup(buildParameters, actionFolder); await PlatformSetup.setup(buildParameters, actionFolder);
if (process.platform === 'darwin') { if (process.platform === 'darwin') {
MacBuilder.run(actionFolder, workspace, buildParameters); MacBuilder.run(actionFolder, workspace, buildParameters);
@ -40,7 +41,7 @@ async function runMain() {
// Set output // Set output
await Output.setBuildVersion(buildParameters.buildVersion); await Output.setBuildVersion(buildParameters.buildVersion);
} catch (error) { } catch (error) {
core.error(error); log.error(error);
core.setFailed((error as Error).message); core.setFailed((error as Error).message);
} }
} }

View File

@ -11,7 +11,7 @@ export default class AndroidVersioning {
static versionToVersionCode(version) { static versionToVersionCode(version) {
if (version === 'none') { if (version === 'none') {
core.info(`Versioning strategy is set to ${version}, so android version code should not be applied.`); log.info(`Versioning strategy is set to ${version}, so android version code should not be applied.`);
return 0; return 0;
} }
@ -19,7 +19,7 @@ export default class AndroidVersioning {
const parsedVersion = semver.parse(version); const parsedVersion = semver.parse(version);
if (!parsedVersion) { if (!parsedVersion) {
core.warning(`Could not parse "${version}" to semver, defaulting android version code to 1`); log.warning(`Could not parse "${version}" to semver, defaulting android version code to 1`);
return 1; return 1;
} }
@ -33,7 +33,7 @@ export default class AndroidVersioning {
`Generated versionCode ${versionCode} is dangerously close to the maximum allowed number 2100000000. Consider a different versioning scheme to be able to continue updating your application.`, `Generated versionCode ${versionCode} is dangerously close to the maximum allowed number 2100000000. Consider a different versioning scheme to be able to continue updating your application.`,
); );
} }
core.info(`Using android versionCode ${versionCode}`); log.info(`Using android versionCode ${versionCode}`);
return versionCode; return versionCode;
} }

View File

@ -14,7 +14,7 @@ class Cache {
return; return;
} }
core.warning(` log.warning(`
Library folder does not exist. Library folder does not exist.
Consider setting up caching to speed up your workflow, Consider setting up caching to speed up your workflow,
if this is not your first build. if this is not your first build.

View File

@ -68,8 +68,8 @@ export class Cli {
@CliFunction(`print-input`, `prints all input`) @CliFunction(`print-input`, `prints all input`)
private static logInput() { private static logInput() {
core.info(`\n`); log.info(`\n`);
core.info(`INPUT:`); log.info(`INPUT:`);
const properties = Object.getOwnPropertyNames(Input); const properties = Object.getOwnPropertyNames(Input);
for (const element of properties) { for (const element of properties) {
if ( if (
@ -80,10 +80,10 @@ export class Cli {
element !== 'cliOptions' && element !== 'cliOptions' &&
element !== 'prototype' element !== 'prototype'
) { ) {
core.info(`${element} ${Input[element]}`); log.info(`${element} ${Input[element]}`);
} }
} }
core.info(`\n`); log.info(`\n`);
} }
@CliFunction(`cli`, `runs a cloud runner build`) @CliFunction(`cli`, `runs a cloud runner build`)

View File

@ -96,7 +96,7 @@ export class AWSBaseStack {
} }
CloudRunnerLogger.log('base stack is now ready'); CloudRunnerLogger.log('base stack is now ready');
} catch (error) { } catch (error) {
core.error(JSON.stringify(await describeStack(), undefined, 4)); log.error(JSON.stringify(await describeStack(), undefined, 4));
throw error; throw error;
} }
} }

View File

@ -5,7 +5,7 @@ import CloudRunner from '../../cloud-runner.ts';
export class AWSError { export class AWSError {
static async handleStackCreationFailure(error: any, CF: aws.CloudFormation, taskDefStackName: string) { static async handleStackCreationFailure(error: any, CF: aws.CloudFormation, taskDefStackName: string) {
CloudRunnerLogger.log('aws error: '); CloudRunnerLogger.log('aws error: ');
core.error(JSON.stringify(error, undefined, 4)); log.error(JSON.stringify(error, undefined, 4));
if (CloudRunner.buildParameters.cloudRunnerIntegrationTests) { if (CloudRunner.buildParameters.cloudRunnerIntegrationTests) {
CloudRunnerLogger.log('Getting events and resources for task stack'); CloudRunnerLogger.log('Getting events and resources for task stack');
const events = (await CF.describeStackEvents({ StackName: taskDefStackName }).promise()).StackEvents; const events = (await CF.describeStackEvents({ StackName: taskDefStackName }).promise()).StackEvents;

View File

@ -96,7 +96,7 @@ class AWSTaskRunner {
); );
core.setFailed(error); core.setFailed(error);
core.error(error); log.error(error);
} }
} }

View File

@ -131,7 +131,7 @@ class Kubernetes implements ProviderInterface {
return output; return output;
} catch (error) { } catch (error) {
CloudRunnerLogger.log('Running job failed'); CloudRunnerLogger.log('Running job failed');
core.error(JSON.stringify(error, undefined, 4)); log.error(JSON.stringify(error, undefined, 4));
await this.cleanupTaskResources(); await this.cleanupTaskResources();
throw error; throw error;
} }
@ -151,7 +151,7 @@ class Kubernetes implements ProviderInterface {
await new Promise((promise) => setTimeout(promise, 5000)); await new Promise((promise) => setTimeout(promise, 5000));
} catch (error) { } catch (error) {
CloudRunnerLogger.log('Failed to cleanup, error:'); CloudRunnerLogger.log('Failed to cleanup, error:');
core.error(JSON.stringify(error, undefined, 4)); log.error(JSON.stringify(error, undefined, 4));
CloudRunnerLogger.log('Abandoning cleanup, build error:'); CloudRunnerLogger.log('Abandoning cleanup, build error:');
throw error; throw error;
} }

View File

@ -37,8 +37,8 @@ class KubernetesStorage {
try { try {
return (await kubeClient.readNamespacedPersistentVolumeClaim(name, namespace)).body.status?.phase; return (await kubeClient.readNamespacedPersistentVolumeClaim(name, namespace)).body.status?.phase;
} catch (error) { } catch (error) {
core.error('Failed to get PVC phase'); log.error('Failed to get PVC phase');
core.error(JSON.stringify(error, undefined, 4)); log.error(JSON.stringify(error, undefined, 4));
throw error; throw error;
} }
} }
@ -57,9 +57,9 @@ class KubernetesStorage {
}, },
); );
} catch (error: any) { } catch (error: any) {
core.error('Failed to watch PVC'); log.error('Failed to watch PVC');
core.error(error.toString()); log.error(error.toString());
core.error( log.error(
`PVC Body: ${JSON.stringify( `PVC Body: ${JSON.stringify(
(await kubeClient.readNamespacedPersistentVolumeClaim(name, namespace)).body, (await kubeClient.readNamespacedPersistentVolumeClaim(name, namespace)).body,
undefined, undefined,

View File

@ -44,8 +44,8 @@ class KubernetesTaskRunner {
throw resultError; throw resultError;
} }
if (!didStreamAnyLogs) { if (!didStreamAnyLogs) {
core.error('Failed to stream any logs, listing namespace events, check for an error with the container'); log.error('Failed to stream any logs, listing namespace events, check for an error with the container');
core.error( log.error(
JSON.stringify( JSON.stringify(
{ {
events: (await kubeClient.listNamespacedEvent(namespace)).body.items events: (await kubeClient.listNamespacedEvent(namespace)).body.items

View File

@ -10,24 +10,24 @@ class CloudRunnerLogger {
} }
public static log(message: string) { public static log(message: string) {
core.info(message); log.info(message);
} }
public static logWarning(message: string) { public static logWarning(message: string) {
core.warning(message); log.warning(message);
} }
public static logLine(message: string) { public static logLine(message: string) {
core.info(`${message}\n`); log.info(`${message}\n`);
} }
public static error(message: string) { public static error(message: string) {
core.error(message); log.error(message);
} }
public static logWithTime(message: string) { public static logWithTime(message: string) {
const newTimestamp = this.createTimestamp(); const newTimestamp = this.createTimestamp();
core.info( log.info(
`${message} (Since previous: ${this.calculateTimeDiff( `${message} (Since previous: ${this.calculateTimeDiff(
newTimestamp, newTimestamp,
this.timestamp, this.timestamp,

View File

@ -9,19 +9,19 @@ export class FollowLogStreamService {
CloudRunnerLogger.log('End of log transmission received'); CloudRunnerLogger.log('End of log transmission received');
shouldReadLogs = false; shouldReadLogs = false;
} else if (message.includes('Rebuilding Library because the asset database could not be found!')) { } else if (message.includes('Rebuilding Library because the asset database could not be found!')) {
core.warning('LIBRARY NOT FOUND!'); log.warning('LIBRARY NOT FOUND!');
core.setOutput('library-found', 'false'); core.setOutput('library-found', 'false');
} else if (message.includes('Build succeeded')) { } else if (message.includes('Build succeeded')) {
core.setOutput('build-result', 'success'); core.setOutput('build-result', 'success');
} else if (message.includes('Build fail')) { } else if (message.includes('Build fail')) {
core.setOutput('build-result', 'failed'); core.setOutput('build-result', 'failed');
core.setFailed('unity build failed'); core.setFailed('unity build failed');
core.error('BUILD FAILED!'); log.error('BUILD FAILED!');
} else if (CloudRunner.buildParameters.cloudRunnerIntegrationTests && message.includes(': Listening for Jobs')) { } else if (CloudRunner.buildParameters.cloudRunnerIntegrationTests && message.includes(': Listening for Jobs')) {
core.setOutput('cloud runner stop watching', 'true'); core.setOutput('cloud runner stop watching', 'true');
shouldReadLogs = false; shouldReadLogs = false;
shouldCleanup = false; shouldCleanup = false;
core.warning('cloud runner stop watching'); log.warning('cloud runner stop watching');
} }
message = `[${CloudRunnerStatics.logPrefix}] ${message}`; message = `[${CloudRunnerStatics.logPrefix}] ${message}`;
if (CloudRunner.buildParameters.cloudRunnerIntegrationTests) { if (CloudRunner.buildParameters.cloudRunnerIntegrationTests) {

View File

@ -7,6 +7,6 @@ describe(`github cli`, () => {
const token = await GithubCliReader.GetGitHubAuthToken(); const token = await GithubCliReader.GetGitHubAuthToken();
// Todo - use expect(result).toStrictEqual(something) // Todo - use expect(result).toStrictEqual(something)
core.info(token); log.info(token);
}); });
}); });

View File

@ -18,7 +18,7 @@ export class GithubCliReader {
.replace(/ /g, '') .replace(/ /g, '')
.replace(/\n/g, ''); .replace(/\n/g, '');
} catch (error: any) { } catch (error: any) {
core.info(error || 'Failed to get github auth token from gh cli'); log.info(error || 'Failed to get github auth token from gh cli');
return ''; return '';
} }

View File

@ -20,19 +20,19 @@ class System {
const showOutput = () => { const showOutput = () => {
if (debug !== '' && shouldLog) { if (debug !== '' && shouldLog) {
core.debug(debug); log.debug(debug);
} }
if (result !== '' && shouldLog) { if (result !== '' && shouldLog) {
core.info(result); log.info(result);
} }
if (error !== '' && shouldLog) { if (error !== '' && shouldLog) {
core.warning(error); log.warning(error);
} }
}; };
const throwContextualError = (message) => { const throwContextualError = (message: string) => {
let commandAsString = command; let commandAsString = command;
if (Array.isArray(arguments_)) { if (Array.isArray(arguments_)) {
commandAsString += ` ${arguments_.join(' ')}`; commandAsString += ` ${arguments_.join(' ')}`;

View File

@ -89,6 +89,8 @@ export default class Versioning {
throw new ValidationError(`Versioning strategy should be one of ${Object.values(this.strategies).join(', ')}.`); throw new ValidationError(`Versioning strategy should be one of ${Object.values(this.strategies).join(', ')}.`);
} }
log.info('Versioning strategy:', strategy);
let version; let version;
switch (strategy) { switch (strategy) {
case this.strategies.None: case this.strategies.None:
@ -107,6 +109,8 @@ export default class Versioning {
throw new NotImplementedException(`Strategy ${strategy} is not implemented.`); throw new NotImplementedException(`Strategy ${strategy} is not implemented.`);
} }
log.info('Version of this build:', version);
return version; return version;
} }
@ -133,7 +137,7 @@ export default class Versioning {
if (!(await this.hasAnyVersionTags())) { if (!(await this.hasAnyVersionTags())) {
const version = `0.0.${await this.getTotalNumberOfCommits()}`; const version = `0.0.${await this.getTotalNumberOfCommits()}`;
core.info(`Generated version ${version} (no version tags found).`); log.info(`Generated version ${version} (no version tags found).`);
return version; return version;
} }
@ -146,13 +150,13 @@ export default class Versioning {
const [major, minor, patch] = `${tag}.${commits}`.split('.'); const [major, minor, patch] = `${tag}.${commits}`.split('.');
const threeDigitVersion = /^\d+$/.test(patch) ? `${major}.${minor}.${patch}` : `${major}.0.${minor}`; const threeDigitVersion = /^\d+$/.test(patch) ? `${major}.${minor}.${patch}` : `${major}.0.${minor}`;
core.info(`Found semantic version ${threeDigitVersion} for ${this.branch}@${hash}`); log.info(`Found semantic version ${threeDigitVersion} for ${this.branch}@${hash}`);
return `${threeDigitVersion}`; return `${threeDigitVersion}`;
} }
const version = `0.0.${await this.getTotalNumberOfCommits()}`; const version = `0.0.${await this.getTotalNumberOfCommits()}`;
core.info(`Generated version ${version} (semantic version couldn't be determined).`); log.info(`Generated version ${version} (semantic version couldn't be determined).`);
return version; return version;
} }
@ -206,7 +210,7 @@ export default class Versioning {
hash, hash,
}; };
} catch { } catch {
core.warning( log.warning(
`Failed to parse git describe output or version can not be determined through: "${description}".`, `Failed to parse git describe output or version can not be determined through: "${description}".`,
); );
@ -235,8 +239,10 @@ export default class Versioning {
static async fetch() { static async fetch() {
try { try {
await this.git(['fetch', '--unshallow']); await this.git(['fetch', '--unshallow']);
} catch (error) { } catch {
core.warning(`Fetch --unshallow caught: ${error}`); log.warning(
`fetch --unshallow did not work, falling back to regular fetch (which probably just means it's not running on GH actions)`,
);
await this.git(['fetch']); await this.git(['fetch']);
} }
} }
@ -261,8 +267,8 @@ export default class Versioning {
const isDirty = output !== ''; const isDirty = output !== '';
if (isDirty) { if (isDirty) {
core.warning('Changes were made to the following files and folders:\n'); log.warning('Changes were made to the following files and folders:\n');
core.warning(output); log.warning(output);
} }
return isDirty; return isDirty;

View File

@ -1,4 +1,4 @@
import { exec as originalExec } from 'https://deno.land/x/exec/mod.ts'; import { exec as originalExec } from 'https://deno.land/x/exec@0.0.5/mod.ts';
import { core } from './core.ts'; import { core } from './core.ts';
export enum OutputMode { export enum OutputMode {
@ -8,22 +8,34 @@ export enum OutputMode {
Tee, // both dump and capture the output Tee, // both dump and capture the output
} }
export interface IExecResponse { export interface ICommandResult {
code: number; status?: {
code: number;
success: boolean;
};
output: string;
}
export interface ISanitisedCommandResult {
exitCode: number;
success: boolean; success: boolean;
output: string; output: string;
} }
interface IOptions { interface IOptions {
silent?: boolean;
ignoreReturnCode?: boolean;
output?: OutputMode; output?: OutputMode;
verbose?: boolean; verbose?: boolean;
continueOnError?: boolean; continueOnError?: boolean;
} }
// Todo - change signature of exec inside the code instead of adapting the exec method // Todo - change signature of exec inside the code instead of adapting the exec method
const exec = async (command, args: string | string[] = [], ghActionsOptions: IOptions = {}): Promise<IExecResponse> => { const exec = async (
core.info('Running command: ', command, args); command: string,
args: string | string[] = [],
ghActionsOptions: IOptions = {},
): Promise<ISanitisedCommandResult> => {
const options = { const options = {
output: OutputMode.Tee, output: OutputMode.Tee,
verbose: false, verbose: false,
@ -31,16 +43,20 @@ const exec = async (command, args: string | string[] = [], ghActionsOptions: IOp
}; };
const { silent = false, ignoreReturnCode } = ghActionsOptions; const { silent = false, ignoreReturnCode } = ghActionsOptions;
if (silent) options.output = OutputMode.None; if (silent) options.output = OutputMode.Capture;
if (ignoreReturnCode) options.continueOnError = true; if (ignoreReturnCode) options.continueOnError = true;
const result = await originalExec(`${command} ${args.join(' ')}`, options); const argsString = typeof args === 'string' ? args : args.join(' ');
core.info('result:', result); log.debug('Command: ', command, argsString);
const { status = {}, output = '' } = result; const result: ICommandResult = await originalExec(`${command} ${argsString}`, options);
log.debug('Result:', result);
const { status, output = '' } = result;
const { code: exitCode, success } = status; const { code: exitCode, success } = status;
return { exitCode, success, output }; return { exitCode, success, output: output.trim() };
}; };
export { exec }; export { exec };