-
Notifications
You must be signed in to change notification settings - Fork 6
SSAST-10519 Add option to collect debug data #92
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
Merged
Merged
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
9ddf2ab
SSAST-10519 Add option to collect debug data
davdres 0e288f8
SSAST-10519 Address swiftlint issues
davdres cea8457
SSAST-10519 NSFilePresenter cannot throw
davdres 3d1343a
SSAST-10519 Handle arguments on call.
davdres 1684608
SSAST-10519 Capture debug data to xcarchive
davdres 237628a
SSAST-10519 Try MultiplexLogHandler
davdres 9d9d414
Initialize logging with MultiplexLogHandler
davdres 1001471
SSAST-10519 Consolidate log handler streams & execution context writes
davdres ce159a3
SSAST-10519 Revert log changes in PIF. Misc cleanup
davdres dbee23b
SSAST-10519 Make DebugData optional; init logging on startup
davdres File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,206 @@ | ||
| import Foundation | ||
| import ArgumentParser // To use ValidationError | ||
| import LogHandlers | ||
| import Logging | ||
|
|
||
| /// | ||
| /// This file contains the DebugData struct, which is responsible for capturing debug data during the execution of the program. | ||
| /// It includes methods for initializing the capture path and logging relevant information. The data is captured in a sub-directory | ||
| /// of the xcarchive and therefore will be included with the submission to the Veracode Platform. | ||
| /// | ||
| /// The struct is initialized with the xcarchive and a flag indicating whether debug data is to be captured. | ||
| /// The directory structure will be: | ||
| /// - xcarchive | ||
| /// - debug-data | ||
| /// - Gen-IR log output file. | ||
| /// - xcodebuild log which was input to Gen-IR | ||
| /// - PIF cache directory | ||
| /// - xcode-select ouput | ||
| /// - xcodebuild --version output | ||
| /// - swift --version output | ||
| /// - env | grep DEVELOPER_DIR output | ||
| /// - data.zip | ||
| /// | ||
| struct DebugData: Decodable { | ||
|
|
||
| let capturePath: URL | ||
| let logDataPath: URL | ||
|
|
||
| init (xcodeArchivePath: URL) throws { | ||
|
|
||
| // Setup the capture path to hold the debug data. This path is a sub-directory of the xcarchive. | ||
| capturePath = xcodeArchivePath.appendingPathComponent("debug-data", isDirectory: true) | ||
|
|
||
| // Make sure the directory to hold debug data exists and is empty | ||
| if !FileManager.default.directoryExists(at: capturePath) { | ||
| // It doesn't exist, so create it | ||
| try FileManager.default.createDirectory(at: capturePath, withIntermediateDirectories: true) | ||
| } | ||
|
|
||
| // Create a subdirectory for the logs and add a file log handler to write the log there. | ||
| let zipLogPath = capturePath.appendingPathComponent("log") | ||
| try FileManager.default.createDirectory(at: zipLogPath, withIntermediateDirectories: true) | ||
|
|
||
| logDataPath = zipLogPath.appendingPathComponent("genir-capture.log", isDirectory: false) | ||
| } | ||
|
|
||
| /// | ||
| /// Tell the user what information will be captured. | ||
| /// | ||
| public func displayCaptureInfo() { | ||
| let captureInfo = Logger.Message( | ||
| """ | ||
| \n | ||
| \u{001B}[1m The Gen-IR capture option is enabled.\u{001B}[0m | ||
| The following data will be added to the xcarchive and sent to Veracode: | ||
| - Gen-IR log output file | ||
| - xcodebuild log which was input to Gen-IR | ||
| - PIF cache directory and its contents | ||
| - The location of the developer directory (e.g. /Applications/Xcode.app/Contents/Developer) | ||
| This is obtained via the xcode-select -p command and from the value of the DEVELOPER_DIR environment variable. | ||
| No other environment variables are captured. | ||
| - The xcodebuild version | ||
| - The swift-version | ||
| \n | ||
| """) | ||
| GenIRLogger.logger.info(captureInfo) | ||
| GenIRLogger.logger.info("Debug data will be captured to: \(capturePath)") | ||
| } | ||
|
|
||
| /// | ||
| /// Capture the execution context: | ||
| /// This includes the xcodebuild log, the configured developer directory, the xcodebuild version, and the swift version. | ||
| /// | ||
| public func captureExecutionContext(logPath: URL) throws { | ||
|
|
||
| // Capture the xcodebuild log | ||
| try FileManager.default.copyItem(at: logPath, to: capturePath.appendingPathComponent("xcodebuild.log")) | ||
|
|
||
| // Capture the configured developer directory | ||
| let developerDir = try execShellCommand(command: "xcode-select", args: ["-p"]) | ||
|
|
||
| // Capture a possible override of the developer directory | ||
| let developerDirOverride = ProcessInfo.processInfo.environment["DEVELOPER_DIR"] ?? "Not set" | ||
|
|
||
| // Capture the xcodebuild version | ||
| let xcodeBuildVersion = try execShellCommand(command: "xcodebuild", args: ["-version"]) | ||
|
|
||
| // Capture the swift version | ||
| let swiftVersion = try execShellCommand(command: "swift", args: ["-version"]) | ||
|
|
||
| do { | ||
| let versionsUrl = capturePath.appendingPathComponent("versions.txt") | ||
| FileManager.default.createFile(atPath: versionsUrl.path, contents: nil) | ||
| let versionsFile = try FileHandle(forWritingTo: versionsUrl) | ||
| versionsFile.write(Data(""" | ||
| DEVELOPER_DIR: \(developerDir)\n | ||
| DEVELOPER_DIR_OVERRIDE: \(developerDirOverride)\n | ||
| XCODEBUILD_VERSION: \(xcodeBuildVersion)\n | ||
| SWIFT_VERSION: \(swiftVersion)\n | ||
| """.utf8)) | ||
| versionsFile.closeFile() | ||
| } catch { | ||
| GenIRLogger.logger.error("Debug data capture Error \(error) occurred creating the versions.txt file while capturing debug data.") | ||
| } | ||
| GenIRLogger.logger.info("Debug data capture execution context data captured.") | ||
| } | ||
|
|
||
| /// | ||
| /// Capture the PIF cache: | ||
| /// This is a copy of the PIF cache from the location specified in the xcarchive. | ||
| /// The location is determined by the PIFCache.pifCachePath(in:) method. | ||
| /// | ||
| public func capturePIFCache(pifLocation: URL) throws { | ||
|
|
||
| // Capture the PIF cache | ||
| let savedPif = capturePath.appendingPathComponent("pif-data") | ||
| do { | ||
| // Perform the copy operation and skip broken symlinks | ||
| try copyDirectorySkippingBrokenSymlinks(from: pifLocation, to: savedPif) | ||
| } catch { | ||
| GenIRLogger.logger.error("Debug data capture of PIF Cache error: \(error.localizedDescription)") | ||
| } | ||
| GenIRLogger.logger.info("Debug data capture PIF cache data captured.") | ||
| } | ||
|
|
||
| /// | ||
| /// Do any final data captures and log the completion message. | ||
| /// | ||
| public func captureComplete(xcarchive: URL) throws { | ||
| GenIRLogger.logger.info("Debug data capture complete.") | ||
| } | ||
|
|
||
| /// | ||
| /// Copy a directory and skip broken symlinks. | ||
| /// This is used to copy the PIF cache from the location based on the build cache path parsed from the xcode build log. | ||
| /// The location is determined by the PIFCache.pifCachePath(in:) method. | ||
| /// | ||
| private func copyDirectorySkippingBrokenSymlinks(from sourceURL: URL, to destinationURL: URL) throws { | ||
|
|
||
| let fileManager = FileManager.default | ||
| let contents = try fileManager.contentsOfDirectory(at: sourceURL, includingPropertiesForKeys: [.isSymbolicLinkKey], options: []) | ||
| let sourceBaseName = sourceURL.path.hasSuffix("/") ? sourceURL.path : sourceURL.path + "/" | ||
|
|
||
| for item in contents { | ||
| let resourceValues = try item.resourceValues(forKeys: [.isSymbolicLinkKey]) | ||
|
|
||
| if resourceValues.isSymbolicLink == true { | ||
| // Check if the symlink target exists | ||
| let targetPath = try fileManager.destinationOfSymbolicLink(atPath: item.path) | ||
| if !fileManager.fileExists(atPath: targetPath) { | ||
| GenIRLogger.logger.info("Skipping broken symlink while copying PIFCache: \(item.path)") | ||
| continue | ||
| } | ||
| } | ||
| // Find the relative path of the item | ||
| let relativePart = item.path.replacingOccurrences(of: sourceBaseName, with: "") | ||
|
|
||
| // Define destination path | ||
| let destinationItemURL = destinationURL.appendingPathComponent(relativePart) | ||
| let parentDestinationURL = destinationItemURL.deletingLastPathComponent() | ||
| do { | ||
| // Make sure the destination directory exists | ||
| try fileManager.createDirectory(at: parentDestinationURL, withIntermediateDirectories: true) | ||
| // Copy item | ||
| try fileManager.copyItem(at: item, to: destinationItemURL) | ||
| } catch { | ||
| GenIRLogger.logger.error("Error while copying a PIFCache item \(item) : \(error.localizedDescription)") | ||
| continue | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /// | ||
| /// Given a command string and it's arguments, invoke a shell to execute the command and return the command output. | ||
| /// | ||
| private func execShellCommand(command: String, args: [String]) throws -> String { | ||
| let result: Process.ReturnValue | ||
| do { | ||
| result = try Process.runShell(command, arguments: args, runInDirectory: FileManager.default.currentDirectoryPath.fileURL) | ||
| } catch { | ||
| GenIRLogger.logger.error( | ||
| """ | ||
| Debug data capture couldn't create process for command: \(command) with arguments: \(args.joined(separator: " ")). \ | ||
| Output will not be captured. | ||
| """ | ||
| ) | ||
| return "" | ||
| } | ||
|
|
||
| if result.code != 0 { | ||
| GenIRLogger.logger.error( | ||
| """ | ||
| Debug data capture command finished with non-zero exit code. Output will not be captured. | ||
| - code: \(result.code) | ||
| - command: \(command) \(args.joined(separator: " ")) | ||
| - stdout: \(String(describing: result.stdout)) | ||
| - stderr: \(String(describing: result.stderr)) | ||
| """ | ||
| ) | ||
|
|
||
| return "" | ||
| } | ||
|
|
||
| return result.stdout ?? "" | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.