From 3a716df620e8ac80ee676d036c2efb7286eb4f3e Mon Sep 17 00:00:00 2001 From: Carlos McEvilly <12955+carlosmcevilly@users.noreply.github.com> Date: Fri, 10 Jan 2025 14:19:13 -0800 Subject: [PATCH] Feature/demrum 439 ios dsyms upload (#60) * DEMRUM-439: add initial iOSInputValidations * DEMRUM-439: add node_modules.local to .gitignore * DEMRUM-439: implement iOS dSYMs upload * DEMRUM-439: scoped name for env variable * DEMRUM-439: fix case for iOS * DEMRUM-439: fix case in program.addCommand(iOSCommand) * DEMRUM-439: fix lint errors * DEMRUM-439: remove unused validations * DEMRUM-439: remove unused fields * DEMRUM: 439: remove trailing comma * DEMRUM-439: update message and restore missing terminator * DEMRUM-439: make inputValidations shared * DEMRUM-439: improve messages * DEMRUM-439: update import and filename to reflect new shared filename of inputValidations file --- .gitignore | 1 + src/commands/android.ts | 2 +- src/commands/ios.ts | 85 +++++++++++++++++-- src/index.ts | 4 +- ...nputValidations.ts => inputValidations.ts} | 0 ...tions.test.ts => inputValidations.test.ts} | 2 +- 6 files changed, 84 insertions(+), 10 deletions(-) rename src/utils/{androidInputValidations.ts => inputValidations.ts} (100%) rename test/utils/{androidInputValidations.test.ts => inputValidations.test.ts} (98%) diff --git a/.gitignore b/.gitignore index 8f4d47a..532e399 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # Node modules node_modules/ +node_modules.local/ # Logs logs diff --git a/src/commands/android.ts b/src/commands/android.ts index 5334e0e..bcb07e6 100644 --- a/src/commands/android.ts +++ b/src/commands/android.ts @@ -22,7 +22,7 @@ import { isValidAppId, isValidVersionCode, isValidUUID -} from '../utils/androidInputValidations'; +} from '../utils/inputValidations'; import { UserFriendlyError } from '../utils/userFriendlyErrors'; import { createLogger, LogLevel } from '../utils/logger'; import axios from 'axios'; diff --git a/src/commands/ios.ts b/src/commands/ios.ts index 59aa393..a369bfa 100644 --- a/src/commands/ios.ts +++ b/src/commands/ios.ts @@ -15,13 +15,86 @@ */ import { Command } from 'commander'; +import { + isValidFile, + hasValidExtension, +} from '../utils/inputValidations'; +import { UserFriendlyError } from '../utils/userFriendlyErrors'; +import { createLogger, LogLevel } from '../utils/logger'; +import { uploadFile } from '../utils/httpUtils'; -export const iosCommand = new Command('ios'); +// Constants +const DEFAULT_REALM = 'us0'; +const DSYM_FIELD_NAME = 'dSYM'; +const API_BASE_URL = process.env.O11Y_API_BASE_URL || 'https://api.splunk.com'; +const API_VERSION_STRING = 'v1'; +const API_PATH = 'dsyms'; -iosCommand +export const iOSCommand = new Command('iOS'); + +const iOSUploadDescription = `This subcommand uploads the specified zipped dSYMs file. +`; + +interface UploadiOSOptions { + 'file': string, + 'debug'?: boolean +} + +const generateUrl = (): string => { + const realm = process.env.O11Y_REALM || DEFAULT_REALM; + return `${API_BASE_URL}/${realm}/${API_VERSION_STRING}/${API_PATH}`; +}; + +iOSCommand + .name('ios') + .description('Upload and list zipped iOS symbolication files (dSYMs)'); + +iOSCommand .command('upload') - .description('Upload an iOS dSYM file') - .requiredOption('--file ', 'Path to the dSYM file') - .action((options) => { - console.log(`Uploading iOS dSYM file from: ${options.file}`); + .showHelpAfterError(true) + .usage('--file ') + .description(iOSUploadDescription) + .summary('Upload a dSYMs .zip file to the symbolication service') + .requiredOption('--file ', 'Path to the dSYMs .zip file') + .option('--debug', 'Enable debug logs') + .action(async (options: UploadiOSOptions) => { + const logger = createLogger(options.debug ? LogLevel.DEBUG : LogLevel.INFO); + + try { + if (!isValidFile(options.file)) { + throw new UserFriendlyError(null, `Invalid dSYMs file path: ${options.file}.`); + } + + if (!hasValidExtension(options.file, '.zip')) { + throw new UserFriendlyError(null, `dSYMs file does not have .zip extension: ${options.file}.`); + } + + const fileData = { + filePath: options.file, + fieldName: DSYM_FIELD_NAME, + }; + + const url = generateUrl(); + + logger.info(`url: ${url}`); + + logger.info(`Preparing to upload dSYMs file: ${options.file}`); + + await uploadFile({ + url, + file: fileData, + parameters: {} + }); + + logger.info('\nUpload complete!'); + } catch (error) { + if (error instanceof Error) { + logger.error('Failed to upload the dSYMs file:', error.message); + throw error; + } else { + const errorMessage = `Unexpected error type: ${JSON.stringify(error)}`; + logger.error('Failed to upload the dSYMs file:', errorMessage); + throw new Error(errorMessage); + } + } }); diff --git a/src/index.ts b/src/index.ts index 0e9e8e9..a0d958c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -19,7 +19,7 @@ */ import { Command } from 'commander'; -import { iosCommand } from './commands/ios'; +import { iOSCommand } from './commands/ios'; import { androidCommand } from './commands/android'; import { sourcemapsCommand } from './commands/sourcemaps'; @@ -36,7 +36,7 @@ program .description(helpDescription) .usage('[ios|android|sourcemaps] [sub-command] [options]'); -program.addCommand(iosCommand); +program.addCommand(iOSCommand); program.addCommand(androidCommand); program.addCommand(sourcemapsCommand); diff --git a/src/utils/androidInputValidations.ts b/src/utils/inputValidations.ts similarity index 100% rename from src/utils/androidInputValidations.ts rename to src/utils/inputValidations.ts diff --git a/test/utils/androidInputValidations.test.ts b/test/utils/inputValidations.test.ts similarity index 98% rename from test/utils/androidInputValidations.test.ts rename to test/utils/inputValidations.test.ts index d2e282d..fb03791 100644 --- a/test/utils/androidInputValidations.test.ts +++ b/test/utils/inputValidations.test.ts @@ -15,7 +15,7 @@ */ import fs from 'fs'; -import * as utils from '../../src/utils/androidInputValidations'; +import * as utils from '../../src/utils/inputValidations'; describe('Utility functions', () => {