fastlane-plugin-luciq_agent_release_tracking 0.1.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: cd71cf314acb1b8ba7b76f6cd776d83658b1ec802e225690aaed727c28e7909f
4
+ data.tar.gz: c804f939bdb34491ea55433c52ca920b3ad4523faa4d4b44531f541257a676e3
5
+ SHA512:
6
+ metadata.gz: 926f5ba839179d68847c3b33887f3dcc9bc97c5ee006b78d41708f96a1ec380d6bc67dd031acefbfdd0a57d84fd97853c948d8177f633f578ecb14efecf6ec1b
7
+ data.tar.gz: d1f6194fd71df8df8a9dcdc111a093fb0d6130b690aa62ec0796bb46746bd25ceb7afde25ff9ffa377021db132026bef9d975839a5b25000e0a280e5bb6216ac
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2025 yousif-ahmed <yousif.lasheen60@gmail.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,124 @@
1
+ # luciq_agent_release_tracking plugin
2
+
3
+ [![fastlane Plugin Badge](https://rawcdn.githack.com/fastlane/fastlane/master/fastlane/assets/plugin-badge.svg)](https://rubygems.org/gems/fastlane-plugin-luciq_agent_release_tracking)
4
+
5
+ ## Getting Started
6
+
7
+ This project is a [_fastlane_](https://github.com/fastlane/fastlane) plugin. To get started with `fastlane-plugin-luciq_agent_release_tracking`, add it to your project by running:
8
+
9
+ ```bash
10
+ fastlane add_plugin luciq_agent_release_tracking
11
+ ```
12
+
13
+ ## About luciq_agent_release_tracking
14
+
15
+ Luciq agent for tracking release builds and uploads to App Store and Play Store with comprehensive metadata reporting. This plugin provides custom actions that wrap the standard Fastlane actions and automatically report build and upload events to Luciq systems for better observability and integration into internal pipelines.
16
+
17
+ ### Available Actions
18
+
19
+ - `luciq_build_ios_app` - Build iOS apps with Luciq agent reporting
20
+ - `luciq_build_android_app` - Build Android apps with Luciq agent reporting
21
+ - `luciq_upload_to_app_store` - Upload iOS builds to App Store with Luciq agent reporting
22
+ - `luciq_upload_to_play_store` - Upload Android builds to Play Store with Luciq agent reporting
23
+
24
+ ### Features
25
+
26
+ - Automatic reporting of build and upload events to Luciq
27
+ - Branch-based tracking for comprehensive release observability
28
+ - Integration with existing Fastlane workflows
29
+ - Support for both iOS and Android platforms
30
+ - Secure API communication with Luciq services
31
+
32
+ ## Example
33
+
34
+ Check out the [example `Fastfile`](fastlane/Fastfile) to see how to use this plugin. Try it by cloning the repo, running `fastlane install_plugins` and `bundle exec fastlane test`.
35
+
36
+ ### Usage Examples
37
+
38
+ #### iOS Build
39
+ ```ruby
40
+ lane :build_ios do
41
+ luciq_build_ios_app(
42
+ branch_name: "main",
43
+ luciq_api_key: ENV["LUCIQ_API_KEY"],
44
+ workspace: "MyApp.xcworkspace",
45
+ scheme: "MyApp",
46
+ export_method: "app-store",
47
+ configuration: "Release"
48
+ )
49
+ end
50
+ ```
51
+
52
+ #### Android Build
53
+ ```ruby
54
+ lane :build_android do
55
+ luciq_build_android_app(
56
+ branch_name: "main",
57
+ luciq_api_key: ENV["LUCIQ_API_KEY"],
58
+ task: "assembleRelease",
59
+ project_dir: "android/",
60
+ properties: {
61
+ "android.injected.signing.store.file" => "keystore.jks",
62
+ "android.injected.signing.store.password" => ENV["KEYSTORE_PASSWORD"],
63
+ "android.injected.signing.key.alias" => "key0",
64
+ "android.injected.signing.key.password" => ENV["KEY_PASSWORD"]
65
+ }
66
+ )
67
+ end
68
+ ```
69
+
70
+ #### iOS Upload
71
+ ```ruby
72
+ lane :upload_ios do
73
+ luciq_upload_to_app_store(
74
+ branch_name: "main",
75
+ luciq_api_key: ENV["LUCIQ_API_KEY"],
76
+ ipa: "path/to/your/app.ipa",
77
+ skip_screenshots: true,
78
+ skip_metadata: true
79
+ )
80
+ end
81
+ ```
82
+
83
+ #### Android Upload
84
+ ```ruby
85
+ lane :upload_android do
86
+ luciq_upload_to_play_store(
87
+ branch_name: "main",
88
+ luciq_api_key: ENV["LUCIQ_API_KEY"],
89
+ package_name: "com.example.app",
90
+ aab: "path/to/your/app.aab",
91
+ track: "internal",
92
+ skip_upload_screenshots: true
93
+ )
94
+ end
95
+ ```
96
+
97
+ ## Run tests for this plugin
98
+
99
+ To run both the tests, and code style validation, run
100
+
101
+ ```
102
+ rake
103
+ ```
104
+
105
+ To automatically fix many of the styling issues, use
106
+ ```
107
+ rubocop -a
108
+ ```
109
+
110
+ ## Issues and Feedback
111
+
112
+ For any other issues and feedback about this plugin, please submit it to this repository.
113
+
114
+ ## Troubleshooting
115
+
116
+ If you have trouble using plugins, check out the [Plugins Troubleshooting](https://docs.fastlane.tools/plugins/plugins-troubleshooting/) guide.
117
+
118
+ ## Using _fastlane_ Plugins
119
+
120
+ For more information about how the `fastlane` plugin system works, check out the [Plugins documentation](https://docs.fastlane.tools/plugins/create-plugin/).
121
+
122
+ ## About _fastlane_
123
+
124
+ _fastlane_ is the easiest way to automate beta deployments and releases for your iOS and Android apps. To learn more, check out [fastlane.tools](https://fastlane.tools).
@@ -0,0 +1,47 @@
1
+ require 'fastlane/action'
2
+ require_relative '../helper/luciq_agent_release_tracking_helper'
3
+
4
+ module Fastlane
5
+ module Actions
6
+ class LuciqAgentReleaseTrackingAction < Action
7
+ def self.run(params)
8
+ UI.message("The luciq_agent_release_tracking plugin is working!")
9
+ end
10
+
11
+ def self.description
12
+ "Luciq agent for tracking release builds and uploads to App Store and Play Store with comprehensive metadata reporting."
13
+ end
14
+
15
+ def self.authors
16
+ ["Luciq Company"]
17
+ end
18
+
19
+ def self.return_value
20
+ # If your method provides a return value, you can describe here what it does
21
+ end
22
+
23
+ def self.details
24
+ # Optional:
25
+ "This Fastlane plugin provides wrapper actions around the standard build and upload actions. It automatically reports build and upload events to the Luciq systems via secure HTTP requests. This allows engineering teams to track release pipeline steps per branch and platform with comprehensive observability and integration into internal systems."
26
+ end
27
+
28
+ def self.available_options
29
+ [
30
+ # FastlaneCore::ConfigItem.new(key: :your_option,
31
+ # env_name: "INSTABUG_STORES_UPLOAD_YOUR_OPTION",
32
+ # description: "A description of your option",
33
+ # optional: false,
34
+ # type: String)
35
+ ]
36
+ end
37
+
38
+ def self.is_supported?(platform)
39
+ # Adjust this if your plugin only works for a particular platform (iOS vs. Android, for example)
40
+ # See: https://docs.fastlane.tools/advanced/#control-configuration-by-lane-and-by-platform
41
+ #
42
+ # [:ios, :mac, :android].include?(platform)
43
+ true
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,174 @@
1
+ require 'fastlane/action'
2
+ require_relative '../helper/luciq_agent_release_tracking_helper'
3
+
4
+ module Fastlane
5
+ module Actions
6
+ class LuciqBuildAndroidAppAction < Action
7
+ def self.run(params)
8
+ UI.message("Starting Luciq Android build...")
9
+
10
+ # Extract Luciq-specific parameters
11
+ branch_name = params[:branch_name]
12
+ luciq_api_key = params[:luciq_api_key]
13
+
14
+ # Validate required parameters
15
+ if branch_name.nil? || branch_name.empty?
16
+ UI.user_error!("branch_name is required for Luciq reporting")
17
+ end
18
+
19
+ # Filter out Luciq-specific parameters before passing to gradle
20
+ filtered_params = Helper::LuciqAgentReleaseTrackingHelper.filter_luciq_params(params, Actions::GradleAction)
21
+
22
+ begin
23
+ # Report build start to Luciq
24
+ Helper::LuciqAgentReleaseTrackingHelper.report_status(
25
+ branch_name:,
26
+ api_key: luciq_api_key,
27
+ status: "inprogress",
28
+ step: "build_app"
29
+ )
30
+
31
+ # Start timing the build
32
+ build_start_time = Time.now
33
+
34
+ # Execute the actual Android build using gradle
35
+ result = Actions::GradleAction.run(filtered_params)
36
+
37
+ # Calculate build time in seconds
38
+ build_time = (Time.now - build_start_time).round
39
+
40
+ # Extract Android build path (APK or AAB)
41
+ build_path = fetch_android_build_path(Actions.lane_context)
42
+
43
+ if build_path.nil? || build_path.empty?
44
+ UI.user_error!("Could not find any generated APK or AAB. Please check your gradle settings.")
45
+ else
46
+ UI.success("Successfully found build artifact(s) at: #{build_path}")
47
+ end
48
+
49
+ # Report build success to Luciq
50
+ Helper::LuciqAgentReleaseTrackingHelper.report_status(
51
+ branch_name:,
52
+ api_key: luciq_api_key,
53
+ status: "success",
54
+ step: "build_app",
55
+ extras: {
56
+ build_time:,
57
+ build_path: Array(build_path)
58
+ }
59
+ )
60
+
61
+ UI.success("Android build completed successfully!")
62
+ result
63
+ rescue StandardError => e
64
+ error_message = Helper::LuciqAgentReleaseTrackingHelper.extract_error_message(e.message, :build_app)
65
+ UI.error("Android build failed: #{error_message}")
66
+
67
+ # Report build failure to Luciq
68
+ Helper::LuciqAgentReleaseTrackingHelper.report_status(
69
+ branch_name:,
70
+ api_key: luciq_api_key,
71
+ status: "failure",
72
+ step: "build_app",
73
+ error_message:
74
+ )
75
+ raise e
76
+ end
77
+ end
78
+
79
+ def self.description
80
+ "Build Android app with Luciq agent metadata reporting"
81
+ end
82
+
83
+ def self.authors
84
+ ["Luciq Company"]
85
+ end
86
+
87
+ def self.return_value
88
+ "Returns the result from gradle action"
89
+ end
90
+
91
+ def self.details
92
+ "This action wraps the standard gradle action and adds Luciq agent metadata reporting. It tracks build events per branch and provides better observability for engineering teams."
93
+ end
94
+
95
+ def self.available_options
96
+ # Start with the original gradle options
97
+ options = Actions::GradleAction.available_options
98
+
99
+ # Add Luciq-specific options
100
+ luciq_options = [
101
+ FastlaneCore::ConfigItem.new(
102
+ key: :branch_name,
103
+ env_name: "LUCIQ_BRANCH_NAME",
104
+ description: "The branch name for tracking builds",
105
+ optional: false,
106
+ type: String
107
+ ),
108
+ FastlaneCore::ConfigItem.new(
109
+ key: :luciq_api_key,
110
+ env_name: "LUCIQ_API_KEY",
111
+ description: "Luciq API key for reporting build events",
112
+ optional: false,
113
+ type: String,
114
+ sensitive: true
115
+ ),
116
+ FastlaneCore::ConfigItem.new(
117
+ key: :luciq_api_base_url,
118
+ env_name: "LUCIQ_API_BASE_URL",
119
+ description: "Luciq API base URL (defaults to https://api.instabug.com)",
120
+ optional: true,
121
+ type: String,
122
+ skip_type_validation: true # Since we don't extract this param
123
+ )
124
+ ]
125
+
126
+ # Combine both sets of options
127
+ options + luciq_options
128
+ end
129
+
130
+ def self.is_supported?(platform)
131
+ platform == :android
132
+ end
133
+
134
+ def self.example_code
135
+ [
136
+ 'luciq_build_android_app(
137
+ branch_name: "main",
138
+ luciq_api_key: "your-api-key",
139
+ task: "assembleRelease",
140
+ project_dir: "android/",
141
+ properties: {
142
+ "android.injected.signing.store.file" => "keystore.jks",
143
+ "android.injected.signing.store.password" => "password",
144
+ "android.injected.signing.key.alias" => "key0",
145
+ "android.injected.signing.key.password" => "password"
146
+ }
147
+ )'
148
+ ]
149
+ end
150
+
151
+ def self.category
152
+ :building
153
+ end
154
+
155
+ # This helper method provides a clean and prioritized way to get the Android build output.
156
+ # It checks for the most common output types in a specific order.
157
+ # This is used to get the build path for the Android build artifact.
158
+ def self.fetch_android_build_path(lane_context)
159
+ build_keys = [
160
+ SharedValues::GRADLE_ALL_APK_OUTPUT_PATHS,
161
+ SharedValues::GRADLE_APK_OUTPUT_PATH,
162
+ SharedValues::GRADLE_ALL_AAB_OUTPUT_PATHS,
163
+ SharedValues::GRADLE_AAB_OUTPUT_PATH
164
+ ]
165
+ build_keys.each do |build_key|
166
+ build_path = lane_context[build_key]
167
+ return build_path if build_path && !build_path.empty?
168
+ end
169
+
170
+ nil
171
+ end
172
+ end
173
+ end
174
+ end
@@ -0,0 +1,152 @@
1
+ require 'fastlane/action'
2
+ require_relative '../helper/luciq_agent_release_tracking_helper'
3
+
4
+ module Fastlane
5
+ module Actions
6
+ class LuciqBuildIosAppAction < Action
7
+ def self.run(params)
8
+ UI.message("Starting Luciq iOS build...")
9
+
10
+ # Extract Luciq-specific parameters
11
+ branch_name = params[:branch_name]
12
+ luciq_api_key = params[:luciq_api_key]
13
+
14
+ # Validate required parameters
15
+ if branch_name.nil? || branch_name.empty?
16
+ UI.user_error!("branch_name is required for Luciq reporting")
17
+ end
18
+
19
+ # Filter out Luciq-specific parameters before passing to build_ios_app
20
+ filtered_params = Helper::LuciqAgentReleaseTrackingHelper.filter_luciq_params(params, Actions::BuildIosAppAction)
21
+
22
+ begin
23
+ # Report build start to Luciq
24
+ Helper::LuciqAgentReleaseTrackingHelper.report_status(
25
+ branch_name:,
26
+ api_key: luciq_api_key,
27
+ status: "inprogress",
28
+ step: "build_app"
29
+ )
30
+
31
+ # Start timing the build
32
+ build_start_time = Time.now
33
+
34
+ # Execute the actual iOS build
35
+ result = Actions::BuildIosAppAction.run(filtered_params)
36
+
37
+ # Calculate build time in seconds
38
+ build_time = (Time.now - build_start_time).round
39
+
40
+ # Extract IPA path from Fastlane environment
41
+ build_path = Actions.lane_context[SharedValues::IPA_OUTPUT_PATH]
42
+
43
+ if build_path
44
+ UI.success("IPA Output Path: #{build_path}")
45
+ else
46
+ UI.error("No IPA path found.")
47
+ end
48
+
49
+ # Report build success to Luciq
50
+ Helper::LuciqAgentReleaseTrackingHelper.report_status(
51
+ branch_name:,
52
+ api_key: luciq_api_key,
53
+ status: "success",
54
+ step: "build_app",
55
+ extras: {
56
+ build_time:,
57
+ build_path: Array(build_path)
58
+ }
59
+ )
60
+
61
+ UI.success("iOS build completed successfully!")
62
+ result
63
+ rescue StandardError => e
64
+ error_message = Helper::LuciqAgentReleaseTrackingHelper.extract_error_message(e.message, :build_app)
65
+ UI.error("iOS build failed: #{error_message}")
66
+
67
+ # Report build failure to Luciq
68
+ Helper::LuciqAgentReleaseTrackingHelper.report_status(
69
+ branch_name:,
70
+ api_key: luciq_api_key,
71
+ status: "failure",
72
+ step: "build_app",
73
+ error_message:
74
+ )
75
+ raise e
76
+ end
77
+ end
78
+
79
+ def self.description
80
+ "Build iOS app with Luciq agent metadata reporting"
81
+ end
82
+
83
+ def self.authors
84
+ ["Luciq Company"]
85
+ end
86
+
87
+ def self.return_value
88
+ "Returns the result from build_ios_app action"
89
+ end
90
+
91
+ def self.details
92
+ "This action wraps the standard build_ios_app action and adds Luciq agent metadata reporting. It tracks build events per branch and provides better observability for engineering teams."
93
+ end
94
+
95
+ def self.available_options
96
+ # Start with the original build_ios_app options
97
+ options = Actions::BuildIosAppAction.available_options
98
+
99
+ # Add Luciq-specific options
100
+ luciq_options = [
101
+ FastlaneCore::ConfigItem.new(
102
+ key: :branch_name,
103
+ env_name: "LUCIQ_BRANCH_NAME",
104
+ description: "The branch name for tracking builds",
105
+ optional: false,
106
+ type: String
107
+ ),
108
+ FastlaneCore::ConfigItem.new(
109
+ key: :luciq_api_key,
110
+ env_name: "LUCIQ_API_KEY",
111
+ description: "Luciq API key for reporting build events",
112
+ optional: false,
113
+ type: String,
114
+ sensitive: true
115
+ ),
116
+ FastlaneCore::ConfigItem.new(
117
+ key: :luciq_api_base_url,
118
+ env_name: "LUCIQ_API_BASE_URL",
119
+ description: "Luciq API base URL (defaults to https://api.instabug.com)",
120
+ optional: true,
121
+ type: String,
122
+ skip_type_validation: true # Since we don't extract this param
123
+ )
124
+ ]
125
+
126
+ # Combine both sets of options
127
+ options + luciq_options
128
+ end
129
+
130
+ def self.is_supported?(platform)
131
+ [:ios, :mac].include?(platform)
132
+ end
133
+
134
+ def self.example_code
135
+ [
136
+ 'luciq_build_ios_app(
137
+ branch_name: "main",
138
+ luciq_api_key: "your-api-key",
139
+ workspace: "MyApp.xcworkspace",
140
+ scheme: "MyApp",
141
+ export_method: "app-store",
142
+ configuration: "Release"
143
+ )'
144
+ ]
145
+ end
146
+
147
+ def self.category
148
+ :building
149
+ end
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,155 @@
1
+ require 'fastlane/action'
2
+ require 'fastlane_core/ipa_file_analyser'
3
+ require_relative '../helper/luciq_agent_release_tracking_helper'
4
+
5
+ module Fastlane
6
+ module Actions
7
+ class LuciqUploadToAppStoreAction < Action
8
+ def self.run(params)
9
+ UI.message("Starting Luciq App Store upload...")
10
+
11
+ # Extract Luciq-specific parameters
12
+ branch_name = params[:branch_name]
13
+ luciq_api_key = params[:luciq_api_key]
14
+
15
+ # Validate required parameters
16
+ if branch_name.nil? || branch_name.empty?
17
+ UI.user_error!("branch_name is required for Luciq reporting")
18
+ end
19
+
20
+ # Filter out Luciq-specific parameters before passing to upload_to_app_store
21
+ filtered_params = Helper::LuciqAgentReleaseTrackingHelper.filter_luciq_params(params, Actions::UploadToAppStoreAction)
22
+
23
+ begin
24
+ # Report upload start to Luciq
25
+ Helper::LuciqAgentReleaseTrackingHelper.report_status(
26
+ branch_name:,
27
+ api_key: luciq_api_key,
28
+ status: "inprogress",
29
+ step: "upload_to_store"
30
+ )
31
+
32
+ # Execute the actual upload to App Store
33
+ result = Actions::UploadToAppStoreAction.run(filtered_params)
34
+
35
+ # Extract version information for iOS
36
+ version_string = detect_app_version(params)
37
+
38
+ # Report upload success to Luciq
39
+ Helper::LuciqAgentReleaseTrackingHelper.report_status(
40
+ branch_name:,
41
+ api_key: luciq_api_key,
42
+ status: "success",
43
+ step: "upload_to_store",
44
+ extras: {
45
+ version_string:
46
+ }.compact
47
+ )
48
+
49
+ UI.success("App Store upload completed successfully!")
50
+ result
51
+ rescue StandardError => e
52
+ error_message = Helper::LuciqAgentReleaseTrackingHelper.extract_error_message(e.message, :upload_to_store)
53
+
54
+ UI.error("App Store upload failed: #{error_message}")
55
+
56
+ # Report upload failure to Luciq
57
+ Helper::LuciqAgentReleaseTrackingHelper.report_status(
58
+ branch_name:,
59
+ api_key: luciq_api_key,
60
+ status: "failure",
61
+ step: "upload_to_store",
62
+ error_message:
63
+ )
64
+ raise e
65
+ end
66
+ end
67
+
68
+ def self.description
69
+ "Upload to App Store with Luciq agent metadata reporting"
70
+ end
71
+
72
+ def self.authors
73
+ ["Luciq Company"]
74
+ end
75
+
76
+ def self.return_value
77
+ "Returns the result from upload_to_app_store action"
78
+ end
79
+
80
+ def self.details
81
+ "This action wraps the standard upload_to_app_store action and adds Luciq agent metadata reporting. It tracks upload events per branch and provides better observability for engineering teams."
82
+ end
83
+
84
+ def self.available_options
85
+ # Start with the original upload_to_app_store options
86
+ options = Actions::UploadToAppStoreAction.available_options
87
+
88
+ # Add Luciq-specific options
89
+ luciq_options = [
90
+ FastlaneCore::ConfigItem.new(
91
+ key: :branch_name,
92
+ env_name: "LUCIQ_BRANCH_NAME",
93
+ description: "The branch name for tracking uploads",
94
+ optional: false,
95
+ type: String
96
+ ),
97
+ FastlaneCore::ConfigItem.new(
98
+ key: :luciq_api_key,
99
+ env_name: "LUCIQ_API_KEY",
100
+ description: "Luciq API key for reporting upload events",
101
+ optional: false,
102
+ type: String,
103
+ sensitive: true
104
+ )
105
+ ]
106
+
107
+ # Combine both sets of options
108
+ options + luciq_options
109
+ end
110
+
111
+ def self.is_supported?(platform)
112
+ [:ios, :mac].include?(platform)
113
+ end
114
+
115
+ def self.example_code
116
+ [
117
+ 'luciq_upload_to_app_store(
118
+ branch_name: "main",
119
+ luciq_api_key: "your-api-key",
120
+ ipa: "path/to/your.ipa",
121
+ skip_screenshots: true,
122
+ skip_metadata: true
123
+ )'
124
+ ]
125
+ end
126
+
127
+ def self.category
128
+ :app_store_connect
129
+ end
130
+
131
+ # Detect app version
132
+ def self.detect_app_version(params)
133
+ return params[:app_version] if params[:app_version]
134
+
135
+ ipa = params[:ipa] || Actions.lane_context[SharedValues::IPA_OUTPUT_PATH]
136
+ return nil unless ipa && File.exist?(ipa)
137
+
138
+ begin
139
+ version = FastlaneCore::IpaFileAnalyser.fetch_app_version(ipa)
140
+
141
+ if version.to_s.strip.empty?
142
+ UI.error("Could not extract version from IPA")
143
+ return nil
144
+ end
145
+
146
+ UI.success("Found app version: #{version}")
147
+ version
148
+ rescue StandardError => e
149
+ UI.verbose("Could not extract version from IPA: #{e.message}")
150
+ nil
151
+ end
152
+ end
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,172 @@
1
+ require 'fastlane/action'
2
+ require_relative '../helper/luciq_agent_release_tracking_helper'
3
+
4
+ module Fastlane
5
+ module Actions
6
+ class LuciqUploadToPlayStoreAction < Action
7
+ def self.run(params)
8
+ UI.message("Starting Luciq Play Store upload...")
9
+
10
+ # Extract Luciq-specific parameters
11
+ branch_name = params[:branch_name]
12
+ luciq_api_key = params[:luciq_api_key]
13
+
14
+ # Validate required parameters
15
+ if branch_name.nil? || branch_name.empty?
16
+ UI.user_error!("branch_name is required for Luciq reporting")
17
+ end
18
+
19
+ # Filter out Luciq-specific parameters before passing to upload_to_play_store
20
+ filtered_params = Helper::LuciqAgentReleaseTrackingHelper.filter_luciq_params(params, Actions::UploadToPlayStoreAction)
21
+
22
+ begin
23
+ # Report upload start to Luciq
24
+ Helper::LuciqAgentReleaseTrackingHelper.report_status(
25
+ branch_name:,
26
+ api_key: luciq_api_key,
27
+ status: "inprogress",
28
+ step: "upload_to_store"
29
+ )
30
+
31
+ # Execute the actual upload to Play Store
32
+ result = Actions::UploadToPlayStoreAction.run(filtered_params)
33
+
34
+ # Extract version information for Android
35
+ version_code = detect_version_code(params)
36
+
37
+ # Report upload success to Luciq
38
+ Helper::LuciqAgentReleaseTrackingHelper.report_status(
39
+ branch_name:,
40
+ api_key: luciq_api_key,
41
+ status: "success",
42
+ step: "upload_to_store",
43
+ extras: {
44
+ version_code:
45
+ }.compact
46
+ )
47
+
48
+ UI.success("Play Store upload completed successfully!")
49
+ result
50
+ rescue StandardError => e
51
+ error_message = Helper::LuciqAgentReleaseTrackingHelper.extract_error_message(e.message, :upload_to_store)
52
+ UI.error("Play Store upload failed: #{error_message}")
53
+
54
+ # Report upload failure to Luciq
55
+ Helper::LuciqAgentReleaseTrackingHelper.report_status(
56
+ branch_name:,
57
+ api_key: luciq_api_key,
58
+ status: "failure",
59
+ step: "upload_to_store",
60
+ error_message:
61
+ )
62
+ raise e
63
+ end
64
+ end
65
+
66
+ def self.description
67
+ "Upload to Play Store with Luciq agent metadata reporting"
68
+ end
69
+
70
+ def self.authors
71
+ ["Luciq Company"]
72
+ end
73
+
74
+ def self.return_value
75
+ "Returns the result from upload_to_play_store action"
76
+ end
77
+
78
+ def self.details
79
+ "This action wraps the standard upload_to_play_store action and adds Luciq agent metadata reporting. It tracks upload events per branch and provides better observability for engineering teams."
80
+ end
81
+
82
+ def self.available_options
83
+ # Start with the original upload_to_play_store options
84
+ options = Actions::UploadToPlayStoreAction.available_options
85
+
86
+ # Add Luciq-specific options
87
+ luciq_options = [
88
+ FastlaneCore::ConfigItem.new(
89
+ key: :branch_name,
90
+ env_name: "LUCIQ_BRANCH_NAME",
91
+ description: "The branch name for tracking uploads",
92
+ optional: false,
93
+ type: String
94
+ ),
95
+ FastlaneCore::ConfigItem.new(
96
+ key: :luciq_api_key,
97
+ env_name: "LUCIQ_API_KEY",
98
+ description: "Luciq API key for reporting upload events",
99
+ optional: false,
100
+ type: String,
101
+ sensitive: true
102
+ )
103
+ ]
104
+
105
+ # Combine both sets of options
106
+ options + luciq_options
107
+ end
108
+
109
+ def self.is_supported?(platform)
110
+ platform == :android
111
+ end
112
+
113
+ def self.example_code
114
+ [
115
+ 'luciq_upload_to_play_store(
116
+ branch_name: "main",
117
+ luciq_api_key: "your-api-key",
118
+ package_name: "com.example.app",
119
+ aab: "path/to/your.aab",
120
+ track: "internal",
121
+ skip_upload_screenshots: true
122
+ )'
123
+ ]
124
+ end
125
+
126
+ def self.category
127
+ :google_play_console
128
+ end
129
+
130
+ def self.detect_version_code(params)
131
+ return params[:version_code] if params[:version_code]
132
+
133
+ UI.message("Fetching latest version code from Google Play Console...")
134
+
135
+ begin
136
+ # Build parameters hash
137
+ google_play_params = build_google_play_params(params)
138
+
139
+ version_codes = Actions::GooglePlayTrackVersionCodesAction.run(google_play_params)
140
+
141
+ if version_codes&.any?
142
+ latest_version = version_codes.first
143
+ UI.success("Found latest version code: #{latest_version}")
144
+ latest_version
145
+ else
146
+ UI.error("No version codes found on Google Play")
147
+ nil
148
+ end
149
+ rescue StandardError => e
150
+ UI.error("Failed to fetch from Google Play: #{e.message}")
151
+ nil
152
+ end
153
+ end
154
+
155
+ # Build parameters for google_play_track_version_codes action
156
+ def self.build_google_play_params(params)
157
+ google_params = {
158
+ package_name: params[:package_name],
159
+ track: params[:track] || 'production'
160
+ }
161
+
162
+ # Add authentication parameters
163
+ auth_keys = %i[key issuer json_key json_key_data root_url timeout]
164
+ auth_keys.each do |key|
165
+ google_params[key] = params[key]
166
+ end
167
+
168
+ google_params.compact
169
+ end
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,112 @@
1
+ require 'fastlane_core/ui/ui'
2
+ require 'net/http'
3
+ require 'uri'
4
+ require 'json'
5
+
6
+ module Fastlane
7
+ UI = FastlaneCore::UI unless Fastlane.const_defined?(:UI)
8
+
9
+ module Helper
10
+ class LuciqAgentReleaseTrackingHelper
11
+ # Default Base URL for Luciq API
12
+ DEFAULT_LUCIQ_API_BASE_URL = "https://api.instabug.com".freeze
13
+ LUCIQ_KEYS = %i[branch_name luciq_api_key luciq_api_base_url].freeze
14
+ FASTLANE_ERROR_MESSAGE = {
15
+ build_app: "Your build was triggered but failed during execution. This could be due to missing environment variables or incorrect build credentials. Check CI logs for full details.",
16
+ upload_to_store: "Something went wrong while uploading your build. Check your Fastlane run for more details."
17
+ }
18
+
19
+ # Extract the important part of an error message
20
+ def self.extract_error_message(error_message, step)
21
+ return error_message unless error_message.kind_of?(String)
22
+
23
+ lines = error_message.split("\n")
24
+ start_index = lines.find_index { |line| line.strip.start_with?("* What went wrong:") }
25
+ end_index = lines.find_index { |line| line.strip.start_with?("* Try:") }
26
+
27
+ if start_index && end_index && end_index > start_index
28
+ extracted_lines = lines[(start_index + 1)...end_index].map(&:strip).reject(&:empty?)
29
+ return extracted_lines.join(" ")[0, 250] unless extracted_lines.empty?
30
+ end
31
+
32
+ # Fallback message
33
+ FASTLANE_ERROR_MESSAGE[step]
34
+ end
35
+
36
+ def self.show_message
37
+ UI.message("Hello from the luciq_agent_release_tracking plugin helper!")
38
+ end
39
+
40
+ # Filters out Luciq-specific parameters from the params configuration
41
+ # and returns a new FastlaneCore::Configuration object with only the target action's parameters
42
+ def self.filter_luciq_params(params, target_action_class)
43
+ filtered_config = {}
44
+ params.available_options.each do |option|
45
+ key = option.key
46
+ filtered_config[key] = params[key] unless LUCIQ_KEYS.include?(key)
47
+ end
48
+
49
+ FastlaneCore::Configuration.create(target_action_class.available_options, filtered_config)
50
+ end
51
+
52
+ def self.report_status(branch_name:, api_key:, status:, step:, extras: {}, error_message: nil)
53
+ return unless branch_name.start_with?('crash-fix/instabug-crash-')
54
+
55
+ UI.message("📡 Reporting #{step} status to Luciq for #{branch_name}/#{status}")
56
+
57
+ make_api_request(
58
+ branch_name:,
59
+ status:,
60
+ api_key:,
61
+ step:,
62
+ extras:,
63
+ error_message:
64
+ )
65
+ end
66
+
67
+ def self.make_api_request(branch_name:, status:, api_key:, step:, extras: {}, error_message: nil)
68
+ return unless api_key
69
+
70
+ # Determine API base URL from env var or default
71
+ base_url = ENV['LUCIQ_API_BASE_URL'] || DEFAULT_LUCIQ_API_BASE_URL
72
+ uri = URI.parse("#{base_url}/api/web/public/agent_fastlane/status")
73
+
74
+ payload = {
75
+ branch_name:,
76
+ status:,
77
+ step:,
78
+ extras:,
79
+ error_message:
80
+ }
81
+
82
+ begin
83
+ http = Net::HTTP.new(uri.host, uri.port)
84
+ http.use_ssl = true
85
+ http.read_timeout = 30
86
+ http.open_timeout = 30
87
+
88
+ request = Net::HTTP::Patch.new(uri.path)
89
+ request['Content-Type'] = 'application/json'
90
+ request['Authorization'] = "Bearer #{api_key}"
91
+ request['User-Agent'] = "fastlane-plugin-luciq_agent_release_tracking"
92
+ request.body = payload.to_json
93
+
94
+ response = http.request(request)
95
+
96
+ case response.code.to_i
97
+ when 200..299
98
+ UI.success("✅ Successfully reported to Luciq")
99
+ else
100
+ UI.error("❌ Unknown error reporting to Luciq: #{response.code} #{response.message}")
101
+ end
102
+ rescue Net::TimeoutError
103
+ UI.error("❌ Timeout while reporting to Luciq")
104
+ rescue Net::OpenTimeout
105
+ UI.error("❌ Connection timeout while reporting to Luciq")
106
+ rescue StandardError => e
107
+ UI.error("❌ Error reporting to Luciq: #{e.message}")
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,5 @@
1
+ module Fastlane
2
+ module LuciqAgentReleaseTracking
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,16 @@
1
+ require 'fastlane/plugin/luciq_agent_release_tracking/version'
2
+
3
+ module Fastlane
4
+ module LuciqAgentReleaseTracking
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::LuciqAgentReleaseTracking.all_classes.each do |current|
15
+ require current
16
+ end
metadata ADDED
@@ -0,0 +1,165 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fastlane-plugin-luciq_agent_release_tracking
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Luciq
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2025-09-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: fastlane
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 2.228.0
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 2.228.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '='
74
+ - !ruby/object:Gem::Version
75
+ version: 1.50.2
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '='
81
+ - !ruby/object:Gem::Version
82
+ version: 1.50.2
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop-performance
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop-require_tools
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: simplecov
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ description:
126
+ email: support@luciq.ai
127
+ executables: []
128
+ extensions: []
129
+ extra_rdoc_files: []
130
+ files:
131
+ - LICENSE
132
+ - README.md
133
+ - lib/fastlane/plugin/luciq_agent_release_tracking.rb
134
+ - lib/fastlane/plugin/luciq_agent_release_tracking/actions/luciq_agent_release_tracking_action.rb
135
+ - lib/fastlane/plugin/luciq_agent_release_tracking/actions/luciq_build_android_app_action.rb
136
+ - lib/fastlane/plugin/luciq_agent_release_tracking/actions/luciq_build_ios_app_action.rb
137
+ - lib/fastlane/plugin/luciq_agent_release_tracking/actions/luciq_upload_to_app_store_action.rb
138
+ - lib/fastlane/plugin/luciq_agent_release_tracking/actions/luciq_upload_to_play_store_action.rb
139
+ - lib/fastlane/plugin/luciq_agent_release_tracking/helper/luciq_agent_release_tracking_helper.rb
140
+ - lib/fastlane/plugin/luciq_agent_release_tracking/version.rb
141
+ homepage: https://github.com/Instabug/fastlane-plugin-instabug-stores-upload
142
+ licenses:
143
+ - MIT
144
+ metadata: {}
145
+ post_install_message:
146
+ rdoc_options: []
147
+ require_paths:
148
+ - lib
149
+ required_ruby_version: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - ">="
152
+ - !ruby/object:Gem::Version
153
+ version: 3.2.2
154
+ required_rubygems_version: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - ">="
157
+ - !ruby/object:Gem::Version
158
+ version: '0'
159
+ requirements: []
160
+ rubygems_version: 3.4.10
161
+ signing_key:
162
+ specification_version: 4
163
+ summary: Luciq agent for tracking release builds and uploads to App Store and Play
164
+ Store with comprehensive metadata reporting.
165
+ test_files: []