diff --git a/.github/workflows/code-pushup-fork.yml b/.github/workflows/code-pushup-fork.yml index 27d9c04e0..7276391e3 100644 --- a/.github/workflows/code-pushup-fork.yml +++ b/.github/workflows/code-pushup-fork.yml @@ -1,4 +1,4 @@ -name: Code PushUp (fork) +name: Code PushUp - Standalone Mode (fork) # separated from code-pushup.yml for security reasons # => requires permissions to create PR comment @@ -20,19 +20,23 @@ permissions: jobs: code-pushup: runs-on: ubuntu-latest - name: Code PushUp + name: Run Code PushUp (fork) if: github.event.pull_request.head.repo.fork steps: - name: Checkout repository uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Set up Node.js uses: actions/setup-node@v4 with: node-version-file: .nvmrc cache: npm + - name: Set base and head for Nx affected commands + uses: nrwl/nx-set-shas@v4 - name: Install dependencies run: npm ci - name: Run Code PushUp action uses: code-pushup/github-action@v0 with: - bin: npx nx code-pushup -- + bin: npx nx code-pushup --nx-bail -- diff --git a/.github/workflows/code-pushup.yml b/.github/workflows/code-pushup.yml index aaa929329..21d7bce27 100644 --- a/.github/workflows/code-pushup.yml +++ b/.github/workflows/code-pushup.yml @@ -1,4 +1,4 @@ -name: Code PushUp +name: Code PushUp - Standalone Mode on: push: @@ -16,7 +16,7 @@ permissions: jobs: code-pushup: runs-on: ubuntu-latest - name: Code PushUp + name: Run Code PushUp # ignore PRs from forks, handled by code-pushup-fork.yml if: ${{ !github.event.pull_request.head.repo.fork }} env: @@ -27,14 +27,18 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Set up Node.js uses: actions/setup-node@v4 with: node-version-file: .nvmrc cache: npm + - name: Set base and head for Nx affected commands + uses: nrwl/nx-set-shas@v4 - name: Install dependencies run: npm ci - name: Run Code PushUp action uses: code-pushup/github-action@v0 with: - bin: npx nx code-pushup -- + bin: npx nx code-pushup --nx-bail -- diff --git a/code-pushup.config.ts b/code-pushup.config.ts index b9234bacd..4ea96774f 100644 --- a/code-pushup.config.ts +++ b/code-pushup.config.ts @@ -1,5 +1,4 @@ import 'dotenv/config'; -import { z } from 'zod'; import { coverageCoreConfigNx, eslintCoreConfigNx, @@ -11,25 +10,17 @@ import { import type { CoreConfig } from './packages/models/src/index.js'; import { mergeConfigs } from './packages/utils/src/index.js'; -// load upload configuration from environment -const envSchema = z.object({ - CP_SERVER: z.string().url(), - CP_API_KEY: z.string().min(1), - CP_ORGANIZATION: z.string().min(1), - CP_PROJECT: z.string().min(1), -}); -const { data: env } = await envSchema.safeParseAsync(process.env); +const project = process.env['NX_TASK_TARGET_PROJECT'] || 'cli-workspace'; const config: CoreConfig = { - ...(env && { + ...(process.env['CP_API_KEY'] && { upload: { - server: env.CP_SERVER, - apiKey: env.CP_API_KEY, - organization: env.CP_ORGANIZATION, - project: 'cli-workspace', + project, + organization: 'code-pushup', + server: 'https://api.staging.code-pushup.dev/graphql', + apiKey: process.env['CP_API_KEY'], }, }), - plugins: [], }; diff --git a/code-pushup.preset.ts b/code-pushup.preset.ts index 0b033bcd8..58ae48c4e 100644 --- a/code-pushup.preset.ts +++ b/code-pushup.preset.ts @@ -7,7 +7,7 @@ import coveragePlugin, { getNxCoveragePaths, } from './packages/plugin-coverage/src/index.js'; import eslintPlugin, { - eslintConfigFromNxProject, + eslintConfigFromAllNxProjects, } from './packages/plugin-eslint/src/index.js'; import type { ESLintTarget } from './packages/plugin-eslint/src/lib/config.js'; import { nxProjectsToConfig } from './packages/plugin-eslint/src/lib/nx/projects-to-config.js'; @@ -172,11 +172,12 @@ export const eslintCoreConfigNx = async ( projectName?: string, ): Promise => ({ plugins: [ - await eslintPlugin( - await (projectName - ? eslintConfigFromNxProject(projectName) - : eslintConfigFromPublishableNxProjects()), - ), + projectName + ? await eslintPlugin({ + eslintrc: `packages/${projectName}/eslint.config.js`, + patterns: ['.'], + }) + : await eslintPlugin(await eslintConfigFromAllNxProjects()), ], categories: eslintCategories, }); diff --git a/examples/plugins/code-pushup.config.ts b/examples/plugins/code-pushup.config.ts index 4d3d19abe..aaa5ce5b2 100644 --- a/examples/plugins/code-pushup.config.ts +++ b/examples/plugins/code-pushup.config.ts @@ -16,7 +16,7 @@ import { * `nx run-collect examples-plugins` * * - For all formats use `--persist.format=md,json` - * - For better debugging use `--verbose --no-progress` + * - For better debugging, use `--verbose --no-progress` * */ diff --git a/nx.json b/nx.json index a472f35ed..4acf25eda 100644 --- a/nx.json +++ b/nx.json @@ -1,5 +1,59 @@ { "$schema": "./node_modules/nx/schemas/nx-schema.json", + "namedInputs": { + "default": ["{projectRoot}/**/*", "sharedGlobals"], + "production": [ + "default", + "!{projectRoot}/README.md", + "!{projectRoot}/CHANGELOG.md", + "!{projectRoot}/perf/**/*", + "!{projectRoot}/tools/**/*", + "!{projectRoot}/zod2md.config.ts", + "!{projectRoot}/eslint.config.?(c)js", + "!{workspaceRoot}/**/.code-pushup/**/*", + "!{projectRoot}/code-pushup.config.?(*.).?(m)[jt]s", + "!{projectRoot}/@(test|mocks|mock)/**/*", + "!{projectRoot}/**/?(*.)test.[jt]s?(x)?(.snap)", + "!{projectRoot}/**/?(*.)mocks.[jt]s?(x)", + "!{projectRoot}/**/?(*.)mock.[jt]s?(x)", + "!{projectRoot}/vitest.@(unit|int|e2e).config.[jt]s", + "!{projectRoot}/dist/**/*", + "!{projectRoot}/tsconfig.@(test|tools).json", + "!{workspaceRoot}/**/(*.)coverage/**/*" + ], + "test-vitest-inputs": [ + "default", + { + "externalDependencies": ["vitest"] + } + ], + "lint-eslint-inputs": [ + "default", + { + "externalDependencies": ["eslint"] + } + ], + "typecheck-typescript-inputs": [ + "default", + { + "externalDependencies": ["typescript"] + } + ], + "code-pushup-inputs": [ + "default", + { + "env": "NODE_OPTIONS" + }, + { + "env": "TSX_TSCONFIG_PATH" + } + ], + "sharedGlobals": [ + { "runtime": "node -e \"console.log(require('os').platform())\"" }, + { "runtime": "node -v" }, + { "runtime": "npm -v" } + ] + }, "targetDefaults": { "build": { "dependsOn": ["^build"], @@ -15,34 +69,34 @@ } }, "unit-test": { - "cache": true, "outputs": [ "{workspaceRoot}/coverage/{projectName}/unit-tests/lcov.info" ], "executor": "@nx/vite:test", "options": { "configFile": "{projectRoot}/vitest.unit.config.ts", - "passWithNoTests": true, "coverage": { "enabled": true } } }, "int-test": { - "cache": true, "outputs": ["{workspaceRoot}/coverage/{projectName}/int-tests/lcov.info"], "executor": "@nx/vite:test", "options": { "configFile": "{projectRoot}/vitest.int.config.ts", - "passWithNoTests": true, "coverage": { "enabled": true } } }, - "e2e": { "dependsOn": ["^build"] }, + "e2e": { + "dependsOn": ["^build"], + "inputs": ["default"], + "cache": true + }, "lint": { - "inputs": ["default", "{workspaceRoot}/eslint.config.?(c)js"], + "inputs": ["lint-eslint-inputs"], "executor": "@nx/eslint:lint", "outputs": ["{options.outputFile}"], "cache": true, @@ -76,12 +130,170 @@ }, "@nx/vite:test": { "cache": true, - "inputs": ["default", "^production"], + "inputs": ["test-vitest-inputs"], "options": { "passWithNoTests": true, "watch": false } }, + "code-pushup": { + "cache": false, + "outputs": [ + "{projectRoot}/.code-pushup/report.md", + "{projectRoot}/.code-pushup/report.json" + ], + "executor": "nx:run-commands", + "options": { + "command": "node packages/cli/src/index.ts", + "args": [ + "--no-progress", + "--verbose", + "--config={projectRoot}/code-pushup.config.ts", + "--cache.read", + "--persist.outputDir={projectRoot}/.code-pushup", + "--upload.project=cli-{projectName}" + ], + "env": { + "NODE_OPTIONS": "--import tsx", + "TSX_TSCONFIG_PATH": "tsconfig.base.json" + } + } + }, + "code-pushup-coverage": { + "cache": true, + "inputs": ["code-pushup-inputs", "test-vitest-inputs"], + "outputs": ["{projectRoot}/.code-pushup/coverage/runner-output.json"], + "executor": "nx:run-commands", + "dependsOn": ["*-test"], + "options": { + "command": "node packages/cli/src/index.ts collect", + "args": [ + "--no-progress", + "--verbose", + "--config={projectRoot}/code-pushup.config.ts", + "--cache.write", + "--onlyPlugins=coverage", + "--persist.skipReports=true", + "--persist.outputDir={projectRoot}/.code-pushup" + ], + "env": { + "NODE_OPTIONS": "--import tsx", + "TSX_TSCONFIG_PATH": "tsconfig.base.json" + } + } + }, + "code-pushup-eslint": { + "cache": true, + "inputs": ["code-pushup-inputs", "lint-eslint-inputs"], + "outputs": ["{projectRoot}/.code-pushup/eslint/runner-output.json"], + "executor": "nx:run-commands", + "options": { + "command": "node packages/cli/src/index.ts collect", + "args": [ + "--no-progress", + "--verbose", + "--config={projectRoot}/code-pushup.config.ts", + "--cache.write", + "--onlyPlugins=eslint", + "--persist.skipReports", + "--persist.outputDir={projectRoot}/.code-pushup" + ], + "env": { + "NODE_OPTIONS": "--import tsx", + "TSX_TSCONFIG_PATH": "tsconfig.base.json" + } + } + }, + "code-pushup-js-packages": { + "cache": false, + "inputs": [ + { + "runtime": "date +%Y-%m-%d" + } + ], + "outputs": ["{projectRoot}/.code-pushup/js-packages/runner-output.json"], + "executor": "nx:run-commands", + "options": { + "command": "node packages/cli/src/index.ts collect", + "args": [ + "--no-progress", + "--verbose", + "--config={projectRoot}/code-pushup.config.ts", + "--onlyPlugins=js-packages", + "--persist.outputDir={projectRoot}/.code-pushup" + ], + "env": { + "NODE_OPTIONS": "--import tsx", + "TSX_TSCONFIG_PATH": "tsconfig.base.json" + } + } + }, + "code-pushup-lighthouse": { + "cache": true, + "inputs": ["code-pushup-inputs", "production", "^production"], + "outputs": ["{projectRoot}/.code-pushup/lighthouse/runner-output.json"], + "executor": "nx:run-commands", + "options": { + "command": "node packages/cli/src/index.ts collect", + "args": [ + "--no-progress", + "--verbose", + "--config={projectRoot}/code-pushup.config.ts", + "--cache.write", + "--onlyPlugins=lighthouse", + "--persist.skipReports", + "--persist.outputDir={projectRoot}/.code-pushup" + ], + "env": { + "NODE_OPTIONS": "--import tsx", + "TSX_TSCONFIG_PATH": "tsconfig.base.json" + } + } + }, + "code-pushup-jsdocs": { + "cache": true, + "inputs": ["code-pushup-inputs", "typecheck-typescript-inputs"], + "outputs": ["{projectRoot}/.code-pushup/jsdocs/runner-output.json"], + "executor": "nx:run-commands", + "options": { + "command": "node packages/cli/src/index.ts collect", + "args": [ + "--no-progress", + "--verbose", + "--config={projectRoot}/code-pushup.config.ts", + "--cache.write", + "--onlyPlugins=jsdocs", + "--persist.skipReports", + "--persist.outputDir={projectRoot}/.code-pushup" + ], + "env": { + "NODE_OPTIONS": "--import tsx", + "TSX_TSCONFIG_PATH": "tsconfig.base.json" + } + } + }, + "code-pushup-typescript": { + "cache": true, + "inputs": ["code-pushup-inputs", "typecheck-typescript-inputs"], + "outputs": ["{projectRoot}/.code-pushup/typescript/runner-output.json"], + "executor": "nx:run-commands", + "options": { + "command": "node packages/cli/src/index.ts collect", + "args": [ + "--no-progress", + "--verbose", + "--config={projectRoot}/code-pushup.config.ts", + "--cache.write", + "--onlyPlugins=typescript", + "--persist.skipReports", + "--persist.outputDir={projectRoot}/.code-pushup" + ], + "env": { + "NODE_OPTIONS": "--import tsx", + "TSX_TSCONFIG_PATH": "tsconfig.base.json" + } + } + }, "nx-release-publish": { "dependsOn": ["build"], "executor": "@nx/js:release-publish", @@ -91,33 +303,6 @@ } } }, - "namedInputs": { - "default": [ - "{projectRoot}/**/*", - "sharedGlobals", - "!{projectRoot}/dist/**/*" - ], - "production": [ - "default", - "!{projectRoot}/eslint.config.?(c)js", - "!{projectRoot}/**/?(*.)test.[jt]s?(x)?(.snap)", - "!{projectRoot}/tsconfig.test.json", - "!{projectRoot}/src/test-setup.[jt]s", - "!{projectRoot}/test-setup.[jt]s", - "!{projectRoot}/**/?(*.)mock.[jt]s?(x)", - "!{projectRoot}/vitest.@(unit|int|e2e).config.[jt]s", - "!{projectRoot}/@(test|mocks)/**/*", - "!{projectRoot}/perf/**/*", - "!{projectRoot}/tools/**/*", - "!{projectRoot}/code-pushup.config.?(m)[jt]s", - "!{projectRoot}/zod2md.config.ts" - ], - "sharedGlobals": [ - { "runtime": "node -e \"console.log(require('os').platform())\"" }, - { "runtime": "node -v" }, - { "runtime": "npm -v" } - ] - }, "workspaceLayout": { "appsDir": "examples", "libsDir": "packages" diff --git a/package-lock.json b/package-lock.json index a08a34481..53ab456d8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "@code-pushup/cli-source", + "name": "@code-pushup/cli-workspace", "version": "0.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "@code-pushup/cli-source", + "name": "@code-pushup/cli-workspace", "version": "0.0.0", "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index e1f97c332..ba7596460 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "@code-pushup/cli-source", + "name": "@code-pushup/cli-workspace", "version": "0.0.0", "license": "MIT", "description": "A CLI to run all kinds of code quality measurements to align your team with company goals", diff --git a/packages/models/transformers/package.json b/packages/models/transformers/package.json index 4d245947f..e5d33d01b 100644 --- a/packages/models/transformers/package.json +++ b/packages/models/transformers/package.json @@ -7,5 +7,8 @@ "dependencies": { "typescript": "^5.5.4", "ts-patch": "^3.3.0" - } + }, + "files": [ + "src" + ] } diff --git a/packages/models/transformers/project.json b/packages/models/transformers/project.json index fdd783682..b377da537 100644 --- a/packages/models/transformers/project.json +++ b/packages/models/transformers/project.json @@ -7,9 +7,13 @@ "build": { "executor": "@nx/js:tsc", "outputs": ["{options.outputPath}"], - "dependsOn": [{ "target": "pre-build" }], + "dependsOn": [ + { + "target": "pre-build" + } + ], "options": { - "outputPath": "dist/packages/models-transformers", + "outputPath": "packages/models/transformers/dist", "main": "packages/models/transformers/src/index.ts", "tsConfig": "packages/models/transformers/tsconfig.lib.json" } diff --git a/packages/models/transformers/tsconfig.lib.json b/packages/models/transformers/tsconfig.lib.json index fa583ace0..48174f134 100644 --- a/packages/models/transformers/tsconfig.lib.json +++ b/packages/models/transformers/tsconfig.lib.json @@ -1,7 +1,7 @@ { "extends": "./tsconfig.json", "compilerOptions": { - "outDir": "../../../dist/packages/models-transformers", + "outDir": "./dist", "rootDir": "./", "module": "commonjs", "types": ["node"] diff --git a/packages/models/tsconfig.lib.json b/packages/models/tsconfig.lib.json index b741c8920..2c92983e0 100644 --- a/packages/models/tsconfig.lib.json +++ b/packages/models/tsconfig.lib.json @@ -6,7 +6,7 @@ "types": ["node"], "plugins": [ { - "transform": "./dist/packages/models-transformers", + "transform": "./packages/models/transformers/dist", "afterDeclarations": true } ] diff --git a/packages/plugin-coverage/src/lib/nx/coverage-paths.ts b/packages/plugin-coverage/src/lib/nx/coverage-paths.ts index b5140bbbf..5f587e33b 100644 --- a/packages/plugin-coverage/src/lib/nx/coverage-paths.ts +++ b/packages/plugin-coverage/src/lib/nx/coverage-paths.ts @@ -8,9 +8,28 @@ import type { JestExecutorOptions } from '@nx/jest/src/executors/jest/schema'; import type { VitestExecutorOptions } from '@nx/vite/executors'; import { bold } from 'ansis'; import path from 'node:path'; -import { importModule, ui } from '@code-pushup/utils'; +import { importModule, stringifyError, ui } from '@code-pushup/utils'; import type { CoverageResult } from '../config.js'; +/** + * Resolves the cached project graph for the current Nx workspace. + * First tries to read cache and if not possible, go for the async creation. + */ +async function resolveCachedProjectGraph() { + const { readCachedProjectGraph, createProjectGraphAsync } = await import( + '@nx/devkit' + ); + try { + return readCachedProjectGraph(); + } catch (error) { + ui().logger.info( + `Could not read cached project graph, falling back to async creation. + ${stringifyError(error)}`, + ); + return await createProjectGraphAsync({ exitOnError: false }); + } +} + /** * @param targets nx targets to be used for measuring coverage, test by default * @returns An array of coverage result information for the coverage plugin. @@ -25,8 +44,7 @@ export async function getNxCoveragePaths( ); } - const { createProjectGraphAsync } = await import('@nx/devkit'); - const { nodes } = await createProjectGraphAsync({ exitOnError: false }); + const { nodes } = await resolveCachedProjectGraph(); const coverageResults = await Promise.all( targets.map(async target => { diff --git a/packages/plugin-eslint/src/lib/nx/find-all-projects.ts b/packages/plugin-eslint/src/lib/nx/find-all-projects.ts index 11ecfb284..10306f83e 100644 --- a/packages/plugin-eslint/src/lib/nx/find-all-projects.ts +++ b/packages/plugin-eslint/src/lib/nx/find-all-projects.ts @@ -1,7 +1,27 @@ +import { stringifyError, ui } from '@code-pushup/utils'; import type { ESLintTarget } from '../config.js'; import { filterProjectGraph } from './filter-project-graph.js'; import { nxProjectsToConfig } from './projects-to-config.js'; +/** + * Resolves the cached project graph for the current Nx workspace. + * First tries to read cache and if not possible, go for the async creation. + */ +async function resolveCachedProjectGraph() { + const { readCachedProjectGraph, createProjectGraphAsync } = await import( + '@nx/devkit' + ); + try { + return readCachedProjectGraph(); + } catch (error) { + ui().logger.info( + `Could not read cached project graph, falling back to async creation. + ${stringifyError(error)}`, + ); + return await createProjectGraphAsync({ exitOnError: false }); + } +} + /** * Finds all Nx projects in workspace and converts their lint configurations to Code PushUp ESLint plugin parameters. * @@ -31,8 +51,7 @@ import { nxProjectsToConfig } from './projects-to-config.js'; export async function eslintConfigFromAllNxProjects( options: { exclude?: string[] } = {}, ): Promise { - const { createProjectGraphAsync } = await import('@nx/devkit'); - const projectGraph = await createProjectGraphAsync({ exitOnError: false }); + const projectGraph = await resolveCachedProjectGraph(); const filteredProjectGraph = filterProjectGraph( projectGraph, options.exclude, diff --git a/project.json b/project.json index 1f19a828e..aa58578bd 100644 --- a/project.json +++ b/project.json @@ -2,14 +2,23 @@ "name": "cli-workspace", "$schema": "node_modules/nx/schemas/project-schema.json", "targets": { + "code-pushup-js-packages": {}, + "code-pushup-lighthouse": {}, + "code-pushup-coverage": {}, + "code-pushup-eslint": {}, + "code-pushup-jsdocs": {}, + "code-pushup-typescript": {}, "code-pushup": { + "dependsOn": ["code-pushup-*"], "executor": "nx:run-commands", "options": { - "command": "node packages/cli/src/index.ts --no-progress --verbose", - "env": { - "NODE_OPTIONS": "--import tsx", - "TSX_TSCONFIG_PATH": "tsconfig.base.json" - } + "args": [ + "--no-progress", + "--verbose", + "--cache.read", + "--persist.outputDir={projectRoot}/.code-pushup", + "--upload.project={projectName}" + ] } } }