Skip to content

feat: adding a source map upload CLI package #102

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

Merged
merged 10 commits into from
Mar 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion .github/workflows/publish-npm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
- name: Setup .npmrc file for NPM registry
uses: actions/setup-node@v3
with:
node-version: 19.x
node-version: 20.x
registry-url: "https://registry.npmjs.org"

- name: Install dependencies
Expand All @@ -35,6 +35,7 @@ jobs:
yarn install
yarn build
cd ../../..
yarn install && yarn build
yarn test

- name: Build production bundle
Expand Down
9 changes: 7 additions & 2 deletions .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@ on:

jobs:
run-tests:
name: Run tests
name: Testing with Node version (${{ matrix.node }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node: [18.x, 20.x, 22.x, 23.x]
steps:
- name: Checkout
uses: actions/checkout@v2
Expand All @@ -16,7 +20,7 @@ jobs:
- name: Setup NPM
uses: actions/setup-node@v3
with:
node-version: 19.x
node-version: ${{ matrix.node }}
registry-url: "https://registry.npmjs.org"

- name: Install dependencies
Expand All @@ -34,6 +38,7 @@ jobs:
yarn install
yarn build
cd ../../..
yarn install && yarn build
yarn test

- name: Build production bundle
Expand Down
91 changes: 91 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ export default defineConfig(({ mode }) => {
}),
],
};
});
```

### Configuration Options
Expand All @@ -134,3 +135,93 @@ The following options are available for the Faro JavaScript bundler plugins:
After initial configuration, the Faro JavaScript bundler plugins automatically uploads your source maps to Grafana Cloud when you build your application. You can verify that the source maps upload successfully by in the "Settings" -> "Source Maps" tab in the Frontend Observability plugin. From there you are able to see the source maps that you have uploaded.

After you have completed all the required steps, you have finished - the Faro Collector begins processing your source maps and associating them with your telemetry data. The portions of your stack traces with source maps uploaded to the Faro Collector are automatically de-obfuscated and displayed in the Frontend Observability plugin when viewing your error data.

## CLI for Sourcemap Uploads
Copy link
Contributor

@codecapitano codecapitano Mar 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's also add this to the cloud docs as well


In addition to the bundler plugins, this repository also provides a CLI tool for uploading source maps to the Faro source map API. This is useful if you want to separate the build process from the source map upload process, or if you want to upload source maps from a CI/CD pipeline.

The CLI uses cURL under the hood to make HTTP requests, which means cURL must be installed on your system. It also provides options for gzipping the payload to reduce upload sizes, which is especially useful for large source map files.

### Installation

To install the CLI with `npm`, run:

```bash
npm install --save-dev @grafana/faro-cli
```

To install the CLI with `yarn`, run:

```bash
yarn add --dev @grafana/faro-cli
```

### Usage with Bundler Plugins

When using with the Faro bundler plugins, you can set the `skipUpload` option to `true` in the plugin configuration to skip uploading source maps during the build process and instead use the CLI to upload them later.

#### Webpack Example

```javascript
// webpack.config.js
const FaroSourceMapUploaderPlugin = require('@grafana/faro-webpack-plugin');

module.exports = {
// other configs
plugins: [
// other plugins
new FaroSourceMapUploaderPlugin({
appName: "$your-app-name",
endpoint: "$your-faro-collector-url",
apiKey: "$your-api-key",
appId: "$your-app-id",
stackId: "$your-stack-id",
skipUpload: true, // Skip uploading during build
verbose: true,
}),
],
};
```

#### Rollup/Vite Example

```javascript
// rollup.config.js or vite.config.js
import faroUploader from '@grafana/faro-rollup-plugin';

export default defineConfig(({ mode }) => {
return {
// other configs
plugins: [
// other plugins
faroUploader({
appName: "$your-app-name",
endpoint: "$your-faro-collector-url",
apiKey: "$your-api-key",
appId: "$your-app-id",
stackId: "$your-stack-id",
skipUpload: true, // Skip uploading during build
verbose: true,
}),
],
};
});
```

Then, after the build, you can upload the source maps using the CLI:

```bash
npx faro-cli upload \
--endpoint "$your-faro-collector-url" \
--app-id "$your-app-id" \
--api-key "$your-api-key" \
--stack-id "$your-stack-id" \
--bundle-id env \
--app-name "$your-app-name" \
--output-path "./dist" \
--verbose
```

Note the use of `--bundle-id env` and `--app-name "$your-app-name"` to read the bundle ID from the environment variable set by the bundler plugin.

For more information about the CLI, see the [CLI README](packages/faro-cli/README.md).
10 changes: 9 additions & 1 deletion packages/faro-bundlers-shared/rollup.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,15 @@ import babel from "@rollup/plugin-babel";
import commonjs from "@rollup/plugin-commonjs";
import json from "@rollup/plugin-json";
import typescript from "@rollup/plugin-typescript";
import packageJson from "./package.json" assert { type: "json" };
import fs from 'node:fs';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';

const configPath = join(
dirname(fileURLToPath(import.meta.url)),
'./package.json'
);
const packageJson = JSON.parse(fs.readFileSync(configPath, 'utf8'));

const extensions = [".ts"];

Expand Down
17 changes: 17 additions & 0 deletions packages/faro-bundlers-shared/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import fs from "fs";
import { create } from "tar";
import fetch from "cross-fetch";
import { ansi256 } from "ansis";
import path from "path";

export interface FaroSourceMapUploaderPluginOptions {
endpoint: string;
Expand All @@ -16,6 +17,7 @@ export interface FaroSourceMapUploaderPluginOptions {
keepSourcemaps?: boolean;
gzipContents?: boolean;
verbose?: boolean;
skipUpload?: boolean;
}

interface UploadSourceMapOptions {
Expand Down Expand Up @@ -165,4 +167,19 @@ export const ROLLUP_PLUGIN_NAME = "rollup-plugin-faro-source-map-uploader";

export const THIRTY_MB_IN_BYTES = 30 * 1024 * 1024;

/**
* Exports the bundleId to an environment variable for use in the CLI
* @param bundleId The bundleId to export
* @param appName The name of the app
* @param verbose Whether to log the export
*/
export const exportBundleIdToEnv = (bundleId: string, appName: string, verbose?: boolean): void => {
const envVarName = `FARO_BUNDLE_ID_${appName.toUpperCase().replace(/[^A-Z0-9]/g, '_')}`;
process.env[envVarName] = bundleId;

if (verbose) {
consoleInfoOrange(`Exported bundleId ${bundleId} to environment variable ${envVarName}`);
}
};

crypto.randomUUID();
Loading