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 });
}