@splunk/rum-cli
Version:
Tools for handling symbol and mapping files for symbolication
204 lines (203 loc) • 8.63 kB
JavaScript
;
/*
* 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);
}
}