fastlane-plugin-match_import_multiple 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 +7 -0
- data/LICENSE +21 -0
- data/README.md +123 -0
- data/lib/fastlane/plugin/match_import_multiple/actions/match_import_multiple_action.rb +144 -0
- data/lib/fastlane/plugin/match_import_multiple/helper/match_import_multiple_helper.rb +16 -0
- data/lib/fastlane/plugin/match_import_multiple/version.rb +5 -0
- data/lib/fastlane/plugin/match_import_multiple.rb +16 -0
- metadata +51 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: f4b052227308ac34eb0e6f3b05c963c1ab386c7506ff8c4be155982e44983763
|
|
4
|
+
data.tar.gz: 898a6d58c6bc0f12909ea7a30238168f615634f52262a976259478c1a73ae53d
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: a0d5310bb184ba21c31df014c4cc6584eef5f67d27a62c54f546b1f211532827329b8956da9a2c1b7353bf18a96029aff9bf3bb573fcb0e91430112faa4cbe93
|
|
7
|
+
data.tar.gz: 7d01e6f41d0d22fe2a71242afe07374dd32daa7f360ca53b5a8a93fd720f3926ad6109af7a0dc28437867b823b6fe2030a37e1e384714d9bab2e2e61ab97147e
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Bogdan Matran <bogdancristian.matran@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,123 @@
|
|
|
1
|
+
# match_import_multiple plugin
|
|
2
|
+
|
|
3
|
+
[](https://rubygems.org/gems/fastlane-plugin-match_import_multiple)
|
|
4
|
+
|
|
5
|
+
## About match_import_multiple
|
|
6
|
+
|
|
7
|
+
Wraps _match_'s `Importer` so a single invocation can register many provisioning profiles for the same certificate. _match_'s built-in `match import` only accepts one `.mobileprovision` (or `.provisionprofile`) per call, which means teams managing many bundle identifiers under the same signing certificate have to run the import once per profile — repeating the same App Store Connect login, the same git/S3 storage round trip, and the same Matchfile configuration each time.
|
|
8
|
+
|
|
9
|
+
This plugin keeps the same UX as upstream `fastlane match import` (cert → p12 → profile prompts, `Matchfile` auto-loaded, all _match_ options forwarded as-is) and only changes the profile argument, which now accepts an `Array` of paths or a comma-separated `String`. The action loops over the profiles and delegates each one to the unmodified upstream `Match::Importer#import_cert`, so behavior stays consistent with stock _fastlane_ and future _fastlane_ releases require no code changes here.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
Add the plugin to your project's `fastlane/Pluginfile`:
|
|
14
|
+
|
|
15
|
+
```ruby
|
|
16
|
+
gem 'fastlane-plugin-match_import_multiple',
|
|
17
|
+
git: 'https://github.com/BogdanMatran/fastlane-plugin-match_import_multiple.git',
|
|
18
|
+
branch: 'main'
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Then make sure your project's `Gemfile` evaluates the `Pluginfile`:
|
|
22
|
+
|
|
23
|
+
```ruby
|
|
24
|
+
# Gemfile
|
|
25
|
+
source "https://rubygems.org"
|
|
26
|
+
gem 'fastlane'
|
|
27
|
+
|
|
28
|
+
plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile')
|
|
29
|
+
eval_gemfile(plugins_path) if File.exist?(plugins_path)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Install:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
bundle install
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Usage
|
|
39
|
+
|
|
40
|
+
### From a `Fastfile`
|
|
41
|
+
|
|
42
|
+
```ruby
|
|
43
|
+
lane :import_profiles do
|
|
44
|
+
match_import_multiple(
|
|
45
|
+
type: "appstore",
|
|
46
|
+
cert_path: "./assets/dist.cer",
|
|
47
|
+
p12_path: "./assets/dist.p12",
|
|
48
|
+
profile_paths: [
|
|
49
|
+
"./assets/AppStore_app.mobileprovision",
|
|
50
|
+
"./assets/AppStore_app.ShareExtension.mobileprovision",
|
|
51
|
+
"./assets/AppStore_app.NotificationExtension.mobileprovision"
|
|
52
|
+
]
|
|
53
|
+
)
|
|
54
|
+
end
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
`git_url`, `username`, `team_id`, and other _match_ options are picked up from your project's `Matchfile` and `Appfile` automatically — exactly like with `fastlane match import`.
|
|
58
|
+
|
|
59
|
+
Run it:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
bundle exec fastlane import_profiles
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### From the CLI (no lane needed)
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
bundle exec fastlane run match_import_multiple \
|
|
69
|
+
type:appstore \
|
|
70
|
+
cert_path:./assets/dist.cer \
|
|
71
|
+
p12_path:./assets/dist.p12 \
|
|
72
|
+
profile_paths:./assets/profile1.mobileprovision,./assets/profile2.mobileprovision
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`profile_paths` accepts a comma-separated string on the CLI; arrays are normalized internally.
|
|
76
|
+
|
|
77
|
+
### Interactive (mirrors upstream `fastlane match import`)
|
|
78
|
+
|
|
79
|
+
If you omit any of `cert_path`, `p12_path`, or `profile_paths`, the plugin prompts for them in the same order as `fastlane match import`:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
bundle exec fastlane run match_import_multiple type:appstore
|
|
83
|
+
# Certificate (.cer) path: ./assets/dist.cer
|
|
84
|
+
# Private key (.p12) path: ./assets/dist.p12
|
|
85
|
+
# Provisioning profile (.mobileprovision or .provisionprofile) path(s),
|
|
86
|
+
# comma-separated, or leave empty to skip: ./a.mobileprovision,./b.mobileprovision
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Parameters
|
|
90
|
+
|
|
91
|
+
| Parameter | Type | Description |
|
|
92
|
+
| ---------------- | --------------- | ---------------------------------------------------------------------------------------------------- |
|
|
93
|
+
| `profile_paths` | `Array[String]` or comma-separated `String` | Provisioning profile paths to import. Prompts interactively if omitted. |
|
|
94
|
+
| `cert_path` | `String` | Path to the `.cer` certificate (shared across all profiles). Prompts interactively if omitted. |
|
|
95
|
+
| `p12_path` | `String` | Path to the `.p12` private key (shared across all profiles). Prompts interactively if omitted. |
|
|
96
|
+
| _all match opts_ | varies | Every option that `fastlane match import` accepts (`type`, `git_url`, `storage_mode`, `api_key_path`, `username`, `team_id`, `skip_certificate_matching`, `force_legacy_encryption`, etc.) is forwarded as-is. |
|
|
97
|
+
|
|
98
|
+
## Behavior notes
|
|
99
|
+
|
|
100
|
+
- **One commit per profile.** Each profile triggers its own `Match::Importer#import_cert` call, which clones the certs repo, copies the profile, and pushes a commit. Importing _N_ profiles produces _N_ commits in the certs repo. This keeps the plugin a thin wrapper around upstream and resilient to future _match_ changes.
|
|
101
|
+
- **`app_identifier` is not required for import.** _match_'s option list declares `app_identifier` as required, but the import flow reads bundle ids from each `.mobileprovision` file directly. The plugin avoids forcing the prompt by passing the configuration straight through to `Match::Importer`, the same way upstream `match import` does.
|
|
102
|
+
- **`Matchfile` is auto-loaded** by the plugin, mirroring upstream's behavior. You don't need to repeat `git_url`, `type`, `storage_mode`, etc. in the action call if your `Matchfile` already sets them.
|
|
103
|
+
|
|
104
|
+
## Run tests for this plugin
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
bundle install
|
|
108
|
+
bundle exec rake
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
This runs RSpec specs and RuboCop. To auto-fix style issues:
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
bundle exec rubocop -a
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Troubleshooting
|
|
118
|
+
|
|
119
|
+
If you have trouble using _fastlane_ plugins, check the [Plugins Troubleshooting](https://docs.fastlane.tools/plugins/plugins-troubleshooting/) guide.
|
|
120
|
+
|
|
121
|
+
## About _fastlane_
|
|
122
|
+
|
|
123
|
+
_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,144 @@
|
|
|
1
|
+
require 'fastlane/action'
|
|
2
|
+
require 'fastlane_core/configuration/configuration'
|
|
3
|
+
require_relative '../helper/match_import_multiple_helper'
|
|
4
|
+
|
|
5
|
+
module Fastlane
|
|
6
|
+
module Actions
|
|
7
|
+
class MatchImportMultipleAction < Action
|
|
8
|
+
def self.run(params)
|
|
9
|
+
require 'match/importer'
|
|
10
|
+
|
|
11
|
+
# Mirror what `fastlane match import` does after building its Configuration:
|
|
12
|
+
# load Matchfile defaults so storage/type/etc are picked up automatically.
|
|
13
|
+
begin
|
|
14
|
+
params.load_configuration_file("Matchfile")
|
|
15
|
+
rescue StandardError
|
|
16
|
+
# Matchfile is optional
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
importer = ::Match::Importer.new
|
|
20
|
+
|
|
21
|
+
# Same prompt order as upstream `fastlane match import`: cert -> p12 -> profile(s).
|
|
22
|
+
cert_path = importer.ensure_valid_file_path(params[:cert_path], "Certificate", ".cer")
|
|
23
|
+
p12_path = importer.ensure_valid_file_path(params[:p12_path], "Private key", ".p12")
|
|
24
|
+
|
|
25
|
+
profile_paths = resolve_profile_paths(params[:profile_paths])
|
|
26
|
+
UI.user_error!("No provisioning profiles provided") if profile_paths.empty?
|
|
27
|
+
|
|
28
|
+
profile_paths.each_with_index do |profile_path, idx|
|
|
29
|
+
UI.message("Importing profile #{idx + 1}/#{profile_paths.length}: #{profile_path}")
|
|
30
|
+
importer.import_cert(
|
|
31
|
+
params,
|
|
32
|
+
cert_path: cert_path,
|
|
33
|
+
p12_path: p12_path,
|
|
34
|
+
profile_path: profile_path
|
|
35
|
+
)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
UI.success("Imported #{profile_paths.length} provisioning profile(s) into the match repo")
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Mirrors upstream match's interactive UX:
|
|
42
|
+
# - Array given -> use as-is
|
|
43
|
+
# - String (single/CSV) -> split on commas
|
|
44
|
+
# - nil -> prompt the user (comma-separated input)
|
|
45
|
+
# Each path is normalized to an absolute path and verified to exist.
|
|
46
|
+
def self.resolve_profile_paths(value)
|
|
47
|
+
raw = case value
|
|
48
|
+
when Array
|
|
49
|
+
value
|
|
50
|
+
when String
|
|
51
|
+
value.split(",")
|
|
52
|
+
when nil
|
|
53
|
+
UI.input("Provisioning profile (.mobileprovision or .provisionprofile) path(s), comma-separated, or leave empty to skip:").split(",")
|
|
54
|
+
else
|
|
55
|
+
UI.user_error!("Invalid profile_paths value: #{value.inspect}")
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
raw.map { |entry| entry.to_s.strip }.reject(&:empty?).map do |path|
|
|
59
|
+
absolute = File.absolute_path(path)
|
|
60
|
+
UI.user_error!("Provisioning profile does not exist at path: #{absolute}") unless File.exist?(absolute)
|
|
61
|
+
absolute
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def self.description
|
|
66
|
+
"Import multiple provisioning profiles into a match repo in a single invocation"
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def self.details
|
|
70
|
+
<<~DETAILS
|
|
71
|
+
Wraps fastlane match's Importer so you can pass an array of provisioning
|
|
72
|
+
profile paths together with a single shared certificate and private key.
|
|
73
|
+
Iterates over the profiles and delegates to the unmodified upstream
|
|
74
|
+
Match::Importer#import_cert. All match options (storage backend, type,
|
|
75
|
+
team, App Store Connect API key, encryption settings, Matchfile, etc.)
|
|
76
|
+
are forwarded as-is.
|
|
77
|
+
DETAILS
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def self.authors
|
|
81
|
+
["Bogdan Matran"]
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def self.return_value
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def self.is_supported?(platform)
|
|
88
|
+
[:ios, :mac].include?(platform)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def self.available_options
|
|
92
|
+
require 'match/options'
|
|
93
|
+
|
|
94
|
+
plugin_options = [
|
|
95
|
+
FastlaneCore::ConfigItem.new(
|
|
96
|
+
key: :profile_paths,
|
|
97
|
+
description: "Array (or comma-separated string) of provisioning profile paths to import. Prompts interactively if omitted, mirroring `fastlane match import`",
|
|
98
|
+
skip_type_validation: true,
|
|
99
|
+
optional: true
|
|
100
|
+
),
|
|
101
|
+
FastlaneCore::ConfigItem.new(
|
|
102
|
+
key: :cert_path,
|
|
103
|
+
description: "Path to the .cer certificate (shared across all profiles). Prompts interactively if omitted",
|
|
104
|
+
type: String,
|
|
105
|
+
optional: true,
|
|
106
|
+
verify_block: proc { |value| UI.user_error!("Certificate not found at path: #{value}") if value && !File.exist?(value) }
|
|
107
|
+
),
|
|
108
|
+
FastlaneCore::ConfigItem.new(
|
|
109
|
+
key: :p12_path,
|
|
110
|
+
description: "Path to the .p12 private key (shared across all profiles). Prompts interactively if omitted",
|
|
111
|
+
type: String,
|
|
112
|
+
optional: true,
|
|
113
|
+
verify_block: proc { |value| UI.user_error!("Private key not found at path: #{value}") if value && !File.exist?(value) }
|
|
114
|
+
)
|
|
115
|
+
]
|
|
116
|
+
|
|
117
|
+
reserved_keys = plugin_options.map(&:key)
|
|
118
|
+
match_options = ::Match::Options.available_options.reject { |opt| reserved_keys.include?(opt.key) }
|
|
119
|
+
|
|
120
|
+
plugin_options + match_options
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def self.example_code
|
|
124
|
+
[
|
|
125
|
+
'match_import_multiple(
|
|
126
|
+
type: "appstore",
|
|
127
|
+
git_url: "git@github.com:your-org/certs.git",
|
|
128
|
+
cert_path: "./assets/dist.cer",
|
|
129
|
+
p12_path: "./assets/dist.p12",
|
|
130
|
+
profile_paths: [
|
|
131
|
+
"./assets/AppStore_app1.mobileprovision",
|
|
132
|
+
"./assets/AppStore_app2.mobileprovision",
|
|
133
|
+
"./assets/AppStore_app3.mobileprovision"
|
|
134
|
+
]
|
|
135
|
+
)'
|
|
136
|
+
]
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def self.category
|
|
140
|
+
:code_signing
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
require 'fastlane_core/ui/ui'
|
|
2
|
+
|
|
3
|
+
module Fastlane
|
|
4
|
+
UI = FastlaneCore::UI unless Fastlane.const_defined?(:UI)
|
|
5
|
+
|
|
6
|
+
module Helper
|
|
7
|
+
class MatchImportMultipleHelper
|
|
8
|
+
# class methods that you define here become available in your action
|
|
9
|
+
# as `Helper::MatchImportMultipleHelper.your_method`
|
|
10
|
+
#
|
|
11
|
+
def self.show_message
|
|
12
|
+
UI.message("Hello from the match_import_multiple plugin helper!")
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
require 'fastlane/plugin/match_import_multiple/version'
|
|
2
|
+
|
|
3
|
+
module Fastlane
|
|
4
|
+
module MatchImportMultiple
|
|
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::MatchImportMultiple.all_classes.each do |current|
|
|
15
|
+
require current
|
|
16
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: fastlane-plugin-match_import_multiple
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Bogdan Matran
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-05-07 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: Wraps fastlane match's Importer to accept an array of provisioning profile
|
|
14
|
+
paths so you can register many profiles for the same certificate in one command,
|
|
15
|
+
instead of running fastlane match import once per profile.
|
|
16
|
+
email: bogdancristian.matran@gmail.com
|
|
17
|
+
executables: []
|
|
18
|
+
extensions: []
|
|
19
|
+
extra_rdoc_files: []
|
|
20
|
+
files:
|
|
21
|
+
- LICENSE
|
|
22
|
+
- README.md
|
|
23
|
+
- lib/fastlane/plugin/match_import_multiple.rb
|
|
24
|
+
- lib/fastlane/plugin/match_import_multiple/actions/match_import_multiple_action.rb
|
|
25
|
+
- lib/fastlane/plugin/match_import_multiple/helper/match_import_multiple_helper.rb
|
|
26
|
+
- lib/fastlane/plugin/match_import_multiple/version.rb
|
|
27
|
+
homepage:
|
|
28
|
+
licenses:
|
|
29
|
+
- MIT
|
|
30
|
+
metadata:
|
|
31
|
+
rubygems_mfa_required: 'true'
|
|
32
|
+
post_install_message:
|
|
33
|
+
rdoc_options: []
|
|
34
|
+
require_paths:
|
|
35
|
+
- lib
|
|
36
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '2.7'
|
|
41
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
42
|
+
requirements:
|
|
43
|
+
- - ">="
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: '0'
|
|
46
|
+
requirements: []
|
|
47
|
+
rubygems_version: 3.3.26
|
|
48
|
+
signing_key:
|
|
49
|
+
specification_version: 4
|
|
50
|
+
summary: Import multiple provisioning profiles into a match repo in a single invocation
|
|
51
|
+
test_files: []
|