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",
// From experience, named exports are almost always desired. I got tired of this rule
"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
"@typescript-eslint/no-unused-vars": "off",
// For this project only use kebab-case
@ -65,9 +67,13 @@
"unicorn/prevent-abbreviations": "off",
// Allow disabling eslint per file
"eslint-comments/no-use": "off",
// Deno import style
// Deno import style not supported (enable after upgrading)
"import/extensions": "off",
// Deno import style
"import/no-unresolved": "off"
// Deno import style not supported (enable after upgrading)
"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,
depth = 3,
} = {}): 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) => {
let line = '';
@ -31,7 +37,7 @@ export const createFormatter = ({
if (showLevelName) {
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) {

14
src/global.d.ts vendored
View File

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

View File

@ -8,7 +8,8 @@ import PlatformSetup from './model/platform-setup.ts';
async function runMain() {
try {
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();
return;
@ -28,7 +29,7 @@ async function runMain() {
if (buildParameters.cloudRunnerCluster !== 'local') {
await CloudRunner.run(buildParameters, baseImage.toString());
} else {
core.info('Building locally');
log.info('Building locally');
await PlatformSetup.setup(buildParameters, actionFolder);
if (process.platform === 'darwin') {
MacBuilder.run(actionFolder, workspace, buildParameters);
@ -40,7 +41,7 @@ async function runMain() {
// Set output
await Output.setBuildVersion(buildParameters.buildVersion);
} catch (error) {
core.error(error);
log.error(error);
core.setFailed((error as Error).message);
}
}

View File

@ -11,7 +11,7 @@ export default class AndroidVersioning {
static versionToVersionCode(version) {
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;
}
@ -19,7 +19,7 @@ export default class AndroidVersioning {
const parsedVersion = semver.parse(version);
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;
}
@ -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.`,
);
}
core.info(`Using android versionCode ${versionCode}`);
log.info(`Using android versionCode ${versionCode}`);
return versionCode;
}

View File

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

View File

@ -68,8 +68,8 @@ export class Cli {
@CliFunction(`print-input`, `prints all input`)
private static logInput() {
core.info(`\n`);
core.info(`INPUT:`);
log.info(`\n`);
log.info(`INPUT:`);
const properties = Object.getOwnPropertyNames(Input);
for (const element of properties) {
if (
@ -80,10 +80,10 @@ export class Cli {
element !== 'cliOptions' &&
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`)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -9,19 +9,19 @@ export class FollowLogStreamService {
CloudRunnerLogger.log('End of log transmission received');
shouldReadLogs = false;
} 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');
} else if (message.includes('Build succeeded')) {
core.setOutput('build-result', 'success');
} else if (message.includes('Build fail')) {
core.setOutput('build-result', 'failed');
core.setFailed('unity build failed');
core.error('BUILD FAILED!');
log.error('BUILD FAILED!');
} else if (CloudRunner.buildParameters.cloudRunnerIntegrationTests && message.includes(': Listening for Jobs')) {
core.setOutput('cloud runner stop watching', 'true');
shouldReadLogs = false;
shouldCleanup = false;
core.warning('cloud runner stop watching');
log.warning('cloud runner stop watching');
}
message = `[${CloudRunnerStatics.logPrefix}] ${message}`;
if (CloudRunner.buildParameters.cloudRunnerIntegrationTests) {

View File

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

View File

@ -18,7 +18,7 @@ export class GithubCliReader {
.replace(/ /g, '')
.replace(/\n/g, '');
} 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 '';
}

View File

@ -20,19 +20,19 @@ class System {
const showOutput = () => {
if (debug !== '' && shouldLog) {
core.debug(debug);
log.debug(debug);
}
if (result !== '' && shouldLog) {
core.info(result);
log.info(result);
}
if (error !== '' && shouldLog) {
core.warning(error);
log.warning(error);
}
};
const throwContextualError = (message) => {
const throwContextualError = (message: string) => {
let commandAsString = command;
if (Array.isArray(arguments_)) {
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(', ')}.`);
}
log.info('Versioning strategy:', strategy);
let version;
switch (strategy) {
case this.strategies.None:
@ -107,6 +109,8 @@ export default class Versioning {
throw new NotImplementedException(`Strategy ${strategy} is not implemented.`);
}
log.info('Version of this build:', version);
return version;
}
@ -133,7 +137,7 @@ export default class Versioning {
if (!(await this.hasAnyVersionTags())) {
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;
}
@ -146,13 +150,13 @@ export default class Versioning {
const [major, minor, patch] = `${tag}.${commits}`.split('.');
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}`;
}
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;
}
@ -206,7 +210,7 @@ export default class Versioning {
hash,
};
} catch {
core.warning(
log.warning(
`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() {
try {
await this.git(['fetch', '--unshallow']);
} catch (error) {
core.warning(`Fetch --unshallow caught: ${error}`);
} catch {
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']);
}
}
@ -261,8 +267,8 @@ export default class Versioning {
const isDirty = output !== '';
if (isDirty) {
core.warning('Changes were made to the following files and folders:\n');
core.warning(output);
log.warning('Changes were made to the following files and folders:\n');
log.warning(output);
}
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';
export enum OutputMode {
@ -8,22 +8,34 @@ export enum OutputMode {
Tee, // both dump and capture the output
}
export interface IExecResponse {
export interface ICommandResult {
status?: {
code: number;
success: boolean;
};
output: string;
}
export interface ISanitisedCommandResult {
exitCode: number;
success: boolean;
output: string;
}
interface IOptions {
silent?: boolean;
ignoreReturnCode?: boolean;
output?: OutputMode;
verbose?: boolean;
continueOnError?: boolean;
}
// 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> => {
core.info('Running command: ', command, args);
const exec = async (
command: string,
args: string | string[] = [],
ghActionsOptions: IOptions = {},
): Promise<ISanitisedCommandResult> => {
const options = {
output: OutputMode.Tee,
verbose: false,
@ -31,16 +43,20 @@ const exec = async (command, args: string | string[] = [], ghActionsOptions: IOp
};
const { silent = false, ignoreReturnCode } = ghActionsOptions;
if (silent) options.output = OutputMode.None;
if (silent) options.output = OutputMode.Capture;
if (ignoreReturnCode) options.continueOnError = true;
const result = await originalExec(`${command} ${args.join(' ')}`, options);
core.info('result:', result);
const argsString = typeof args === 'string' ? args : args.join(' ');
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;
return { exitCode, success, output };
return { exitCode, success, output: output.trim() };
};
export { exec };