fastlane-plugin-sentry 0

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.
@@ -0,0 +1,209 @@
1
+ module Fastlane
2
+ module Actions
3
+ class SentryUploadSourcemapAction < Action
4
+ def self.run(params)
5
+ require 'shellwords'
6
+
7
+ Helper::SentryConfig.parse_api_params(params)
8
+
9
+ version = params[:version]
10
+ version = "#{params[:app_identifier]}@#{params[:version]}" if params[:app_identifier]
11
+ version = "#{version}+#{params[:build]}" if params[:build]
12
+
13
+ sourcemaps = params[:sourcemap]
14
+
15
+ command = [
16
+ "sourcemaps",
17
+ "upload",
18
+ "--release",
19
+ version
20
+ ]
21
+ command += sourcemaps
22
+
23
+ command.push('--no-rewrite') unless params[:rewrite]
24
+ command.push('--strip-prefix').push(params[:strip_prefix]) if params[:strip_prefix]
25
+ command.push('--strip-common-prefix') if params[:strip_common_prefix]
26
+ command.push('--url-prefix').push(params[:url_prefix]) unless params[:url_prefix].nil?
27
+ command.push('--url-suffix').push(params[:url_suffix]) unless params[:url_suffix].nil?
28
+ command.push('--dist').push(params[:dist]) unless params[:dist].nil?
29
+ command.push('--note').push(params[:note]) unless params[:note].nil?
30
+ command.push('--validate') if params[:validate]
31
+ command.push('--decompress') if params[:decompress]
32
+ command.push('--wait') if params[:wait]
33
+ command.push('--wait-for').push(params[:wait_for]) unless params[:wait_for].nil?
34
+ command.push('--no-sourcemap-reference') if params[:no_sourcemap_reference]
35
+ command.push('--debug-id-reference') if params[:debug_id_reference]
36
+ command.push('--bundle').push(params[:bundle]) unless params[:bundle].nil?
37
+ command.push('--bundle-sourcemap').push(params[:bundle_sourcemap]) unless params[:bundle_sourcemap].nil?
38
+ command.push('--strict') if params[:strict]
39
+
40
+ unless params[:ignore].nil?
41
+ # normalize to array
42
+ unless params[:ignore].kind_of?(Enumerable)
43
+ params[:ignore] = [params[:ignore]]
44
+ end
45
+ # no nil or empty strings
46
+ params[:ignore].reject! do |e|
47
+ e.strip.empty?
48
+ rescue StandardError
49
+ true
50
+ end
51
+ params[:ignore].each do |pattern|
52
+ command.push('--ignore').push(pattern)
53
+ end
54
+ end
55
+
56
+ command.push('--ignore-file').push(params[:ignore_file]) unless params[:ignore_file].nil?
57
+
58
+ unless params[:ext].nil?
59
+ # normalize to array
60
+ unless params[:ext].kind_of?(Enumerable)
61
+ params[:ext] = [params[:ext]]
62
+ end
63
+ params[:ext].each do |extension|
64
+ command.push('--ext').push(extension)
65
+ end
66
+ end
67
+
68
+ Helper::SentryHelper.call_sentry_cli(params, command)
69
+ UI.success("Successfully uploaded files to release: #{version}")
70
+ end
71
+
72
+ #####################################################
73
+ # @!group Documentation
74
+ #####################################################
75
+
76
+ def self.description
77
+ "Upload one or more sourcemap(s) to a release of a project on Sentry"
78
+ end
79
+
80
+ def self.details
81
+ [
82
+ "This action allows you to upload one or more sourcemap(s) to a release of a project on Sentry.",
83
+ "See https://docs.sentry.io/learn/cli/releases/#upload-sourcemaps for more information."
84
+ ].join(" ")
85
+ end
86
+
87
+ def self.available_options
88
+ Helper::SentryConfig.common_api_config_items + [
89
+ FastlaneCore::ConfigItem.new(key: :version,
90
+ description: "Release version on Sentry"),
91
+ FastlaneCore::ConfigItem.new(key: :app_identifier,
92
+ short_option: "-a",
93
+ env_name: "SENTRY_APP_IDENTIFIER",
94
+ description: "App Bundle Identifier, prepended to version",
95
+ optional: true),
96
+ FastlaneCore::ConfigItem.new(key: :build,
97
+ short_option: "-b",
98
+ description: "Release build on Sentry",
99
+ optional: true),
100
+ FastlaneCore::ConfigItem.new(key: :dist,
101
+ description: "Distribution in release",
102
+ optional: true),
103
+ FastlaneCore::ConfigItem.new(key: :sourcemap,
104
+ description: "Path or an array of paths to the sourcemap(s) to upload",
105
+ type: Array,
106
+ verify_block: proc do |values|
107
+ [*values].each do |value|
108
+ UI.user_error! "Could not find sourcemap at path '#{value}'" unless File.exist?(value)
109
+ end
110
+ end),
111
+ FastlaneCore::ConfigItem.new(key: :rewrite,
112
+ description: "Rewrite the sourcemaps before upload",
113
+ default_value: false,
114
+ is_string: false,
115
+ optional: true),
116
+ FastlaneCore::ConfigItem.new(key: :strip_prefix,
117
+ conflicting_options: [:strip_common_prefix],
118
+ description: "Chop-off a prefix from uploaded files. Strips the given prefix from all \
119
+ sources references inside the upload sourcemaps (paths used within the sourcemap \
120
+ content, to map minified code to it's original source). Only sources that start \
121
+ with the given prefix will be stripped. This will not modify the uploaded sources \
122
+ paths",
123
+ optional: true),
124
+ FastlaneCore::ConfigItem.new(key: :strip_common_prefix,
125
+ conflicting_options: [:strip_prefix],
126
+ description: "Automatically guess what the common prefix is and chop that one off",
127
+ default_value: false,
128
+ is_string: false,
129
+ optional: true),
130
+ FastlaneCore::ConfigItem.new(key: :url_prefix,
131
+ description: "Sets a URL prefix in front of all files",
132
+ optional: true),
133
+ FastlaneCore::ConfigItem.new(key: :url_suffix,
134
+ description: "Sets a URL suffix to append to all filenames",
135
+ optional: true),
136
+ FastlaneCore::ConfigItem.new(key: :note,
137
+ description: "Adds an optional note to the uploaded artifact bundle",
138
+ optional: true),
139
+ FastlaneCore::ConfigItem.new(key: :validate,
140
+ description: "Enable basic sourcemap validation",
141
+ is_string: false,
142
+ optional: true),
143
+ FastlaneCore::ConfigItem.new(key: :decompress,
144
+ description: "Enable files gzip decompression prior to upload",
145
+ is_string: false,
146
+ optional: true),
147
+ FastlaneCore::ConfigItem.new(key: :wait,
148
+ description: "Wait for the server to fully process uploaded files",
149
+ is_string: false,
150
+ optional: true),
151
+ FastlaneCore::ConfigItem.new(key: :wait_for,
152
+ description: "Wait for the server to fully process uploaded files, but at most \
153
+ for the given number of seconds",
154
+ type: Integer,
155
+ optional: true),
156
+ FastlaneCore::ConfigItem.new(key: :no_sourcemap_reference,
157
+ description: "Disable emitting of automatic sourcemap references. By default the \
158
+ tool will store a 'Sourcemap' header with minified files so that sourcemaps \
159
+ are located automatically if the tool can detect a link. If this causes issues \
160
+ it can be disabled",
161
+ is_string: false,
162
+ optional: true),
163
+ FastlaneCore::ConfigItem.new(key: :debug_id_reference,
164
+ description: "Enable emitting of automatic debug id references. By default Debug ID \
165
+ reference has to be present both in the source and the related sourcemap. But in \
166
+ cases of binary bundles, the tool can't verify presence of the Debug ID. This flag \
167
+ allows use of Debug ID from the linked sourcemap",
168
+ is_string: false,
169
+ optional: true),
170
+ FastlaneCore::ConfigItem.new(key: :bundle,
171
+ description: "Path to the application bundle (indexed, file, or regular)",
172
+ optional: true),
173
+ FastlaneCore::ConfigItem.new(key: :bundle_sourcemap,
174
+ description: "Path to the bundle sourcemap",
175
+ optional: true),
176
+ FastlaneCore::ConfigItem.new(key: :ext,
177
+ description: "Set the file extensions that are considered for upload. This overrides \
178
+ the default extensions. To add an extension, all default extensions must be repeated. \
179
+ Specify once per extension. Defaults to: js, cjs, mjs, map, jsbundle, bundle",
180
+ type: Array,
181
+ optional: true),
182
+ FastlaneCore::ConfigItem.new(key: :strict,
183
+ description: "Fail with a non-zero exit code if the specified source map file cannot be uploaded",
184
+ is_string: false,
185
+ optional: true),
186
+ FastlaneCore::ConfigItem.new(key: :ignore,
187
+ description: "Ignores all files and folders matching the given glob or array of globs",
188
+ is_string: false,
189
+ optional: true),
190
+ FastlaneCore::ConfigItem.new(key: :ignore_file,
191
+ description: "Ignore all files and folders specified in the given ignore file, e.g. .gitignore",
192
+ optional: true)
193
+ ]
194
+ end
195
+
196
+ def self.return_value
197
+ nil
198
+ end
199
+
200
+ def self.authors
201
+ ["wschurman"]
202
+ end
203
+
204
+ def self.is_supported?(platform)
205
+ true
206
+ end
207
+ end
208
+ end
209
+ end
@@ -0,0 +1,100 @@
1
+ module Fastlane
2
+ module Helper
3
+ class SentryConfig
4
+ def self.common_cli_config_items
5
+ [
6
+ FastlaneCore::ConfigItem.new(key: :sentry_cli_path,
7
+ env_name: "SENTRY_CLI_PATH",
8
+ description: "Path to your sentry-cli. Defaults to `which sentry-cli`",
9
+ optional: true,
10
+ verify_block: proc do |value|
11
+ UI.user_error! "'#{value}' is not executable" unless FastlaneCore::Helper.executable?(value)
12
+ end)
13
+ ]
14
+ end
15
+
16
+ def self.common_api_config_items
17
+ [
18
+ FastlaneCore::ConfigItem.new(key: :url,
19
+ env_name: "SENTRY_URL",
20
+ description: "Url for Sentry",
21
+ is_string: true,
22
+ optional: true),
23
+ FastlaneCore::ConfigItem.new(key: :auth_token,
24
+ env_name: "SENTRY_AUTH_TOKEN",
25
+ description: "Authentication token for Sentry",
26
+ optional: true),
27
+ FastlaneCore::ConfigItem.new(key: :org_slug,
28
+ env_name: "SENTRY_ORG_SLUG",
29
+ description: "Organization slug for Sentry project",
30
+ optional: true),
31
+ FastlaneCore::ConfigItem.new(key: :project_slug,
32
+ env_name: "SENTRY_PROJECT_SLUG",
33
+ description: "Project slug for Sentry",
34
+ optional: true),
35
+ FastlaneCore::ConfigItem.new(key: :log_level,
36
+ env_name: "SENTRY_LOG_LEVEL",
37
+ description: "Configures the log level used by sentry-cli",
38
+ optional: true,
39
+ verify_block: proc do |value|
40
+ UI.user_error! "Invalid log level '#{value}'" unless ['trace', 'debug', 'info', 'warn', 'error'].include? value.downcase
41
+ end)
42
+ ] + self.common_cli_config_items
43
+ end
44
+
45
+ def self.parse_api_params(params)
46
+ require 'shellwords'
47
+
48
+ url = params[:url]
49
+ auth_token = params[:auth_token]
50
+ org = params[:org_slug]
51
+ project = params[:project_slug]
52
+ log_level = params[:log_level]
53
+
54
+ has_org = !org.to_s.empty?
55
+ has_project = !project.to_s.empty?
56
+ has_auth_token = !auth_token.to_s.empty?
57
+
58
+ ENV['SENTRY_URL'] = url unless url.to_s.empty?
59
+
60
+ if log_level.to_s.empty?
61
+ ENV['SENTRY_LOG_LEVEL'] = 'debug' if FastlaneCore::Globals.verbose?
62
+ else
63
+ ENV['SENTRY_LOG_LEVEL'] = log_level
64
+ end
65
+
66
+ # Fallback to .sentryclirc if possible when no auth token is provided
67
+ if !has_auth_token && fallback_sentry_cli_auth(params)
68
+ UI.important("No auth config provided, will fallback to .sentryclirc")
69
+ else
70
+ # Will fail if no authentication token is provided
71
+ unless has_auth_token
72
+ UI.user_error!("No authentication token found for SentryAction given, pass using `auth_token: 'token'`")
73
+ end
74
+ ENV['SENTRY_AUTH_TOKEN'] = auth_token unless auth_token.to_s.empty?
75
+ end
76
+
77
+ if has_org && has_project
78
+ ENV['SENTRY_ORG'] = Shellwords.escape(org) unless org.to_s.empty?
79
+ ENV['SENTRY_PROJECT'] = Shellwords.escape(project) unless project.to_s.empty?
80
+ elsif !has_org
81
+ UI.important("Missing 'org_slug' parameter. Provide both 'org_slug' and 'project_slug'. Falling back to .sentryclirc")
82
+ elsif !has_project
83
+ UI.important("Missing 'project_slug' parameter. Provide both 'org_slug' and 'project_slug'. Falling back to .sentryclirc")
84
+ end
85
+ end
86
+
87
+ def self.fallback_sentry_cli_auth(params)
88
+ sentry_cli_result = JSON.parse(SentryHelper.call_sentry_cli(
89
+ params,
90
+ [
91
+ "info",
92
+ "--config-status-json"
93
+ ]
94
+ ))
95
+ return (sentry_cli_result["auth"]["successful"] &&
96
+ !sentry_cli_result["auth"]["type"].nil?)
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,87 @@
1
+ require 'os'
2
+
3
+ module Fastlane
4
+ module Helper
5
+ class SentryHelper
6
+ def self.find_and_check_sentry_cli_path!(params)
7
+ bundled_sentry_cli_path = self.bundled_sentry_cli_path
8
+ bundled_sentry_cli_version = Gem::Version.new(`#{bundled_sentry_cli_path} --version`.scan(/(?:\d+\.?){3}/).first)
9
+
10
+ sentry_cli_path = params[:sentry_cli_path] || bundled_sentry_cli_path
11
+
12
+ sentry_cli_version = Gem::Version.new(`#{sentry_cli_path} --version`.scan(/(?:\d+\.?){3}/).first)
13
+
14
+ if sentry_cli_version < bundled_sentry_cli_version
15
+ UI.user_error!("Your sentry-cli is outdated, please upgrade to at least version #{bundled_sentry_cli_version} and start your lane again!")
16
+ end
17
+
18
+ UI.success("Using sentry-cli #{sentry_cli_version}")
19
+ sentry_cli_path
20
+ end
21
+
22
+ def self.call_sentry_cli(params, sub_command)
23
+ sentry_path = self.find_and_check_sentry_cli_path!(params)
24
+ command = [sentry_path] + sub_command
25
+ UI.message "Starting sentry-cli..."
26
+ require 'open3'
27
+
28
+ final_command = command.map { |arg| Shellwords.escape(arg) }.join(" ")
29
+
30
+ if FastlaneCore::Globals.verbose?
31
+ UI.command(final_command)
32
+ end
33
+
34
+ env = { 'SENTRY_PIPELINE' => "sentry-fastlane-plugin/#{Fastlane::Sentry::VERSION}" }
35
+ Open3.popen3(env, final_command) do |stdin, stdout, stderr, status_thread|
36
+ out_reader = Thread.new do
37
+ output = []
38
+
39
+ stdout.each_line do |line|
40
+ l = line.strip!
41
+ UI.message(l)
42
+ output << l
43
+ end
44
+
45
+ output.join
46
+ end
47
+
48
+ err_reader = Thread.new do
49
+ stderr.each_line do |line|
50
+ UI.message(line.strip!)
51
+ end
52
+ end
53
+
54
+ unless status_thread.value.success?
55
+ UI.user_error!('Error while calling Sentry CLI')
56
+ end
57
+
58
+ err_reader.join
59
+ out_reader.value
60
+ end
61
+ end
62
+
63
+ def self.bundled_sentry_cli_path
64
+ if OS.mac?
65
+ self.bin_folder('sentry-cli-Darwin-universal')
66
+ elsif OS.windows?
67
+ if OS.bits == 64
68
+ self.bin_folder('sentry-cli-Windows-x86_64.exe')
69
+ else
70
+ self.bin_folder('sentry-cli-Windows-i686.exe')
71
+ end
72
+ else
73
+ if OS.bits == 64
74
+ self.bin_folder('sentry-cli-Linux-x86_64')
75
+ else
76
+ self.bin_folder('sentry-cli-Linux-i686')
77
+ end
78
+ end
79
+ end
80
+
81
+ # Get path for files in bin folder. Paths are resolved relative to this file, for which there are also unit tests.
82
+ def self.bin_folder(filename)
83
+ File.expand_path("../../../../../bin/#{filename}", File.dirname(__FILE__))
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,5 @@
1
+ module Fastlane
2
+ module Sentry
3
+ VERSION = ""
4
+ end
5
+ end
@@ -0,0 +1,16 @@
1
+ require 'fastlane/plugin/sentry/version'
2
+
3
+ module Fastlane
4
+ module Sentry
5
+ # Return all .rb files inside the "actions" and "helper" directory
6
+ def self.all_classes
7
+ Dir[File.expand_path('**/{actions,helper}/*.rb', File.dirname(__FILE__))]
8
+ end
9
+ end
10
+ end
11
+
12
+ # By default we want to import all available actions and helpers
13
+ # A plugin can contain any number of actions and plugins
14
+ Fastlane::Sentry.all_classes.each do |current|
15
+ require current
16
+ end
metadata ADDED
@@ -0,0 +1,152 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fastlane-plugin-sentry
3
+ version: !ruby/object:Gem::Version
4
+ version: '0'
5
+ platform: ruby
6
+ authors:
7
+ - Sentry
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-01-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: os
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.1'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 1.1.4
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '1.1'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 1.1.4
33
+ - !ruby/object:Gem::Dependency
34
+ name: bundler
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ type: :development
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: fastlane
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: 2.10.0
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: 2.10.0
61
+ - !ruby/object:Gem::Dependency
62
+ name: pry
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ - !ruby/object:Gem::Dependency
76
+ name: rspec
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ - !ruby/object:Gem::Dependency
90
+ name: rubocop
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ description:
104
+ email: hello@sentry.io
105
+ executables: []
106
+ extensions: []
107
+ extra_rdoc_files: []
108
+ files:
109
+ - LICENSE
110
+ - README.md
111
+ - bin/sentry-cli-Darwin-universal
112
+ - bin/sentry-cli-Linux-i686
113
+ - bin/sentry-cli-Linux-x86_64
114
+ - bin/sentry-cli-Windows-i686.exe
115
+ - bin/sentry-cli-Windows-x86_64.exe
116
+ - lib/fastlane/plugin/sentry.rb
117
+ - lib/fastlane/plugin/sentry/actions/sentry_check_cli_installed.rb
118
+ - lib/fastlane/plugin/sentry/actions/sentry_create_deploy.rb
119
+ - lib/fastlane/plugin/sentry/actions/sentry_create_release.rb
120
+ - lib/fastlane/plugin/sentry/actions/sentry_debug_files_upload.rb
121
+ - lib/fastlane/plugin/sentry/actions/sentry_finalize_release.rb
122
+ - lib/fastlane/plugin/sentry/actions/sentry_set_commits.rb
123
+ - lib/fastlane/plugin/sentry/actions/sentry_upload_build.rb
124
+ - lib/fastlane/plugin/sentry/actions/sentry_upload_proguard.rb
125
+ - lib/fastlane/plugin/sentry/actions/sentry_upload_sourcemap.rb
126
+ - lib/fastlane/plugin/sentry/helper/sentry_config.rb
127
+ - lib/fastlane/plugin/sentry/helper/sentry_helper.rb
128
+ - lib/fastlane/plugin/sentry/version.rb
129
+ homepage: https://github.com/getsentry/sentry-fastlane-plugin
130
+ licenses:
131
+ - MIT
132
+ metadata: {}
133
+ post_install_message:
134
+ rdoc_options: []
135
+ require_paths:
136
+ - lib
137
+ required_ruby_version: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - ">="
140
+ - !ruby/object:Gem::Version
141
+ version: 2.6.0
142
+ required_rubygems_version: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ version: '0'
147
+ requirements: []
148
+ rubygems_version: 3.1.6
149
+ signing_key:
150
+ specification_version: 4
151
+ summary: Upload symbols to Sentry
152
+ test_files: []