Skip to content

Commit

Permalink
feat(cli): add history command (#273)
Browse files Browse the repository at this point in the history
  • Loading branch information
BioPhoton authored Apr 11, 2024
1 parent 94da246 commit 1860b07
Show file tree
Hide file tree
Showing 18 changed files with 452 additions and 54 deletions.
2 changes: 1 addition & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
/dist
/coverage
/.nx/cache
__snapshots__
__snapshots__
1 change: 1 addition & 0 deletions e2e/cli-e2e/tests/__snapshots__/help.e2e.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Commands:
code-pushup autorun Shortcut for running collect followed by upload
code-pushup collect Run Plugins and collect results
code-pushup upload Upload report results to the portal
code-pushup history Collect reports for commit history
code-pushup compare Compare 2 report files and create a diff file
code-pushup print-config Print config
Expand Down
19 changes: 19 additions & 0 deletions packages/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,25 @@ Run plugins, collect results and upload the report to the Code PushUp portal.

Refer to the [Common Command Options](#common-command-options) for the list of available options.

#### `history` command

Usage:
`code-pushup history`

Description:
Run plugins, collect results and upload the report to the Code PushUp portal for a specified number of commits.

Refer to the [Common Command Options](#common-command-options) for the list of available options.

| Option | Type | Default | Description |
| ------------------------ | --------- | ------- | ---------------------------------------------------------------- |
| **`--targetBranch`** | `string` | 'main' | Branch to crawl history. |
| **`--forceCleanStatus`** | `boolean` | `false` | If we reset the status to a clean git history forcefully or not. |
| **`--maxCount`** | `number` | 5 | Number of commits. |
| **`--skipUploads`** | `boolean` | `false` | Upload created reports |
| **`--from`** | `string` | n/a | Hash to start in history |
| **`--to`** | `string` | n/a | Hash to end in history |

### `compare` command

Usage:
Expand Down
3 changes: 2 additions & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@
"dependencies": {
"@code-pushup/models": "0.35.0",
"@code-pushup/core": "0.35.0",
"@code-pushup/utils": "0.35.0",
"yargs": "^17.7.2",
"chalk": "^5.3.0",
"@code-pushup/utils": "0.35.0"
"simple-git": "^3.20.0"
}
}
2 changes: 2 additions & 0 deletions packages/cli/src/lib/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { CommandModule } from 'yargs';
import { yargsAutorunCommandObject } from './autorun/autorun-command';
import { yargsCollectCommandObject } from './collect/collect-command';
import { yargsCompareCommandObject } from './compare/compare-command';
import { yargsHistoryCommandObject } from './history/history-command';
import { yargsConfigCommandObject } from './print-config/print-config-command';
import { yargsUploadCommandObject } from './upload/upload-command';

Expand All @@ -13,6 +14,7 @@ export const commands: CommandModule[] = [
yargsAutorunCommandObject(),
yargsCollectCommandObject(),
yargsUploadCommandObject(),
yargsHistoryCommandObject(),
yargsCompareCommandObject(),
yargsConfigCommandObject(),
];
60 changes: 60 additions & 0 deletions packages/cli/src/lib/history/history-command.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import chalk from 'chalk';
import { ArgumentsCamelCase, CommandModule } from 'yargs';
import { HistoryOptions, getHashes, history } from '@code-pushup/core';
import { getCurrentBranchOrTag, safeCheckout, ui } from '@code-pushup/utils';
import { CLI_NAME } from '../constants';
import { yargsOnlyPluginsOptionsDefinition } from '../implementation/only-plugins.options';
import { HistoryCliOptions } from './history.model';
import { yargsHistoryOptionsDefinition } from './history.options';

export function yargsHistoryCommandObject() {
const command = 'history';
return {
command,
describe: 'Collect reports for commit history',
builder: yargs => {
yargs.options({
...yargsHistoryOptionsDefinition(),
...yargsOnlyPluginsOptionsDefinition(),
});
yargs.group(
Object.keys(yargsHistoryOptionsDefinition()),
'History Options:',
);
return yargs;
},
handler: async <T>(args: ArgumentsCamelCase<T>) => {
ui().logger.info(chalk.bold(CLI_NAME));
ui().logger.info(chalk.gray(`Run ${command}`));

const currentBranch = await getCurrentBranchOrTag();
const {
targetBranch = currentBranch,
forceCleanStatus,
maxCount,
from,
to,
...restOptions
} = args as unknown as HistoryCliOptions & HistoryOptions;

// determine history to walk
const commits: string[] = await getHashes({ maxCount, from, to });
try {
// run history logic
const reports = await history(
{
...restOptions,
targetBranch,
forceCleanStatus,
},
commits,
);

ui().logger.log(`Reports: ${reports.length}`);
} finally {
// go back to initial branch
await safeCheckout(currentBranch);
}
},
} satisfies CommandModule;
}
88 changes: 88 additions & 0 deletions packages/cli/src/lib/history/history-command.unit.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { describe, expect, vi } from 'vitest';
import { type HistoryOptions, history } from '@code-pushup/core';
import { safeCheckout } from '@code-pushup/utils';
import { DEFAULT_CLI_CONFIGURATION } from '../../../mocks/constants';
import { yargsCli } from '../yargs-cli';
import { yargsHistoryCommandObject } from './history-command';

vi.mock('@code-pushup/core', async () => {
const {
MINIMAL_HISTORY_CONFIG_MOCK,
}: typeof import('@code-pushup/test-utils') = await vi.importActual(
'@code-pushup/test-utils',
);
const core: object = await vi.importActual('@code-pushup/core');
return {
...core,
history: vi
.fn()
.mockImplementation((options: HistoryOptions, commits: string[]) =>
commits.map(commit => `${commit}-report.json`),
),
readRcByPath: vi.fn().mockResolvedValue(MINIMAL_HISTORY_CONFIG_MOCK),
};
});

vi.mock('@code-pushup/utils', async () => {
const utils: object = await vi.importActual('@code-pushup/utils');
return {
...utils,
safeCheckout: vi.fn(),
getCurrentBranchOrTag: vi.fn().mockReturnValue('main'),
};
});

vi.mock('simple-git', async () => {
const actual = await vi.importActual('simple-git');
return {
...actual,
simpleGit: () => ({
log: ({ maxCount }: { maxCount: number } = { maxCount: 1 }) =>
Promise.resolve({
all: [
{ hash: 'commit-6' },
{ hash: 'commit-5' },
{ hash: 'commit-4' },
{ hash: 'commit-3' },
{ hash: 'commit-2' },
{ hash: 'commit-1' },
].slice(-maxCount),
}),
}),
};
});

describe('history-command', () => {
it('should return the last 5 commits', async () => {
await yargsCli(['history', '--config=/test/code-pushup.config.ts'], {
...DEFAULT_CLI_CONFIGURATION,
commands: [yargsHistoryCommandObject()],
}).parseAsync();

expect(history).toHaveBeenCalledWith(
expect.objectContaining({
targetBranch: 'main',
}),
['commit-1', 'commit-2', 'commit-3', 'commit-4', 'commit-5'],
);

expect(safeCheckout).toHaveBeenCalledTimes(1);
});

it('should have 2 commits to crawl in history if maxCount is set to 2', async () => {
await yargsCli(
['history', '--config=/test/code-pushup.config.ts', '--maxCount=2'],
{
...DEFAULT_CLI_CONFIGURATION,
commands: [yargsHistoryCommandObject()],
},
).parseAsync();

expect(history).toHaveBeenCalledWith(expect.any(Object), [
'commit-1',
'commit-2',
]);

expect(safeCheckout).toHaveBeenCalledTimes(1);
});
});
7 changes: 7 additions & 0 deletions packages/cli/src/lib/history/history.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { type LogOptions } from 'simple-git';
import { HistoryOnlyOptions } from '@code-pushup/core';

export type HistoryCliOptions = {
targetBranch?: string;
} & Pick<LogOptions, 'maxCount' | 'from' | 'to'> &
HistoryOnlyOptions;
43 changes: 43 additions & 0 deletions packages/cli/src/lib/history/history.options.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Options } from 'yargs';
import { HistoryCliOptions } from './history.model';

export function yargsHistoryOptionsDefinition(): Record<
keyof HistoryCliOptions,
Options
> {
return {
targetBranch: {
describe: 'Branch to crawl history',
type: 'string',
default: 'main',
},
forceCleanStatus: {
describe:
'If we reset the status to a clean git history forcefully or not.',
type: 'boolean',
default: false,
},
skipUploads: {
describe: 'Upload created reports',
type: 'boolean',
default: false,
},
maxCount: {
// https://git-scm.com/docs/git-log#Documentation/git-log.txt---max-countltnumbergt
describe: 'Number of steps in history',
type: 'number',
// eslint-disable-next-line no-magic-numbers
default: 5,
},
from: {
// https://git-scm.com/docs/git-log#Documentation/git-log.txt-ltrevision-rangegt
describe: 'hash to first commit in history',
type: 'string',
},
to: {
// https://git-scm.com/docs/git-log#Documentation/git-log.txt-ltrevision-rangegt
describe: 'hash to last commit in history',
type: 'string',
},
};
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { describe, expect, vi } from 'vitest';
import { autoloadRc, readRcByPath } from '@code-pushup/core';
import { coreConfigMiddleware } from './core-config.middleware';
import { CoreConfigCliOptions } from './core-config.model';
import { GeneralCliOptions } from './global.model';
import { OnlyPluginsOptions } from './only-plugins.model';

vi.mock('@code-pushup/core', async () => {
const { CORE_CONFIG_MOCK }: typeof import('@code-pushup/test-utils') =
Expand All @@ -15,12 +18,16 @@ vi.mock('@code-pushup/core', async () => {

describe('coreConfigMiddleware', () => {
it('should attempt to load code-pushup.config.(ts|mjs|js) by default', async () => {
await coreConfigMiddleware({});
await coreConfigMiddleware(
{} as GeneralCliOptions & CoreConfigCliOptions & OnlyPluginsOptions,
);
expect(autoloadRc).toHaveBeenCalled();
});

it('should directly attempt to load passed config', async () => {
await coreConfigMiddleware({ config: 'cli/custom-config.mjs' });
await coreConfigMiddleware({
config: 'cli/custom-config.mjs',
} as GeneralCliOptions & CoreConfigCliOptions & OnlyPluginsOptions);
expect(autoloadRc).not.toHaveBeenCalled();
expect(readRcByPath).toHaveBeenCalledWith(
'cli/custom-config.mjs',
Expand All @@ -29,15 +36,17 @@ describe('coreConfigMiddleware', () => {
});

it('should forward --tsconfig option to config autoload', async () => {
await coreConfigMiddleware({ tsconfig: 'tsconfig.base.json' });
await coreConfigMiddleware({
tsconfig: 'tsconfig.base.json',
} as GeneralCliOptions & CoreConfigCliOptions & OnlyPluginsOptions);
expect(autoloadRc).toHaveBeenCalledWith('tsconfig.base.json');
});

it('should forward --tsconfig option to custom config load', async () => {
await coreConfigMiddleware({
config: 'apps/website/code-pushup.config.ts',
tsconfig: 'apps/website/tsconfig.json',
});
} as GeneralCliOptions & CoreConfigCliOptions & OnlyPluginsOptions);
expect(readRcByPath).toHaveBeenCalledWith(
'apps/website/code-pushup.config.ts',
'apps/website/tsconfig.json',
Expand Down
29 changes: 29 additions & 0 deletions packages/cli/src/lib/yargs-cli.integration.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { describe, expect, it } from 'vitest';
import { CoreConfig, Format } from '@code-pushup/models';
import { yargsHistoryOptionsDefinition } from './history/history.options';
import { CompareOptions } from './implementation/compare.model';
import { yargsCompareOptionsDefinition } from './implementation/compare.options';
import {
Expand Down Expand Up @@ -140,4 +141,32 @@ describe('yargsCli', () => {
}).parse(),
).toThrow('Missing required arguments: before, after');
});

it('should provide default arguments for history command', async () => {
const result = await yargsCli(['history'], {
options: { ...options, ...yargsHistoryOptionsDefinition() },
}).parseAsync();

expect(result).toEqual(
expect.objectContaining({
targetBranch: 'main',
maxCount: 5,
skipUploads: false,
}),
);
});

it('should parse history options and have 2 commits to crawl in history if maxCount is set to 2', async () => {
const result = await yargsCli(['history', '--maxCount=2'], {
options: { ...options, ...yargsHistoryOptionsDefinition() },
}).parseAsync();

expect(result).toEqual(
expect.objectContaining({
targetBranch: 'main',
maxCount: 2,
skipUploads: false,
}),
);
});
});
6 changes: 6 additions & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ export {
PersistError,
persistReport,
} from './lib/implementation/persist';
export {
history,
HistoryOptions,
HistoryOnlyOptions,
getHashes,
} from './lib/history';
export {
ConfigPathError,
autoloadRc,
Expand Down
Loading

0 comments on commit 1860b07

Please sign in to comment.