sentry-cli 2.57.0__tar.gz → 2.58.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/Cargo.lock +1 -1
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/Cargo.toml +1 -1
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/PKG-INFO +1 -1
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/apple-catalog-parsing/native/swift/AssetCatalogParser/Sources/AssetCatalogParser/AssetCatalogReader.swift +142 -10
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/sentry_cli.egg-info/PKG-INFO +1 -1
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/api/data_types/chunking/upload/capability.rs +4 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/api/data_types/chunking/upload/options.rs +3 -1
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/build/mod.rs +2 -10
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/build/upload.rs +11 -7
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/dart_symbol_map/upload.rs +1 -1
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/debug_files/bundle_jvm.rs +0 -1
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/files/upload.rs +0 -1
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/react_native/appcenter.rs +0 -2
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/react_native/gradle.rs +0 -2
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/react_native/xcode.rs +0 -3
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/sourcemaps/upload.rs +9 -6
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/upload_proguard.rs +26 -3
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/build/mod.rs +1 -1
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/build/normalize.rs +32 -3
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/chunks/types.rs +7 -7
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/dif_upload/mod.rs +3 -3
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/file_upload.rs +3 -10
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/fs.rs +11 -18
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/proguard/upload.rs +3 -2
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/releases.rs +2 -1
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/sourcemaps.rs +1 -52
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/vcs.rs +70 -48
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/LICENSE +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/MANIFEST.in +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/README.md +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/apple-catalog-parsing/Cargo.toml +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/apple-catalog-parsing/build.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/apple-catalog-parsing/native/swift/AssetCatalogParser/Package.swift +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/apple-catalog-parsing/native/swift/AssetCatalogParser/Sources/ObjcSupport/include/safeValueForKey.h +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/apple-catalog-parsing/native/swift/AssetCatalogParser/Sources/ObjcSupport/safeValueForKey.m +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/apple-catalog-parsing/native/swift/AssetCatalogParser/Tests/AssetCatalogParserTests/AssetCatalogParserTests.swift +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/apple-catalog-parsing/native/swift/AssetCatalogParser/Tests/AssetCatalogParserTests/Resources/test.xcarchive/Info.plist +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/apple-catalog-parsing/native/swift/AssetCatalogParser/Tests/AssetCatalogParserTests/Resources/test.xcarchive/Products/Applications/DemoApp.app/Assets.car +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/apple-catalog-parsing/src/asset_catalog.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/apple-catalog-parsing/src/lib.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/build.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/pyproject.toml +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/sentry_cli.egg-info/SOURCES.txt +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/sentry_cli.egg-info/dependency_links.txt +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/sentry_cli.egg-info/top_level.txt +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/setup.cfg +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/setup.py +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/api/connection_manager.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/api/data_types/chunking/artifact.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/api/data_types/chunking/build.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/api/data_types/chunking/compression.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/api/data_types/chunking/dif.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/api/data_types/chunking/file_state.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/api/data_types/chunking/hash_algorithm.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/api/data_types/chunking/mod.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/api/data_types/chunking/upload/mod.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/api/data_types/deploy.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/api/data_types/mod.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/api/encoding.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/api/envelopes_api.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/api/errors/api_error.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/api/errors/mod.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/api/errors/sentry_error.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/api/mod.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/api/pagination.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/bashsupport.sh +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/bash_hook.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/dart_symbol_map/mod.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/debug_files/bundle_sources.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/debug_files/check.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/debug_files/find.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/debug_files/mod.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/debug_files/print_sources.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/debug_files/upload.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/deploys/list.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/deploys/mod.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/deploys/new.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/derive_parser.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/events/list.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/events/mod.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/files/delete.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/files/list.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/files/mod.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/info.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/issues/list.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/issues/mod.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/issues/mute.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/issues/resolve.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/issues/unresolve.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/login.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/logs/list.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/logs/mod.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/mod.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/monitors/list.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/monitors/mod.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/monitors/run.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/organizations/list.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/organizations/mod.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/projects/list.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/projects/mod.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/react_native/mod.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/releases/archive.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/releases/delete.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/releases/finalize.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/releases/info.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/releases/list.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/releases/mod.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/releases/new.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/releases/propose_version.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/releases/restore.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/releases/set_commits.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/repos/list.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/repos/mod.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/send_envelope.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/send_event.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/send_metric/common_args.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/send_metric/distribution.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/send_metric/gauge.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/send_metric/increment.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/send_metric/mod.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/send_metric/set.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/sourcemaps/explain.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/sourcemaps/inject.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/sourcemaps/mod.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/sourcemaps/resolve.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/uninstall.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/update.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/upload_dif.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/commands/upload_dsym.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/config.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/constants.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/main.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/android.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/appcenter.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/args.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/auth_token/auth_token_impl.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/auth_token/error.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/auth_token/mod.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/auth_token/org_auth_token.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/auth_token/redacting.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/auth_token/test.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/auth_token/user_auth_token.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/build/apple.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/build/validation.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/chunks/mod.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/chunks/options.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/chunks/upload.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/cordova.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/dif.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/dif_upload/error.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/event.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/file_search.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/formatting.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/http.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/logging.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/metrics.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/mod.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/non_empty.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/progress.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/proguard/mapping.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/proguard/mod.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/retry.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/snapshots/sentry_cli__utils__vcs__tests__generate_patch_default_twenty.snap +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/snapshots/sentry_cli__utils__vcs__tests__generate_patch_ignore_missing.snap +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/snapshots/sentry_cli__utils__vcs__tests__generate_patch_set_base.snap +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/snapshots/sentry_cli__utils__vcs__tests__generate_patch_set_previous_commit.snap +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/snapshots/sentry_cli__utils__vcs__tests__get_commits_from_git.snap +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/sourcemaps/inject.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/system.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/ui.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/update.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/value_parsers.rs +0 -0
- {sentry_cli-2.57.0 → sentry_cli-2.58.0}/src/utils/xcode.rs +0 -0
|
@@ -38,6 +38,8 @@ struct AssetCatalogEntry: Encodable {
|
|
|
38
38
|
let height: Int?
|
|
39
39
|
let filename: String?
|
|
40
40
|
let type: AssetType?
|
|
41
|
+
let idiom: String?
|
|
42
|
+
let colorspace: String?
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
enum Error: Swift.Error {
|
|
@@ -48,7 +50,40 @@ typealias objectiveCMethodImp = @convention(c) (AnyObject, Selector, UnsafeRawPo
|
|
|
48
50
|
AnyObject
|
|
49
51
|
>?
|
|
50
52
|
|
|
53
|
+
private struct MultisizeSetInfo {
|
|
54
|
+
let name: String
|
|
55
|
+
let element: UInt
|
|
56
|
+
let part: UInt
|
|
57
|
+
let identifier: UInt
|
|
58
|
+
let sizeIndexes: [(idiom: UInt, subtype: UInt)]
|
|
59
|
+
}
|
|
60
|
+
|
|
51
61
|
enum AssetUtil {
|
|
62
|
+
private static func idiomToString(_ idiom: UInt?) -> String? {
|
|
63
|
+
guard let idiom = idiom else { return nil }
|
|
64
|
+
switch idiom {
|
|
65
|
+
case 0: return "universal"
|
|
66
|
+
case 1: return "phone"
|
|
67
|
+
case 2: return "pad"
|
|
68
|
+
case 3: return "tv"
|
|
69
|
+
case 4: return "carplay"
|
|
70
|
+
case 5: return "watch"
|
|
71
|
+
case 6: return "marketing"
|
|
72
|
+
default: return nil
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
private static func colorSpaceIDToString(_ colorSpaceID: UInt?) -> String? {
|
|
77
|
+
guard let colorSpaceID = colorSpaceID else { return nil }
|
|
78
|
+
switch colorSpaceID {
|
|
79
|
+
case 1: return "srgb"
|
|
80
|
+
case 2: return "gray gamma 22"
|
|
81
|
+
case 3: return "displayP3"
|
|
82
|
+
case 4: return "extended srgb"
|
|
83
|
+
default: return nil
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
52
87
|
private static func createResultsPath(assetURL: URL, outputURL: URL) throws -> URL {
|
|
53
88
|
var archiveURL = assetURL
|
|
54
89
|
var tailComponents: [String] = []
|
|
@@ -78,8 +113,12 @@ enum AssetUtil {
|
|
|
78
113
|
let (structuredThemeStore, assetKeys) = initializeCatalog(from: file)
|
|
79
114
|
|
|
80
115
|
var images: [String: (cgImage: CGImage, format: String)] = [:]
|
|
81
|
-
|
|
82
|
-
|
|
116
|
+
|
|
117
|
+
// First pass: Build map of multisize sets and cache renditions for performance
|
|
118
|
+
var multisizeSets: [MultisizeSetInfo] = []
|
|
119
|
+
var renditionCache: [Int: NSObject] = [:]
|
|
120
|
+
|
|
121
|
+
for (index, key) in assetKeys.enumerated() {
|
|
83
122
|
let keyList = unsafeBitCast(
|
|
84
123
|
key.perform(Selector(("keyList"))),
|
|
85
124
|
to: UnsafeMutableRawPointer.self
|
|
@@ -87,6 +126,49 @@ enum AssetUtil {
|
|
|
87
126
|
guard let rendition = createRendition(from: structuredThemeStore, keyList) else {
|
|
88
127
|
continue
|
|
89
128
|
}
|
|
129
|
+
renditionCache[index] = rendition
|
|
130
|
+
|
|
131
|
+
let type = rendition.getUInt(forKey: "type") ?? 0
|
|
132
|
+
if type == 1010 { // Multisize image set
|
|
133
|
+
let renditionTypeName = rendition.perform(Selector(("name"))).takeUnretainedValue() as! String
|
|
134
|
+
let keyElement = key.getUInt(forKey: "themeElement") ?? 0
|
|
135
|
+
let keyPart = key.getUInt(forKey: "themePart") ?? 0
|
|
136
|
+
let keyIdentifier = key.getUInt(forKey: "themeIdentifier") ?? 0
|
|
137
|
+
|
|
138
|
+
// Extract size indexes to identify which images belong to this set
|
|
139
|
+
var sizeIndexes: [(idiom: UInt, subtype: UInt)] = []
|
|
140
|
+
if rendition.responds(to: Selector(("sizeIndexes"))),
|
|
141
|
+
let sizeIndexesResult = rendition.perform(Selector(("sizeIndexes"))),
|
|
142
|
+
let sizeIndexesArray = sizeIndexesResult.takeUnretainedValue() as? NSArray {
|
|
143
|
+
for sizeIndexObj in sizeIndexesArray {
|
|
144
|
+
if let obj = sizeIndexObj as? NSObject {
|
|
145
|
+
let idiom = obj.getUInt(forKey: "idiom") ?? 0
|
|
146
|
+
let subtype = obj.getUInt(forKey: "subtype") ?? 0
|
|
147
|
+
sizeIndexes.append((idiom: idiom, subtype: subtype))
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
multisizeSets.append(MultisizeSetInfo(
|
|
153
|
+
name: renditionTypeName,
|
|
154
|
+
element: keyElement,
|
|
155
|
+
part: keyPart,
|
|
156
|
+
identifier: keyIdentifier,
|
|
157
|
+
sizeIndexes: sizeIndexes
|
|
158
|
+
))
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Second pass: Process all assets using cached renditions
|
|
163
|
+
for (index, key) in assetKeys.enumerated() {
|
|
164
|
+
guard let rendition = renditionCache[index] else {
|
|
165
|
+
continue
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
let keyList = unsafeBitCast(
|
|
169
|
+
key.perform(Selector(("keyList"))),
|
|
170
|
+
to: UnsafeMutableRawPointer.self
|
|
171
|
+
)
|
|
90
172
|
|
|
91
173
|
let data = rendition.value(forKey: "_srcData") as! Data
|
|
92
174
|
let length = UInt(data.count)
|
|
@@ -126,26 +208,50 @@ enum AssetUtil {
|
|
|
126
208
|
let type = rendition.getUInt(forKey: "type") ?? 0
|
|
127
209
|
|
|
128
210
|
let isVector = type == 9
|
|
129
|
-
let
|
|
211
|
+
let isMultisizeImageSet = type == 1010
|
|
130
212
|
let assetType = determineAssetType(key)
|
|
131
213
|
let imageId = UUID().uuidString
|
|
132
|
-
|
|
133
214
|
let fileExtension = (renditionTypeName as NSString).pathExtension.lowercased()
|
|
134
215
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
216
|
+
var width: Int?
|
|
217
|
+
var height: Int?
|
|
218
|
+
var unslicedImage: CGImage?
|
|
219
|
+
|
|
220
|
+
if isMultisizeImageSet {
|
|
221
|
+
continue
|
|
222
|
+
} else {
|
|
223
|
+
// Get image dimensions from regular rendition
|
|
224
|
+
(width, height, unslicedImage) = resolveImageDimensions(rendition, isVector)
|
|
225
|
+
|
|
226
|
+
// Skip SVGs, but save images even if they don't have an extension (default to png)
|
|
227
|
+
if fileExtension != "svg", let unslicedImage = unslicedImage {
|
|
228
|
+
let format = fileExtension.isEmpty ? "png" : fileExtension
|
|
229
|
+
images[imageId] = (cgImage: unslicedImage, format: format)
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
let idiomValue = key.getUInt(forKey: "themeIdiom")
|
|
234
|
+
let colorSpaceID = rendition.getUInt(forKey: "colorSpaceID")
|
|
235
|
+
|
|
236
|
+
// Include multisize set name in the name field if it exists
|
|
237
|
+
let finalName: String
|
|
238
|
+
if let setName = findMultisizeSetName(key, in: multisizeSets) {
|
|
239
|
+
finalName = "\(setName)/\(name)"
|
|
240
|
+
} else {
|
|
241
|
+
finalName = name
|
|
138
242
|
}
|
|
139
243
|
|
|
140
244
|
let asset = AssetCatalogEntry(
|
|
141
245
|
imageId: imageId,
|
|
142
246
|
size: length,
|
|
143
|
-
name:
|
|
247
|
+
name: finalName,
|
|
144
248
|
vector: isVector,
|
|
145
249
|
width: width,
|
|
146
250
|
height: height,
|
|
147
251
|
filename: renditionTypeName,
|
|
148
|
-
type: assetType
|
|
252
|
+
type: assetType,
|
|
253
|
+
idiom: idiomToString(idiomValue),
|
|
254
|
+
colorspace: colorSpaceIDToString(colorSpaceID)
|
|
149
255
|
)
|
|
150
256
|
assets.append(asset)
|
|
151
257
|
}
|
|
@@ -158,7 +264,9 @@ enum AssetUtil {
|
|
|
158
264
|
width: nil,
|
|
159
265
|
height: nil,
|
|
160
266
|
filename: nil,
|
|
161
|
-
type: nil
|
|
267
|
+
type: nil,
|
|
268
|
+
idiom: nil,
|
|
269
|
+
colorspace: nil
|
|
162
270
|
))
|
|
163
271
|
|
|
164
272
|
let data = try! JSONEncoder().encode(assets)
|
|
@@ -254,6 +362,30 @@ enum AssetUtil {
|
|
|
254
362
|
}
|
|
255
363
|
return .image
|
|
256
364
|
}
|
|
365
|
+
|
|
366
|
+
private static func findMultisizeSetName(
|
|
367
|
+
_ key: NSObject,
|
|
368
|
+
in multisizeSets: [MultisizeSetInfo]
|
|
369
|
+
) -> String? {
|
|
370
|
+
let element = key.getUInt(forKey: "themeElement") ?? 0
|
|
371
|
+
let identifier = key.getUInt(forKey: "themeIdentifier") ?? 0
|
|
372
|
+
let idiom = key.getUInt(forKey: "themeIdiom") ?? 0
|
|
373
|
+
let subtype = key.getUInt(forKey: "themeSubtype") ?? 0
|
|
374
|
+
|
|
375
|
+
for setInfo in multisizeSets {
|
|
376
|
+
guard setInfo.element == element,
|
|
377
|
+
setInfo.identifier == identifier else {
|
|
378
|
+
continue
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
for sizeIndex in setInfo.sizeIndexes {
|
|
382
|
+
if sizeIndex.idiom == idiom && sizeIndex.subtype == subtype {
|
|
383
|
+
return setInfo.name
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
return nil
|
|
388
|
+
}
|
|
257
389
|
|
|
258
390
|
private static func resolveRenditionName(
|
|
259
391
|
_ structuredThemeStore: NSObject,
|
|
@@ -36,6 +36,9 @@ pub enum ChunkUploadCapability {
|
|
|
36
36
|
/// Upload of preprod artifacts
|
|
37
37
|
PreprodArtifacts,
|
|
38
38
|
|
|
39
|
+
/// Upload of ProGuard mappings
|
|
40
|
+
Proguard,
|
|
41
|
+
|
|
39
42
|
/// Any other unsupported capability (ignored)
|
|
40
43
|
Unknown,
|
|
41
44
|
}
|
|
@@ -57,6 +60,7 @@ impl<'de> Deserialize<'de> for ChunkUploadCapability {
|
|
|
57
60
|
"il2cpp" => ChunkUploadCapability::Il2Cpp,
|
|
58
61
|
"dartsymbolmap" => ChunkUploadCapability::DartSymbolMap,
|
|
59
62
|
"preprod_artifacts" => ChunkUploadCapability::PreprodArtifacts,
|
|
63
|
+
"proguard" => ChunkUploadCapability::Proguard,
|
|
60
64
|
_ => ChunkUploadCapability::Unknown,
|
|
61
65
|
})
|
|
62
66
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
use std::num::NonZeroUsize;
|
|
2
|
+
|
|
1
3
|
use serde::Deserialize;
|
|
2
4
|
|
|
3
5
|
use super::{ChunkCompression, ChunkHashAlgorithm, ChunkUploadCapability};
|
|
@@ -17,7 +19,7 @@ pub struct ChunkServerOptions {
|
|
|
17
19
|
pub max_wait: u64,
|
|
18
20
|
#[expect(dead_code)]
|
|
19
21
|
pub hash_algorithm: ChunkHashAlgorithm,
|
|
20
|
-
pub chunk_size:
|
|
22
|
+
pub chunk_size: NonZeroUsize,
|
|
21
23
|
pub concurrency: u8,
|
|
22
24
|
#[serde(default)]
|
|
23
25
|
pub compression: Vec<ChunkCompression>,
|
|
@@ -21,24 +21,16 @@ pub fn make_command(mut command: Command) -> Command {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
command = command
|
|
24
|
-
.about("
|
|
24
|
+
.about("Manage builds.")
|
|
25
25
|
.subcommand_required(true)
|
|
26
26
|
.arg_required_else_help(true)
|
|
27
27
|
.org_arg()
|
|
28
|
-
.project_arg(true)
|
|
29
|
-
// TODO: Remove this when ready for release
|
|
30
|
-
.hide(true);
|
|
28
|
+
.project_arg(true);
|
|
31
29
|
each_subcommand!(add_subcommand);
|
|
32
30
|
command
|
|
33
31
|
}
|
|
34
32
|
|
|
35
33
|
pub fn execute(matches: &ArgMatches) -> Result<()> {
|
|
36
|
-
log::warn!(
|
|
37
|
-
"EXPERIMENTAL: The build subcommand is experimental. \
|
|
38
|
-
The command is subject to breaking changes and may be removed \
|
|
39
|
-
without notice in any release."
|
|
40
|
-
);
|
|
41
|
-
|
|
42
34
|
macro_rules! execute_subcommand {
|
|
43
35
|
($name:ident) => {{
|
|
44
36
|
if let Some(sub_matches) =
|
|
@@ -17,7 +17,9 @@ use crate::config::Config;
|
|
|
17
17
|
use crate::utils::args::ArgExt as _;
|
|
18
18
|
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
|
|
19
19
|
use crate::utils::build::{handle_asset_catalogs, ipa_to_xcarchive, is_apple_app, is_ipa_file};
|
|
20
|
-
use crate::utils::build::{
|
|
20
|
+
use crate::utils::build::{
|
|
21
|
+
is_aab_file, is_apk_file, is_zip_file, normalize_directory, write_version_metadata,
|
|
22
|
+
};
|
|
21
23
|
use crate::utils::chunks::{upload_chunks, Chunk};
|
|
22
24
|
use crate::utils::fs::get_sha1_checksums;
|
|
23
25
|
use crate::utils::fs::TempDir;
|
|
@@ -37,7 +39,7 @@ pub fn make_command(command: Command) -> Command {
|
|
|
37
39
|
const HELP_TEXT: &str =
|
|
38
40
|
"The path to the build to upload. Supported files include Apk, and Aab.";
|
|
39
41
|
command
|
|
40
|
-
.about("
|
|
42
|
+
.about("Upload builds to a project.")
|
|
41
43
|
.org_arg()
|
|
42
44
|
.project_arg(false)
|
|
43
45
|
.arg(
|
|
@@ -113,7 +115,7 @@ pub fn execute(matches: &ArgMatches) -> Result<()> {
|
|
|
113
115
|
.get_one("head_sha")
|
|
114
116
|
.map(String::as_str)
|
|
115
117
|
.map(Cow::Borrowed)
|
|
116
|
-
.or_else(|| vcs::
|
|
118
|
+
.or_else(|| vcs::find_head_sha().ok().map(Cow::Owned));
|
|
117
119
|
|
|
118
120
|
let cached_remote = config.get_cached_vcs_remote();
|
|
119
121
|
// Try to open the git repository and find the remote, but handle errors gracefully.
|
|
@@ -233,7 +235,7 @@ pub fn execute(matches: &ArgMatches) -> Result<()> {
|
|
|
233
235
|
.map(String::as_str)
|
|
234
236
|
.map(Cow::Borrowed)
|
|
235
237
|
.or_else(|| {
|
|
236
|
-
vcs::find_base_sha()
|
|
238
|
+
vcs::find_base_sha(&cached_remote)
|
|
237
239
|
.inspect_err(|e| debug!("Error finding base SHA: {e}"))
|
|
238
240
|
.ok()
|
|
239
241
|
.flatten()
|
|
@@ -455,6 +457,8 @@ fn normalize_file(path: &Path, bytes: &[u8]) -> Result<TempFile> {
|
|
|
455
457
|
zip.start_file(file_name, options)?;
|
|
456
458
|
zip.write_all(bytes)?;
|
|
457
459
|
|
|
460
|
+
write_version_metadata(&mut zip)?;
|
|
461
|
+
|
|
458
462
|
zip.finish()?;
|
|
459
463
|
debug!("Successfully created normalized zip for file");
|
|
460
464
|
Ok(temp_file)
|
|
@@ -507,10 +511,10 @@ fn upload_file(
|
|
|
507
511
|
pb.enable_steady_tick(100);
|
|
508
512
|
pb.set_style(progress_style);
|
|
509
513
|
|
|
510
|
-
let chunk_size = chunk_upload_options.chunk_size
|
|
511
|
-
let (checksum, checksums) = get_sha1_checksums(bytes, chunk_size)
|
|
514
|
+
let chunk_size = chunk_upload_options.chunk_size;
|
|
515
|
+
let (checksum, checksums) = get_sha1_checksums(bytes, chunk_size);
|
|
512
516
|
let mut chunks = bytes
|
|
513
|
-
.chunks(chunk_size)
|
|
517
|
+
.chunks(chunk_size.into())
|
|
514
518
|
.zip(checksums.iter())
|
|
515
519
|
.map(|(data, checksum)| Chunk((*checksum, data)))
|
|
516
520
|
.collect::<Vec<_>>();
|
|
@@ -159,7 +159,7 @@ pub(super) fn execute(args: DartSymbolMapUploadArgs) -> Result<()> {
|
|
|
159
159
|
let options = ChunkOptions::new(chunk_upload_options, org, project)
|
|
160
160
|
.with_max_wait(DEFAULT_MAX_WAIT);
|
|
161
161
|
|
|
162
|
-
let chunked = Chunked::from(object, options.server_options().chunk_size
|
|
162
|
+
let chunked = Chunked::from(object, options.server_options().chunk_size);
|
|
163
163
|
let (_uploaded, has_processing_errors) = upload_chunked_objects(&[chunked], options)?;
|
|
164
164
|
if has_processing_errors {
|
|
165
165
|
bail!("Some symbol maps did not process correctly");
|
|
@@ -202,7 +202,6 @@ pub fn execute(matches: &ArgMatches) -> Result<()> {
|
|
|
202
202
|
note: None,
|
|
203
203
|
wait,
|
|
204
204
|
max_wait,
|
|
205
|
-
dedupe: false,
|
|
206
205
|
chunk_upload_options: chunk_upload_options.as_ref(),
|
|
207
206
|
})?;
|
|
208
207
|
}
|
|
@@ -221,7 +220,6 @@ pub fn execute(matches: &ArgMatches) -> Result<()> {
|
|
|
221
220
|
note: None,
|
|
222
221
|
wait,
|
|
223
222
|
max_wait,
|
|
224
|
-
dedupe: false,
|
|
225
223
|
chunk_upload_options: chunk_upload_options.as_ref(),
|
|
226
224
|
})?;
|
|
227
225
|
}
|
|
@@ -129,7 +129,6 @@ pub fn execute(matches: &ArgMatches) -> Result<()> {
|
|
|
129
129
|
note: None,
|
|
130
130
|
wait,
|
|
131
131
|
max_wait,
|
|
132
|
-
dedupe: false,
|
|
133
132
|
chunk_upload_options: chunk_upload_options.as_ref(),
|
|
134
133
|
})?;
|
|
135
134
|
}
|
|
@@ -143,7 +142,6 @@ pub fn execute(matches: &ArgMatches) -> Result<()> {
|
|
|
143
142
|
note: None,
|
|
144
143
|
wait,
|
|
145
144
|
max_wait,
|
|
146
|
-
dedupe: false,
|
|
147
145
|
chunk_upload_options: chunk_upload_options.as_ref(),
|
|
148
146
|
})?;
|
|
149
147
|
}
|
|
@@ -351,7 +351,6 @@ pub fn execute(matches: &ArgMatches) -> Result<()> {
|
|
|
351
351
|
note: None,
|
|
352
352
|
wait,
|
|
353
353
|
max_wait,
|
|
354
|
-
dedupe: false,
|
|
355
354
|
chunk_upload_options: chunk_upload_options.as_ref(),
|
|
356
355
|
})?;
|
|
357
356
|
} else {
|
|
@@ -387,7 +386,6 @@ pub fn execute(matches: &ArgMatches) -> Result<()> {
|
|
|
387
386
|
note: None,
|
|
388
387
|
wait,
|
|
389
388
|
max_wait,
|
|
390
|
-
dedupe: false,
|
|
391
389
|
chunk_upload_options: chunk_upload_options.as_ref(),
|
|
392
390
|
})?;
|
|
393
391
|
}
|
|
@@ -401,7 +399,6 @@ pub fn execute(matches: &ArgMatches) -> Result<()> {
|
|
|
401
399
|
note: None,
|
|
402
400
|
wait,
|
|
403
401
|
max_wait,
|
|
404
|
-
dedupe: false,
|
|
405
402
|
chunk_upload_options: chunk_upload_options.as_ref(),
|
|
406
403
|
})?;
|
|
407
404
|
}
|
|
@@ -193,11 +193,8 @@ pub fn make_command(command: Command) -> Command {
|
|
|
193
193
|
Arg::new("no_dedupe")
|
|
194
194
|
.long("no-dedupe")
|
|
195
195
|
.action(ArgAction::SetTrue)
|
|
196
|
-
.
|
|
197
|
-
|
|
198
|
-
This will force all artifacts to be uploaded, \
|
|
199
|
-
no matter whether they are already present on the server.",
|
|
200
|
-
),
|
|
196
|
+
.hide(true)
|
|
197
|
+
.help("[DEPRECATED] This flag has no effect and is scheduled for removal."),
|
|
201
198
|
)
|
|
202
199
|
.arg(
|
|
203
200
|
Arg::new("extensions")
|
|
@@ -419,6 +416,13 @@ fn process_sources_from_paths(
|
|
|
419
416
|
}
|
|
420
417
|
|
|
421
418
|
pub fn execute(matches: &ArgMatches) -> Result<()> {
|
|
419
|
+
if matches.get_flag("no_dedupe") {
|
|
420
|
+
log::warn!(
|
|
421
|
+
"[DEPRECATION NOTICE] The --no-dedupe flag is deprecated and has no \
|
|
422
|
+
effect. It will be removed in the next major version."
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
|
|
422
426
|
let config = Config::current();
|
|
423
427
|
let version = config.get_release_with_legacy_fallback(matches).ok();
|
|
424
428
|
let org = config.get_org(matches)?;
|
|
@@ -457,7 +461,6 @@ pub fn execute(matches: &ArgMatches) -> Result<()> {
|
|
|
457
461
|
note: matches.get_one::<String>("note").map(String::as_str),
|
|
458
462
|
wait,
|
|
459
463
|
max_wait,
|
|
460
|
-
dedupe: !matches.get_flag("no_dedupe"),
|
|
461
464
|
chunk_upload_options: chunk_upload_options.as_ref(),
|
|
462
465
|
};
|
|
463
466
|
|
|
@@ -85,12 +85,13 @@ pub fn make_command(command: Command) -> Command {
|
|
|
85
85
|
)
|
|
86
86
|
.arg(
|
|
87
87
|
Arg::new("platform")
|
|
88
|
+
.hide(true)
|
|
88
89
|
.long("platform")
|
|
89
90
|
.value_name("PLATFORM")
|
|
90
91
|
.requires("app_id")
|
|
91
92
|
.help(
|
|
92
|
-
"
|
|
93
|
-
|
|
93
|
+
"[DEPRECATED] This flag is a no-op, scheduled \
|
|
94
|
+
for removal in Sentry CLI 3.0.0.",
|
|
94
95
|
),
|
|
95
96
|
)
|
|
96
97
|
.arg(
|
|
@@ -110,7 +111,10 @@ pub fn make_command(command: Command) -> Command {
|
|
|
110
111
|
.value_name("PATH")
|
|
111
112
|
.conflicts_with("app_id")
|
|
112
113
|
.hide(true)
|
|
113
|
-
.help(
|
|
114
|
+
.help(
|
|
115
|
+
"[DEPRECATED] This flag is a no-op, scheduled \
|
|
116
|
+
for removal in Sentry CLI 3.0.0.",
|
|
117
|
+
),
|
|
114
118
|
)
|
|
115
119
|
.arg(
|
|
116
120
|
Arg::new("write_properties")
|
|
@@ -145,6 +149,22 @@ pub fn make_command(command: Command) -> Command {
|
|
|
145
149
|
}
|
|
146
150
|
|
|
147
151
|
pub fn execute(matches: &ArgMatches) -> Result<()> {
|
|
152
|
+
if matches.get_one::<String>("platform").is_some() {
|
|
153
|
+
log::warn!(
|
|
154
|
+
"[DEPRECATION NOTICE] The --platform argument is deprecated, \
|
|
155
|
+
and is scheduled for removal in Sentry CLI 3.0.0. \
|
|
156
|
+
The argument is a no-op."
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if matches.get_one::<String>("android_manifest").is_some() {
|
|
161
|
+
log::warn!(
|
|
162
|
+
"[DEPRECATION NOTICE] The --android-manifest argument is deprecated, \
|
|
163
|
+
and is scheduled for removal in Sentry CLI 3.0.0. \
|
|
164
|
+
The argument is a no-op."
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
148
168
|
let paths: Vec<_> = match matches.get_many::<String>("paths") {
|
|
149
169
|
Some(paths) => paths.collect(),
|
|
150
170
|
None => {
|
|
@@ -194,6 +214,9 @@ pub fn execute(matches: &ArgMatches) -> Result<()> {
|
|
|
194
214
|
}
|
|
195
215
|
}
|
|
196
216
|
|
|
217
|
+
// We are done constructing the mappings, redeclare as immutable.
|
|
218
|
+
let mappings = mappings;
|
|
219
|
+
|
|
197
220
|
let api = Api::current();
|
|
198
221
|
let config = Config::current();
|
|
199
222
|
|
|
@@ -5,7 +5,7 @@ mod validation;
|
|
|
5
5
|
|
|
6
6
|
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
|
|
7
7
|
pub use self::apple::{handle_asset_catalogs, ipa_to_xcarchive};
|
|
8
|
-
pub use self::normalize::normalize_directory;
|
|
8
|
+
pub use self::normalize::{normalize_directory, write_version_metadata};
|
|
9
9
|
pub use self::validation::{is_aab_file, is_apk_file, is_zip_file};
|
|
10
10
|
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
|
|
11
11
|
pub use self::validation::{is_apple_app, is_ipa_file};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
use std::borrow::Cow;
|
|
1
2
|
#[cfg(not(windows))]
|
|
2
3
|
use std::fs;
|
|
3
4
|
use std::fs::File;
|
|
@@ -6,8 +7,9 @@ use std::io::Write as _;
|
|
|
6
7
|
use std::os::unix::fs::PermissionsExt as _;
|
|
7
8
|
use std::path::{Path, PathBuf};
|
|
8
9
|
|
|
10
|
+
use crate::constants::VERSION;
|
|
9
11
|
use crate::utils::fs::TempFile;
|
|
10
|
-
use anyhow::Result;
|
|
12
|
+
use anyhow::{Context as _, Result};
|
|
11
13
|
use itertools::Itertools as _;
|
|
12
14
|
use log::debug;
|
|
13
15
|
use symbolic::common::ByteView;
|
|
@@ -15,6 +17,14 @@ use walkdir::WalkDir;
|
|
|
15
17
|
use zip::write::SimpleFileOptions;
|
|
16
18
|
use zip::{DateTime, ZipWriter};
|
|
17
19
|
|
|
20
|
+
fn get_version() -> Cow<'static, str> {
|
|
21
|
+
// Integration tests can override the version for consistent test results.
|
|
22
|
+
// This ensures deterministic checksums in test fixtures by using a fixed version.
|
|
23
|
+
std::env::var("SENTRY_CLI_INTEGRATION_TEST_VERSION_OVERRIDE")
|
|
24
|
+
.map(Cow::Owned)
|
|
25
|
+
.unwrap_or(Cow::Borrowed(VERSION))
|
|
26
|
+
}
|
|
27
|
+
|
|
18
28
|
fn sort_entries(path: &Path) -> Result<impl Iterator<Item = (PathBuf, PathBuf)>> {
|
|
19
29
|
Ok(WalkDir::new(path)
|
|
20
30
|
.into_iter()
|
|
@@ -61,10 +71,12 @@ fn add_entries_to_zip(
|
|
|
61
71
|
let target_str = target.to_string_lossy();
|
|
62
72
|
|
|
63
73
|
// Create a symlink entry in the zip
|
|
64
|
-
zip.add_symlink(zip_path, &target_str, options)
|
|
74
|
+
zip.add_symlink(zip_path.as_str(), &target_str, options)
|
|
75
|
+
.with_context(|| format!("Failed to add symlink '{zip_path}' to zip archive"))?;
|
|
65
76
|
} else {
|
|
66
77
|
// Handle regular files
|
|
67
|
-
zip.start_file(zip_path, options)
|
|
78
|
+
zip.start_file(zip_path.as_str(), options)
|
|
79
|
+
.with_context(|| format!("Failed to add file '{zip_path}' to zip archive"))?;
|
|
68
80
|
let file_byteview = ByteView::open(&entry_path)?;
|
|
69
81
|
zip.write_all(file_byteview.as_slice())?;
|
|
70
82
|
}
|
|
@@ -74,6 +86,21 @@ fn add_entries_to_zip(
|
|
|
74
86
|
Ok(file_count)
|
|
75
87
|
}
|
|
76
88
|
|
|
89
|
+
fn metadata_file_options() -> SimpleFileOptions {
|
|
90
|
+
SimpleFileOptions::default()
|
|
91
|
+
.compression_method(zip::CompressionMethod::Deflated)
|
|
92
|
+
.last_modified_time(DateTime::default())
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
pub fn write_version_metadata<W: std::io::Write + std::io::Seek>(
|
|
96
|
+
zip: &mut ZipWriter<W>,
|
|
97
|
+
) -> Result<()> {
|
|
98
|
+
let version = get_version();
|
|
99
|
+
zip.start_file(".sentry-cli-metadata.txt", metadata_file_options())?;
|
|
100
|
+
writeln!(zip, "sentry-cli-version: {version}")?;
|
|
101
|
+
Ok(())
|
|
102
|
+
}
|
|
103
|
+
|
|
77
104
|
// For XCArchive directories, we'll zip the entire directory
|
|
78
105
|
// It's important to not change the contents of the directory or the size
|
|
79
106
|
// analysis will be wrong and the code signature will break.
|
|
@@ -106,6 +133,8 @@ pub fn normalize_directory(path: &Path, parsed_assets_path: &Path) -> Result<Tem
|
|
|
106
133
|
)?;
|
|
107
134
|
}
|
|
108
135
|
|
|
136
|
+
write_version_metadata(&mut zip)?;
|
|
137
|
+
|
|
109
138
|
zip.finish()?;
|
|
110
139
|
debug!("Successfully created normalized zip for directory with {file_count} files");
|
|
111
140
|
Ok(temp_file)
|