diff --git a/.gitignore b/.gitignore index 76add87..1ef9d19 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ node_modules -dist \ No newline at end of file +dist +.env +.DS_Store \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index 47aee52..dc6aae0 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,27 +5,17 @@ "version": "0.2.0", "configurations": [ { - "type": "pwa-node", + "name": "Launch via NPM", "request": "launch", - "name": "Launch Program", + "runtimeArgs": [ + "run-script", + "start" + ], + "runtimeExecutable": "npm", "skipFiles": [ "/**" ], - "program": "${workspaceFolder}/bin/index.ts", - "preLaunchTask": "tsc: build - tsconfig.json", - "args": [ - "-database", - "fred", - "-application", - "my-ts-crasher", - "-version", - "1.0.0", - "-directory", - "./dist", - ], - "outFiles": [ - "${workspaceFolder}/dist/**/*.js" - ] - } + "type": "pwa-node" + }, ] } \ No newline at end of file diff --git a/README.md b/README.md index e4250ec..5290f83 100644 --- a/README.md +++ b/README.md @@ -16,30 +16,35 @@ bobby@BugSplat % ~ % symbol-upload -h Usage - -h, --help Print this usage guide. - -b, --database string Your BugSplat database name. - -a, --application string The name of your application. The value of application must match the - value used to post crash reports. - -v, --version string Your application's version. The value of version must match the value - used to post crash reports. - -u, --user string (optional) The email address used to log into your BugSplat account. If provided - --password must also be provided. This value can also be provided via the - SYMBOL_UPLOAD_USER environment variable. - -p, --password string (optional) The password for your BugSplat account. If provided --user must also be - provided. This value can also be provided via the SYMBOL_UPLOAD_PASSWORD - environment variable. - -i, --clientId string (optional) An OAuth2 Client Credentials Client ID for the specified database. If - provided --clientSecret must also be provided. This value can also be - provided via the SYMBOL_UPLOAD_CLIENT_ID environment variable. - -s, --clientSecret string (optional) An OAuth2 Client Credentials Client Secret for the specified database. If - provided --clientId must also be provided. This value can also be - provided via the SYMBOL_UPLOAD_CLIENT_SECRET environment variable. - -r, --remove Removes symbols for a specified database, application, and version. If - this option is provided no other actions are taken. - -f, --files string (optional) Glob pattern that specifies a set of files to upload. Defaults to - '*.js.map' - -d, --directory string (optional) Path of the base directory used to search for symbol files. This value - will be combined with the --files glob. Defaults to '.' + -h, --help Print this usage guide. + -b, --database string Your BugSplat database name. The value of database must match the value used + to post crash reports. This value can also be provided via the + BUGSPLAT_DATABASE environment variable. + -a, --application string The name of your application. The value of application must match the value + used to post crash reports. If not provided symbol-upload will attempt to use + the value of the name field in package.json if it exists in the current + working directory. + -v, --version string Your application's version. The value of version must match the value used to + post crash reports. If not provided symbol-upload will attempt to use the + value of the version field in package.json if it exists in the current + working directory. + -u, --user string (optional) The email address used to log into your BugSplat account. If provided + --password must also be provided. This value can also be provided via the + SYMBOL_UPLOAD_USER environment variable. + -p, --password string (optional) The password for your BugSplat account. If provided --user must also be + provided. This value can also be provided via the SYMBOL_UPLOAD_PASSWORD + environment variable. + -i, --clientId string (optional) An OAuth2 Client Credentials Client ID for the specified database. If + provided --clientSecret must also be provided. This value can also be + provided via the SYMBOL_UPLOAD_CLIENT_ID environment variable. + -s, --clientSecret string (optional) An OAuth2 Client Credentials Client Secret for the specified database. If + provided --clientId must also be provided. This value can also be provided + via the SYMBOL_UPLOAD_CLIENT_SECRET environment variable. + -r, --remove Removes symbols for a specified database, application, and version. If this + option is provided no other actions are taken. + -f, --files string (optional) Glob pattern that specifies a set of files to upload. Defaults to '*.js.map' + -d, --directory string (optional) Path of the base directory used to search for symbol files. This value will + be combined with the --files glob. Defaults to '.' The -u and -p arguments are not required if you set the environment variables SYMBOL_UPLOAD_USER and SYMBOL_UPLOAD_PASSWORD, or provide a clientId and @@ -61,7 +66,7 @@ Links 💻 https://github.com/BugSplat-Git/symbol-upload - 💌 support@bugsplat.com + 💌 support@bugsplat.com ``` 3. Run symbol-upload specifying a [glob](https://www.npmjs.com/package/glob#glob-primer) pattern for `-f` and a path with forward slashes for `-d` diff --git a/bin/command-line-definitions.ts b/bin/command-line-definitions.ts index 11f5c08..ba4c723 100644 --- a/bin/command-line-definitions.ts +++ b/bin/command-line-definitions.ts @@ -15,21 +15,21 @@ export const argDefinitions: Array = [ alias: 'b', type: String, typeLabel: '{underline string}', - description: 'Your BugSplat database name.' + description: 'Your BugSplat database name. The value of database must match the value used to post crash reports. This value can also be provided via the BUGSPLAT_DATABASE environment variable.' }, { name: 'application', alias: 'a', type: String, typeLabel: '{underline string}', - description: 'The name of your application. The value of application must match the value used to post crash reports.', + description: 'The name of your application. The value of application must match the value used to post crash reports. If not provided symbol-upload will attempt to use the value of the name field in package.json if it exists in the current working directory.', }, { name: 'version', alias: 'v', type: String, typeLabel: '{underline string}', - description: 'Your application\'s version. The value of version must match the value used to post crash reports.', + description: 'Your application\'s version. The value of version must match the value used to post crash reports. If not provided symbol-upload will attempt to use the value of the version field in package.json if it exists in the current working directory.', }, { name: 'user', diff --git a/bin/index.ts b/bin/index.ts index 95adb4d..31e40b6 100644 --- a/bin/index.ts +++ b/bin/index.ts @@ -1,59 +1,60 @@ #! /usr/bin/env node import { ApiClient, BugSplatApiClient, OAuthClientCredentialsClient, SymbolsApiClient } from '@bugsplat/js-api-client'; -import commandLineArgs from 'command-line-args'; +import commandLineArgs, { CommandLineOptions } from 'command-line-args'; import commandLineUsage from 'command-line-usage'; import fs from 'fs'; +import { readFile, stat } from 'fs/promises'; import glob from 'glob-promise'; import { basename } from 'path'; -import { argDefinitions, usageDefinitions } from './command-line-definitions'; +import { argDefinitions, CommandLineDefinition, usageDefinitions } from './command-line-definitions'; -let { - help, - database, - application, - version, - user, - password, - clientId, - clientSecret, - remove, - files, - directory -} = commandLineArgs(argDefinitions); - -if (help) { - logHelpAndExit(); -} - -if (!database) { - logMissingArgAndExit('database'); -} - -if (!application) { - logMissingArgAndExit('application'); -} - -if (!version) { - logMissingArgAndExit('version'); -} - -user = user ?? process.env.SYMBOL_UPLOAD_USER; -password = password ?? process.env.SYMBOL_UPLOAD_PASSWORD; -clientId = clientId ?? process.env.SYMBOL_UPLOAD_CLIENT_ID; -clientSecret = clientSecret ?? process.env.SYMBOL_UPLOAD_CLIENT_SECRET; - -if ( - !validAuthenticationArguments({ +(async () => { + let { + help, + database, + application, + version, user, password, clientId, - clientSecret - }) -) { - logMissingAuthAndExit(); -} + clientSecret, + remove, + files, + directory + } = await getCommandLineOptions(argDefinitions); + + if (help) { + logHelpAndExit(); + } -(async () => { + database = database ?? process.env.BUGSPLAT_DATABASE; + user = user ?? process.env.SYMBOL_UPLOAD_USER; + password = password ?? process.env.SYMBOL_UPLOAD_PASSWORD; + clientId = clientId ?? process.env.SYMBOL_UPLOAD_CLIENT_ID; + clientSecret = clientSecret ?? process.env.SYMBOL_UPLOAD_CLIENT_SECRET; + + if (!database) { + logMissingArgAndExit('database'); + } + + if (!application) { + logMissingArgAndExit('application'); + } + + if (!version) { + logMissingArgAndExit('version'); + } + + if ( + !validAuthenticationArguments({ + user, + password, + clientId, + clientSecret + }) + ) { + logMissingAuthAndExit(); + } console.log('About to authenticate...') @@ -99,17 +100,18 @@ if ( console.log(`Found files:\n ${paths}`); console.log(`About to upload symbols for ${database}-${application}-${version}...`); - const files = paths.map(path => { - const stat = fs.statSync(path); - const size = stat.size; - const name = basename(path); - const file = fs.createReadStream(path); - return { - name, - size, - file - }; - }); + const files = await Promise.all( + paths.map(async (path) => { + const size = (await stat(path)).size; + const name = basename(path); + const file = fs.createReadStream(path); + return { + name, + size, + file + }; + }) + ); await symbolsApiClient.postSymbols( database, @@ -142,6 +144,39 @@ async function createBugSplatClient({ return client; } +async function fileExists(path: string): Promise { + try { + return !!(await stat(path)); + } catch { + return false; + } +} + +async function getCommandLineOptions(argDefinitions: Array): Promise { + const options = commandLineArgs(argDefinitions); + let { application, version } = options; + let packageJson; + + if (!application || !version) { + const packageJsonPath = './package.json'; + packageJson = await fileExists(packageJsonPath) ? JSON.parse((await readFile(packageJsonPath)).toString()) : null; + } + + if (!application && packageJson) { + application = packageJson.name; + } + + if (!version && packageJson) { + version = packageJson.version; + } + + return { + ...options, + application, + version + } +} + function logHelpAndExit() { const help = commandLineUsage(usageDefinitions); console.log(help); diff --git a/package-lock.json b/package-lock.json index 10716c8..025f2ca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,6 +26,7 @@ "@types/command-line-usage": "^5.0.2", "@types/glob": "^7.1.3", "@types/node": "^14.14.37", + "dotenv": "^10.0.0", "ts-node": "^10.2.1", "typescript": "^4.3.2" } @@ -326,6 +327,15 @@ "node": ">=0.3.1" } }, + "node_modules/dotenv": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", + "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -913,6 +923,12 @@ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true }, + "dotenv": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", + "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", + "dev": true + }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", diff --git a/package.json b/package.json index 150664f..7c2a8da 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "dist" ], "scripts": { - "start": "ts-node ./bin/index.ts -b fred -a test -v 1.0 -d ./dist", + "start": "ts-node -r dotenv/config ./bin/index.ts -d ./dist", "help": "ts-node ./bin/index.ts -h", "build": "rm -rf ./dist && tsc", "release": "npm run build && npm publish --access public" @@ -38,6 +38,7 @@ "@types/command-line-usage": "^5.0.2", "@types/glob": "^7.1.3", "@types/node": "^14.14.37", + "dotenv": "^10.0.0", "ts-node": "^10.2.1", "typescript": "^4.3.2" },