fastlane-plugin-rearchive 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: aaaa414623759395560b9da0d24a575fd59678d3327b12ed85d68dbe73bd2fa3
4
+ data.tar.gz: c50be7ac069ee08357a829cf507583a2477022e8af405ff1b38f43d6a7c0ffc2
5
+ SHA512:
6
+ metadata.gz: 996314def095d3347e27e4649435b80fff572e69ca34e227a34394dc031d96e3d2ea052b5be11d161280f2ac5216765e3ea38d3ecbe2f8f0532b7d4e2718168d
7
+ data.tar.gz: 695e6151afae12e91c696caab88a33ca8ca7ccf8ecc2c94744cb17b42bd9f56c0691def6fb80daccb92f1a63aa51a9d74717ce0682c668c9cc9c38a1f40e0cb1
data/LICENSE.md ADDED
@@ -0,0 +1,22 @@
1
+ # The MIT License (MIT)
2
+
3
+ Copyright © 2016 Richard Szalay <richard.szalay@mullenloweprofero.com>
4
+ Copyright © 2023 naoigcat <17925623+naoigcat@users.noreply.github.com>
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,130 @@
1
+ # rearchive plugin
2
+
3
+ [![fastlane Plugin Badge](https://rawcdn.githack.com/fastlane/fastlane/master/fastlane/assets/plugin-badge.svg)](https://rubygems.org/gems/fastlane-plugin-rearchive)
4
+
5
+ ## Getting Started
6
+
7
+ This project is a [_fastlane_](https://github.com/fastlane/fastlane) plugin. To get started with `fastlane-plugin-rearchive`, add it to your project by running:
8
+
9
+ ```bash
10
+ fastlane add_plugin rearchive
11
+ ```
12
+
13
+ ## About rearchive
14
+
15
+ Modify files inside ipa/xcarchive for publishing multiple configurations without rearchiving.
16
+
17
+ ## Example
18
+
19
+ 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`.
20
+
21
+ ## Usage
22
+
23
+ The `resign` is required after executing the following actions.
24
+
25
+ Modify the application Info.plist.
26
+
27
+ ```ruby
28
+ plist_value(
29
+ archive_path: "example/Example.xcarchive",
30
+ plist_values: {
31
+ ":CustomApplicationKey" => "Replaced!"
32
+ }
33
+ )
34
+ ```
35
+
36
+ Execute commands for the application Info.plist.
37
+
38
+ ```ruby
39
+ plist_command(
40
+ archive_path: "example/Example.xcarchive",
41
+ plist_commands: [
42
+ "Delete :DebugApplicationKey"
43
+ ]
44
+ )
45
+ ```
46
+
47
+ Replace icons of the application.
48
+
49
+ ```ruby
50
+ iconset(
51
+ archive_path: "example/Example.xcarchive",
52
+ iconset_path: "example/Blue.appiconset"
53
+ )
54
+ ```
55
+
56
+ Modify a different application plist
57
+
58
+ ```ruby
59
+ plist_value(
60
+ archive_path: "example/Example.xcarchive",
61
+ plist_path: "GoogleService-Info.plist",
62
+ plist_values: {
63
+ ":TRACKING_ID" => "UA-22222222-22"
64
+ }
65
+ )
66
+ ```
67
+
68
+ Prefixing with a / allows you to target any plist in the archive.
69
+
70
+ ```ruby
71
+ plist_value(
72
+ archive_path: "example/Example.xcarchive",
73
+ plist_path: "/Info.plist",
74
+ plist_values: {
75
+ ":TRACKING_ID" => "UA-22222222-22"
76
+ }
77
+ )
78
+ ```
79
+
80
+ Replace a file with a local one (files only - asset catalog items are not supported)
81
+
82
+ ```ruby
83
+ replace_file(
84
+ archive_path: "example/Example.ipa",
85
+ files: {
86
+ "GoogleService-Info.plist" => "example/New-GoogleService-Info.plist"
87
+ }
88
+ )
89
+ ```
90
+
91
+ Remove a file (files only - asset catalog items are not supported)
92
+
93
+ ```ruby
94
+ remove_file(
95
+ archive_path: "example/Example.ipa",
96
+ files: [
97
+ "GoogleService-Info.plist"
98
+ ]
99
+ )
100
+ ```
101
+
102
+ ## Run tests for this plugin
103
+
104
+ To run both the tests, and code style validation, run
105
+
106
+ ```sh
107
+ rake
108
+ ```
109
+
110
+ To automatically fix many of the styling issues, use
111
+
112
+ ```sh
113
+ rubocop -a
114
+ ```
115
+
116
+ ## Issues and Feedback
117
+
118
+ For any other issues and feedback about this plugin, please submit it to this repository.
119
+
120
+ ## Troubleshooting
121
+
122
+ If you have trouble using plugins, check out the [Plugins Troubleshooting](https://docs.fastlane.tools/plugins/plugins-troubleshooting/) guide.
123
+
124
+ ## Using _fastlane_ Plugins
125
+
126
+ For more information about how the `fastlane` plugin system works, check out the [Plugins documentation](https://docs.fastlane.tools/plugins/create-plugin/).
127
+
128
+ ## About _fastlane_
129
+
130
+ _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,141 @@
1
+ require "fastlane/action"
2
+ require "fastlane_core/configuration/config_item"
3
+ require_relative "../helper/archives/ipa_archive"
4
+ require_relative "../helper/archives/xc_archive"
5
+
6
+ module Fastlane
7
+ module Actions
8
+ class IconsetAction < Action
9
+ def self.run(params) # rubocop:disable Metrics/PerceivedComplexity
10
+ archive_path = File.expand_path(params[:archive_path])
11
+ raise "Archive path #{archive_path} does not exist" unless File.exist?(archive_path)
12
+
13
+ iconset_path = File.expand_path(params[:iconset_path])
14
+ iconset_manifest_path = File.expand_path("#{iconset_path}/Contents.json")
15
+ raise ".iconset manifest #{iconset_manifest_path} does not exist" unless File.exist?(iconset_manifest_path)
16
+
17
+ if File.directory?(archive_path)
18
+ archive = RearchiveHelper::XCArchive.new(archive_path)
19
+ else
20
+ archive = RearchiveHelper::IPAArchive.new(archive_path)
21
+ end
22
+ FastlaneCore::UI.message("Patching icons from: #{iconset_path}")
23
+ plist_path = archive.extract(archive.app_path("Info.plist"))
24
+ plist_buddy = RearchiveHelper::PlistBuddy.new(archive.local_path(plist_path))
25
+ plist_buddy.parse_dict_keys(plist_buddy.exec("Print")).filter_map do |key|
26
+ key.match(/^CFBundleIcons(~.+)?$/)
27
+ end.map do |(key, idiom_suffix)|
28
+ [":#{key}:CFBundlePrimaryIcon:CFBundleIconFiles", idiom_suffix]
29
+ end.each do |(icon_files_key, idiom_suffix)|
30
+ plist_buddy.parse_scalar_array(plist_buddy.exec("Print #{icon_files_key}")).map do |name|
31
+ %W[#{name}#{idiom_suffix}* #{name}@*x#{idiom_suffix}*].each do |path|
32
+ archive.delete(archive.app_path(path))
33
+ end
34
+ end
35
+ plist_buddy.exec("Delete #{icon_files_key}")
36
+ rescue RuntimeError => _e
37
+ next
38
+ end
39
+ JSON.parse(File.read(iconset_manifest_path))["images"].select do |image|
40
+ image["filename"]
41
+ end.map do |entry|
42
+ scale_suffix = entry["scale"] == "1x" ? "" : "@#{entry["scale"]}"
43
+ idiom_suffix = entry["idiom"] == "iphone" ? "" : "~#{entry["idiom"]}"
44
+ file_extension = File.extname(entry["filename"])
45
+ {
46
+ source: "#{iconset_path}/#{entry["filename"]}",
47
+ name: "#{File.basename(iconset_path, ".appiconset")}#{entry["size"]}",
48
+ idiom: entry["idiom"],
49
+ target: "#{File.basename(iconset_path, ".appiconset")}#{entry["size"]}#{scale_suffix}#{idiom_suffix}#{file_extension}"
50
+ }
51
+ end.group_by do |icon|
52
+ icon[:idiom]
53
+ end.each do |idiom, icons|
54
+ idiom_suffix = idiom == "iphone" ? "" : "~#{idiom}"
55
+ icons_plist_key = ":CFBundleIcons#{idiom_suffix}:CFBundlePrimaryIcon:CFBundleIconFiles"
56
+ plist_buddy.exec("Add #{icons_plist_key} array")
57
+ icons.each do |i|
58
+ relative_path = archive.app_path((i[:target]).to_s)
59
+ local_path = archive.local_path(relative_path)
60
+ system("cp #{i[:source].shellescape} #{local_path.shellescape}", exception: true)
61
+ archive.replace(relative_path)
62
+ end
63
+ icons.map { |i| i[:name] }.uniq.each_with_index do |key, index|
64
+ plist_buddy.exec("Add #{icons_plist_key}:#{index} string #{key}")
65
+ end
66
+ end
67
+ Dir.mktmpdir do |dir|
68
+ Dir.chdir(dir) do
69
+ IO.popen(%W[
70
+ #{IO.popen("xcode-select -p", &:read).chomp}/usr/bin/actool
71
+ --output-format=human-readable-text
72
+ --notices
73
+ --warnings
74
+ --output-partial-info-plist=assetcatalog_generated_info.plist
75
+ --app-icon=#{File.basename(iconset_path, ".appiconset")}
76
+ --compress-pngs
77
+ --enable-on-demand-resources=YES
78
+ --sticker-pack-identifier-prefix=#{plist_buddy.exec("Print CFBundleIdentifier")}.sticker-pack.
79
+ --development-region=English
80
+ --target-device=iphone
81
+ --target-device=ipad
82
+ --minimum-deployment-target=#{plist_buddy.exec("Print MinimumOSVersion")}
83
+ --platform=iphoneos
84
+ --product-type=com.apple.product-type.application
85
+ --compile
86
+ .
87
+ #{File.dirname(iconset_path)}
88
+ ].map(&:shellescape).join(" "), "r") do |io|
89
+ FastlaneCore::UI.verbose(io.read) if params[:verbose]
90
+ end
91
+ generated_plist_buddy = RearchiveHelper::PlistBuddy.new("assetcatalog_generated_info.plist")
92
+ plist_buddy.parse_dict_keys(generated_plist_buddy.exec("Print")).filter_map do |key|
93
+ key.match(/^CFBundleIcons(~.+)?$/)
94
+ end.each do |key|
95
+ plist_buddy.exec("Delete #{key}")
96
+ end
97
+ plist_buddy.exec("Merge assetcatalog_generated_info.plist")
98
+ archive.replace(plist_path)
99
+ relative_path = archive.app_path("Assets.car")
100
+ local_path = archive.local_path(relative_path)
101
+ system("mkdir -p #{File.dirname(local_path).shellescape}", exception: true)
102
+ system("mv Assets.car #{local_path.shellescape}", exception: true)
103
+ archive.replace(relative_path)
104
+ end
105
+ end
106
+ end
107
+
108
+ def self.description
109
+ "Replace icons inside .ipa/.xcarchive"
110
+ end
111
+
112
+ def self.authors
113
+ ["naoigcat"]
114
+ end
115
+
116
+ def self.available_options
117
+ [
118
+ FastlaneCore::ConfigItem.new(key: :archive_path,
119
+ description: "The path of the .ipa or .xcarchive to be modified",
120
+ optional: false,
121
+ type: String),
122
+
123
+ FastlaneCore::ConfigItem.new(key: :iconset_path,
124
+ description: "The path to iconset to swap into the .ipa or .xcarchive",
125
+ optional: false,
126
+ type: String),
127
+
128
+ FastlaneCore::ConfigItem.new(key: :verbose,
129
+ description: "Display the output of commands",
130
+ optional: true,
131
+ default_value: false,
132
+ type: [TrueClass, FalseClass])
133
+ ]
134
+ end
135
+
136
+ def self.is_supported?(platform)
137
+ [:ios].include?(platform)
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,66 @@
1
+ require "fastlane/action"
2
+ require "fastlane_core/configuration/config_item"
3
+ require_relative "../helper/archives/ipa_archive"
4
+ require_relative "../helper/archives/xc_archive"
5
+ require_relative "../helper/plist_buddy"
6
+
7
+ module Fastlane
8
+ module Actions
9
+ class PlistCommandAction < Action
10
+ def self.run(params)
11
+ archive_path = File.expand_path(params[:archive_path])
12
+ raise "Archive path #{archive_path} does not exist" unless File.exist?(archive_path)
13
+
14
+ if File.directory?(archive_path)
15
+ archive = RearchiveHelper::XCArchive.new(archive_path)
16
+ else
17
+ archive = RearchiveHelper::IPAArchive.new(archive_path)
18
+ end
19
+ if params[:plist_path]
20
+ plist_path = archive.app_path(params[:plist_path])
21
+ else
22
+ plist_path = archive.app_path("Info.plist")
23
+ end
24
+ FastlaneCore::UI.message("Patching Plist: #{plist_path}")
25
+ archive.extract(plist_path)
26
+ plist_buddy = RearchiveHelper::PlistBuddy.new(archive.local_path(plist_path))
27
+ params[:plist_commands].each do |command|
28
+ plist_buddy.exec(command)
29
+ end
30
+ archive.replace(plist_path)
31
+ end
32
+
33
+ def self.description
34
+ "Execute commands for .plists inside .ipa/.xcarchive"
35
+ end
36
+
37
+ def self.authors
38
+ ["naoigcat"]
39
+ end
40
+
41
+ def self.available_options
42
+ [
43
+ FastlaneCore::ConfigItem.new(key: :archive_path,
44
+ description: "The path of the .ipa or .xcarchive to be modified",
45
+ optional: false,
46
+ type: String),
47
+
48
+ FastlaneCore::ConfigItem.new(key: :plist_path,
49
+ description: "The name of the .plist file to modify, relative to the .app bundle",
50
+ optional: true,
51
+ default_value: "Info.plist",
52
+ type: String),
53
+
54
+ FastlaneCore::ConfigItem.new(key: :plist_commands,
55
+ description: "Array of PlistBuddy commands to invoke",
56
+ optional: false,
57
+ type: Array)
58
+ ]
59
+ end
60
+
61
+ def self.is_supported?(platform)
62
+ [:ios].include?(platform)
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,66 @@
1
+ require "fastlane/action"
2
+ require "fastlane_core/configuration/config_item"
3
+ require_relative "../helper/archives/ipa_archive"
4
+ require_relative "../helper/archives/xc_archive"
5
+ require_relative "../helper/plist_buddy"
6
+
7
+ module Fastlane
8
+ module Actions
9
+ class PlistValueAction < Action
10
+ def self.run(params)
11
+ archive_path = File.expand_path(params[:archive_path])
12
+ raise "Archive path #{archive_path} does not exist" unless File.exist?(archive_path)
13
+
14
+ if File.directory?(archive_path)
15
+ archive = RearchiveHelper::XCArchive.new(archive_path)
16
+ else
17
+ archive = RearchiveHelper::IPAArchive.new(archive_path)
18
+ end
19
+ if params[:plist_path]
20
+ plist_path = archive.app_path(params[:plist_path])
21
+ else
22
+ plist_path = archive.app_path("Info.plist")
23
+ end
24
+ FastlaneCore::UI.message("Patching Plist: #{plist_path}")
25
+ archive.extract(plist_path)
26
+ plist_buddy = RearchiveHelper::PlistBuddy.new(archive.local_path(plist_path))
27
+ params[:plist_values].each do |key, value|
28
+ plist_buddy.exec("Set #{key} #{value}")
29
+ end
30
+ archive.replace(plist_path)
31
+ end
32
+
33
+ def self.description
34
+ "Overwrite values of .plists inside .ipa/.xcarchive"
35
+ end
36
+
37
+ def self.authors
38
+ ["naoigcat"]
39
+ end
40
+
41
+ def self.available_options
42
+ [
43
+ FastlaneCore::ConfigItem.new(key: :archive_path,
44
+ description: "The path of the .ipa or .xcarchive to be modified",
45
+ optional: false,
46
+ type: String),
47
+
48
+ FastlaneCore::ConfigItem.new(key: :plist_path,
49
+ description: "The name of the .plist file to modify, relative to the .app bundle",
50
+ optional: true,
51
+ default_value: "Info.plist",
52
+ type: String),
53
+
54
+ FastlaneCore::ConfigItem.new(key: :plist_values,
55
+ description: "Hash of plist values to set to the plist file",
56
+ optional: false,
57
+ type: Hash)
58
+ ]
59
+ end
60
+
61
+ def self.is_supported?(platform)
62
+ [:ios].include?(platform)
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,52 @@
1
+ require "fastlane/action"
2
+ require "fastlane_core/configuration/config_item"
3
+ require_relative "../helper/archives/ipa_archive"
4
+ require_relative "../helper/archives/xc_archive"
5
+
6
+ module Fastlane
7
+ module Actions
8
+ class RemoveFileAction < Action
9
+ def self.run(params)
10
+ archive_path = File.expand_path(params[:archive_path])
11
+ raise "Archive path #{archive_path} does not exist" unless File.exist?(archive_path)
12
+
13
+ if File.directory?(archive_path)
14
+ archive = RearchiveHelper::XCArchive.new(archive_path)
15
+ else
16
+ archive = RearchiveHelper::IPAArchive.new(archive_path)
17
+ end
18
+ params[:files].each do |file|
19
+ FastlaneCore::UI.message("Deleting #{file}")
20
+ relative_path = archive.app_path(file)
21
+ archive.delete(relative_path)
22
+ end
23
+ end
24
+
25
+ def self.description
26
+ "Remove files inside .ipa/.xcarchive"
27
+ end
28
+
29
+ def self.authors
30
+ ["naoigcat"]
31
+ end
32
+
33
+ def self.available_options
34
+ [
35
+ FastlaneCore::ConfigItem.new(key: :archive_path,
36
+ description: "The path of the .ipa or .xcarchive to be modified",
37
+ optional: false,
38
+ type: String),
39
+
40
+ FastlaneCore::ConfigItem.new(key: :files,
41
+ description: "Files that should be removed",
42
+ optional: false,
43
+ type: Array)
44
+ ]
45
+ end
46
+
47
+ def self.is_supported?(platform)
48
+ [:ios].include?(platform)
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,55 @@
1
+ require "fastlane/action"
2
+ require "fastlane_core/configuration/config_item"
3
+ require_relative "../helper/archives/ipa_archive"
4
+ require_relative "../helper/archives/xc_archive"
5
+
6
+ module Fastlane
7
+ module Actions
8
+ class ReplaceFileAction < Action
9
+ def self.run(params)
10
+ archive_path = File.expand_path(params[:archive_path])
11
+ raise "Archive path #{archive_path} does not exist" unless File.exist?(archive_path)
12
+
13
+ if File.directory?(archive_path)
14
+ archive = RearchiveHelper::XCArchive.new(archive_path)
15
+ else
16
+ archive = RearchiveHelper::IPAArchive.new(archive_path)
17
+ end
18
+ params[:files].each do |old_file, new_file|
19
+ FastlaneCore::UI.message("Replacing #{old_file}")
20
+ relative_path = archive.app_path(old_file)
21
+ local_path = archive.local_path(relative_path)
22
+ system("mkdir -p #{File.dirname(local_path).shellescape}", exception: true)
23
+ system("cp #{new_file.shellescape} #{local_path.shellescape}", exception: true)
24
+ archive.replace(relative_path)
25
+ end
26
+ end
27
+
28
+ def self.description
29
+ "Replace files inside .ipa/.xcarchive"
30
+ end
31
+
32
+ def self.authors
33
+ ["naoigcat"]
34
+ end
35
+
36
+ def self.available_options
37
+ [
38
+ FastlaneCore::ConfigItem.new(key: :archive_path,
39
+ description: "The path of the .ipa or .xcarchive to be modified",
40
+ optional: false,
41
+ type: String),
42
+
43
+ FastlaneCore::ConfigItem.new(key: :replace_files,
44
+ description: "Files that should be replaced",
45
+ optional: false,
46
+ type: Hash)
47
+ ]
48
+ end
49
+
50
+ def self.is_supported?(platform)
51
+ [:ios].include?(platform)
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,61 @@
1
+ require "fastlane_core/ui/ui"
2
+
3
+ module Fastlane
4
+ module RearchiveHelper
5
+ class IPAArchive
6
+ def initialize(archive_path)
7
+ @archive_path = archive_path
8
+ @temp_dir = Dir.mktmpdir
9
+ FastlaneCore::UI.verbose("Working in temp dir: #{@temp_dir}")
10
+ @app_path = self.class.extract_app_path(@archive_path)
11
+ end
12
+
13
+ # Returns the full path to the given file that can be modified
14
+ def local_path(path)
15
+ "#{@temp_dir}/#{path}"
16
+ end
17
+
18
+ # Returns an archive-relative path to the given application file
19
+ def app_path(path)
20
+ if path.start_with?("/")
21
+ path.sub(%r{^/}, "")
22
+ else
23
+ "#{@app_path}/#{path}"
24
+ end
25
+ end
26
+
27
+ # Extract files to the temp dir
28
+ def extract(path)
29
+ FastlaneCore::UI.verbose("Extracting #{path}")
30
+ Dir.chdir(@temp_dir) do
31
+ result = IO.popen("unzip -o -q #{@archive_path.shellescape} #{path.shellescape}", &:read).chomp
32
+ if $?.exitstatus.nonzero?
33
+ FastlaneCore::UI.important(result)
34
+ raise "extract operation failed with exit code #{$?.exitstatus}"
35
+ end
36
+ end
37
+ path
38
+ end
39
+
40
+ # Restore extracted files from the temp dir
41
+ def replace(path)
42
+ FastlaneCore::UI.verbose("Replacing #{path}")
43
+ Dir.chdir(@temp_dir) do
44
+ system("zip -q #{@archive_path.shellescape} #{path.shellescape}", exception: true)
45
+ end
46
+ end
47
+
48
+ # Delete path inside the ipa
49
+ def delete(path)
50
+ FastlaneCore::UI.verbose("Deleting #{path}")
51
+ Dir.chdir(@temp_dir) do
52
+ system("zip -dq #{@archive_path.shellescape} #{path.shellescape} >/dev/null 2>&1", exception: false)
53
+ end
54
+ end
55
+
56
+ def self.extract_app_path(archive_path)
57
+ IO.popen("zipinfo -1 #{archive_path.shellescape} \"Payload/*.app/\" | sed -n '1 p'", &:read).strip.chomp("/")
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,48 @@
1
+ require "fastlane_core/ui/ui"
2
+ require_relative "../plist_buddy"
3
+
4
+ module Fastlane
5
+ module RearchiveHelper
6
+ class XCArchive
7
+ def initialize(archive_path)
8
+ @archive_path = archive_path
9
+ @app_path = "Products/#{self.class.extract_app_path(archive_path)}"
10
+ end
11
+
12
+ # Returns the full path to the given file that can be modified
13
+ def local_path(path)
14
+ "#{@archive_path}/#{path}"
15
+ end
16
+
17
+ # Returns an archive-relative path to the given application file
18
+ def app_path(path)
19
+ if path.start_with?("/")
20
+ path.sub(%r{^/}, "")
21
+ else
22
+ "#{@app_path}/#{path}"
23
+ end
24
+ end
25
+
26
+ # Extract files to the temp dir
27
+ def extract(path)
28
+ path
29
+ end
30
+
31
+ # Restore extracted files from the temp dir
32
+ def replace(path)
33
+ end
34
+
35
+ # Delete path inside the ipa
36
+ def delete(path)
37
+ FastlaneCore::UI.verbose("Deleting #{path}")
38
+
39
+ Dir.glob(local_path(path)).each { |f| File.delete(f) }
40
+ end
41
+
42
+ def self.extract_app_path(archive_path)
43
+ plist_buddy = PlistBuddy.new("#{archive_path}/Info.plist")
44
+ plist_buddy.exec("Print :ApplicationProperties:ApplicationPath").strip
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,52 @@
1
+ require "fastlane_core/ui/ui"
2
+
3
+ module Fastlane
4
+ module RearchiveHelper
5
+ class PlistBuddy
6
+ def initialize(plist_path)
7
+ @plist_path = plist_path
8
+ end
9
+
10
+ def exec(command)
11
+ FastlaneCore::UI.verbose("/usr/libexec/PlistBuddy -c \"#{command}\" \"#{@plist_path}\"")
12
+ result = IO.popen("/usr/libexec/PlistBuddy -c \"#{command}\" \"#{@plist_path}\"", &:read).chomp
13
+
14
+ if $?.exitstatus.nonzero?
15
+ FastlaneCore::UI.important("PlistBuddy command failed: #{result}")
16
+ raise "PlistBuddy command failed failed with exit code #{$?.exitstatus} - #{result}"
17
+ end
18
+
19
+ return result
20
+ end
21
+
22
+ def parse_scalar_array(result)
23
+ # This should probably use -x and parse the xml using Nokogiri
24
+
25
+ return [] unless result =~ /\S/
26
+
27
+ result_lines = result.lines.map(&:chop)
28
+
29
+ raise "value is not an array: #{result_lines}" unless result_lines.first == "Array {"
30
+
31
+ array_values = result_lines.drop(1).take(result_lines.size - 2)
32
+
33
+ return array_values.map { |line| line[4..line.size] }
34
+ end
35
+
36
+ def parse_dict_keys(entry)
37
+ # This should probably use -x and parse the xml using Nokogiri
38
+
39
+ result_lines = entry.lines.map(&:chop)
40
+
41
+ raise "value is not an dict: #{result_lines}" unless result_lines.first == "Dict {"
42
+
43
+ keys = result_lines
44
+ .map { |l| l.match(/^\s{4}([^\s}]+)/) }
45
+ .select { |l| l }
46
+ .map { |l| l[1] }
47
+
48
+ return keys
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,5 @@
1
+ module Fastlane
2
+ module Rearchive
3
+ VERSION = "1.0.0"
4
+ end
5
+ end
@@ -0,0 +1,16 @@
1
+ require "fastlane/plugin/rearchive/version"
2
+
3
+ module Fastlane
4
+ module Rearchive
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::Rearchive.all_classes.each do |current|
15
+ require current
16
+ end
metadata ADDED
@@ -0,0 +1,195 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fastlane-plugin-rearchive
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - naoigcat
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-05-04 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.212.2
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 2.212.2
41
+ - !ruby/object:Gem::Dependency
42
+ name: pry
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: rake
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: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec_junit_formatter
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
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '='
102
+ - !ruby/object:Gem::Version
103
+ version: 1.12.1
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '='
109
+ - !ruby/object:Gem::Version
110
+ version: 1.12.1
111
+ - !ruby/object:Gem::Dependency
112
+ name: rubocop-performance
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
+ - !ruby/object:Gem::Dependency
126
+ name: rubocop-require_tools
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: simplecov
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ description:
154
+ email: 17925623+naoigcat@users.noreply.github.com
155
+ executables: []
156
+ extensions: []
157
+ extra_rdoc_files: []
158
+ files:
159
+ - LICENSE.md
160
+ - README.md
161
+ - lib/fastlane/plugin/rearchive.rb
162
+ - lib/fastlane/plugin/rearchive/actions/iconset_action.rb
163
+ - lib/fastlane/plugin/rearchive/actions/plist_command_action.rb
164
+ - lib/fastlane/plugin/rearchive/actions/plist_value_action.rb
165
+ - lib/fastlane/plugin/rearchive/actions/remove_file_action.rb
166
+ - lib/fastlane/plugin/rearchive/actions/replace_file_action.rb
167
+ - lib/fastlane/plugin/rearchive/helper/archives/ipa_archive.rb
168
+ - lib/fastlane/plugin/rearchive/helper/archives/xc_archive.rb
169
+ - lib/fastlane/plugin/rearchive/helper/plist_buddy.rb
170
+ - lib/fastlane/plugin/rearchive/version.rb
171
+ homepage: https://github.com/naoigcat/fastlane-plugin-rearchive
172
+ licenses:
173
+ - MIT
174
+ metadata: {}
175
+ post_install_message:
176
+ rdoc_options: []
177
+ require_paths:
178
+ - lib
179
+ required_ruby_version: !ruby/object:Gem::Requirement
180
+ requirements:
181
+ - - ">="
182
+ - !ruby/object:Gem::Version
183
+ version: '2.7'
184
+ required_rubygems_version: !ruby/object:Gem::Requirement
185
+ requirements:
186
+ - - ">="
187
+ - !ruby/object:Gem::Version
188
+ version: '0'
189
+ requirements: []
190
+ rubygems_version: 3.4.9
191
+ signing_key:
192
+ specification_version: 4
193
+ summary: Modify files inside ipa/xcarchive for publishing multiple configurations
194
+ without rearchiving.
195
+ test_files: []