UNPKG

@splunk/rum-cli

Version:

Tools for handling symbol and mapping files for symbolication

204 lines (203 loc) 8.63 kB
"use strict"; /* * Copyright Splunk Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.generateUrl = void 0; exports.validateDSYMsPath = validateDSYMsPath; exports.prepareUploadFiles = prepareUploadFiles; exports.getZippedDSYMs = getZippedDSYMs; exports.cleanupTemporaryZips = cleanupTemporaryZips; const os_1 = require("os"); const child_process_1 = require("child_process"); const constants_1 = require("../utils/constants"); const path_1 = require("path"); const fs_1 = require("fs"); const userFriendlyErrors_1 = require("../utils/userFriendlyErrors"); /** * Helper function to generate API URLs. */ const generateUrl = ({ apiPath, realm, domain = 'signalfx.com', }) => { return `${constants_1.BASE_URL_PREFIX}.${realm}.${domain}/${constants_1.API_VERSION_STRING}/${apiPath}`; }; exports.generateUrl = generateUrl; /** * Helper functions for locating and zipping dSYMs **/ function validateDSYMsPath(dsymsPath) { const absPath = (0, path_1.resolve)(dsymsPath); if (absPath.endsWith('/dSYMs') || absPath === 'dSYMs') { try { const stats = (0, fs_1.statSync)(absPath); if (!stats.isDirectory()) { throw new userFriendlyErrors_1.UserFriendlyError(null, `Invalid input: Expected a 'dSYMs/' directory but got a file.`); } } catch (err) { (0, userFriendlyErrors_1.throwAsUserFriendlyErrnoException)(err, { ENOENT: `Path not found: Ensure the provided directory exists before re-running.`, }); } return absPath; } if (absPath.endsWith('.dSYM.zip') || absPath.endsWith('.dSYMs.zip')) { try { const stats = (0, fs_1.statSync)(absPath); if (!stats.isFile()) { throw new userFriendlyErrors_1.UserFriendlyError(null, `Invalid input: Expected a '.dSYM.zip' or '.dSYMs.zip' file.`); } } catch (err) { (0, userFriendlyErrors_1.throwAsUserFriendlyErrnoException)(err, { ENOENT: `File not found: Ensure the provided file [${absPath}] exists before re-running.`, }); } return absPath; } if (absPath.endsWith('.dSYM')) { try { const stats = (0, fs_1.statSync)(absPath); if (!stats.isDirectory()) { throw new userFriendlyErrors_1.UserFriendlyError(null, `Invalid input: Expected a '.dSYM' directory but got a file.`); } } catch (err) { (0, userFriendlyErrors_1.throwAsUserFriendlyErrnoException)(err, { ENOENT: `Directory not found: Ensure the provided directory exists before re-running.`, }); } return absPath; } throw new userFriendlyErrors_1.UserFriendlyError(null, `Invalid input: Expected a path named 'dSYMs' or ending in '.dSYM', '.dSYMs.zip', or '.dSYM.zip'.`); } /** * Validate the input path and prepare zipped files. */ function prepareUploadFiles(dsymsPath, logger) { const absPath = validateDSYMsPath(dsymsPath); // Get the list of zipped dSYM files const { zipFiles, uploadPath } = getZippedDSYMs(absPath, logger); // Log files for dry-run mode if (zipFiles.length === 0) { logger.info(`No files found to upload for directory: ${dsymsPath}.`); throw new Error('No files to upload.'); } return { zipFiles, uploadPath }; } /** * Given a dSYMs path, process the input as per the specified format and return * the zipped files or copied files as necessary. **/ function getZippedDSYMs(dsymsPath, logger) { const absPath = validateDSYMsPath(dsymsPath); // Create a unique system temp directory for storing zip files const uploadPath = (0, fs_1.mkdtempSync)((0, path_1.join)((0, os_1.tmpdir)(), 'splunk_dSYMs_upload_')); if (absPath.endsWith('dSYMs')) { const { dSYMDirs, dSYMZipFiles } = scanDSYMsDirectory(absPath); const results = []; for (const dSYMDir of dSYMDirs) { logger.info(`Zipping dSYM directory ${dSYMDir}`); results.push(zipDSYMDirectory(absPath, dSYMDir, uploadPath)); } for (const zipFile of dSYMZipFiles) { const srcPath = (0, path_1.join)(absPath, zipFile); const destPath = (0, path_1.join)(uploadPath, zipFile); try { (0, fs_1.copyFileSync)(srcPath, destPath); } catch (err) { (0, userFriendlyErrors_1.throwAsUserFriendlyErrnoException)(err, { ENOENT: `Failed to copy ${srcPath} to ${destPath}. Please ensure the file exists and is not in use.`, EACCES: `Permission denied while copying ${srcPath}. Please check your access rights.`, }); } results.push(destPath); } return { zipFiles: results, uploadPath }; } else if (absPath.endsWith('.dSYM.zip') || absPath.endsWith('.dSYMs.zip')) { const destPath = (0, path_1.join)(uploadPath, (0, path_1.basename)(absPath)); try { (0, fs_1.copyFileSync)(absPath, destPath); } catch (err) { (0, userFriendlyErrors_1.throwAsUserFriendlyErrnoException)(err, { ENOENT: `Failed to copy ${absPath} to ${destPath}. Please ensure the file exists and is not in use.`, EACCES: `Permission denied while copying ${absPath}. Please check your access rights.`, }); } return { zipFiles: [destPath], uploadPath }; } else if (absPath.endsWith('.dSYM')) { const zipPath = zipDSYMDirectory((0, path_1.dirname)(absPath), (0, path_1.basename)(absPath), uploadPath); return { zipFiles: [zipPath], uploadPath }; } throw new userFriendlyErrors_1.UserFriendlyError(null, `Unexpected error with the provided input path.`); } /** * Scan the `dSYMs/` directory and return categorized lists of `.dSYM/` directories and `.dSYM.zip` files. */ function scanDSYMsDirectory(dsymsPath) { const files = (0, fs_1.readdirSync)(dsymsPath); const dSYMDirs = []; const dSYMZipFiles = []; for (const file of files) { const fullPath = (0, path_1.join)(dsymsPath, file); try { if (file.endsWith('.dSYM') && (0, fs_1.statSync)(fullPath).isDirectory()) { dSYMDirs.push(file); } else if (file.endsWith('.dSYM.zip') && (0, fs_1.statSync)(fullPath).isFile()) { dSYMZipFiles.push(file); } } catch (err) { (0, userFriendlyErrors_1.throwAsUserFriendlyErrnoException)(err, { ENOENT: `Error accessing file or directory at ${fullPath}. Please ensure it exists and is accessible.`, }); } } return { dSYMDirs, dSYMZipFiles }; } /** * Zip a single `dSYM/` directory into the provided `uploadPath` directory. * Returns the full path of the created `.zip` file. */ function zipDSYMDirectory(parentPath, dsymDirectory, uploadPath) { const sourcePath = (0, path_1.join)(parentPath, dsymDirectory); const zipPath = (0, path_1.join)(uploadPath, `${dsymDirectory}.zip`); try { (0, child_process_1.execSync)(`zip -r '${zipPath}' '${sourcePath}'`, { stdio: 'ignore' }); } catch (err) { throw new userFriendlyErrors_1.UserFriendlyError(err, `Failed to zip ${sourcePath}. Please ensure you have the necessary permissions and that the zip command is available.`); } return zipPath; } /** * Remove the temporary upload directory and all files inside it. */ function cleanupTemporaryZips(uploadPath) { if (!uploadPath.includes('splunk_dSYMs_upload_')) { console.warn(`Warning: refusing to delete '${uploadPath}' as it does not appear to be a temp dSYMs upload directory.`); return; } try { (0, fs_1.rmSync)(uploadPath, { recursive: true, force: true }); } catch (err) { console.warn(`Warning: Failed to remove temporary directory '${uploadPath}'.`, err); } }