From 2b2974e1dfbf6c6e35c3b70d8d6b1433856e382c Mon Sep 17 00:00:00 2001 From: Goggags123 Date: Wed, 10 Nov 2021 01:25:34 +0700 Subject: [PATCH 01/15] Add second command in yargs --- src/Config/@types/configArgument.ts | 6 +- src/Config/Config.ts | 115 +++++++++++++++++++--------- src/Config/constants/defaults.ts | 5 ++ src/Config/constants/required.ts | 20 ++++- 4 files changed, 105 insertions(+), 41 deletions(-) diff --git a/src/Config/@types/configArgument.ts b/src/Config/@types/configArgument.ts index ab7ffb3..25822c3 100644 --- a/src/Config/@types/configArgument.ts +++ b/src/Config/@types/configArgument.ts @@ -2,10 +2,12 @@ import { BuildLogFile } from './buildLogFile'; export type ConfigArgument = { url: string; - pr: number; buildLogFile: BuildLogFile[]; output: string; token: string; - removeOldComment: boolean; config: string; + pr?: number; + latestCommit?: string; + runId?: number; + removeOldComment: boolean; }; diff --git a/src/Config/Config.ts b/src/Config/Config.ts index d66d1b8..38d0869 100644 --- a/src/Config/Config.ts +++ b/src/Config/Config.ts @@ -1,11 +1,21 @@ -import yargs from 'yargs'; +import yargs, { Arguments } from 'yargs'; import { ProjectType } from './@enums'; import { BuildLogFile, ConfigArgument, ConfigObject } from './@types'; import { buildAppConfig, buildProviderConfig } from './configBuilder'; -import { DEFAULT_OUTPUT_FILE } from './constants/defaults'; -import { REQUIRED_ARGS } from './constants/required'; +import { DEFAULT_OUTPUT_FILE, COMMAND } from './constants/defaults'; +import { DATA_REQUIRED_ARGS, PR_REQUIRED_ARGS } from './constants/required'; const projectTypes = Object.keys(ProjectType); +let command: COMMAND = COMMAND.DEFAULT; + +const validateRequiredArgs = (options: Arguments, requiredArgs: string[]) => { + const validRequiredArgs = requiredArgs.every( + (el) => options[el] != undefined || options[el] != null, + ); + if (!validRequiredArgs) + throw new Error(`please fill all required fields ${requiredArgs.join(', ')}`); + return true; +}; const args = yargs .option('config', { @@ -16,14 +26,16 @@ const args = yargs describe: 'GitHub repo url (https or ssh)', type: 'string', }) - .option('pr', { - describe: 'PR number', - type: 'number', - }) .option('token', { describe: 'GitHub token', type: 'string', }) + .option('output', { + alias: 'o', + describe: 'Output parsed log file', + type: 'string', + default: DEFAULT_OUTPUT_FILE, + }) .option('buildLogFile', { alias: 'f', describe: `Build log content files formatted in ';[;]' @@ -42,45 +54,76 @@ and is build root directory (optional (Will use current context as cwd)). return { type, path, cwd: cwd ?? process.cwd() } as BuildLogFile; }); }) - .option('output', { - alias: 'o', - describe: 'Output parsed log file', - type: 'string', - default: DEFAULT_OUTPUT_FILE, - }) - .option('removeOldComment', { - type: 'boolean', - describe: 'Remove existing CodeCoach comments before putting new one', - default: false, - }) - .check((options) => { - // check required arguments - const useConfigArgs = options.config === undefined; - const validRequiredArgs = REQUIRED_ARGS.every( - (el) => options[el] != undefined || options[el] != null, - ); - if (useConfigArgs && !validRequiredArgs) - throw `please fill all required fields ${REQUIRED_ARGS.join(', ')}`; - return true; - }) + .command(['$0', 'comment'], 'desc', (yarg) => + yarg + .option('pr', { + describe: 'PR number', + type: 'number', + global: true, + }) + .option('removeOldComment', { + type: 'boolean', + describe: 'Remove existing CodeCoach comments before putting new one', + default: false, + }) + .check((options) => { + // check arguments parsing + const useConfigArgs = options.config === undefined; + if (!useConfigArgs) return true; + + validateRequiredArgs(options, PR_REQUIRED_ARGS); + + if (!options.pr || Array.isArray(options.pr)) + throw new Error('--pr config should be a single number'); + return true; + }), + ) + .command( + 'collect', + 'des', + (yarg) => + yarg + .option('latestCommit', { + alias: 'c', + describe: 'The latest commit sha', + type: 'string', + global: true, + }) + .option('runId', { + alias: 'r', + describe: 'The latest run id', + type: 'number', + global: true, + }) + .check((options) => { + // check arguments parsing + const useConfigArgs = options.config === undefined; + if (!useConfigArgs) return true; + + validateRequiredArgs(options, DATA_REQUIRED_ARGS); + + if (!options.runId || Array.isArray(options.runId)) + throw new Error('--runId config should be a single number'); + return true; + }), + () => { + command = COMMAND.COLLECT; + }, + ) .check((options) => { // check arguments parsing const useConfigArgs = options.config === undefined; if (!useConfigArgs) return true; - - if (!options.pr || Array.isArray(options.pr)) - throw '--pr config should be a single number'; if (!options.buildLogFile || options.buildLogFile.some((file) => file === null)) - throw 'all of `--buildLogFile` options should have correct format'; + throw new Error('all of `--buildLogFile` options should have correct format'); return true; }) .help() - .wrap(120) - .parse(process.argv.slice(1)) as ConfigArgument; + .wrap(120).argv as ConfigArgument; export const Config: Promise = (async () => { return Object.freeze({ - app: await buildAppConfig(args), - provider: await buildProviderConfig(args), + app: await buildAppConfig(args, command), + provider: await buildProviderConfig(args, command), }); })(); diff --git a/src/Config/constants/defaults.ts b/src/Config/constants/defaults.ts index 2198be8..1bfb22d 100644 --- a/src/Config/constants/defaults.ts +++ b/src/Config/constants/defaults.ts @@ -2,3 +2,8 @@ export const DEFAULT_OUTPUT_FILE = 'build.json'; export const USER_AGENT = 'CodeCoach'; export const TIME_ZONE = 'Asia/Bangkok'; + +export enum COMMAND { + DEFAULT = 'default', + COLLECT = 'collect', +} diff --git a/src/Config/constants/required.ts b/src/Config/constants/required.ts index 691a375..9b91b19 100644 --- a/src/Config/constants/required.ts +++ b/src/Config/constants/required.ts @@ -1,15 +1,29 @@ import { ConfigArgument } from '..'; import { ConfigYAML } from '../@types/configYAML'; -type RequiredArgs = (keyof ConfigArgument)[]; -export const REQUIRED_ARGS: RequiredArgs = ['url', 'pr', 'buildLogFile', 'token']; +type DataRequiredArgs = (keyof ConfigArgument)[]; +export const DATA_REQUIRED_ARGS: DataRequiredArgs = [ + 'url', + 'latestCommit', + 'runId', + 'buildLogFile', + 'token', +]; + +type PrRequiredArgs = (keyof ConfigArgument)[]; +export const PR_REQUIRED_ARGS: PrRequiredArgs = ['pr', 'buildLogFile', 'token', 'url']; type RequiredYamlArgs = (keyof ConfigYAML)[]; export const REQUIRED_YAML_ARGS: RequiredYamlArgs = ['repo', 'buildLogFiles']; type RequiredYamlProviderArgs = (keyof ConfigYAML['repo'])[]; -export const REQUIRED_YAML_PROVIDER_ARGS: RequiredYamlProviderArgs = [ +export const PR_REQUIRED_YAML_PROVIDER_ARGS: RequiredYamlProviderArgs = [ 'url', 'pr', 'token', ]; +export const DATA_REQUIRED_YAML_PROVIDER_ARGS: RequiredYamlProviderArgs = [ + 'url', + 'latestCommit', + 'token', +]; From 66f8f7d35f9d8c22bf7d0a043b79cfdd498f6023 Mon Sep 17 00:00:00 2001 From: Goggags123 Date: Wed, 10 Nov 2021 01:26:13 +0700 Subject: [PATCH 02/15] Modify config builder to have command as a parameter --- src/Config/@types/appConfig.ts | 2 ++ src/Config/@types/providerConfig.ts | 4 +++- src/Config/configBuilder.ts | 19 +++++++++++++------ 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/Config/@types/appConfig.ts b/src/Config/@types/appConfig.ts index 2f8878b..1742d8f 100644 --- a/src/Config/@types/appConfig.ts +++ b/src/Config/@types/appConfig.ts @@ -1,6 +1,8 @@ +import { COMMAND } from '../constants/defaults'; import { BuildLogFile } from './buildLogFile'; export type AppConfig = { + command: COMMAND; logFilePath: string; buildLogFiles: BuildLogFile[]; }; diff --git a/src/Config/@types/providerConfig.ts b/src/Config/@types/providerConfig.ts index bb3f034..639ecb5 100644 --- a/src/Config/@types/providerConfig.ts +++ b/src/Config/@types/providerConfig.ts @@ -1,6 +1,8 @@ export type ProviderConfig = { token: string; repoUrl: string; - prId: number; + prId?: number; + runId?: number; + latestCommit?: string; removeOldComment: boolean; }; diff --git a/src/Config/configBuilder.ts b/src/Config/configBuilder.ts index f686039..6454ae1 100644 --- a/src/Config/configBuilder.ts +++ b/src/Config/configBuilder.ts @@ -1,27 +1,34 @@ import { AppConfig, ConfigArgument, ProviderConfig } from './@types'; +import { COMMAND } from './constants/defaults'; import { YML } from './YML'; -const buildYMLConfig = async (args: ConfigArgument) => { +const buildYMLConfig = async (args: ConfigArgument, command: COMMAND) => { if (!args.config) return; - return YML.parse(args.config); + return YML.parse(args.config, command); }; export const buildProviderConfig = async ( arg: ConfigArgument, + command: COMMAND, ): Promise => { - const configFile = await buildYMLConfig(arg); - + const configFile = await buildYMLConfig(arg, command); return { token: configFile?.repo.token || arg.token, repoUrl: configFile?.repo.url || arg.url, + runId: configFile?.repo.runId || arg.runId, + latestCommit: configFile?.repo.latestCommit || arg.latestCommit, prId: configFile?.repo.pr || arg.pr, removeOldComment: configFile?.repo.removeOldComment || arg.removeOldComment, }; }; -export const buildAppConfig = async (arg: ConfigArgument): Promise => { - const configFile = await buildYMLConfig(arg); +export const buildAppConfig = async ( + arg: ConfigArgument, + command: COMMAND, +): Promise => { + const configFile = await buildYMLConfig(arg, command); return { + command: command, logFilePath: configFile?.output || arg.output, buildLogFiles: configFile?.buildLogFiles || arg.buildLogFile, }; From 41494f4c00a41d755d102208352f56e231034435 Mon Sep 17 00:00:00 2001 From: Goggags123 Date: Wed, 10 Nov 2021 01:27:10 +0700 Subject: [PATCH 03/15] Modify YML handler to handle another command --- src/Config/@types/configYAML.ts | 4 ++- src/Config/YML.ts | 55 +++++++++++++++++++++++++++------ 2 files changed, 48 insertions(+), 11 deletions(-) diff --git a/src/Config/@types/configYAML.ts b/src/Config/@types/configYAML.ts index ca5d438..794caed 100644 --- a/src/Config/@types/configYAML.ts +++ b/src/Config/@types/configYAML.ts @@ -3,7 +3,9 @@ import { BuildLogFile } from './buildLogFile'; export type ConfigYAML = { repo: { url: string; - pr: number; + pr?: number; + latestCommit?: string; + runId?: number; token: string; userAgent: string; timeZone: string; diff --git a/src/Config/YML.ts b/src/Config/YML.ts index 1345d8e..4b28310 100644 --- a/src/Config/YML.ts +++ b/src/Config/YML.ts @@ -1,24 +1,59 @@ import { File } from '../File'; import yaml from 'js-yaml'; -import { REQUIRED_YAML_ARGS, REQUIRED_YAML_PROVIDER_ARGS } from './constants/required'; +import { + REQUIRED_YAML_ARGS, + PR_REQUIRED_YAML_PROVIDER_ARGS, + DATA_REQUIRED_YAML_PROVIDER_ARGS, +} from './constants/required'; import { ConfigYAML } from './@types/configYAML'; +import { COMMAND } from './constants/defaults'; export class YML { - private static transform(config: ConfigYAML): ConfigYAML { - if (!config.repo.pr || !Number.isInteger(config.repo.pr)) - throw 'provider.pr is required or invalid number type'; - + private static transform(config: ConfigYAML, command: COMMAND): ConfigYAML { // required types const validRequiredArgs = REQUIRED_YAML_ARGS.every( (el) => config[el] != undefined || config[el] != null, ); if (!validRequiredArgs) - throw `please fill all required fields ${REQUIRED_YAML_ARGS.join(', ')}`; - const validRequiredProviderArgs = REQUIRED_YAML_PROVIDER_ARGS.every( + throw new Error(`please fill all required fields ${REQUIRED_YAML_ARGS.join(', ')}`); + + if (command === COMMAND.COLLECT) return this.transformPrConfig(config); + else if (command === COMMAND.DEFAULT) return this.transformDataConfig(config); + throw new Error(`Command ${command} is invalid`); + } + + private static transformDataConfig(config: ConfigYAML): ConfigYAML { + if (!config.repo.runId || !Number.isInteger(config.repo.runId)) + throw new Error('provider.runId is required or invalid number type'); + + const validRequiredProviderArgs = DATA_REQUIRED_YAML_PROVIDER_ARGS.every( + (el) => config.repo[el] != undefined || config.repo[el] != null, + ); + if (!validRequiredProviderArgs) + throw new Error( + `please fill all required fields ${DATA_REQUIRED_YAML_PROVIDER_ARGS.join(', ')}`, + ); + + return { + ...config, + repo: { + ...config.repo, + runId: Number(config.repo.runId), + }, + }; + } + + private static transformPrConfig(config: ConfigYAML): ConfigYAML { + if (!config.repo.pr || !Number.isInteger(config.repo.pr)) + throw new Error('provider.pr is required or invalid number type'); + + const validRequiredProviderArgs = PR_REQUIRED_YAML_PROVIDER_ARGS.every( (el) => config.repo[el] != undefined || config.repo[el] != null, ); if (!validRequiredProviderArgs) - throw `please fill all required fields ${REQUIRED_YAML_PROVIDER_ARGS.join(', ')}`; + throw new Error( + `please fill all required fields ${PR_REQUIRED_YAML_PROVIDER_ARGS.join(', ')}`, + ); return { ...config, @@ -30,10 +65,10 @@ export class YML { }; } - static async parse(path: string): Promise { + static async parse(path: string, command: COMMAND): Promise { const file = await File.readFileHelper(path); const ymlFile = yaml.loadAll(file); - const transformed = this.transform(ymlFile[0]); + const transformed = this.transform(ymlFile[0], command); return transformed; } } From cc422b04e331502475621be693817b59cb658ffe Mon Sep 17 00:00:00 2001 From: Goggags123 Date: Wed, 10 Nov 2021 02:32:24 +0700 Subject: [PATCH 04/15] Fix YML parser --- src/Config/YML.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Config/YML.ts b/src/Config/YML.ts index 4b28310..3cf8276 100644 --- a/src/Config/YML.ts +++ b/src/Config/YML.ts @@ -17,8 +17,8 @@ export class YML { if (!validRequiredArgs) throw new Error(`please fill all required fields ${REQUIRED_YAML_ARGS.join(', ')}`); - if (command === COMMAND.COLLECT) return this.transformPrConfig(config); - else if (command === COMMAND.DEFAULT) return this.transformDataConfig(config); + if (command === COMMAND.COLLECT) return this.transformDataConfig(config); + else if (command === COMMAND.DEFAULT) return this.transformPrConfig(config); throw new Error(`Command ${command} is invalid`); } From 26c6c04a9606c061bc7bbd8d8dc17b4561fda0f0 Mon Sep 17 00:00:00 2001 From: Goggags123 Date: Wed, 10 Nov 2021 02:32:41 +0700 Subject: [PATCH 05/15] Add new config file --- sample/config/dataConfig.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 sample/config/dataConfig.yaml diff --git a/sample/config/dataConfig.yaml b/sample/config/dataConfig.yaml new file mode 100644 index 0000000..1a90dd8 --- /dev/null +++ b/sample/config/dataConfig.yaml @@ -0,0 +1,10 @@ +repo: + url: https://github.com/codeleague/codecoach + runId: 40 + latestCommit: 123qwe123qwe123qwe123qwe123qwe123qwe123q + token: 123qwe123qwe123qwe123 +buildLogFiles: + - type: tslint + path: /path/to/log.json + cwd: /repo/src +output: ./output.json From d437404b178e5de214ad7413fe74e277ce17d646 Mon Sep 17 00:00:00 2001 From: Goggags123 Date: Wed, 10 Nov 2021 02:32:53 +0700 Subject: [PATCH 06/15] Fix unit test --- src/Config/Config.spec.ts | 88 +++++++++++++++++++++++++++++++++++---- src/Config/YML.spec.ts | 14 ++++++- 2 files changed, 92 insertions(+), 10 deletions(-) diff --git a/src/Config/Config.spec.ts b/src/Config/Config.spec.ts index 548902d..56df505 100644 --- a/src/Config/Config.spec.ts +++ b/src/Config/Config.spec.ts @@ -1,6 +1,6 @@ import { Config } from './Config'; -const MOCK_ARGS = [ +const PR_MOCK_ARGS = [ '/usr/local/Cellar/node/15.13.0/bin/node', '/Users/codecoach/src/app.ts', '--url=https://github.com/codeleague/codecoach.git', @@ -11,13 +11,25 @@ const MOCK_ARGS = [ '-o=./tmp/out.json', ]; -const MOCK_ARGS_W_CONFIG_YAML = [ +const PR_MOCK_ARGS_W_COMMAND = [ + '/usr/local/Cellar/node/15.13.0/bin/node', + '/Users/codecoach/src/app.ts', + 'comment', + '--url=https://github.com/codeleague/codecoach.git', + '--removeOldComment', + '--token=placeyourtokenhere', + '--pr=15', + '-f=dotnetbuild;./sample/dotnetbuild/build.content;/repo/src', + '-o=./tmp/out.json', +]; + +const PR_MOCK_ARGS_W_CONFIG_YAML = [ '/usr/local/Cellar/node/15.13.0/bin/node', '/Users/codecoach/src/app.ts', '--config=sample/config/config.yaml', ]; -export const EXPECTED_MOCK_ARGS = [ +export const PR_EXPECTED_MOCK_ARGS = [ '/usr/local/Cellar/node/15.13.0/bin/node', '/Users/codecoach/src/app.ts', 'https://github.com/codeleague/codecoach.git', @@ -28,7 +40,66 @@ export const EXPECTED_MOCK_ARGS = [ './tmp/out.json', ]; -describe('Config Test', () => { +const DATA_MOCK_ARGS = [ + '/usr/local/Cellar/node/15.13.0/bin/node', + '/Users/codecoach/src/app.ts', + 'collect', + '--url=https://github.com/codeleague/codecoach.git', + '-r=3', + '-c=latestcommitsha', + '--token=placeyourtokenhere', + '-f=dotnetbuild;./sample/dotnetbuild/build.content;/repo/src', + '-o=./tmp/out.json', +]; + +const DATA_MOCK_ARGS_W_CONFIG_YAML = [ + '/usr/local/Cellar/node/15.13.0/bin/node', + '/Users/codecoach/src/app.ts', + 'collect', + '--config=sample/config/dataConfig.yaml', +]; + +export const DATA_EXPECTED_MOCK_ARGS = [ + '/usr/local/Cellar/node/15.13.0/bin/node', + '/Users/codecoach/src/app.ts', + 'https://github.com/codeleague/codecoach.git', + 3, + 'latestcommitsha', + 'placeyourtokenhere', + 'dotnetbuild;./sample/dotnetbuild/build.content;/repo/src', + './tmp/out.json', +]; + +describe('PR config Test', () => { + let config: typeof Config; + + beforeEach(() => { + jest.resetModules(); + }); + + it('Should able to parse this args and run without throwing error', async () => { + process.argv = PR_MOCK_ARGS; + config = (await import('./Config')).Config; + let fullfillConfig = await config; + expect(fullfillConfig.provider.repoUrl).toBe(PR_EXPECTED_MOCK_ARGS[2]); + expect(fullfillConfig.provider.removeOldComment).toBe(PR_EXPECTED_MOCK_ARGS[3]); + + process.argv = PR_MOCK_ARGS_W_COMMAND; + config = (await import('./Config')).Config; + fullfillConfig = await config; + expect(fullfillConfig.provider.repoUrl).toBe(PR_EXPECTED_MOCK_ARGS[2]); + expect(fullfillConfig.provider.removeOldComment).toBe(PR_EXPECTED_MOCK_ARGS[3]); + }); + + it('Should able to use a config file without passing other args', async () => { + process.argv = PR_MOCK_ARGS_W_CONFIG_YAML; + config = (await import('./Config')).Config; + const fullfillConfig = await config; + expect(fullfillConfig.app.buildLogFiles[0].type).toBe('tslint'); + }); +}); + +describe('Data config Test', () => { let config: typeof Config; beforeEach(() => { @@ -36,15 +107,16 @@ describe('Config Test', () => { }); it('Should able to parse this args and run without throwing error', async () => { - process.argv = MOCK_ARGS; + process.argv = DATA_MOCK_ARGS; config = (await import('./Config')).Config; const fullfillConfig = await config; - expect(fullfillConfig.provider.repoUrl).toBe(EXPECTED_MOCK_ARGS[2]); - expect(fullfillConfig.provider.removeOldComment).toBe(EXPECTED_MOCK_ARGS[3]); + expect(fullfillConfig.provider.repoUrl).toBe(DATA_EXPECTED_MOCK_ARGS[2]); + expect(fullfillConfig.provider.runId).toBe(DATA_EXPECTED_MOCK_ARGS[3]); + expect(fullfillConfig.provider.latestCommit).toBe(DATA_EXPECTED_MOCK_ARGS[4]); }); it('Should able to use a config file without passing other args', async () => { - process.argv = MOCK_ARGS_W_CONFIG_YAML; + process.argv = DATA_MOCK_ARGS_W_CONFIG_YAML; config = (await import('./Config')).Config; const fullfillConfig = await config; expect(fullfillConfig.app.buildLogFiles[0].type).toBe('tslint'); diff --git a/src/Config/YML.spec.ts b/src/Config/YML.spec.ts index 7a55a06..fc1e23a 100644 --- a/src/Config/YML.spec.ts +++ b/src/Config/YML.spec.ts @@ -1,10 +1,20 @@ +import { COMMAND } from './constants/defaults'; import { YML } from './YML'; -describe('YML Test', () => { +describe('PR YML Test', () => { it('Should able to validate yaml file correctly', async () => { - const config = await YML.parse('sample/config/config.yaml'); + const config = await YML.parse('sample/config/config.yaml', COMMAND.DEFAULT); expect(config.output).toBe('./output.json'); expect(config.repo.pr).toBe(40); expect(config.repo.removeOldComment).toBe(false); }); }); + +describe('Data YML Test', () => { + it('Should able to validate yaml file correctly', async () => { + const config = await YML.parse('sample/config/dataConfig.yaml', COMMAND.COLLECT); + expect(config.output).toBe('./output.json'); + expect(config.repo.runId).toBe(40); + expect(config.repo.latestCommit).toBe('123qwe123qwe123qwe123qwe123qwe123qwe123q'); + }); +}); From 063d90c353168891f95bd3203988bf91ab9c74da Mon Sep 17 00:00:00 2001 From: Goggags123 Date: Wed, 10 Nov 2021 16:31:35 +0700 Subject: [PATCH 07/15] Separate type --- src/Config/@types/configArgument.ts | 18 +++++++--- src/Config/@types/configYAML.ts | 22 ++++++++++--- src/Config/@types/index.ts | 5 +-- src/Config/@types/providerConfig.ts | 18 +++++++--- src/Config/Config.spec.ts | 21 ++++++------ src/Config/YML.spec.ts | 11 +++++-- src/Config/YML.ts | 18 ++++++---- src/Config/configBuilder.ts | 51 ++++++++++++++++++++++++----- src/Config/constants/required.ts | 21 ++++++++---- 9 files changed, 134 insertions(+), 51 deletions(-) diff --git a/src/Config/@types/configArgument.ts b/src/Config/@types/configArgument.ts index 25822c3..dd96826 100644 --- a/src/Config/@types/configArgument.ts +++ b/src/Config/@types/configArgument.ts @@ -1,13 +1,21 @@ import { BuildLogFile } from './buildLogFile'; -export type ConfigArgument = { +type BaseConfigArgument = { url: string; buildLogFile: BuildLogFile[]; output: string; token: string; config: string; - pr?: number; - latestCommit?: string; - runId?: number; - removeOldComment: boolean; }; + +export type PrConfigArgument = { + pr: number; + removeOldComment: boolean; +} & BaseConfigArgument; + +export type DataConfigArgument = { + latestCommit: string; + runId: number; +} & BaseConfigArgument; + +export type ConfigArgument = PrConfigArgument | DataConfigArgument; \ No newline at end of file diff --git a/src/Config/@types/configYAML.ts b/src/Config/@types/configYAML.ts index 794caed..8281ae4 100644 --- a/src/Config/@types/configYAML.ts +++ b/src/Config/@types/configYAML.ts @@ -1,16 +1,28 @@ import { BuildLogFile } from './buildLogFile'; -export type ConfigYAML = { +type BaseConfigYAML = { repo: { url: string; - pr?: number; - latestCommit?: string; - runId?: number; token: string; userAgent: string; timeZone: string; - removeOldComment: boolean; }; buildLogFiles: BuildLogFile[]; output: string; }; + +export type PrConfigYAML = { + repo: { + pr: number; + removeOldComment: boolean; + }; +} & BaseConfigYAML; + +export type DataConfigYAML = { + repo: { + latestCommit: string; + runId: number; + }; +} & BaseConfigYAML; + +export type ConfigYAML = PrConfigYAML | DataConfigYAML; diff --git a/src/Config/@types/index.ts b/src/Config/@types/index.ts index 1307efc..22c4350 100644 --- a/src/Config/@types/index.ts +++ b/src/Config/@types/index.ts @@ -1,5 +1,6 @@ export { AppConfig } from './appConfig'; export { ConfigObject } from './configObject'; -export { ProviderConfig } from './providerConfig'; -export { ConfigArgument } from './configArgument'; +export * from './providerConfig'; +export * from './configArgument'; +export * from './configYAML'; export { BuildLogFile } from './buildLogFile'; diff --git a/src/Config/@types/providerConfig.ts b/src/Config/@types/providerConfig.ts index 639ecb5..9f90ae0 100644 --- a/src/Config/@types/providerConfig.ts +++ b/src/Config/@types/providerConfig.ts @@ -1,8 +1,16 @@ -export type ProviderConfig = { +type BaseProviderConfig = { token: string; repoUrl: string; - prId?: number; - runId?: number; - latestCommit?: string; - removeOldComment: boolean; }; + +export type PrProviderConfig = { + prId: number; + removeOldComment: boolean; +} & BaseProviderConfig; + +export type DataProviderConfig = { + runId: number; + latestCommit: string; +} & BaseProviderConfig; + +export type ProviderConfig = PrProviderConfig | DataProviderConfig; diff --git a/src/Config/Config.spec.ts b/src/Config/Config.spec.ts index 56df505..124048f 100644 --- a/src/Config/Config.spec.ts +++ b/src/Config/Config.spec.ts @@ -1,3 +1,4 @@ +import { DataProviderConfig, PrProviderConfig } from '.'; import { Config } from './Config'; const PR_MOCK_ARGS = [ @@ -80,15 +81,15 @@ describe('PR config Test', () => { it('Should able to parse this args and run without throwing error', async () => { process.argv = PR_MOCK_ARGS; config = (await import('./Config')).Config; - let fullfillConfig = await config; - expect(fullfillConfig.provider.repoUrl).toBe(PR_EXPECTED_MOCK_ARGS[2]); - expect(fullfillConfig.provider.removeOldComment).toBe(PR_EXPECTED_MOCK_ARGS[3]); + let fullfillConfig = (await config).provider as PrProviderConfig; + expect(fullfillConfig.repoUrl).toBe(PR_EXPECTED_MOCK_ARGS[2]); + expect(fullfillConfig.removeOldComment).toBe(PR_EXPECTED_MOCK_ARGS[3]); process.argv = PR_MOCK_ARGS_W_COMMAND; config = (await import('./Config')).Config; - fullfillConfig = await config; - expect(fullfillConfig.provider.repoUrl).toBe(PR_EXPECTED_MOCK_ARGS[2]); - expect(fullfillConfig.provider.removeOldComment).toBe(PR_EXPECTED_MOCK_ARGS[3]); + fullfillConfig = (await config).provider as PrProviderConfig; + expect(fullfillConfig.repoUrl).toBe(PR_EXPECTED_MOCK_ARGS[2]); + expect(fullfillConfig.removeOldComment).toBe(PR_EXPECTED_MOCK_ARGS[3]); }); it('Should able to use a config file without passing other args', async () => { @@ -109,10 +110,10 @@ describe('Data config Test', () => { it('Should able to parse this args and run without throwing error', async () => { process.argv = DATA_MOCK_ARGS; config = (await import('./Config')).Config; - const fullfillConfig = await config; - expect(fullfillConfig.provider.repoUrl).toBe(DATA_EXPECTED_MOCK_ARGS[2]); - expect(fullfillConfig.provider.runId).toBe(DATA_EXPECTED_MOCK_ARGS[3]); - expect(fullfillConfig.provider.latestCommit).toBe(DATA_EXPECTED_MOCK_ARGS[4]); + const fullfillConfig = (await config).provider as DataProviderConfig; + expect(fullfillConfig.repoUrl).toBe(DATA_EXPECTED_MOCK_ARGS[2]); + expect(fullfillConfig.runId).toBe(DATA_EXPECTED_MOCK_ARGS[3]); + expect(fullfillConfig.latestCommit).toBe(DATA_EXPECTED_MOCK_ARGS[4]); }); it('Should able to use a config file without passing other args', async () => { diff --git a/src/Config/YML.spec.ts b/src/Config/YML.spec.ts index fc1e23a..08bfb95 100644 --- a/src/Config/YML.spec.ts +++ b/src/Config/YML.spec.ts @@ -1,9 +1,13 @@ +import { DataConfigYAML, PrConfigYAML } from '.'; import { COMMAND } from './constants/defaults'; import { YML } from './YML'; describe('PR YML Test', () => { it('Should able to validate yaml file correctly', async () => { - const config = await YML.parse('sample/config/config.yaml', COMMAND.DEFAULT); + const config = (await YML.parse( + 'sample/config/config.yaml', + COMMAND.DEFAULT, + )) as PrConfigYAML; expect(config.output).toBe('./output.json'); expect(config.repo.pr).toBe(40); expect(config.repo.removeOldComment).toBe(false); @@ -12,7 +16,10 @@ describe('PR YML Test', () => { describe('Data YML Test', () => { it('Should able to validate yaml file correctly', async () => { - const config = await YML.parse('sample/config/dataConfig.yaml', COMMAND.COLLECT); + const config = (await YML.parse( + 'sample/config/dataConfig.yaml', + COMMAND.COLLECT, + )) as DataConfigYAML; expect(config.output).toBe('./output.json'); expect(config.repo.runId).toBe(40); expect(config.repo.latestCommit).toBe('123qwe123qwe123qwe123qwe123qwe123qwe123q'); diff --git a/src/Config/YML.ts b/src/Config/YML.ts index 3cf8276..77fa7fe 100644 --- a/src/Config/YML.ts +++ b/src/Config/YML.ts @@ -5,7 +5,7 @@ import { PR_REQUIRED_YAML_PROVIDER_ARGS, DATA_REQUIRED_YAML_PROVIDER_ARGS, } from './constants/required'; -import { ConfigYAML } from './@types/configYAML'; +import { ConfigYAML, DataConfigYAML, PrConfigYAML } from './@types/configYAML'; import { COMMAND } from './constants/defaults'; export class YML { @@ -17,12 +17,17 @@ export class YML { if (!validRequiredArgs) throw new Error(`please fill all required fields ${REQUIRED_YAML_ARGS.join(', ')}`); - if (command === COMMAND.COLLECT) return this.transformDataConfig(config); - else if (command === COMMAND.DEFAULT) return this.transformPrConfig(config); - throw new Error(`Command ${command} is invalid`); + switch (command) { + case COMMAND.COLLECT: + return this.transformDataConfig(config as DataConfigYAML); + case COMMAND.DEFAULT: + return this.transformPrConfig(config as PrConfigYAML); + default: + throw new Error(`Command ${command} is invalid`); + } } - private static transformDataConfig(config: ConfigYAML): ConfigYAML { + private static transformDataConfig(config: DataConfigYAML): DataConfigYAML { if (!config.repo.runId || !Number.isInteger(config.repo.runId)) throw new Error('provider.runId is required or invalid number type'); @@ -39,11 +44,12 @@ export class YML { repo: { ...config.repo, runId: Number(config.repo.runId), + latestCommit: config.repo.latestCommit, }, }; } - private static transformPrConfig(config: ConfigYAML): ConfigYAML { + private static transformPrConfig(config: PrConfigYAML): PrConfigYAML { if (!config.repo.pr || !Number.isInteger(config.repo.pr)) throw new Error('provider.pr is required or invalid number type'); diff --git a/src/Config/configBuilder.ts b/src/Config/configBuilder.ts index 6454ae1..4497bff 100644 --- a/src/Config/configBuilder.ts +++ b/src/Config/configBuilder.ts @@ -1,4 +1,14 @@ -import { AppConfig, ConfigArgument, ProviderConfig } from './@types'; +import { + AppConfig, + ConfigArgument, + ProviderConfig, + DataConfigArgument, + PrConfigArgument, + DataConfigYAML, + PrConfigYAML, + PrProviderConfig, + DataProviderConfig, +} from './@types'; import { COMMAND } from './constants/defaults'; import { YML } from './YML'; @@ -12,16 +22,39 @@ export const buildProviderConfig = async ( command: COMMAND, ): Promise => { const configFile = await buildYMLConfig(arg, command); - return { - token: configFile?.repo.token || arg.token, - repoUrl: configFile?.repo.url || arg.url, - runId: configFile?.repo.runId || arg.runId, - latestCommit: configFile?.repo.latestCommit || arg.latestCommit, - prId: configFile?.repo.pr || arg.pr, - removeOldComment: configFile?.repo.removeOldComment || arg.removeOldComment, - }; + switch (command) { + case COMMAND.COLLECT: + return buildDataProviderConfig( + arg as DataConfigArgument, + configFile as DataConfigYAML, + ); + case COMMAND.DEFAULT: + return buildPrProviderConfig(arg as PrConfigArgument, configFile as PrConfigYAML); + default: + throw new Error(`Command ${command} is invalid`); + } }; +const buildPrProviderConfig = ( + arg: PrConfigArgument, + configFile: PrConfigYAML, +): PrProviderConfig => ({ + token: configFile?.repo.token || arg.token, + repoUrl: configFile?.repo.url || arg.url, + prId: configFile?.repo.pr || arg.pr, + removeOldComment: configFile?.repo.removeOldComment || arg.removeOldComment, +}); + +const buildDataProviderConfig = ( + arg: DataConfigArgument, + configFile: DataConfigYAML, +): DataProviderConfig => ({ + token: configFile?.repo.token || arg.token, + repoUrl: configFile?.repo.url || arg.url, + runId: configFile?.repo.runId || arg.runId, + latestCommit: configFile?.repo.latestCommit || arg.latestCommit, +}); + export const buildAppConfig = async ( arg: ConfigArgument, command: COMMAND, diff --git a/src/Config/constants/required.ts b/src/Config/constants/required.ts index 9b91b19..b627309 100644 --- a/src/Config/constants/required.ts +++ b/src/Config/constants/required.ts @@ -1,7 +1,12 @@ -import { ConfigArgument } from '..'; -import { ConfigYAML } from '../@types/configYAML'; +import { + PrConfigYAML, + DataConfigYAML, + ConfigYAML, + PrConfigArgument, + DataConfigArgument, +} from '../@types'; -type DataRequiredArgs = (keyof ConfigArgument)[]; +type DataRequiredArgs = (keyof DataConfigArgument)[]; export const DATA_REQUIRED_ARGS: DataRequiredArgs = [ 'url', 'latestCommit', @@ -10,19 +15,21 @@ export const DATA_REQUIRED_ARGS: DataRequiredArgs = [ 'token', ]; -type PrRequiredArgs = (keyof ConfigArgument)[]; +type PrRequiredArgs = (keyof PrConfigArgument)[]; export const PR_REQUIRED_ARGS: PrRequiredArgs = ['pr', 'buildLogFile', 'token', 'url']; type RequiredYamlArgs = (keyof ConfigYAML)[]; export const REQUIRED_YAML_ARGS: RequiredYamlArgs = ['repo', 'buildLogFiles']; -type RequiredYamlProviderArgs = (keyof ConfigYAML['repo'])[]; -export const PR_REQUIRED_YAML_PROVIDER_ARGS: RequiredYamlProviderArgs = [ +type PrRequiredYamlProviderArgs = (keyof PrConfigYAML['repo'])[]; +export const PR_REQUIRED_YAML_PROVIDER_ARGS: PrRequiredYamlProviderArgs = [ 'url', 'pr', 'token', ]; -export const DATA_REQUIRED_YAML_PROVIDER_ARGS: RequiredYamlProviderArgs = [ + +type DataRequiredYamlProviderArgs = (keyof DataConfigYAML['repo'])[]; +export const DATA_REQUIRED_YAML_PROVIDER_ARGS: DataRequiredYamlProviderArgs = [ 'url', 'latestCommit', 'token', From 7e236d82e415b6877ab666b7bd2edc6df8db76be Mon Sep 17 00:00:00 2001 From: Goggags123 Date: Thu, 11 Nov 2021 13:23:37 +0700 Subject: [PATCH 08/15] Change config name and remove unnecessary config --- sample/config/{dataConfig.yaml => data-config.yaml} | 3 +-- src/Config/@types/configArgument.ts | 6 +++--- src/Config/@types/configYAML.ts | 4 ++-- src/Config/@types/providerConfig.ts | 4 ++-- src/Config/Config.spec.ts | 8 +++----- src/Config/Config.ts | 10 +++++----- src/Config/YML.spec.ts | 4 ++-- src/Config/YML.ts | 2 +- src/Config/configBuilder.ts | 3 +-- src/Config/constants/required.ts | 6 ++---- 10 files changed, 22 insertions(+), 28 deletions(-) rename sample/config/{dataConfig.yaml => data-config.yaml} (65%) diff --git a/sample/config/dataConfig.yaml b/sample/config/data-config.yaml similarity index 65% rename from sample/config/dataConfig.yaml rename to sample/config/data-config.yaml index 1a90dd8..7e512cc 100644 --- a/sample/config/dataConfig.yaml +++ b/sample/config/data-config.yaml @@ -1,8 +1,7 @@ repo: url: https://github.com/codeleague/codecoach runId: 40 - latestCommit: 123qwe123qwe123qwe123qwe123qwe123qwe123q - token: 123qwe123qwe123qwe123 + headCommit: 123qwe123qwe123qwe123qwe123qwe123qwe123q buildLogFiles: - type: tslint path: /path/to/log.json diff --git a/src/Config/@types/configArgument.ts b/src/Config/@types/configArgument.ts index dd96826..97cbed2 100644 --- a/src/Config/@types/configArgument.ts +++ b/src/Config/@types/configArgument.ts @@ -4,18 +4,18 @@ type BaseConfigArgument = { url: string; buildLogFile: BuildLogFile[]; output: string; - token: string; config: string; }; export type PrConfigArgument = { pr: number; removeOldComment: boolean; + token: string; } & BaseConfigArgument; export type DataConfigArgument = { - latestCommit: string; + headCommit: string; runId: number; } & BaseConfigArgument; -export type ConfigArgument = PrConfigArgument | DataConfigArgument; \ No newline at end of file +export type ConfigArgument = PrConfigArgument | DataConfigArgument; diff --git a/src/Config/@types/configYAML.ts b/src/Config/@types/configYAML.ts index 8281ae4..35a8633 100644 --- a/src/Config/@types/configYAML.ts +++ b/src/Config/@types/configYAML.ts @@ -3,7 +3,6 @@ import { BuildLogFile } from './buildLogFile'; type BaseConfigYAML = { repo: { url: string; - token: string; userAgent: string; timeZone: string; }; @@ -13,6 +12,7 @@ type BaseConfigYAML = { export type PrConfigYAML = { repo: { + token: string; pr: number; removeOldComment: boolean; }; @@ -20,7 +20,7 @@ export type PrConfigYAML = { export type DataConfigYAML = { repo: { - latestCommit: string; + headCommit: string; runId: number; }; } & BaseConfigYAML; diff --git a/src/Config/@types/providerConfig.ts b/src/Config/@types/providerConfig.ts index 9f90ae0..b4314f1 100644 --- a/src/Config/@types/providerConfig.ts +++ b/src/Config/@types/providerConfig.ts @@ -1,16 +1,16 @@ type BaseProviderConfig = { - token: string; repoUrl: string; }; export type PrProviderConfig = { + token: string; prId: number; removeOldComment: boolean; } & BaseProviderConfig; export type DataProviderConfig = { runId: number; - latestCommit: string; + headCommit: string; } & BaseProviderConfig; export type ProviderConfig = PrProviderConfig | DataProviderConfig; diff --git a/src/Config/Config.spec.ts b/src/Config/Config.spec.ts index 124048f..b112247 100644 --- a/src/Config/Config.spec.ts +++ b/src/Config/Config.spec.ts @@ -47,8 +47,7 @@ const DATA_MOCK_ARGS = [ 'collect', '--url=https://github.com/codeleague/codecoach.git', '-r=3', - '-c=latestcommitsha', - '--token=placeyourtokenhere', + '-c=headCommitsha', '-f=dotnetbuild;./sample/dotnetbuild/build.content;/repo/src', '-o=./tmp/out.json', ]; @@ -65,8 +64,7 @@ export const DATA_EXPECTED_MOCK_ARGS = [ '/Users/codecoach/src/app.ts', 'https://github.com/codeleague/codecoach.git', 3, - 'latestcommitsha', - 'placeyourtokenhere', + 'headCommitsha', 'dotnetbuild;./sample/dotnetbuild/build.content;/repo/src', './tmp/out.json', ]; @@ -113,7 +111,7 @@ describe('Data config Test', () => { const fullfillConfig = (await config).provider as DataProviderConfig; expect(fullfillConfig.repoUrl).toBe(DATA_EXPECTED_MOCK_ARGS[2]); expect(fullfillConfig.runId).toBe(DATA_EXPECTED_MOCK_ARGS[3]); - expect(fullfillConfig.latestCommit).toBe(DATA_EXPECTED_MOCK_ARGS[4]); + expect(fullfillConfig.headCommit).toBe(DATA_EXPECTED_MOCK_ARGS[4]); }); it('Should able to use a config file without passing other args', async () => { diff --git a/src/Config/Config.ts b/src/Config/Config.ts index 38d0869..f93d26f 100644 --- a/src/Config/Config.ts +++ b/src/Config/Config.ts @@ -26,10 +26,6 @@ const args = yargs describe: 'GitHub repo url (https or ssh)', type: 'string', }) - .option('token', { - describe: 'GitHub token', - type: 'string', - }) .option('output', { alias: 'o', describe: 'Output parsed log file', @@ -66,6 +62,10 @@ and is build root directory (optional (Will use current context as cwd)). describe: 'Remove existing CodeCoach comments before putting new one', default: false, }) + .option('token', { + describe: 'GitHub token', + type: 'string', + }) .check((options) => { // check arguments parsing const useConfigArgs = options.config === undefined; @@ -83,7 +83,7 @@ and is build root directory (optional (Will use current context as cwd)). 'des', (yarg) => yarg - .option('latestCommit', { + .option('headCommit', { alias: 'c', describe: 'The latest commit sha', type: 'string', diff --git a/src/Config/YML.spec.ts b/src/Config/YML.spec.ts index 08bfb95..97e861c 100644 --- a/src/Config/YML.spec.ts +++ b/src/Config/YML.spec.ts @@ -17,11 +17,11 @@ describe('PR YML Test', () => { describe('Data YML Test', () => { it('Should able to validate yaml file correctly', async () => { const config = (await YML.parse( - 'sample/config/dataConfig.yaml', + 'sample/config/data-config.yaml', COMMAND.COLLECT, )) as DataConfigYAML; expect(config.output).toBe('./output.json'); expect(config.repo.runId).toBe(40); - expect(config.repo.latestCommit).toBe('123qwe123qwe123qwe123qwe123qwe123qwe123q'); + expect(config.repo.headCommit).toBe('123qwe123qwe123qwe123qwe123qwe123qwe123q'); }); }); diff --git a/src/Config/YML.ts b/src/Config/YML.ts index 77fa7fe..0ccfc82 100644 --- a/src/Config/YML.ts +++ b/src/Config/YML.ts @@ -44,7 +44,7 @@ export class YML { repo: { ...config.repo, runId: Number(config.repo.runId), - latestCommit: config.repo.latestCommit, + headCommit: config.repo.headCommit, }, }; } diff --git a/src/Config/configBuilder.ts b/src/Config/configBuilder.ts index 4497bff..9e21893 100644 --- a/src/Config/configBuilder.ts +++ b/src/Config/configBuilder.ts @@ -49,10 +49,9 @@ const buildDataProviderConfig = ( arg: DataConfigArgument, configFile: DataConfigYAML, ): DataProviderConfig => ({ - token: configFile?.repo.token || arg.token, repoUrl: configFile?.repo.url || arg.url, runId: configFile?.repo.runId || arg.runId, - latestCommit: configFile?.repo.latestCommit || arg.latestCommit, + headCommit: configFile?.repo.headCommit || arg.headCommit, }); export const buildAppConfig = async ( diff --git a/src/Config/constants/required.ts b/src/Config/constants/required.ts index b627309..adc6437 100644 --- a/src/Config/constants/required.ts +++ b/src/Config/constants/required.ts @@ -9,10 +9,9 @@ import { type DataRequiredArgs = (keyof DataConfigArgument)[]; export const DATA_REQUIRED_ARGS: DataRequiredArgs = [ 'url', - 'latestCommit', + 'headCommit', 'runId', 'buildLogFile', - 'token', ]; type PrRequiredArgs = (keyof PrConfigArgument)[]; @@ -31,6 +30,5 @@ export const PR_REQUIRED_YAML_PROVIDER_ARGS: PrRequiredYamlProviderArgs = [ type DataRequiredYamlProviderArgs = (keyof DataConfigYAML['repo'])[]; export const DATA_REQUIRED_YAML_PROVIDER_ARGS: DataRequiredYamlProviderArgs = [ 'url', - 'latestCommit', - 'token', + 'headCommit', ]; From fd9a16ef0b2a8d901076dd5df47fac57cebe122d Mon Sep 17 00:00:00 2001 From: Goggags123 Date: Thu, 11 Nov 2021 13:25:52 +0700 Subject: [PATCH 09/15] Fix unit test --- src/Config/Config.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Config/Config.spec.ts b/src/Config/Config.spec.ts index b112247..18ff8d3 100644 --- a/src/Config/Config.spec.ts +++ b/src/Config/Config.spec.ts @@ -56,7 +56,7 @@ const DATA_MOCK_ARGS_W_CONFIG_YAML = [ '/usr/local/Cellar/node/15.13.0/bin/node', '/Users/codecoach/src/app.ts', 'collect', - '--config=sample/config/dataConfig.yaml', + '--config=sample/config/data-config.yaml', ]; export const DATA_EXPECTED_MOCK_ARGS = [ From f1bdc7262b50994a2f134ed714413cad64be3fec Mon Sep 17 00:00:00 2001 From: Goggags123 Date: Fri, 12 Nov 2021 03:56:06 +0700 Subject: [PATCH 10/15] Prepare required data for sending through API --- src/Parser/@types/log.type.ts | 20 +++++++++++ src/app.ts | 67 ++++++++++++++++++++++++++++------- 2 files changed, 74 insertions(+), 13 deletions(-) diff --git a/src/Parser/@types/log.type.ts b/src/Parser/@types/log.type.ts index fcf30c9..61bb8b8 100644 --- a/src/Parser/@types/log.type.ts +++ b/src/Parser/@types/log.type.ts @@ -1,4 +1,5 @@ import { LogSeverity } from '..'; +import { ProjectType } from '../../Config'; export type LogType = { log: string; @@ -9,3 +10,22 @@ export type LogType = { lineOffset?: number; valid: boolean; }; + +export type ParsedLogType = { + linter: ProjectType; + raw?: string; + logs: LogType[]; +}; + +export type Run = { + id: number; + timestamp: Date; + headCommit: { + sha: string; + }; + repository: { + url: string; + }; + branch: string; + issues: ParsedLogType[]; +}; diff --git a/src/app.ts b/src/app.ts index 72ae00e..713fe1c 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,6 +1,14 @@ #!/usr/bin/env node -import { BuildLogFile, Config, ConfigObject, ProjectType } from './Config'; +import { + BuildLogFile, + Config, + ConfigObject, + DataProviderConfig, + ProjectType, + PrProviderConfig, +} from './Config'; +import { COMMAND } from './Config/constants/defaults'; import { File } from './File'; import { Log } from './Logger'; import { @@ -13,6 +21,7 @@ import { ScalaStyleParser, TSLintParser, } from './Parser'; +import { ParsedLogType, Run } from './Parser/@types/log.type'; import { DartLintParser } from './Parser/DartLintParser'; import { GitHub, GitHubPRService, VCS } from './Provider'; @@ -22,15 +31,10 @@ class App { async start(): Promise { this.config = await Config; + const { repoUrl } = this.config.provider; - const githubPRService = new GitHubPRService( - this.config.provider.token, - this.config.provider.repoUrl, - this.config.provider.prId, - ); - this.vcs = new GitHub(githubPRService, this.config.provider.removeOldComment); - - const logs = await this.parseBuildData(this.config.app.buildLogFiles); + const parsedLogs = await this.parseBuildData(this.config.app.buildLogFiles); + const logs = parsedLogs.flatMap(({ logs }) => logs); Log.info('Build data parsing completed'); // Fire and forget, no need to await @@ -38,8 +42,41 @@ class App { .then(() => Log.info('Write output completed')) .catch((error) => Log.error('Write output failed', { error })); - await this.vcs.report(logs); - Log.info('Report to VCS completed'); + switch (this.config.app.command) { + case COMMAND.DEFAULT: + const { token, removeOldComment, prId } = this.config + .provider as PrProviderConfig; + const githubPRService = new GitHubPRService(token, repoUrl, prId); + this.vcs = new GitHub(githubPRService, removeOldComment); + + await this.vcs.report(logs); + Log.info('Report to VCS completed'); + break; + case COMMAND.COLLECT: + const { headCommit, runId } = this.config.provider as DataProviderConfig; + const issues: ParsedLogType[] = parsedLogs.filter(({ logs }) => + logs.filter(({ valid }) => valid), + ); + const runInfomation: Run = { + id: runId, + timestamp: new Date(Date.now()), + issues: issues, + branch: 'something', + headCommit: { + sha: headCommit, + }, + repository: { + url: repoUrl, + }, + }; + + if (issues.length === 0) break; + //TODO: send api + break; + default: + Log.error(`Command: ${this.config.app.command} is invalid`); + break; + } } private static getParser(type: ProjectType, cwd: string): Parser { @@ -61,12 +98,16 @@ class App { } } - private async parseBuildData(files: BuildLogFile[]): Promise { + private async parseBuildData(files: BuildLogFile[]): Promise { const logsTasks = files.map(async ({ type, path, cwd }) => { Log.debug('Parsing', { type, path, cwd }); const content = await File.readFileHelper(path); const parser = App.getParser(type, cwd); - return parser.parse(content); + return { + logs: parser.parse(content), + raw: content, + linter: type, + }; }); return (await Promise.all(logsTasks)).flatMap((x) => x); From f822d6390106c9b95accc8f50aa7afd18973def6 Mon Sep 17 00:00:00 2001 From: Goggags123 Date: Mon, 15 Nov 2021 00:25:39 +0700 Subject: [PATCH 11/15] Add another config property 'branch' --- sample/config/data-config.yaml | 1 + src/Config/@types/configArgument.ts | 1 + src/Config/@types/configYAML.ts | 1 + src/Config/@types/providerConfig.ts | 1 + src/Config/Config.spec.ts | 5 ++++- src/Config/Config.ts | 12 +++++++----- src/Config/YML.ts | 1 + src/Config/configBuilder.ts | 1 + src/Config/constants/required.ts | 1 + 9 files changed, 18 insertions(+), 6 deletions(-) diff --git a/sample/config/data-config.yaml b/sample/config/data-config.yaml index 7e512cc..cbd6fd1 100644 --- a/sample/config/data-config.yaml +++ b/sample/config/data-config.yaml @@ -2,6 +2,7 @@ repo: url: https://github.com/codeleague/codecoach runId: 40 headCommit: 123qwe123qwe123qwe123qwe123qwe123qwe123q + branch: main buildLogFiles: - type: tslint path: /path/to/log.json diff --git a/src/Config/@types/configArgument.ts b/src/Config/@types/configArgument.ts index 97cbed2..fb7ad1e 100644 --- a/src/Config/@types/configArgument.ts +++ b/src/Config/@types/configArgument.ts @@ -16,6 +16,7 @@ export type PrConfigArgument = { export type DataConfigArgument = { headCommit: string; runId: number; + branch: string; } & BaseConfigArgument; export type ConfigArgument = PrConfigArgument | DataConfigArgument; diff --git a/src/Config/@types/configYAML.ts b/src/Config/@types/configYAML.ts index 35a8633..12ef231 100644 --- a/src/Config/@types/configYAML.ts +++ b/src/Config/@types/configYAML.ts @@ -22,6 +22,7 @@ export type DataConfigYAML = { repo: { headCommit: string; runId: number; + branch: string; }; } & BaseConfigYAML; diff --git a/src/Config/@types/providerConfig.ts b/src/Config/@types/providerConfig.ts index b4314f1..45320f3 100644 --- a/src/Config/@types/providerConfig.ts +++ b/src/Config/@types/providerConfig.ts @@ -11,6 +11,7 @@ export type PrProviderConfig = { export type DataProviderConfig = { runId: number; headCommit: string; + branch: string; } & BaseProviderConfig; export type ProviderConfig = PrProviderConfig | DataProviderConfig; diff --git a/src/Config/Config.spec.ts b/src/Config/Config.spec.ts index 18ff8d3..85521be 100644 --- a/src/Config/Config.spec.ts +++ b/src/Config/Config.spec.ts @@ -47,6 +47,7 @@ const DATA_MOCK_ARGS = [ 'collect', '--url=https://github.com/codeleague/codecoach.git', '-r=3', + '-b=main', '-c=headCommitsha', '-f=dotnetbuild;./sample/dotnetbuild/build.content;/repo/src', '-o=./tmp/out.json', @@ -64,6 +65,7 @@ export const DATA_EXPECTED_MOCK_ARGS = [ '/Users/codecoach/src/app.ts', 'https://github.com/codeleague/codecoach.git', 3, + 'main', 'headCommitsha', 'dotnetbuild;./sample/dotnetbuild/build.content;/repo/src', './tmp/out.json', @@ -111,7 +113,8 @@ describe('Data config Test', () => { const fullfillConfig = (await config).provider as DataProviderConfig; expect(fullfillConfig.repoUrl).toBe(DATA_EXPECTED_MOCK_ARGS[2]); expect(fullfillConfig.runId).toBe(DATA_EXPECTED_MOCK_ARGS[3]); - expect(fullfillConfig.headCommit).toBe(DATA_EXPECTED_MOCK_ARGS[4]); + expect(fullfillConfig.branch).toBe(DATA_EXPECTED_MOCK_ARGS[4]); + expect(fullfillConfig.headCommit).toBe(DATA_EXPECTED_MOCK_ARGS[5]); }); it('Should able to use a config file without passing other args', async () => { diff --git a/src/Config/Config.ts b/src/Config/Config.ts index f93d26f..f0370e3 100644 --- a/src/Config/Config.ts +++ b/src/Config/Config.ts @@ -50,12 +50,11 @@ and is build root directory (optional (Will use current context as cwd)). return { type, path, cwd: cwd ?? process.cwd() } as BuildLogFile; }); }) - .command(['$0', 'comment'], 'desc', (yarg) => + .command(['$0', 'comment'], 'Report Lint results on a pull request', (yarg) => yarg .option('pr', { describe: 'PR number', type: 'number', - global: true, }) .option('removeOldComment', { type: 'boolean', @@ -80,20 +79,23 @@ and is build root directory (optional (Will use current context as cwd)). ) .command( 'collect', - 'des', + 'Collect and store Lint results', (yarg) => yarg .option('headCommit', { alias: 'c', describe: 'The latest commit sha', type: 'string', - global: true, }) .option('runId', { alias: 'r', describe: 'The latest run id', type: 'number', - global: true, + }) + .option('branch', { + alias: 'b', + describe: 'The branch that this command is run on', + type: 'string', }) .check((options) => { // check arguments parsing diff --git a/src/Config/YML.ts b/src/Config/YML.ts index 0ccfc82..57a13f7 100644 --- a/src/Config/YML.ts +++ b/src/Config/YML.ts @@ -45,6 +45,7 @@ export class YML { ...config.repo, runId: Number(config.repo.runId), headCommit: config.repo.headCommit, + branch: config.repo.branch, }, }; } diff --git a/src/Config/configBuilder.ts b/src/Config/configBuilder.ts index 9e21893..914a24f 100644 --- a/src/Config/configBuilder.ts +++ b/src/Config/configBuilder.ts @@ -52,6 +52,7 @@ const buildDataProviderConfig = ( repoUrl: configFile?.repo.url || arg.url, runId: configFile?.repo.runId || arg.runId, headCommit: configFile?.repo.headCommit || arg.headCommit, + branch: configFile?.repo.branch || arg.branch, }); export const buildAppConfig = async ( diff --git a/src/Config/constants/required.ts b/src/Config/constants/required.ts index adc6437..018b5e4 100644 --- a/src/Config/constants/required.ts +++ b/src/Config/constants/required.ts @@ -31,4 +31,5 @@ type DataRequiredYamlProviderArgs = (keyof DataConfigYAML['repo'])[]; export const DATA_REQUIRED_YAML_PROVIDER_ARGS: DataRequiredYamlProviderArgs = [ 'url', 'headCommit', + 'branch', ]; From aab08fae445d7265a8451b79a15f70389a7c8a5b Mon Sep 17 00:00:00 2001 From: Goggags123 Date: Mon, 15 Nov 2021 03:25:18 +0700 Subject: [PATCH 12/15] Add branch to required arguments --- src/Config/constants/required.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Config/constants/required.ts b/src/Config/constants/required.ts index 018b5e4..f3bc961 100644 --- a/src/Config/constants/required.ts +++ b/src/Config/constants/required.ts @@ -6,17 +6,18 @@ import { DataConfigArgument, } from '../@types'; +type PrRequiredArgs = (keyof PrConfigArgument)[]; +export const PR_REQUIRED_ARGS: PrRequiredArgs = ['pr', 'buildLogFile', 'token', 'url']; + type DataRequiredArgs = (keyof DataConfigArgument)[]; export const DATA_REQUIRED_ARGS: DataRequiredArgs = [ 'url', 'headCommit', 'runId', 'buildLogFile', + 'branch', ]; -type PrRequiredArgs = (keyof PrConfigArgument)[]; -export const PR_REQUIRED_ARGS: PrRequiredArgs = ['pr', 'buildLogFile', 'token', 'url']; - type RequiredYamlArgs = (keyof ConfigYAML)[]; export const REQUIRED_YAML_ARGS: RequiredYamlArgs = ['repo', 'buildLogFiles']; From 6b4b0a141c6fe5637ec6bf06dff9c4518ec8d1fc Mon Sep 17 00:00:00 2001 From: Goggags123 Date: Mon, 15 Nov 2021 03:31:37 +0700 Subject: [PATCH 13/15] Add groupby functionality --- src/Parser/@types/log.type.ts | 12 +++++----- src/app.ts | 42 ++++++++++++++++++++++------------- 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/src/Parser/@types/log.type.ts b/src/Parser/@types/log.type.ts index 61bb8b8..f37831b 100644 --- a/src/Parser/@types/log.type.ts +++ b/src/Parser/@types/log.type.ts @@ -9,13 +9,13 @@ export type LogType = { line?: number; lineOffset?: number; valid: boolean; + linter?: ProjectType; }; -export type ParsedLogType = { - linter: ProjectType; - raw?: string; - logs: LogType[]; -}; +export type Issue = { message: string; filePath: string; column?: number } & Omit< + LogType, + 'msg' | 'valid' | 'lineOffset' | 'log' | 'source' +>; export type Run = { id: number; @@ -27,5 +27,5 @@ export type Run = { url: string; }; branch: string; - issues: ParsedLogType[]; + issues: Issue[]; }; diff --git a/src/app.ts b/src/app.ts index 713fe1c..aa16d12 100644 --- a/src/app.ts +++ b/src/app.ts @@ -21,7 +21,7 @@ import { ScalaStyleParser, TSLintParser, } from './Parser'; -import { ParsedLogType, Run } from './Parser/@types/log.type'; +import { Issue, Run } from './Parser/@types/log.type'; import { DartLintParser } from './Parser/DartLintParser'; import { GitHub, GitHubPRService, VCS } from './Provider'; @@ -33,8 +33,7 @@ class App { this.config = await Config; const { repoUrl } = this.config.provider; - const parsedLogs = await this.parseBuildData(this.config.app.buildLogFiles); - const logs = parsedLogs.flatMap(({ logs }) => logs); + const logs = await this.parseBuildData(this.config.app.buildLogFiles); Log.info('Build data parsing completed'); // Fire and forget, no need to await @@ -53,15 +52,13 @@ class App { Log.info('Report to VCS completed'); break; case COMMAND.COLLECT: - const { headCommit, runId } = this.config.provider as DataProviderConfig; - const issues: ParsedLogType[] = parsedLogs.filter(({ logs }) => - logs.filter(({ valid }) => valid), - ); + const { headCommit, runId, branch } = this.config.provider as DataProviderConfig; + const issues = this.mapLogTypeToIssue(logs); const runInfomation: Run = { id: runId, timestamp: new Date(Date.now()), - issues: issues, - branch: 'something', + issues: issues['false'], + branch: branch, headCommit: { sha: headCommit, }, @@ -70,14 +67,31 @@ class App { }, }; - if (issues.length === 0) break; + if (issues['false'].length === 0) break; //TODO: send api + Log.info('Data sent'); break; default: Log.error(`Command: ${this.config.app.command} is invalid`); break; } } + private mapLogTypeToIssue = (list: LogType[]) => + list.reduce((previous: Record, currentItem: LogType) => { + const { msg, severity, source, valid, line, lineOffset, linter } = currentItem; + const group = valid.toString(); + const issue: Issue = { + filePath: source, + message: msg, + severity: severity, + line: line, + column: lineOffset, + linter: linter, + }; + if (!previous[group]) previous[group] = []; + previous[group].push(issue); + return previous; + }, {} as Record); private static getParser(type: ProjectType, cwd: string): Parser { switch (type) { @@ -98,16 +112,12 @@ class App { } } - private async parseBuildData(files: BuildLogFile[]): Promise { + private async parseBuildData(files: BuildLogFile[]): Promise { const logsTasks = files.map(async ({ type, path, cwd }) => { Log.debug('Parsing', { type, path, cwd }); const content = await File.readFileHelper(path); const parser = App.getParser(type, cwd); - return { - logs: parser.parse(content), - raw: content, - linter: type, - }; + return parser.parse(content).map((log) => ({ ...log, linter: type })); }); return (await Promise.all(logsTasks)).flatMap((x) => x); From c872a59760c4e4bc56ca55968b4f953bdb7bc7bf Mon Sep 17 00:00:00 2001 From: Goggags123 Date: Mon, 29 Nov 2021 17:56:13 +0700 Subject: [PATCH 14/15] Add functionality to send data via api --- package.json | 1 + sample/config/data-config.yaml | 1 + src/Api.ts | 16 ++++++++++++ src/Config/@enums/command.ts | 4 +++ src/Config/@enums/index.ts | 1 + src/Config/@types/appConfig.ts | 3 ++- src/Config/@types/configArgument.ts | 2 ++ src/Config/@types/configYAML.ts | 2 ++ src/Config/Config.spec.ts | 13 ++++++---- src/Config/Config.ts | 9 +++++-- src/Config/YML.spec.ts | 2 +- src/Config/YML.ts | 16 +++++++----- src/Config/configBuilder.ts | 3 ++- src/Config/constants/defaults.ts | 5 ---- src/Config/constants/required.ts | 10 ++++++-- src/app.ts | 40 +++++++++++++++-------------- yarn.lock | 19 ++++++++++++++ 17 files changed, 105 insertions(+), 42 deletions(-) create mode 100644 src/Api.ts create mode 100644 src/Config/@enums/command.ts diff --git a/package.json b/package.json index bf9917a..5d64397 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "typescript": "^4.1.2" }, "dependencies": { + "@codecoach/api-client": "^1.0.45", "@octokit/core": "^3.2.4", "@octokit/rest": "^18.12.0", "js-yaml": "^4.1.0", diff --git a/sample/config/data-config.yaml b/sample/config/data-config.yaml index cbd6fd1..ecb9d16 100644 --- a/sample/config/data-config.yaml +++ b/sample/config/data-config.yaml @@ -8,3 +8,4 @@ buildLogFiles: path: /path/to/log.json cwd: /repo/src output: ./output.json +apiServer: https://localhost:3000 diff --git a/src/Api.ts b/src/Api.ts new file mode 100644 index 0000000..f95f920 --- /dev/null +++ b/src/Api.ts @@ -0,0 +1,16 @@ +import axios from 'axios'; + +import { RunApi } from '@codecoach/api-client'; + +export class Api { + private readonly axiosInstance; + constructor(private readonly apiServer: string) { + this.axiosInstance = axios.create({ + baseURL: process.env.NODE_ENV === 'development' ? '' : this.apiServer, + }); + } + + get runClient(): RunApi { + return new RunApi(undefined, '', this.axiosInstance); + } +} diff --git a/src/Config/@enums/command.ts b/src/Config/@enums/command.ts new file mode 100644 index 0000000..af5450e --- /dev/null +++ b/src/Config/@enums/command.ts @@ -0,0 +1,4 @@ +export enum COMMAND { + DEFAULT = 'default', + COLLECT = 'collect', +} diff --git a/src/Config/@enums/index.ts b/src/Config/@enums/index.ts index 4f98e6c..899e093 100644 --- a/src/Config/@enums/index.ts +++ b/src/Config/@enums/index.ts @@ -1 +1,2 @@ export { ProjectType } from './projectType'; +export { COMMAND } from './command'; diff --git a/src/Config/@types/appConfig.ts b/src/Config/@types/appConfig.ts index 1742d8f..d9dfc55 100644 --- a/src/Config/@types/appConfig.ts +++ b/src/Config/@types/appConfig.ts @@ -1,8 +1,9 @@ -import { COMMAND } from '../constants/defaults'; +import { COMMAND } from '../@enums'; import { BuildLogFile } from './buildLogFile'; export type AppConfig = { command: COMMAND; logFilePath: string; buildLogFiles: BuildLogFile[]; + apiServer: string; }; diff --git a/src/Config/@types/configArgument.ts b/src/Config/@types/configArgument.ts index fb7ad1e..d715a9c 100644 --- a/src/Config/@types/configArgument.ts +++ b/src/Config/@types/configArgument.ts @@ -11,12 +11,14 @@ export type PrConfigArgument = { pr: number; removeOldComment: boolean; token: string; + apiServer: never; } & BaseConfigArgument; export type DataConfigArgument = { headCommit: string; runId: number; branch: string; + apiServer: string; } & BaseConfigArgument; export type ConfigArgument = PrConfigArgument | DataConfigArgument; diff --git a/src/Config/@types/configYAML.ts b/src/Config/@types/configYAML.ts index 12ef231..79b263d 100644 --- a/src/Config/@types/configYAML.ts +++ b/src/Config/@types/configYAML.ts @@ -16,6 +16,7 @@ export type PrConfigYAML = { pr: number; removeOldComment: boolean; }; + apiServer: never; } & BaseConfigYAML; export type DataConfigYAML = { @@ -24,6 +25,7 @@ export type DataConfigYAML = { runId: number; branch: string; }; + apiServer: string; } & BaseConfigYAML; export type ConfigYAML = PrConfigYAML | DataConfigYAML; diff --git a/src/Config/Config.spec.ts b/src/Config/Config.spec.ts index 85521be..8e9abce 100644 --- a/src/Config/Config.spec.ts +++ b/src/Config/Config.spec.ts @@ -51,6 +51,7 @@ const DATA_MOCK_ARGS = [ '-c=headCommitsha', '-f=dotnetbuild;./sample/dotnetbuild/build.content;/repo/src', '-o=./tmp/out.json', + '--api=https://localhost:3000', ]; const DATA_MOCK_ARGS_W_CONFIG_YAML = [ @@ -69,6 +70,7 @@ export const DATA_EXPECTED_MOCK_ARGS = [ 'headCommitsha', 'dotnetbuild;./sample/dotnetbuild/build.content;/repo/src', './tmp/out.json', + 'https://localhost:3000', ]; describe('PR config Test', () => { @@ -110,11 +112,12 @@ describe('Data config Test', () => { it('Should able to parse this args and run without throwing error', async () => { process.argv = DATA_MOCK_ARGS; config = (await import('./Config')).Config; - const fullfillConfig = (await config).provider as DataProviderConfig; - expect(fullfillConfig.repoUrl).toBe(DATA_EXPECTED_MOCK_ARGS[2]); - expect(fullfillConfig.runId).toBe(DATA_EXPECTED_MOCK_ARGS[3]); - expect(fullfillConfig.branch).toBe(DATA_EXPECTED_MOCK_ARGS[4]); - expect(fullfillConfig.headCommit).toBe(DATA_EXPECTED_MOCK_ARGS[5]); + const providerConfig = (await config).provider as DataProviderConfig; + expect(providerConfig.repoUrl).toBe(DATA_EXPECTED_MOCK_ARGS[2]); + expect(providerConfig.runId).toBe(DATA_EXPECTED_MOCK_ARGS[3]); + expect(providerConfig.branch).toBe(DATA_EXPECTED_MOCK_ARGS[4]); + expect(providerConfig.headCommit).toBe(DATA_EXPECTED_MOCK_ARGS[5]); + expect((await config).app.apiServer).toBe(DATA_EXPECTED_MOCK_ARGS[8]); }); it('Should able to use a config file without passing other args', async () => { diff --git a/src/Config/Config.ts b/src/Config/Config.ts index f0370e3..f9c0df3 100644 --- a/src/Config/Config.ts +++ b/src/Config/Config.ts @@ -1,8 +1,8 @@ import yargs, { Arguments } from 'yargs'; -import { ProjectType } from './@enums'; +import { ProjectType, COMMAND } from './@enums'; import { BuildLogFile, ConfigArgument, ConfigObject } from './@types'; import { buildAppConfig, buildProviderConfig } from './configBuilder'; -import { DEFAULT_OUTPUT_FILE, COMMAND } from './constants/defaults'; +import { DEFAULT_OUTPUT_FILE } from './constants/defaults'; import { DATA_REQUIRED_ARGS, PR_REQUIRED_ARGS } from './constants/required'; const projectTypes = Object.keys(ProjectType); @@ -97,6 +97,11 @@ and is build root directory (optional (Will use current context as cwd)). describe: 'The branch that this command is run on', type: 'string', }) + .option('apiServer', { + alias: 'api', + describe: 'The url of api server, e.g., http://localhost:3000', + type: 'string', + }) .check((options) => { // check arguments parsing const useConfigArgs = options.config === undefined; diff --git a/src/Config/YML.spec.ts b/src/Config/YML.spec.ts index 97e861c..1a9922e 100644 --- a/src/Config/YML.spec.ts +++ b/src/Config/YML.spec.ts @@ -1,5 +1,5 @@ import { DataConfigYAML, PrConfigYAML } from '.'; -import { COMMAND } from './constants/defaults'; +import { COMMAND } from './@enums'; import { YML } from './YML'; describe('PR YML Test', () => { diff --git a/src/Config/YML.ts b/src/Config/YML.ts index 57a13f7..c56cd3a 100644 --- a/src/Config/YML.ts +++ b/src/Config/YML.ts @@ -4,18 +4,21 @@ import { REQUIRED_YAML_ARGS, PR_REQUIRED_YAML_PROVIDER_ARGS, DATA_REQUIRED_YAML_PROVIDER_ARGS, + DATA_REQUIRED_YAML_ARGS, } from './constants/required'; import { ConfigYAML, DataConfigYAML, PrConfigYAML } from './@types/configYAML'; -import { COMMAND } from './constants/defaults'; +import { COMMAND } from './@enums'; export class YML { private static transform(config: ConfigYAML, command: COMMAND): ConfigYAML { - // required types - const validRequiredArgs = REQUIRED_YAML_ARGS.every( + // require types + const requiredArgs = + command === COMMAND.COLLECT ? DATA_REQUIRED_YAML_ARGS : REQUIRED_YAML_ARGS; + const validRequiredArg = requiredArgs.every( (el) => config[el] != undefined || config[el] != null, ); - if (!validRequiredArgs) - throw new Error(`please fill all required fields ${REQUIRED_YAML_ARGS.join(', ')}`); + if (!validRequiredArg) + throw new Error(`please fill all required fields ${requiredArgs.join(', ')}`); switch (command) { case COMMAND.COLLECT: @@ -32,8 +35,9 @@ export class YML { throw new Error('provider.runId is required or invalid number type'); const validRequiredProviderArgs = DATA_REQUIRED_YAML_PROVIDER_ARGS.every( - (el) => config.repo[el] != undefined || config.repo[el] != null, + (el) => config.repo[el] !== undefined || config.repo[el] != null, ); + if (!validRequiredProviderArgs) throw new Error( `please fill all required fields ${DATA_REQUIRED_YAML_PROVIDER_ARGS.join(', ')}`, diff --git a/src/Config/configBuilder.ts b/src/Config/configBuilder.ts index 914a24f..d6ceb03 100644 --- a/src/Config/configBuilder.ts +++ b/src/Config/configBuilder.ts @@ -1,3 +1,4 @@ +import { COMMAND } from './@enums'; import { AppConfig, ConfigArgument, @@ -9,7 +10,6 @@ import { PrProviderConfig, DataProviderConfig, } from './@types'; -import { COMMAND } from './constants/defaults'; import { YML } from './YML'; const buildYMLConfig = async (args: ConfigArgument, command: COMMAND) => { @@ -64,5 +64,6 @@ export const buildAppConfig = async ( command: command, logFilePath: configFile?.output || arg.output, buildLogFiles: configFile?.buildLogFiles || arg.buildLogFile, + apiServer: configFile?.apiServer || arg.apiServer, }; }; diff --git a/src/Config/constants/defaults.ts b/src/Config/constants/defaults.ts index 1bfb22d..2198be8 100644 --- a/src/Config/constants/defaults.ts +++ b/src/Config/constants/defaults.ts @@ -2,8 +2,3 @@ export const DEFAULT_OUTPUT_FILE = 'build.json'; export const USER_AGENT = 'CodeCoach'; export const TIME_ZONE = 'Asia/Bangkok'; - -export enum COMMAND { - DEFAULT = 'default', - COLLECT = 'collect', -} diff --git a/src/Config/constants/required.ts b/src/Config/constants/required.ts index f3bc961..b9c4343 100644 --- a/src/Config/constants/required.ts +++ b/src/Config/constants/required.ts @@ -16,6 +16,7 @@ export const DATA_REQUIRED_ARGS: DataRequiredArgs = [ 'runId', 'buildLogFile', 'branch', + 'apiServer', ]; type RequiredYamlArgs = (keyof ConfigYAML)[]; @@ -30,7 +31,12 @@ export const PR_REQUIRED_YAML_PROVIDER_ARGS: PrRequiredYamlProviderArgs = [ type DataRequiredYamlProviderArgs = (keyof DataConfigYAML['repo'])[]; export const DATA_REQUIRED_YAML_PROVIDER_ARGS: DataRequiredYamlProviderArgs = [ - 'url', - 'headCommit', 'branch', + 'headCommit', + 'runId', +]; + +export const DATA_REQUIRED_YAML_ARGS: (keyof DataConfigYAML)[] = [ + ...REQUIRED_YAML_ARGS, + 'apiServer', ]; diff --git a/src/app.ts b/src/app.ts index aa16d12..2bdc61d 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,5 +1,7 @@ #!/usr/bin/env node +import { IIssue } from '@codecoach/api-client'; +import { Api } from './Api'; import { BuildLogFile, Config, @@ -8,7 +10,7 @@ import { ProjectType, PrProviderConfig, } from './Config'; -import { COMMAND } from './Config/constants/defaults'; +import { COMMAND } from '../src/Config/@enums'; import { File } from './File'; import { Log } from './Logger'; import { @@ -21,10 +23,8 @@ import { ScalaStyleParser, TSLintParser, } from './Parser'; -import { Issue, Run } from './Parser/@types/log.type'; import { DartLintParser } from './Parser/DartLintParser'; import { GitHub, GitHubPRService, VCS } from './Provider'; - class App { private vcs: VCS; private config: ConfigObject; @@ -51,47 +51,49 @@ class App { await this.vcs.report(logs); Log.info('Report to VCS completed'); break; + case COMMAND.COLLECT: const { headCommit, runId, branch } = this.config.provider as DataProviderConfig; const issues = this.mapLogTypeToIssue(logs); - const runInfomation: Run = { - id: runId, - timestamp: new Date(Date.now()), - issues: issues['false'], - branch: branch, + + await new Api(this.config.app.apiServer).runClient.createRun({ + githubRunId: runId, + timestamp: new Date().toISOString(), + issues: issues['true'], + invalidIssues: issues['false'], + branch, headCommit: { sha: headCommit, }, repository: { url: repoUrl, }, - }; - - if (issues['false'].length === 0) break; - //TODO: send api - Log.info('Data sent'); + }); + Log.info('Data successfully sent'); break; + default: Log.error(`Command: ${this.config.app.command} is invalid`); break; } } + private mapLogTypeToIssue = (list: LogType[]) => - list.reduce((previous: Record, currentItem: LogType) => { + list.reduce((previous: Record, currentItem: LogType) => { const { msg, severity, source, valid, line, lineOffset, linter } = currentItem; const group = valid.toString(); - const issue: Issue = { - filePath: source, + const issue: IIssue = { + filepath: source, message: msg, severity: severity, - line: line, + line: line as number, column: lineOffset, - linter: linter, + linter: linter ?? '', }; if (!previous[group]) previous[group] = []; previous[group].push(issue); return previous; - }, {} as Record); + }, {} as Record); private static getParser(type: ProjectType, cwd: string): Parser { switch (type) { diff --git a/yarn.lock b/yarn.lock index e324d1a..8bc01d7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -271,6 +271,13 @@ exec-sh "^0.3.2" minimist "^1.2.0" +"@codecoach/api-client@^1.0.45": + version "1.0.45" + resolved "https://registry.yarnpkg.com/@codecoach/api-client/-/api-client-1.0.45.tgz#69688f085852662e81771a2b104f65cae147152f" + integrity sha512-u57Lgyv38bCs71WiQtF+CqOJxcHC/u0OhteCgdXDwQ5/bH9peKNqzVKlEVKuFcZ3RlTU8AU/EE2cB/yllA0P6w== + dependencies: + axios "^0.21.4" + "@dabh/diagnostics@^2.0.2": version "2.0.2" resolved "https://registry.yarnpkg.com/@dabh/diagnostics/-/diagnostics-2.0.2.tgz#290d08f7b381b8f94607dc8f471a12c675f9db31" @@ -1107,6 +1114,13 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.10.1.tgz#e1e82e4f3e999e2cfd61b161280d16a111f86428" integrity sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA== +axios@^0.21.4: + version "0.21.4" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" + integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== + dependencies: + follow-redirects "^1.14.0" + babel-jest@^26.3.0: version "26.3.0" resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-26.3.0.tgz#10d0ca4b529ca3e7d1417855ef7d7bd6fc0c3463" @@ -2180,6 +2194,11 @@ fn.name@1.x.x: resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc" integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw== +follow-redirects@^1.14.0: + version "1.14.5" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.5.tgz#f09a5848981d3c772b5392309778523f8d85c381" + integrity sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA== + for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" From d964a0f73ffe5ee9b6f06c706616f7f727f8000c Mon Sep 17 00:00:00 2001 From: Goggags123 Date: Mon, 29 Nov 2021 18:46:54 +0700 Subject: [PATCH 15/15] Fix error --- src/Api.ts | 2 +- src/app.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Api.ts b/src/Api.ts index f95f920..59093ad 100644 --- a/src/Api.ts +++ b/src/Api.ts @@ -6,7 +6,7 @@ export class Api { private readonly axiosInstance; constructor(private readonly apiServer: string) { this.axiosInstance = axios.create({ - baseURL: process.env.NODE_ENV === 'development' ? '' : this.apiServer, + baseURL: this.apiServer, }); } diff --git a/src/app.ts b/src/app.ts index 2bdc61d..607cf5c 100644 --- a/src/app.ts +++ b/src/app.ts @@ -66,7 +66,7 @@ class App { sha: headCommit, }, repository: { - url: repoUrl, + url: repoUrl.replace(/.git$/i, ''), }, }); Log.info('Data successfully sent');