fastlane-plugin-altoolalt 1.2.0 → 1.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ce6c8c68423d1ba9017e5436c69fa012acf28cece7ac7553656bc49fbfcee8c0
4
- data.tar.gz: b841d6b1d472a044ed2c76ca827bba773fa9e7d151f7648d46856d70b9d70743
3
+ metadata.gz: d6c9572b5575d43a4619c4562a6ab14f0b6b87fc88ff76fa4736261a468da447
4
+ data.tar.gz: 3941b4ed4ed5c1fc8ffcdde9f6714d2d76576145ce8314d5a8095c0e2817ccc3
5
5
  SHA512:
6
- metadata.gz: eb1fa24f8b367aab99595e47485cc29465c5d6dedb52426c46f367f50002fd4327e88e4c12f3efff1becb8d119a46ee052d752c391872f317912e6f350b2b26f
7
- data.tar.gz: 5189d9630f9ba82dbcddf31be0d40d71adb28d1239609d1c31d87734124d1ada5b2bb6c64bea66b3956ec67047e7a667a6c46011e68707bc080196344fa5d0eb
6
+ metadata.gz: 9353a87e96259e966012aefd7edd7a22723f36d2d26f76626bb99d3cbf26ef5a94c239a5378592cdef9447e36c8239acae216817b77b3aa3ce8ad215d00ba59f
7
+ data.tar.gz: b26e26d67c7aba61175828f9890d3b56e9c0ddb4ae3b20fcf797dbda766c5544bfcef18d16ac76485ffe23ecccc15341b9b6998086e31842b9aacf744ba25b3c
data/README.md CHANGED
@@ -1,15 +1,19 @@
1
- # altool plugin
1
+ # altoolalt plugin
2
2
 
3
- [![fastlane Plugin Badge](https://rawcdn.githack.com/fastlane/fastlane/master/fastlane/assets/plugin-badge.svg)](https://rubygems.org/gems/fastlane-plugin-altool)
4
-
5
- <a href="https://travis-ci.org/Shashikant86/fastlane-plugin-altool/"><img src="https://img.shields.io/travis/Shashikant86/fastlane-plugin-altool.svg" /></a>
3
+ [![fastlane Plugin Badge](https://rawcdn.githack.com/fastlane/fastlane/master/fastlane/assets/plugin-badge.svg)](https://rubygems.org/gems/fastlane-plugin-altoolalt)
6
4
 
7
5
  ## Getting Started
8
6
 
9
- This project is a [_fastlane_](https://github.com/fastlane/fastlane) plugin. To get started with `fastlane-plugin-altool`, add it to your project by running:
7
+ This project is a [_fastlane_](https://github.com/fastlane/fastlane) plugin. To get started with `fastlane-plugin-altoolalt`, add it to your project by running:
10
8
 
11
9
  ```bash
12
- fastlane add_plugin altool
10
+ fastlane add_plugin altoolalt
11
+ ```
12
+
13
+ Or add to your `Gemfile`:
14
+
15
+ ```ruby
16
+ gem 'fastlane-plugin-altoolalt'
13
17
  ```
14
18
 
15
19
  ## Pre-requisite
@@ -26,51 +30,125 @@ $ export FASTLANE_PASSWORD="your_super_xecret_password";
26
30
  You can do the same for your choice of shell if you aren't using bash.
27
31
 
28
32
 
29
- ## About altool
33
+ ## About
34
+
35
+ This plugin provides an alternative way to upload IPA files to App Store Connect (formerly iTunes Connect) using Apple's `altool` command-line tool instead of the standard iTMSTransporter.
30
36
 
31
- This plugin can be used to upload IPA to iTunes Connect using altool.
37
+ ### Why use altool?
32
38
 
33
- Currently Fastlane deliver uses iTMSTransporter to upload an ipa files to iTunes Connect but there is slick way to do this using `altool`
39
+ - **Faster uploads** - altool is often faster than iTMSTransporter
40
+ - **Modern API support** - Supports both legacy and modern upload methods
41
+ - **Flexible authentication** - Username/password or API Key authentication
42
+ - **Auto-extraction** (v1.3.0+) - Automatically extracts bundle metadata from IPA
34
43
 
35
- This plugin can be used for uploading generated `ipa` file using Gym to iTunes Connect.
44
+ ### Features
36
45
 
37
- This plugin assume that, you already have that Fastlane setup and your details are configured as ENV variables in `FASTLANE_USER` and `FASTLANE_PASSWORD` by default.
46
+ - Support for both `--upload-app` (legacy) and `--upload-package` (modern) methods
47
+ - ✅ Automatic extraction of bundle_id, bundle_version, and bundle_short_version_string from IPA
48
+ - ✅ API Key authentication (recommended) or username/password
49
+ - ✅ Support for iOS, macOS, tvOS, and visionOS apps
50
+ - ✅ Multi-provider account support
51
+ - ✅ Comprehensive error handling and validation
38
52
 
39
53
 
40
54
  ## Usage
41
55
 
42
- You can configure this plugin using
56
+ ### Basic Usage (Legacy Method)
57
+
58
+ Use the traditional `--upload-app` method with username/password:
43
59
 
44
60
  ```ruby
45
- altool(
46
- username: ENV["FASTLANE_USER"],
47
- password: ENV["FASTLANE_PASSWORD"],
48
- app_type: "ios",
49
- ipa_path: "./build/Your-ipa.ipa",
50
- output_format: "xml",
51
- )
61
+ altoolalt(
62
+ username: ENV["FASTLANE_USER"],
63
+ password: ENV["FASTLANE_PASSWORD"],
64
+ app_type: "ios",
65
+ ipa_path: "./build/Your-ipa.ipa",
66
+ output_format: "xml"
67
+ )
68
+ ```
69
+
70
+ Or with API Key authentication (recommended):
52
71
 
72
+ ```ruby
73
+ altoolalt(
74
+ api_key_id: ENV["APP_STORE_CONNECT_API_KEY_ID"],
75
+ api_issuer: ENV["APP_STORE_CONNECT_ISSUER_ID"],
76
+ app_type: "ios",
77
+ ipa_path: "./build/Your-ipa.ipa",
78
+ output_format: "xml"
79
+ )
53
80
  ```
54
81
 
55
- OR
82
+ ### Modern Method (v1.3.0+) - with Auto-Extraction
83
+
84
+ Use the newer `--upload-package` method with automatic bundle metadata extraction:
56
85
 
57
86
  ```ruby
58
- altool(
59
- api_key_id: "<YOUR_API_KEY_ID>",
60
- api_issuer: "<YOUR_API_ISSUER>",
61
- app_type: "ios",
62
- ipa_path: "./build/Your-ipa.ipa",
63
- output_format: "xml",
64
- )
87
+ altoolalt(
88
+ use_upload_package: true,
89
+ api_key_id: ENV["APP_STORE_CONNECT_API_KEY_ID"],
90
+ api_issuer: ENV["APP_STORE_CONNECT_ISSUER_ID"],
91
+ apple_id: "1234567890", # Your App's Apple ID from App Store Connect
92
+ ipa_path: "./build/Your-ipa.ipa"
93
+ # bundle_id, bundle_version, and bundle_short_version_string
94
+ # are automatically extracted from the IPA!
95
+ )
96
+ ```
97
+
98
+ The plugin will automatically extract `bundle_id`, `bundle_version`, and `bundle_short_version_string` from your IPA's Info.plist. You can still override these if needed:
65
99
 
100
+ ```ruby
101
+ altoolalt(
102
+ use_upload_package: true,
103
+ api_key_id: ENV["APP_STORE_CONNECT_API_KEY_ID"],
104
+ api_issuer: ENV["APP_STORE_CONNECT_ISSUER_ID"],
105
+ apple_id: "1234567890",
106
+ ipa_path: "./build/Your-ipa.ipa",
107
+ bundle_id: "com.example.app", # Optional: override auto-extraction
108
+ bundle_version: "1.0",
109
+ bundle_short_version_string: "1.0.0"
110
+ )
66
111
  ```
67
- Security Note:
68
112
 
69
- This might print the username and password to build console in the commands, pipe the output to ` /dev/null` or use similar approach so that fastlane don't print command to console.
113
+ **Security Note:**
114
+ This might print the username and password to build console in the commands. For username/password authentication, consider using API Keys instead, or pipe the output to `/dev/null`.
115
+
116
+ ## Available Parameters
117
+
118
+ ### Common Parameters
119
+
120
+ | Parameter | Description | Required | Default |
121
+ |-----------|-------------|----------|---------|
122
+ | `ipa_path` | Path to your IPA file | Yes | Most recent .ipa in current directory |
123
+ | `api_key_id` | App Store Connect API Key ID | Yes* | `ENV["ALTOOL_API_KEY"]` |
124
+ | `api_issuer` | App Store Connect API Issuer ID | Yes* | - |
125
+ | `username` | Apple ID username | Yes* | `ENV["FASTLANE_USER"]` |
126
+ | `password` | Apple ID password | Yes* | `ENV["FASTLANE_PASSWORD"]` |
127
+ | `output_format` | Output format (normal, xml, json) | No | `normal` |
128
+
129
+ \* Either `api_key_id`+`api_issuer` OR `username`+`password` is required
130
+
131
+ ### Legacy Method Parameters (use_upload_package: false)
132
+
133
+ | Parameter | Description | Default |
134
+ |-----------|-------------|---------|
135
+ | `app_type` | Platform type (ios, osx, appletvos) | `ios` |
136
+
137
+ ### Modern Method Parameters (use_upload_package: true)
138
+
139
+ | Parameter | Description | Required | Auto-extracted |
140
+ |-----------|-------------|----------|----------------|
141
+ | `use_upload_package` | Enable modern upload method | No (false) | - |
142
+ | `apple_id` | App's Apple ID from App Store Connect | Yes | ❌ No |
143
+ | `platform` | Platform (ios, macos, appletvos, visionos) | No (ios) | - |
144
+ | `bundle_id` | Bundle identifier | No | ✅ Yes |
145
+ | `bundle_version` | CFBundleVersion | No | ✅ Yes |
146
+ | `bundle_short_version_string` | CFBundleShortVersionString | No | ✅ Yes |
147
+ | `provider_public_id` | Provider ID (for multi-provider accounts) | No | - |
70
148
 
71
149
  ## Example Project Repo
72
150
 
73
- This is a example project [Altool-Demo](https://github.com/Shashikant86/Altool-Demo) available on GitHub which has its own README.
151
+ This is an example project [Altool-Demo](https://github.com/Shashikant86/Altool-Demo) available on GitHub which has its own README.
74
152
 
75
153
  ## Run tests for this plugin
76
154
 
@@ -13,34 +13,158 @@ module Fastlane
13
13
  app_type = params[:app_type]
14
14
  ipa_path = "\"#{params[:ipa_path]}\""
15
15
  output_format = params[:output_format]
16
+ use_upload_package = params[:use_upload_package]
16
17
 
17
18
  UI.message("========Validating and Uploading your IPA file to iTunes Connect=========")
19
+
20
+ # Build command based on upload method
21
+ if use_upload_package
22
+ command = build_upload_package_command(altool, params, ipa_path, output_format)
23
+ else
24
+ command = build_upload_app_command(altool, params, ipa_path, app_type, output_format)
25
+ end
26
+
27
+ Actions.sh(command.join(' '))
28
+ UI.message("========It might take long time to fully upload your IPA file=========")
29
+ end
30
+
31
+ def self.build_upload_app_command(altool, params, ipa_path, app_type, output_format)
18
32
  command = [
19
33
  altool,
20
34
  '--upload-app',
35
+ '-f',
36
+ ipa_path,
21
37
  '-t',
22
38
  app_type,
23
- '-f',
39
+ '--output-format',
40
+ output_format
41
+ ]
42
+
43
+ add_authentication(command, params)
44
+ command
45
+ end
46
+
47
+ def self.build_upload_package_command(altool, params, ipa_path, output_format)
48
+ # Get parameters - will try to extract from IPA if not provided
49
+ apple_id = params[:apple_id]
50
+ bundle_version = params[:bundle_version]
51
+ bundle_short_version = params[:bundle_short_version_string]
52
+ bundle_id = params[:bundle_id]
53
+ platform = params[:platform]
54
+
55
+ # Auto-extract bundle metadata from IPA if not all parameters provided
56
+ if bundle_version.to_s.empty? || bundle_short_version.to_s.empty? || bundle_id.to_s.empty?
57
+ UI.message("Attempting to extract bundle metadata from IPA...")
58
+ extracted = extract_bundle_metadata_from_ipa(ipa_path)
59
+
60
+ if extracted
61
+ bundle_id ||= extracted[:bundle_id]
62
+ bundle_version ||= extracted[:bundle_version]
63
+ bundle_short_version ||= extracted[:bundle_short_version_string]
64
+ end
65
+ end
66
+
67
+ # Validate required parameters (after potential extraction)
68
+ if apple_id.to_s.empty?
69
+ UI.user_error!("--upload-package requires apple_id parameter (App ID from App Store Connect)")
70
+ end
71
+
72
+ if bundle_version.to_s.empty? || bundle_short_version.to_s.empty? || bundle_id.to_s.empty?
73
+ UI.user_error!("--upload-package requires: bundle_version, bundle_short_version_string, and bundle_id. " \
74
+ "These can be provided as parameters or will be auto-extracted from the IPA.")
75
+ end
76
+
77
+ command = [
78
+ altool,
79
+ '--upload-package',
24
80
  ipa_path,
81
+ '-t',
82
+ platform || 'ios',
83
+ '--apple-id',
84
+ apple_id,
85
+ '--bundle-version',
86
+ bundle_version,
87
+ '--bundle-short-version-string',
88
+ bundle_short_version,
89
+ '--bundle-id',
90
+ bundle_id,
91
+ '--output-format',
25
92
  output_format
26
93
  ]
27
94
 
95
+ # Add provider-public-id if specified (required for multi-provider accounts with username/password)
96
+ provider_id = params[:provider_public_id]
97
+ if !provider_id.to_s.empty?
98
+ command.concat(['--provider-public-id', provider_id])
99
+ end
100
+
101
+ add_authentication(command, params)
102
+ command
103
+ end
104
+
105
+ def self.add_authentication(command, params)
28
106
  api_key_id = params[:api_key_id]
29
107
  api_issuer = params[:api_issuer]
108
+
30
109
  if !api_key_id.to_s.empty? && !api_issuer.to_s.empty?
31
- command += ['--apiKey', api_key_id, '--apiIssuer', api_issuer]
110
+ command.concat(['--api-key', api_key_id, '--api-issuer', api_issuer])
32
111
  else
33
112
  username = params[:username]
34
113
  ENV["ALTOOL_PASSWORD"] = params[:password]
35
114
  password = "@env:ALTOOL_PASSWORD"
36
115
  if username.to_s.empty? || password.to_s.empty?
37
- UI.user_error!("You must provide either api_key and api_issuer or username and password")
116
+ UI.user_error!("You must provide either api_key_id and api_issuer or username and password")
38
117
  end
39
- command += ['--username', username, '--password', password]
118
+ command.concat(['--username', username, '--password', password])
40
119
  end
120
+ end
41
121
 
42
- Actions.sh(command.join(' '))
43
- UI.message("========It might take long time to fully upload your IPA file=========")
122
+ def self.extract_bundle_metadata_from_ipa(ipa_path)
123
+ require 'tmpdir'
124
+ require 'json'
125
+
126
+ # Remove quotes from ipa_path if present
127
+ clean_ipa_path = ipa_path.gsub(/^"|"$/, '')
128
+
129
+ Dir.mktmpdir do |temp_dir|
130
+ # Extract IPA (which is a ZIP archive)
131
+ unzip_command = "unzip -q \"#{clean_ipa_path}\" -d \"#{temp_dir}\" 2>/dev/null"
132
+ system(unzip_command)
133
+
134
+ # Find Info.plist in the Payload directory
135
+ info_plist_path = Dir.glob("#{temp_dir}/Payload/*.app/Info.plist").first
136
+
137
+ unless info_plist_path
138
+ UI.important("Could not find Info.plist in IPA, will require manual bundle parameters")
139
+ return nil
140
+ end
141
+
142
+ # Convert Info.plist to JSON using plutil
143
+ json_output = `plutil -convert json -o - "#{info_plist_path}" 2>/dev/null`
144
+
145
+ if $?.exitstatus != 0
146
+ UI.important("Failed to parse Info.plist with plutil")
147
+ return nil
148
+ end
149
+
150
+ plist_data = JSON.parse(json_output)
151
+
152
+ metadata = {
153
+ bundle_id: plist_data['CFBundleIdentifier'],
154
+ bundle_version: plist_data['CFBundleVersion'],
155
+ bundle_short_version_string: plist_data['CFBundleShortVersionString']
156
+ }
157
+
158
+ UI.message("Extracted bundle metadata from IPA:")
159
+ UI.message(" Bundle ID: #{metadata[:bundle_id]}")
160
+ UI.message(" Bundle Version: #{metadata[:bundle_version]}")
161
+ UI.message(" Short Version: #{metadata[:bundle_short_version_string]}")
162
+
163
+ metadata
164
+ rescue => e
165
+ UI.important("Error extracting bundle metadata: #{e.message}")
166
+ nil
167
+ end
44
168
  end
45
169
 
46
170
  def self.description
@@ -62,13 +186,26 @@ module Fastlane
62
186
 
63
187
  def self.available_options
64
188
  [
189
+ FastlaneCore::ConfigItem.new(key: :use_upload_package,
190
+ env_name: "ALTOOL_USE_UPLOAD_PACKAGE",
191
+ description: "Use --upload-package instead of --upload-app (newer method, requires additional parameters)",
192
+ default_value: false,
193
+ is_string: false,
194
+ optional: true),
195
+
65
196
  FastlaneCore::ConfigItem.new(key: :app_type,
66
197
  env_name: "ALTOOL_APP_TYPE",
67
- description: "Type or platform of application e.g osx, ios, appletvos ",
198
+ description: "Type or platform of application e.g osx, ios, appletvos (used with --upload-app)",
68
199
  default_value: "ios",
69
200
  is_string: true,
70
201
  optional: true),
71
202
 
203
+ FastlaneCore::ConfigItem.new(key: :platform,
204
+ env_name: "ALTOOL_PLATFORM",
205
+ description: "Platform for --upload-package: macos, ios, appletvos, visionos (defaults to ios if not specified)",
206
+ is_string: true,
207
+ optional: true),
208
+
72
209
  FastlaneCore::ConfigItem.new(key: :ipa_path,
73
210
  env_name: "ALTOOL_IPA_PATH",
74
211
  description: "Path to IPA file ",
@@ -94,7 +231,7 @@ module Fastlane
94
231
  is_string: true,
95
232
  default_value: ENV["FASTLANE_PASSWORD"],
96
233
  optional: true),
97
-
234
+
98
235
  FastlaneCore::ConfigItem.new(key: :api_key_id,
99
236
  env_name: "ALTOOL_API_KEY_ID",
100
237
  description: "Only specify the Key ID without the AuthKey_ and .p8. Place the file in ~/private_keys/ or ~/.private_keys/ or ~/.appstoreconnect/private_keys/ or private_keys/ in directory where altool is excuted",
@@ -108,6 +245,36 @@ module Fastlane
108
245
  is_string: true,
109
246
  optional: true),
110
247
 
248
+ FastlaneCore::ConfigItem.new(key: :apple_id,
249
+ env_name: "ALTOOL_APPLE_ID",
250
+ description: "The Apple ID (App ID) of the app (required for --upload-package)",
251
+ is_string: true,
252
+ optional: true),
253
+
254
+ FastlaneCore::ConfigItem.new(key: :bundle_version,
255
+ env_name: "ALTOOL_BUNDLE_VERSION",
256
+ description: "The CFBundleVersion of the app (required for --upload-package, auto-extracted from IPA if not provided)",
257
+ is_string: true,
258
+ optional: true),
259
+
260
+ FastlaneCore::ConfigItem.new(key: :bundle_short_version_string,
261
+ env_name: "ALTOOL_BUNDLE_SHORT_VERSION_STRING",
262
+ description: "The CFBundleShortVersionString of the app (required for --upload-package, auto-extracted from IPA if not provided)",
263
+ is_string: true,
264
+ optional: true),
265
+
266
+ FastlaneCore::ConfigItem.new(key: :bundle_id,
267
+ env_name: "ALTOOL_BUNDLE_ID",
268
+ description: "The bundle identifier of the app (required for --upload-package, auto-extracted from IPA if not provided)",
269
+ is_string: true,
270
+ optional: true),
271
+
272
+ FastlaneCore::ConfigItem.new(key: :provider_public_id,
273
+ env_name: "ALTOOL_PROVIDER_PUBLIC_ID",
274
+ description: "Provider ID (required for --upload-package with username/password when account has multiple providers)",
275
+ is_string: true,
276
+ optional: true),
277
+
111
278
  FastlaneCore::ConfigItem.new(key: :output_format,
112
279
  env_name: "ALTOOL_OUTPUT_FORMAT",
113
280
  description: "Output formal xml or normal ",
@@ -119,21 +286,45 @@ module Fastlane
119
286
  end
120
287
 
121
288
  def self.example_code
122
- [' altool_alt(
289
+ [
290
+ '# Legacy method using --upload-app (still supported)
291
+ altool_alt(
123
292
  username: ENV["FASTLANE_USER"],
124
293
  password: ENV["FASTLANE_PASSWORD"],
125
294
  app_type: "ios",
126
295
  ipa_path: "./build/Your-ipa.ipa",
127
- output_format: "xml",
128
- )
129
- ',
130
- ' altool_alt(
131
- api_key: "<YOUR_API_KEY_ID>",
296
+ output_format: "xml"
297
+ )',
298
+ '# Legacy method with API Key authentication
299
+ altool_alt(
300
+ api_key_id: "<YOUR_API_KEY_ID>",
132
301
  api_issuer: "<YOUR_API_ISSUER>",
133
302
  app_type: "ios",
134
303
  ipa_path: "./build/Your-ipa.ipa",
135
- output_format: "xml",
136
- )'
304
+ output_format: "xml"
305
+ )',
306
+ '# Newer method using --upload-package with auto-extraction of bundle metadata
307
+ altool_alt(
308
+ use_upload_package: true,
309
+ api_key_id: "<YOUR_API_KEY_ID>",
310
+ api_issuer: "<YOUR_API_ISSUER>",
311
+ platform: "ios",
312
+ ipa_path: "./build/Your-ipa.ipa",
313
+ apple_id: "1234567890"
314
+ )',
315
+ '# Newer method with explicit bundle metadata (if auto-extraction fails)
316
+ altool_alt(
317
+ use_upload_package: true,
318
+ username: ENV["FASTLANE_USER"],
319
+ password: ENV["FASTLANE_PASSWORD"],
320
+ platform: "ios",
321
+ ipa_path: "./build/Your-ipa.ipa",
322
+ apple_id: "1234567890",
323
+ bundle_version: "1.0",
324
+ bundle_short_version_string: "1.0.0",
325
+ bundle_id: "com.example.app",
326
+ output_format: "xml"
327
+ )'
137
328
  ]
138
329
  end
139
330
 
@@ -1,5 +1,5 @@
1
1
  module Fastlane
2
2
  module Altoolalt
3
- VERSION = "1.2.0"
3
+ VERSION = "1.3.0"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fastlane-plugin-altoolalt
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Anand Biligiri
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-07-21 00:00:00.000000000 Z
11
+ date: 2025-11-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pry