[sentry] Add Sentry
This commit is contained in:
parent
2192719650
commit
0ab9a7af4e
|
@ -1,3 +1,5 @@
|
|||
GH_TOKEN=xxxx
|
||||
ENCRYPTION_SECRET=xxxx
|
||||
CERT_PASSWORD=xxxx
|
||||
SENTRY_AUTH_TOKEN=xxx
|
||||
SENTRY_DSN=xxxx
|
|
@ -18,6 +18,10 @@
|
|||
B915ED662063B18B004B6630 /* xcproj.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B915ED672063B18B004B6630 /* xcproj.framework */; };
|
||||
B91834BF207CBCE6008935B4 /* ProjectDescription.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B91834BE207CBCE6008935B4 /* ProjectDescription.framework */; };
|
||||
B91834C0207CBCFE008935B4 /* ProjectDescription.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = B91834BE207CBCE6008935B4 /* ProjectDescription.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
B918A22A20935EC800E64FBE /* Sentry.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B918A22920935EC800E64FBE /* Sentry.framework */; };
|
||||
B918A22B20935ECF00E64FBE /* Sentry.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = B918A22920935EC800E64FBE /* Sentry.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
B918A22C20935ED700E64FBE /* Sentry.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = B918A22920935EC800E64FBE /* Sentry.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
B918A22E20935EE900E64FBE /* ErrorHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = B918A22D20935EE900E64FBE /* ErrorHandler.swift */; };
|
||||
B91FF331206AB4E6005EA520 /* xcbuddy in Copy CLI */ = {isa = PBXBuildFile; fileRef = B915ECF6206395DA004B6630 /* xcbuddy */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
B92BF9FE2075608B00EE4EBD /* Data+TestData.swift in Sources */ = {isa = PBXBuildFile; fileRef = B92BF9FD2075608B00EE4EBD /* Data+TestData.swift */; };
|
||||
B94C7FC12062B8A8009BF596 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B94C7FB72062B8A7009BF596 /* Assets.xcassets */; };
|
||||
|
@ -178,6 +182,7 @@
|
|||
dstPath = "";
|
||||
dstSubfolderSpec = 10;
|
||||
files = (
|
||||
B918A22B20935ECF00E64FBE /* Sentry.framework in Embed Frameworks */,
|
||||
B9E2DCB22087757D0061DF86 /* PathKit.framework in Embed Frameworks */,
|
||||
B9E2DCA520876F660061DF86 /* Utility.framework in Embed Frameworks */,
|
||||
B9FB2DC82086516A00BC2FB3 /* clibc.framework in Embed Frameworks */,
|
||||
|
@ -198,6 +203,7 @@
|
|||
dstPath = "";
|
||||
dstSubfolderSpec = 10;
|
||||
files = (
|
||||
B918A22C20935ED700E64FBE /* Sentry.framework in Embed Frameworks */,
|
||||
B9E2DCB3208775860061DF86 /* PathKit.framework in Embed Frameworks */,
|
||||
B9E2DCAC208775160061DF86 /* clibc.framework in Embed Frameworks */,
|
||||
B9E2DCAD208775160061DF86 /* SPMLibc.framework in Embed Frameworks */,
|
||||
|
@ -225,6 +231,8 @@
|
|||
B915ED4D2063B04C004B6630 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
B915ED672063B18B004B6630 /* xcproj.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = xcproj.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
B91834BE207CBCE6008935B4 /* ProjectDescription.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = ProjectDescription.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
B918A22920935EC800E64FBE /* Sentry.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Sentry.framework; sourceTree = "<group>"; };
|
||||
B918A22D20935EE900E64FBE /* ErrorHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorHandler.swift; sourceTree = "<group>"; };
|
||||
B9287D0520808FFF002DEFEE /* BuildConfigurationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BuildConfigurationTests.swift; sourceTree = "<group>"; };
|
||||
B92BF8152073E66200EE4EBD /* MockUpdateController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockUpdateController.swift; sourceTree = "<group>"; };
|
||||
B92BF9F0207559E600EE4EBD /* MockGraphLoaderCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockGraphLoaderCache.swift; sourceTree = "<group>"; };
|
||||
|
@ -344,6 +352,7 @@
|
|||
B9E2DCA420876F490061DF86 /* Utility.framework in Frameworks */,
|
||||
B9FB2DBF2086506E00BC2FB3 /* Basic.framework in Frameworks */,
|
||||
B91834BF207CBCE6008935B4 /* ProjectDescription.framework in Frameworks */,
|
||||
B918A22A20935EC800E64FBE /* Sentry.framework in Frameworks */,
|
||||
B9013DD5206FEEDF007B1A34 /* Sparkle.framework in Frameworks */,
|
||||
B915ED662063B18B004B6630 /* xcproj.framework in Frameworks */,
|
||||
);
|
||||
|
@ -371,6 +380,7 @@
|
|||
B915EC392062EC97004B6630 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B918A22920935EC800E64FBE /* Sentry.framework */,
|
||||
B9E2DCAF2087756D0061DF86 /* PathKit.framework */,
|
||||
B9FB2DC22086507700BC2FB3 /* clibc.framework */,
|
||||
B9FB2DC42086507700BC2FB3 /* SPMLibc.framework */,
|
||||
|
@ -575,6 +585,7 @@
|
|||
B9553DE92090690000050311 /* Shell.swift */,
|
||||
B9553DF02092181500050311 /* Context.swift */,
|
||||
B9553DF220921AF300050311 /* ResourceLocator.swift */,
|
||||
B918A22D20935EE900E64FBE /* ErrorHandler.swift */,
|
||||
);
|
||||
path = Utils;
|
||||
sourceTree = "<group>";
|
||||
|
@ -770,6 +781,7 @@
|
|||
B9FDBF33206423460010BC33 /* Embed Frameworks */,
|
||||
B9B80AB0206AAFC30057482B /* Copy CLI */,
|
||||
B91834CF207CEB00008935B4 /* Copy ProjectDescription Framework */,
|
||||
B918A22F209365E900E64FBE /* Upload symbols to sentry */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
|
@ -875,6 +887,20 @@
|
|||
shellPath = /bin/sh;
|
||||
shellScript = "${SRCROOT}/scripts/copy-framework.sh ProjectDescription.framework";
|
||||
};
|
||||
B918A22F209365E900E64FBE /* Upload symbols to sentry */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Upload symbols to sentry";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "if which sentry-cli >/dev/null; then\nexport SENTRY_ORG=xcbuddy\nexport SENTRY_PROJECT=app\nERROR=$(bin/sentry-cli upload-dsym 2>&1 >/dev/null)\nif [ ! $? -eq 0 ]; then\necho \"warning: sentry-cli - $ERROR\"\nfi\nelse\necho \"warning: sentry-cli not installed, download from https://github.com/getsentry/sentry-cli/releases\"\nfi";
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
|
@ -890,6 +916,7 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
B918A22E20935EE900E64FBE /* ErrorHandler.swift in Sources */,
|
||||
B9B629AC20864E3A00EE9E07 /* FileHandler.swift in Sources */,
|
||||
B9553DEA2090690000050311 /* Shell.swift in Sources */,
|
||||
B95895F3208A2ACF00F00ACF /* ProjectGenerator.swift in Sources */,
|
||||
|
|
|
@ -24,5 +24,7 @@
|
|||
<string>Copyright © 2018 ppinera.es. All rights reserved.</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string></string>
|
||||
<key>SENTRY_DSN</key>
|
||||
<string>$(SENTRY_DSN)</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -61,7 +61,7 @@ class GraphManifestLoader: GraphManifestLoading {
|
|||
throw GraphManifestLoaderError.swiftNotFound
|
||||
}
|
||||
let swiftPath = AbsolutePath(swiftOutput)
|
||||
let manifestFrameworkPath = try context.resourceLocator.projectDescription(context: context)
|
||||
let manifestFrameworkPath = try context.resourceLocator.projectDescription()
|
||||
let jsonString: String! = try context.shell.run(swiftPath.asString, "-F", manifestFrameworkPath.parentDirectory.asString, "-framework", "ProjectDescription", path.asString, "--dump").chuzzle()
|
||||
if jsonString == nil {
|
||||
throw GraphManifestLoaderError.unexpectedOutput(path)
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
import Foundation
|
||||
import Sentry
|
||||
|
||||
/// Error handling protocol.
|
||||
protocol ErrorHandling: AnyObject {
|
||||
/// It should be called when a fatal error happens. Depending on the error it
|
||||
/// prints, and reports the error to Sentry.
|
||||
///
|
||||
/// - Parameter error: error.
|
||||
func fatal(error: FatalError)
|
||||
}
|
||||
|
||||
/// Fatal errors that can be thrown at any point of the execution.
|
||||
///
|
||||
/// - abort: used when something unexpected happens and the user should be alerted.
|
||||
/// - bug: like abort, but it also reports the event to Sentry.
|
||||
/// - abortSilent: like abort, but it doesn't print anything in the console.
|
||||
/// - bugSilent: like bug, but it doesn't print anything in the console.
|
||||
enum FatalError: Error {
|
||||
case abort(Error & CustomStringConvertible)
|
||||
case bug(Error & CustomStringConvertible)
|
||||
case abortSilent(Error)
|
||||
case bugSilent(Error)
|
||||
|
||||
/// Returns the error description
|
||||
var description: String? {
|
||||
switch self {
|
||||
case let .abort(error):
|
||||
return error.description
|
||||
case let .bug(error):
|
||||
return error.description
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a bug to be reported.
|
||||
var bug: Error? {
|
||||
switch self {
|
||||
case let .bug(error): return error
|
||||
case let .bugSilent(error): return error
|
||||
default: return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Error handler.
|
||||
final class ErrorHandler: ErrorHandling {
|
||||
/// Printer.
|
||||
let printer: Printing
|
||||
|
||||
/// Sentry client.
|
||||
let client: Client?
|
||||
|
||||
/// Initializes the error handler with its attributes.
|
||||
///
|
||||
/// - Parameter printer: printer.
|
||||
init(printer: Printing = Printer()) {
|
||||
if let sentryDsn = Bundle(for: ErrorHandler.self).infoDictionary?["SENTRY_DSN"] as? String {
|
||||
client = try! Client(dsn: sentryDsn)
|
||||
try! client?.startCrashHandler()
|
||||
} else {
|
||||
client = nil
|
||||
}
|
||||
self.printer = printer
|
||||
}
|
||||
|
||||
/// It should be called when a fatal error happens. Depending on the error it
|
||||
/// prints, and reports the error to Sentry.
|
||||
///
|
||||
/// - Parameter error: error.
|
||||
func fatal(error: FatalError) {
|
||||
if let description = error.description {
|
||||
printer.print(errorMessage: description)
|
||||
}
|
||||
if let bug = error.bug {
|
||||
let event = Event(level: .debug)
|
||||
event.message = bug.localizedDescription
|
||||
let semaphore = DispatchSemaphore(value: 0)
|
||||
client?.send(event: event) { _ in
|
||||
semaphore.signal()
|
||||
}
|
||||
semaphore.wait()
|
||||
}
|
||||
exit(1)
|
||||
}
|
||||
}
|
|
@ -8,14 +8,14 @@ protocol ResourceLocating: AnyObject {
|
|||
/// - Parameter context: context.
|
||||
/// - Returns: ProjectDescription.framework path.
|
||||
/// - Throws: an error if the framework cannot be found.
|
||||
func projectDescription(context: Contexting) throws -> AbsolutePath
|
||||
func projectDescription() throws -> AbsolutePath
|
||||
|
||||
/// Returns the CLI path.
|
||||
///
|
||||
/// - Parameter context: context.
|
||||
/// - Returns: path to the xcbuddy CLI.
|
||||
/// - Throws: an error if the CLI cannot be found.
|
||||
func cliPath(context: Contexting) throws -> AbsolutePath
|
||||
func cliPath() throws -> AbsolutePath
|
||||
}
|
||||
|
||||
/// Resource locating error.
|
||||
|
@ -40,18 +40,27 @@ enum ResourceLocatingError: Error, CustomStringConvertible, Equatable {
|
|||
|
||||
/// Resource locator.
|
||||
final class ResourceLocator: ResourceLocating {
|
||||
/// File handler.
|
||||
private let fileHandler: FileHandling
|
||||
|
||||
/// Initializes the locator with its attributes.
|
||||
///
|
||||
/// - Parameter fileHandler: file handler.
|
||||
init(fileHandler: FileHandling = FileHandler()) {
|
||||
self.fileHandler = fileHandler
|
||||
}
|
||||
|
||||
/// Returns the ProjectDescription.framework path.
|
||||
///
|
||||
/// - Parameter context: context.
|
||||
/// - Returns: ProjectDescription.framework path.
|
||||
/// - Throws: an error if the framework cannot be found.
|
||||
func projectDescription(context: Contexting) throws -> AbsolutePath {
|
||||
func projectDescription() throws -> AbsolutePath {
|
||||
let frameworkName = "ProjectDescription.framework"
|
||||
let xcbuddyKitPath = AbsolutePath(Bundle(for: GraphManifestLoader.self).bundleURL.path)
|
||||
let parentPath = xcbuddyKitPath.parentDirectory
|
||||
let pathInProducts = parentPath.appending(component: frameworkName)
|
||||
// Built products directory
|
||||
if context.fileHandler.exists(pathInProducts) {
|
||||
if fileHandler.exists(pathInProducts) {
|
||||
return pathInProducts
|
||||
}
|
||||
// Frameworks directory inside the app bundle.
|
||||
|
@ -63,7 +72,7 @@ final class ResourceLocator: ResourceLocating {
|
|||
throw ResourceLocatingError.notFound(frameworkName)
|
||||
}
|
||||
let frameworkPath = AbsolutePath(frameworksPath).appending(component: frameworkName)
|
||||
if !context.fileHandler.exists(frameworkPath) {
|
||||
if !fileHandler.exists(frameworkPath) {
|
||||
throw ResourceLocatingError.notFound(frameworkName)
|
||||
}
|
||||
return frameworkPath
|
||||
|
@ -71,16 +80,15 @@ final class ResourceLocator: ResourceLocating {
|
|||
|
||||
/// Returns the CLI path.
|
||||
///
|
||||
/// - Parameter context: context.
|
||||
/// - Returns: path to the xcbuddy CLI.
|
||||
/// - Throws: an error if the CLI cannot be found.
|
||||
func cliPath(context: Contexting) throws -> AbsolutePath {
|
||||
func cliPath() throws -> AbsolutePath {
|
||||
let toolName = "xcbuddy"
|
||||
let xcbuddyKitPath = AbsolutePath(Bundle(for: GraphManifestLoader.self).bundleURL.path)
|
||||
let parentPath = xcbuddyKitPath.parentDirectory
|
||||
let pathInProducts = parentPath.appending(component: toolName)
|
||||
// Built products directory
|
||||
if context.fileHandler.exists(pathInProducts) {
|
||||
if fileHandler.exists(pathInProducts) {
|
||||
return pathInProducts
|
||||
}
|
||||
// Frameworks directory inside the app bundle.
|
||||
|
@ -92,7 +100,7 @@ final class ResourceLocator: ResourceLocating {
|
|||
throw ResourceLocatingError.notFound(toolName)
|
||||
}
|
||||
let toolPath = AbsolutePath(frameworksPath).appending(component: toolName)
|
||||
if !context.fileHandler.exists(toolPath) {
|
||||
if !fileHandler.exists(toolPath) {
|
||||
throw ResourceLocatingError.notFound(toolName)
|
||||
}
|
||||
return toolPath
|
||||
|
|
|
@ -3,17 +3,17 @@ import Foundation
|
|||
|
||||
final class MockResourceLocator: ResourceLocating {
|
||||
var projectDescriptionCount: UInt = 0
|
||||
var projectDescriptionStub: ((Contexting) throws -> AbsolutePath)?
|
||||
var projectDescriptionStub: (() throws -> AbsolutePath)?
|
||||
var cliPathCount: UInt = 0
|
||||
var cliPathStub: ((Contexting) throws -> AbsolutePath)?
|
||||
var cliPathStub: (() throws -> AbsolutePath)?
|
||||
|
||||
func projectDescription(context: Contexting) throws -> AbsolutePath {
|
||||
func projectDescription() throws -> AbsolutePath {
|
||||
projectDescriptionCount += 1
|
||||
return try projectDescriptionStub?(context) ?? AbsolutePath("/")
|
||||
return try projectDescriptionStub?() ?? AbsolutePath("/")
|
||||
}
|
||||
|
||||
func cliPath(context: Contexting) throws -> AbsolutePath {
|
||||
func cliPath() throws -> AbsolutePath {
|
||||
cliPathCount += 1
|
||||
return try cliPathStub?(context) ?? AbsolutePath("/")
|
||||
return try cliPathStub?() ?? AbsolutePath("/")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
#!/usr/bin/env node
|
||||
/* eslint-disable no-console */
|
||||
|
||||
'use strict';
|
||||
|
||||
const cli = require('../js');
|
||||
const child = require('child_process').spawn(cli.getPath(), process.argv.slice(2), {
|
||||
stdio: 'inherit',
|
||||
});
|
||||
|
||||
child.on('error', err => {
|
||||
console.error('error: failed to invoke sentry-cli');
|
||||
console.error(err.stack);
|
||||
});
|
||||
|
||||
child.on('exit', code => {
|
||||
process.exit(code);
|
||||
});
|
||||
|
||||
process.on('SIGTERM', () => {
|
||||
child.kill('SIGTERM');
|
||||
});
|
||||
|
||||
process.on('SIGINT', () => {
|
||||
child.kill('SIGINT');
|
||||
});
|
Loading…
Reference in New Issue