diff --git a/e2e/plugin-lighthouse-e2e/tests/collect.e2e.test.ts b/e2e/plugin-lighthouse-e2e/tests/collect.e2e.test.ts index cdd509cc7..3f174bbc5 100644 --- a/e2e/plugin-lighthouse-e2e/tests/collect.e2e.test.ts +++ b/e2e/plugin-lighthouse-e2e/tests/collect.e2e.test.ts @@ -30,7 +30,8 @@ describe('collect report with lighthouse-plugin NPM package', () => { const { code, stdout } = await executeProcess({ command: 'npx', - args: ['@code-pushup/cli', 'collect', '--no-progress'], + // verbose exposes audits with perfect scores that are hidden in the default stdout + args: ['@code-pushup/cli', 'collect', '--no-progress', '--verbose'], cwd, }); diff --git a/packages/cli/README.md b/packages/cli/README.md index 42dca2a5b..d68c65d75 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -118,7 +118,26 @@ _If you're looking for programmatic usage, then refer to the underlying [@code-p ## Portal integration -If you have access to the Code PushUp portal, provide credentials in order to upload reports. +If you have access to the Code PushUp portal, you can enable report uploads by installing the `@code-pushup/portal-client` package. + +
+Installation command for npm, yarn and pnpm + +```sh +npm install --save-dev @code-pushup/portal-client +``` + +```sh +yarn add --dev @code-pushup/portal-client +``` + +```sh +pnpm add --save-dev @code-pushup/portal-client +``` + +
+ +Once the package is installed, update your configuration file to include your portal credentials: ```ts const config: CoreConfig = { diff --git a/packages/cli/src/lib/autorun/autorun-command.ts b/packages/cli/src/lib/autorun/autorun-command.ts index 1e0572a3e..ecdc16d89 100644 --- a/packages/cli/src/lib/autorun/autorun-command.ts +++ b/packages/cli/src/lib/autorun/autorun-command.ts @@ -46,8 +46,10 @@ export function yargsAutorunCommandObject() { } if (options.upload) { - const { url } = await upload(options); - uploadSuccessfulLog(url); + const report = await upload(options); + if (report?.url) { + uploadSuccessfulLog(report.url); + } } else { ui().logger.warning('Upload skipped because configuration is not set.'); renderIntegratePortalHint(); diff --git a/packages/cli/src/lib/upload/upload-command.ts b/packages/cli/src/lib/upload/upload-command.ts index 89546f686..aeba2a609 100644 --- a/packages/cli/src/lib/upload/upload-command.ts +++ b/packages/cli/src/lib/upload/upload-command.ts @@ -22,8 +22,10 @@ export function yargsUploadCommandObject() { renderIntegratePortalHint(); throw new Error('Upload configuration not set'); } - const { url } = await upload(options); - uploadSuccessfulLog(url); + const report = await upload(options); + if (report?.url) { + uploadSuccessfulLog(report.url); + } }, } satisfies CommandModule; } diff --git a/packages/core/package.json b/packages/core/package.json index 5ca51b4fa..b13d39e97 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -6,7 +6,14 @@ "dependencies": { "@code-pushup/models": "0.51.0", "@code-pushup/utils": "0.51.0", - "@code-pushup/portal-client": "^0.9.0", "ansis": "^3.3.0" + }, + "peerDependencies": { + "@code-pushup/portal-client": "^0.9.0" + }, + "peerDependenciesMeta": { + "@code-pushup/portal-client": { + "optional": true + } } } diff --git a/packages/core/src/lib/compare.ts b/packages/core/src/lib/compare.ts index 85e1693f3..40e9a89a7 100644 --- a/packages/core/src/lib/compare.ts +++ b/packages/core/src/lib/compare.ts @@ -1,9 +1,5 @@ import { writeFile } from 'node:fs/promises'; import { join } from 'node:path'; -import { - PortalOperationError, - getPortalComparisonLink, -} from '@code-pushup/portal-client'; import { type Format, type PersistConfig, @@ -28,6 +24,7 @@ import { compareCategories, compareGroups, } from './implementation/compare-scorables'; +import { loadPortalClient } from './load-portal-client'; export async function compareReportFiles( inputPaths: Diff, @@ -119,6 +116,11 @@ async function fetchPortalComparisonLink( commits: NonNullable, ): Promise { const { server, apiKey, organization, project } = uploadConfig; + const portalClient = await loadPortalClient(); + if (!portalClient) { + return; + } + const { PortalOperationError, getPortalComparisonLink } = portalClient; try { return await getPortalComparisonLink({ server, diff --git a/packages/core/src/lib/load-portal-client.ts b/packages/core/src/lib/load-portal-client.ts new file mode 100644 index 000000000..2010b22f9 --- /dev/null +++ b/packages/core/src/lib/load-portal-client.ts @@ -0,0 +1,14 @@ +import { ui } from '@code-pushup/utils'; + +export async function loadPortalClient(): Promise< + typeof import('@code-pushup/portal-client') | null +> { + try { + return await import('@code-pushup/portal-client'); + } catch { + ui().logger.error( + 'Optional peer dependency @code-pushup/portal-client is not available. Make sure it is installed to enable upload functionality.', + ); + return null; + } +} diff --git a/packages/core/src/lib/upload.ts b/packages/core/src/lib/upload.ts index a93a722a1..157e4c228 100644 --- a/packages/core/src/lib/upload.ts +++ b/packages/core/src/lib/upload.ts @@ -1,10 +1,8 @@ -import { - type SaveReportMutationVariables, - uploadToPortal, -} from '@code-pushup/portal-client'; +import type { SaveReportMutationVariables } from '@code-pushup/portal-client'; import type { PersistConfig, Report, UploadConfig } from '@code-pushup/models'; import { loadReport } from '@code-pushup/utils'; import { reportToGQL } from './implementation/report-to-gql'; +import { loadPortalClient } from './load-portal-client'; import type { GlobalOptions } from './types'; export type UploadOptions = { upload?: UploadConfig } & { @@ -16,13 +14,15 @@ export type UploadOptions = { upload?: UploadConfig } & { * @param options * @param uploadFn */ -export async function upload( - options: UploadOptions, - uploadFn: typeof uploadToPortal = uploadToPortal, -) { +export async function upload(options: UploadOptions) { if (options.upload == null) { throw new Error('Upload configuration is not set.'); } + const portalClient = await loadPortalClient(); + if (!portalClient) { + return; + } + const { uploadToPortal } = portalClient; const { apiKey, server, organization, project, timeout } = options.upload; const report: Report = await loadReport({ ...options.persist, @@ -39,5 +39,5 @@ export async function upload( ...reportToGQL(report), }; - return uploadFn({ apiKey, server, data, timeout }); + return uploadToPortal({ apiKey, server, data, timeout }); }