Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ci): new package with code extracted from github-action #839

Merged
merged 18 commits into from
Oct 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
fe788e3
chore(ci): generate new package with @nx/js:library
matejchalk Oct 7, 2024
05391ff
build(ci): customize package configuration from Nx generator
matejchalk Oct 7, 2024
f0639e5
feat(ci): move in monorepo code from github-action
matejchalk Oct 7, 2024
7a5511b
feat(ci): move in code from github-action for issues, git diff and co…
matejchalk Oct 7, 2024
d599887
refactor(ci): refine type definitions
matejchalk Oct 10, 2024
e4a5b4a
feat(ci): move in code for posting comment, generalized to any provider
matejchalk Oct 10, 2024
9a9ed87
feat(ci): move in main run functions from github-action and adapt
matejchalk Oct 11, 2024
1a5bf2b
test(ci): add integration tests for main function
matejchalk Oct 14, 2024
fca2057
test(ci-e2e): basic e2e tests for ci push and pull request flows
matejchalk Oct 14, 2024
2d7548f
docs(ci): document package and exports
matejchalk Oct 14, 2024
2fb9bf1
test(ci-e2e): use new verdaccio setup and test directory structure
matejchalk Oct 15, 2024
2c979ff
test(ci,test-utils,ci-e2e,utils): reuse git utils
matejchalk Oct 15, 2024
6ebe056
refactor(ci): extract projectToFilename to utils
matejchalk Oct 15, 2024
e9c7f8f
refactor(ci): rename command functions to prevent confusion with cli …
matejchalk Oct 15, 2024
25df061
docs(ci): minor readme improvements
matejchalk Oct 15, 2024
8d9d2fe
test(ci): unit test comment helpers
matejchalk Oct 15, 2024
127c063
test(ci-e2e): fix setup by listing indirect dependencies as implicit
matejchalk Oct 15, 2024
f8b0346
test(ci): fix expected paths on Windows
matejchalk Oct 15, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions e2e/ci-e2e/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"extends": ["../../.eslintrc.json"],
"ignorePatterns": ["!**/*", "code-pushup.config*.ts"],
"overrides": [
{
"files": ["*.ts", "*.tsx"],
"parserOptions": {
"project": ["e2e/ci-e2e/tsconfig.*?.json"]
}
}
]
}
49 changes: 49 additions & 0 deletions e2e/ci-e2e/mocks/fixtures/code-pushup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
import type { CoreConfig } from '@code-pushup/models';
import { crawlFileSystem } from '@code-pushup/utils';

const config: CoreConfig = {
plugins: [
{
slug: 'ts-migration',
title: 'TypeScript migration',
icon: 'typescript',
audits: [
{
slug: 'ts-files',
title: 'Source files converted from JavaScript to TypeScript',
},
],
runner: async () => {
const paths = await crawlFileSystem({
directory: fileURLToPath(dirname(import.meta.url)),
pattern: /\.[jt]s$/,
});
const jsPaths = paths.filter(path => path.endsWith('.js'));
const tsPaths = paths.filter(path => path.endsWith('.ts'));
const jsFileCount = jsPaths.length;
const tsFileCount = tsPaths.length;
const ratio = tsFileCount / (jsFileCount + tsFileCount);
const percentage = Math.round(ratio * 100);
return [
{
slug: 'ts-files',
value: percentage,
score: ratio,
displayValue: `${percentage}% converted`,
matejchalk marked this conversation as resolved.
Show resolved Hide resolved
details: {
issues: jsPaths.map(file => ({
message: 'Use .ts file extension instead of .js',
severity: 'warning',
source: { file },
})),
},
},
];
},
},
],
};

export default config;
23 changes: 23 additions & 0 deletions e2e/ci-e2e/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "ci-e2e",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "e2e/ci-e2e/src",
"projectType": "application",
"targets": {
"lint": {
"executor": "@nx/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": ["e2e/ci-e2e/**/*.ts"]
}
},
"e2e": {
"executor": "@nx/vite:test",
"options": {
"configFile": "e2e/ci-e2e/vite.config.e2e.ts"
}
}
},
"implicitDependencies": ["models", "utils", "core", "cli", "ci"],
"tags": ["scope:tooling", "type:e2e"]
}
14 changes: 14 additions & 0 deletions e2e/ci-e2e/tests/__snapshots__/report-diff.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Code PushUp

🥳 Code PushUp report has **improved** – compared target commit `<commit-sha>` with source commit `<commit-sha>`.

<details>
<summary>👍 <strong>1</strong> audit improved</summary>

## 🛡️ Audits

| 🔌 Plugin | 🛡️ Audit | 📏 Previous value | 📏 Current value | 🔄 Value change |
| :------------------- | :--------------------------------------------------- | :---------------: | :-------------------: | :----------------------------------------------------------------------------: |
| TypeScript migration | Source files converted from JavaScript to TypeScript | 🟨 50% converted | 🟩 **100% converted** | ![↑ +100 %](https://img.shields.io/badge/%E2%86%91%20%2B100%E2%80%89%25-green) |

</details>
183 changes: 183 additions & 0 deletions e2e/ci-e2e/tests/ci.e2e.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
import {
copyFile,
mkdir,
readFile,
rename,
rm,
writeFile,
} from 'node:fs/promises';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import {
type DiffResult,
type FetchResult,
type SimpleGit,
simpleGit,
} from 'simple-git';
import {
type Comment,
type GitRefs,
type Options,
type ProviderAPIClient,
type RunResult,
runInCI,
} from '@code-pushup/ci';
import { initGitRepo } from '@code-pushup/test-utils';

describe('CI package', () => {
const fixturesDir = join(
fileURLToPath(dirname(import.meta.url)),
'..',
'mocks',
'fixtures',
);
const workDir = join(
process.cwd(),
'tmp',
'e2e',
'ci-e2e',
'__test__',
'ci-test-repo',
);
const outputDir = join(workDir, '.code-pushup');

const options = {
directory: workDir,
} satisfies Options;

let git: SimpleGit;

beforeEach(async () => {
await rm(workDir, { recursive: true, force: true });
await mkdir(workDir, { recursive: true });
await copyFile(
join(fixturesDir, 'code-pushup.config.ts'),
join(workDir, 'code-pushup.config.ts'),
);
await writeFile(join(workDir, 'index.js'), 'console.log("Hello, world!")');

git = await initGitRepo(simpleGit, { baseDir: workDir });

vi.spyOn(git, 'fetch').mockResolvedValue({} as FetchResult);
vi.spyOn(git, 'diffSummary').mockResolvedValue({
files: [{ file: 'index.ts', binary: false }],
} as DiffResult);
vi.spyOn(git, 'diff').mockResolvedValue('');

await git.add('index.js');
await git.add('code-pushup.config.ts');
await git.commit('Initial commit');
});

afterAll(async () => {
await rm(workDir, { recursive: true, force: true });
});

describe('push event', () => {
beforeEach(async () => {
await git.checkout('main');
});

it('should collect report', async () => {
await expect(
runInCI(
{ head: { ref: 'main', sha: await git.revparse('main') } },
{} as ProviderAPIClient,
options,
git,
),
).resolves.toEqual({
mode: 'standalone',
artifacts: {
report: {
rootDir: outputDir,
files: [
join(outputDir, 'report.json'),
join(outputDir, 'report.md'),
],
},
},
} satisfies RunResult);

const jsonPromise = readFile(join(outputDir, 'report.json'), 'utf8');
await expect(jsonPromise).resolves.toBeTruthy();
const report = JSON.parse(await jsonPromise) as Report;
expect(report).toEqual(
expect.objectContaining({
plugins: [
expect.objectContaining({
slug: 'ts-migration',
audits: [
expect.objectContaining({
score: 0.5,
displayValue: '50% converted',
}),
],
}),
],
}),
);
});
});

describe('pull request event', () => {
const comment: Comment = {
id: 42,
body: '... <!-- generated by @code-pushup/ci -->',
url: 'https://github.com/<owner>/<repo>/pull/1#issuecomment-42',
};
const api: ProviderAPIClient = {
maxCommentChars: 1_000_000,
createComment: () => Promise.resolve(comment),
updateComment: () => Promise.resolve(comment),
listComments: () => Promise.resolve([]),
};

let refs: GitRefs;

beforeEach(async () => {
await git.checkoutLocalBranch('feature-1');

await rename(join(workDir, 'index.js'), join(workDir, 'index.ts'));

await git.add('index.ts');
await git.commit('Convert JS file to TS');

refs = {
head: { ref: 'feature-1', sha: await git.revparse('feature-1') },
base: { ref: 'main', sha: await git.revparse('main') },
};
});

it('should compare reports', async () => {
await expect(runInCI(refs, api, options, git)).resolves.toEqual({
mode: 'standalone',
commentId: comment.id,
newIssues: [],
artifacts: {
report: {
rootDir: outputDir,
files: [
join(outputDir, 'report.json'),
join(outputDir, 'report.md'),
],
},
diff: {
rootDir: outputDir,
files: [
join(outputDir, 'report-diff.json'),
join(outputDir, 'report-diff.md'),
],
},
},
} satisfies RunResult);

const mdPromise = readFile(join(outputDir, 'report-diff.md'), 'utf8');
await expect(mdPromise).resolves.toBeTruthy();
const md = await mdPromise;
await expect(
md.replace(/[\da-f]{40}/g, '`<commit-sha>`'),
).toMatchFileSnapshot('__snapshots__/report-diff.md');
});
});
});
20 changes: 20 additions & 0 deletions e2e/ci-e2e/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"module": "ESNext",
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"types": ["vitest"]
},
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.test.json"
}
]
}
13 changes: 13 additions & 0 deletions e2e/ci-e2e/tsconfig.test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"types": ["vitest/globals", "vitest/importMeta", "vite/client", "node"]
},
"include": [
"vite.config.e2e.ts",
"tests/**/*.e2e.test.ts",
"tests/**/*.d.ts",
"mocks/**/*.ts"
]
}
22 changes: 22 additions & 0 deletions e2e/ci-e2e/vite.config.e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/// <reference types="vitest" />
import { defineConfig } from 'vite';
import { tsconfigPathAliases } from '../../tools/vitest-tsconfig-path-aliases';

export default defineConfig({
cacheDir: '../../node_modules/.vite/ci-e2e',
test: {
reporters: ['basic'],
testTimeout: 120_000,
globals: true,
alias: tsconfigPathAliases(),
pool: 'threads',
poolOptions: { threads: { singleThread: true } },
cache: {
dir: '../../node_modules/.vitest',
},
environment: 'node',
include: ['tests/**/*.e2e.test.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
globalSetup: ['../../global-setup.verdaccio.ts'],
setupFiles: ['../../testing/test-setup/src/lib/reset.mocks.ts'],
},
});
1 change: 0 additions & 1 deletion e2e/cli-e2e/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
"utils",
"core",
"cli",
"cli",
"plugin-eslint",
"plugin-coverage",
"plugin-js-packages",
BioPhoton marked this conversation as resolved.
Show resolved Hide resolved
Expand Down
12 changes: 9 additions & 3 deletions nx.json
Original file line number Diff line number Diff line change
Expand Up @@ -114,15 +114,21 @@
"./tools/src/debug/debug.plugin.ts",
{
"plugin": "./tools/src/npm/npm.plugin.ts",
"options": { "verbose": true }
"options": {
"verbose": true
}
},
{
"plugin": "./tools/src/publish/publish.plugin.ts",
"options": { "verbose": true }
"options": {
"verbose": true
}
},
{
"plugin": "./tools/src/verdaccio/verdaccio.plugin.ts",
"options": { "verbose": true }
"options": {
"verbose": true
}
}
]
}
Loading