-
Notifications
You must be signed in to change notification settings - Fork 4
Initial sourcemaps: upload from target dir #1
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
Changes from all commits
ae7a9ff
ac4383c
05a1405
1367c5a
7ac2a07
ad6e49d
35ec481
298dc52
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| node_modules | ||
| test/fixtures |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| { | ||
| "extends": "eslint:recommended", | ||
| "env": { | ||
| "node": true, | ||
| "es6": true | ||
| }, | ||
| "parserOptions": { | ||
| "ecmaVersion": 8 | ||
| }, | ||
| "rules": { | ||
| "camelcase": ["error", {"properties": "never"}], | ||
| "quotes": ["error", "single", "avoid-escape"], | ||
| "no-underscore-dangle": "off", | ||
| "no-useless-escape": "off", | ||
| "complexity": ["error", { "max": 35 }], | ||
| "no-use-before-define": ["off", { "functions": false }], | ||
| "no-unused-vars": ["error", { "argsIgnorePattern": "^_" }], | ||
| "no-prototype-builtins": "off", | ||
| "no-var": "error", | ||
| "prefer-const": ["error", { "destructuring": "all" }] | ||
| }, | ||
| "globals": { | ||
| "output": true | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node | ||
| # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions | ||
|
|
||
| name: Node.js CI | ||
|
|
||
| on: | ||
| push: | ||
| branches: [ master ] | ||
| pull_request: | ||
| branches: [ master ] | ||
|
|
||
| jobs: | ||
| build: | ||
|
|
||
| runs-on: ubuntu-latest | ||
|
|
||
| strategy: | ||
| matrix: | ||
| node-version: [10.x, 12.x, 14.x] | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@v2 | ||
| - name: Use Node.js ${{ matrix.node-version }} | ||
| uses: actions/setup-node@v1 | ||
| with: | ||
| node-version: ${{ matrix.node-version }} | ||
| - run: npm install | ||
| - run: npm test |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| node_modules | ||
| package-lock.json | ||
| coverage | ||
| .nyc_output | ||
| .DS_Store | ||
| *.swp |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| --- | ||
| check-coverage: true | ||
| lines: 90 | ||
| branches: 80 | ||
| functions: 80 | ||
| statements: 90 | ||
| watermarks: | ||
| lines: [90, 95] | ||
| functions: [70, 90] | ||
| branches: [70, 90] | ||
| statements: [90, 95] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,64 @@ | ||
| # rollbar-cli | ||
| # rollbar-cli | ||
|
|
||
| The Rollbar CLI provides easy command line access to Rollbar's API features, | ||
| starting with source map uploads. | ||
|
|
||
| ## Usage and Reference | ||
| Currently the upload-sourcemaps command is supported. | ||
|
|
||
| ### upload-sourcemaps | ||
| Upload source maps recursively from a directory. | ||
|
|
||
| ``` | ||
| rollbar-cli upload-sourcemaps <path> [options] | ||
|
|
||
| upload sourcemaps | ||
|
|
||
| Options: | ||
| --version Show version number [boolean] | ||
| -v, --verbose Verbose status output [boolean] | ||
| -q, --quiet Silent status output [boolean] | ||
| --help Show help [boolean] | ||
| --access-token Access token for the Rollbar API [string] [required] | ||
| --url-prefix Base part of the stack trace URLs [string] [required] | ||
| --code-version Code version string must match value in the Rollbar item | ||
| [string] [required] | ||
| -D, --dry-run Scan and validate source maps without uploading [boolean] | ||
| ``` | ||
|
|
||
| Example: | ||
| ``` | ||
| rollbar-cli upload-sourcemaps ./dist -access-token 638d... --url-prefix 'http://example.com/' --code-version 123.456 | ||
| ``` | ||
|
|
||
|
|
||
| ## Release History & Changelog | ||
|
|
||
| See our [Releases](https://github.com/rollbar/rollbar-cli/releases) page for a list of all releases, including changes. | ||
|
|
||
| ## Help / Support | ||
|
|
||
| If you run into any issues, please email us at [support@rollbar.com](mailto:support@rollbar.com). | ||
|
|
||
| For bug reports, please [open an issue on GitHub](https://github.com/rollbar/rollbar-cli/issues/new). | ||
|
|
||
| ## Developing | ||
|
|
||
| To set up a development environment, you'll need Node.js and npm. | ||
|
|
||
| 1. Install dependencies | ||
| `npm install` | ||
|
|
||
| 2. Link the rollbar-cli command to the local repo | ||
| `npm link` | ||
|
|
||
| 3. Run the tests | ||
| `npm test` | ||
|
|
||
| ## Contributing | ||
|
|
||
| 1. [Fork it](https://github.com/rollbar/rollbar-cli). | ||
| 2. Create your feature branch (`git checkout -b my-new-feature`). | ||
| 3. Commit your changes (`git commit -am 'Added some feature'`). | ||
| 4. Push to the branch (`git push origin my-new-feature`). | ||
| 5. Create a new Pull Request. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| #!/usr/bin/env node | ||
|
|
||
| 'use strict'; | ||
|
|
||
| (require('../src/index'))(); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| { | ||
| "name": "rollbar-cli", | ||
| "version": "0.1.0", | ||
| "license": "MIT", | ||
| "bin": { | ||
| "rollbar-cli": "bin/rollbar" | ||
| }, | ||
| "scripts": { | ||
| "lint": "./node_modules/.bin/eslint . --ext .js", | ||
| "test": "nyc --reporter=html --reporter=text mocha './test/{,!(fixtures)/**/}*test.js'" | ||
| }, | ||
| "dependencies": { | ||
| "axios": "^0.19.2", | ||
| "chalk": "^4.1.0", | ||
| "form-data": "^3.0.0", | ||
| "glob": "^7.1.6", | ||
| "source-map": "^0.7.3", | ||
| "yargs": "^15.4.1" | ||
| }, | ||
| "devDependencies": { | ||
| "chai": "^4.2.0", | ||
| "eslint": "^6.8.0", | ||
| "mocha": "^7.2.0", | ||
| "nyc": "^15.1.0", | ||
| "sinon": "^9.0.3" | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| 'use strict'; | ||
|
|
||
| const util = require('util'); | ||
| const chalk = require('chalk'); | ||
|
|
||
| class Output { | ||
| constructor(options = {}) { | ||
| this.all = !!options.verbose; | ||
| this.enable = !options.quiet; | ||
| this.labelSize = options.labelSize || 6; | ||
| } | ||
|
|
||
| status() { | ||
| this.write(chalk.white(this.format(...arguments))); | ||
| } | ||
|
|
||
| verbose() { | ||
| if (this.all) { | ||
| this.write(chalk.grey(this.format(...arguments))); | ||
| } | ||
| } | ||
|
|
||
| warn() { | ||
| this.write(chalk.yellow(this.format(...arguments))); | ||
| } | ||
|
|
||
| error() { | ||
| this.write(chalk.red(this.format(...arguments))); | ||
| } | ||
|
|
||
| fail() { | ||
| this.write(chalk.red(this.format(...arguments))); | ||
| } | ||
|
|
||
| success() { | ||
| this.write(chalk.green(this.format(...arguments))); | ||
| } | ||
|
|
||
| format() { | ||
| const args = Array.from(arguments); | ||
| let paddedLabel = args[0].padEnd(this.labelSize, ' '); | ||
|
|
||
| if (args[0].trim().length) { | ||
| paddedLabel = `[${paddedLabel}] `; | ||
| } else { | ||
| paddedLabel = ` ${paddedLabel} `; | ||
| } | ||
|
|
||
| return paddedLabel + util.format(...args.slice(1)) + '\n'; | ||
| } | ||
|
|
||
| write(str) { | ||
| if (this.enable) { | ||
| process.stdout.write(str); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| module.exports = Output; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| 'use strict'; | ||
|
|
||
| const axios = require('axios'); | ||
| const FormData = require('form-data'); | ||
|
|
||
| class RollbarAPI { | ||
| constructor(accessToken) { | ||
| this.accessToken = accessToken; | ||
|
|
||
| this.axios = axios.create({ | ||
| baseURL: 'https://api.rollbar.com/api/1/', | ||
| headers: { 'X-Rollbar-Access-Token': accessToken }, | ||
| // Always resolve, regardless of status code. | ||
| // When we let axios reject, we end up with less specific error messages. | ||
| validateStatus: function (_status) { return true; }, | ||
| }); | ||
| } | ||
|
|
||
| async sourcemaps(request) { | ||
| output.verbose('', 'minified_url: ' + request.minified_url); | ||
|
|
||
| const form = this.convertRequestToForm(request); | ||
| const resp = await this.axios.post( | ||
| '/sourcemap', | ||
| form.getBuffer(), // use buffer to prevent unwanted string escaping. | ||
| { headers: { | ||
| // axios needs some help with headers for form data. | ||
| 'Content-Type': `multipart/form-data; boundary=${form.getBoundary()}`, | ||
| 'Content-Length': form.getLengthSync() | ||
| }} | ||
| ); | ||
|
|
||
| return this.processResponse(resp); | ||
| } | ||
|
|
||
| convertRequestToForm(request) { | ||
| const form = new FormData(); | ||
|
|
||
| form.append('version', request.version); | ||
| form.append('minified_url', request.minified_url); | ||
| form.append('source_map', Buffer.from(request.source_map), { filename: 'source_map' }); | ||
|
|
||
| if (request.sources) { | ||
| for (const filename of Object.keys(request.sources)) { | ||
| if (filename && request.sources[filename]) { | ||
| form.append(filename, Buffer.from(request.sources[filename]), { filename: filename }); | ||
| } | ||
| } | ||
| } | ||
| return form; | ||
| } | ||
|
|
||
| processResponse(resp) { | ||
| output.verbose('', 'response:', resp.data, resp.status, resp.statusText); | ||
| if (resp.status === 200) { | ||
| return null; | ||
| } | ||
|
|
||
| return resp.data; | ||
| } | ||
| } | ||
|
|
||
| module.exports = RollbarAPI; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| 'use strict'; | ||
|
|
||
| const yargs = require('yargs'); | ||
|
|
||
| function cli() { | ||
| yargs | ||
| .option('v', { | ||
| alias: 'verbose', | ||
| describe: 'Verbose status output', | ||
| requiresArg: false, | ||
| type: 'boolean', | ||
| demandOption: false | ||
| }) | ||
| .option('q', { | ||
| alias: 'quiet', | ||
| describe: 'Silent status output', | ||
| requiresArg: false, | ||
| type: 'boolean', | ||
| demandOption: false | ||
| }) | ||
| .command(require('./sourcemaps/command')) | ||
| .help() | ||
| .argv | ||
|
|
||
| return 0; | ||
| } | ||
|
|
||
| module.exports = cli; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| 'use strict'; | ||
|
|
||
| const Scanner = require('./scanner'); | ||
| const Uploader = require('./uploader'); | ||
| const Output = require('../common/output.js'); | ||
|
|
||
| exports.command = 'upload-sourcemaps <path> [options]' | ||
|
|
||
| exports.describe = 'upload sourcemaps' | ||
|
|
||
| exports.builder = function (yargs) { | ||
| return yargs | ||
| .option('access-token', { | ||
| describe: 'Access token for the Rollbar API', | ||
| requiresArg: true, | ||
| type: 'string', | ||
| demandOption: true | ||
| }) | ||
| .option('url-prefix', { | ||
| describe: 'Base part of the stack trace URLs', | ||
| requiresArg: true, | ||
| type: 'string', | ||
| demandOption: true | ||
| }) | ||
| .option('code-version', { | ||
| describe: 'Code version string must match value in the Rollbar item', | ||
| requiresArg: true, | ||
| type: 'string', | ||
| demandOption: true | ||
| }) | ||
| .option('D', { | ||
| alias: 'dry-run', | ||
| describe: 'Scan and validate source maps without uploading', | ||
| requiresArg: false, | ||
| type: 'boolean', | ||
| demandOption: false | ||
| }) | ||
| } | ||
|
|
||
| exports.handler = async function (argv) { | ||
| global.output = new Output({ | ||
| verbose: argv['verbose'], | ||
| quiet: argv['quiet'] | ||
| }); | ||
|
|
||
| const scanner = new Scanner({ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can this scanner adapt to dsym or other symbol files in future?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would assume not since the
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks. So looks like we will have to implement separate scanner for scanning dsym files. |
||
| targetPath: argv['path'], | ||
| sources: argv['sources'] | ||
| }); | ||
|
|
||
| await scanner.scan(); | ||
|
|
||
| const uploader = new Uploader({ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can this uploader adapt to dsym or other symbol files in future?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suppose it depends on how different Rollbar's API is for dsym, and how different the data structures are. It's not something I've evaluated.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Got it. I was just asking so that in near future if we end up adding dsym or proguard symbol upload functionality we shouldn't have to do majore rewrite of the CLI. |
||
| accessToken: argv['access-token'], | ||
| baseUrl: argv['url-prefix'], | ||
| codeVersion: argv['code-version'] | ||
| }) | ||
|
|
||
| uploader.mapFiles(scanner.files); | ||
|
|
||
| await uploader.upload(argv['dry-run']); | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the option of dry run.