fastlane-plugin-xambuild 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 +339 -0
- data/README.md +58 -0
- data/lib/fastlane/plugin/xambuild.rb +35 -0
- data/lib/fastlane/plugin/xambuild/actions/app_version_action.rb +93 -0
- data/lib/fastlane/plugin/xambuild/actions/xambuild_action.rb +69 -0
- data/lib/fastlane/plugin/xambuild/helpers/commands_generator.rb +46 -0
- data/lib/fastlane/plugin/xambuild/helpers/detect_values.rb +145 -0
- data/lib/fastlane/plugin/xambuild/helpers/generators/android_zipalign_command_generator.rb +53 -0
- data/lib/fastlane/plugin/xambuild/helpers/generators/build_command_generator.rb +66 -0
- data/lib/fastlane/plugin/xambuild/helpers/generators/java_sign_command_generator.rb +50 -0
- data/lib/fastlane/plugin/xambuild/helpers/generators/zip_dsym_command_generator.rb +41 -0
- data/lib/fastlane/plugin/xambuild/helpers/manager.rb +17 -0
- data/lib/fastlane/plugin/xambuild/helpers/msbuild/project.rb +43 -0
- data/lib/fastlane/plugin/xambuild/helpers/msbuild/solution.rb +19 -0
- data/lib/fastlane/plugin/xambuild/helpers/msbuild/solution_parser.rb +36 -0
- data/lib/fastlane/plugin/xambuild/helpers/options.rb +102 -0
- data/lib/fastlane/plugin/xambuild/helpers/platform.rb +13 -0
- data/lib/fastlane/plugin/xambuild/helpers/runner.rb +108 -0
- data/lib/fastlane/plugin/xambuild/version.rb +5 -0
- metadata +159 -0
@@ -0,0 +1,69 @@
|
|
1
|
+
require "fastlane/plugin/xambuild/helpers/options"
|
2
|
+
require "fastlane/plugin/xambuild/helpers/platform"
|
3
|
+
require "fastlane/plugin/xambuild/helpers/manager"
|
4
|
+
|
5
|
+
module Fastlane
|
6
|
+
module Actions
|
7
|
+
module SharedValues
|
8
|
+
# for calabash
|
9
|
+
APP_BUNDLE_PATH = :APP_BUNDLE_PATH
|
10
|
+
APP_OUTPUT_PATH = :APP_OUTPUT_PATH
|
11
|
+
end
|
12
|
+
|
13
|
+
class XambuildAction < Action
|
14
|
+
|
15
|
+
def self.run(values)
|
16
|
+
values[:platform] = ::Xambuild::Platform.from_lane_context(Actions.lane_context)
|
17
|
+
::Xambuild.config = values
|
18
|
+
|
19
|
+
if ::Xambuild.project.ios? || ::Xambuild.project.osx?
|
20
|
+
absolute_ipa_path = File.expand_path(::Xambuild::Manager.new.work(values))
|
21
|
+
absolute_app_path = File.join(values[:output_path], "#{values[:assembly_name]}.app")
|
22
|
+
absolute_dsym_path = absolute_ipa_path.gsub(".ipa", ".app.dSYM.zip")
|
23
|
+
|
24
|
+
Actions.lane_context[SharedValues::APP_OUTPUT_PATH] = absolute_app_path
|
25
|
+
Actions.lane_context[SharedValues::IPA_OUTPUT_PATH] = absolute_ipa_path
|
26
|
+
Actions.lane_context[SharedValues::DSYM_OUTPUT_PATH] = absolute_dsym_path if File.exist?(absolute_dsym_path)
|
27
|
+
ENV[SharedValues::APP_OUTPUT_PATH.to_s] = absolute_app_path
|
28
|
+
ENV[SharedValues::APP_BUNDLE_PATH.to_s] = absolute_app_path # for calabash
|
29
|
+
ENV[SharedValues::IPA_OUTPUT_PATH.to_s] = absolute_ipa_path # for deliver
|
30
|
+
ENV[SharedValues::DSYM_OUTPUT_PATH.to_s] = absolute_dsym_path if File.exist?(absolute_dsym_path)
|
31
|
+
|
32
|
+
absolute_ipa_path
|
33
|
+
elsif ::Xambuild.project.android?
|
34
|
+
if values[:keystore_path] && values[:keystore_alias]
|
35
|
+
unless values[:keystore_password]
|
36
|
+
::Xambuild.config[:keystore_password] = ask("Password (for #{values[:keystore_alias]}): ") { |q| q.echo = "*" }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
absolute_apk_path = File.expand_path(::Xambuild::Manager.new.work(values))
|
40
|
+
|
41
|
+
Actions.lane_context[SharedValues::GRADLE_BUILD_TYPE] = values[:build_configuration]
|
42
|
+
Actions.lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH] = absolute_apk_path
|
43
|
+
|
44
|
+
absolute_apk_path
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.description
|
49
|
+
"Easily build and sign your app using `xambuild`"
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.return_value
|
53
|
+
"The absolute path to the generated app file"
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.author
|
57
|
+
"Jake Barnby"
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.available_options
|
61
|
+
::Xambuild::Options.available_options
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.is_supported?(platform)
|
65
|
+
true
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require "commander"
|
2
|
+
require "fastlane"
|
3
|
+
|
4
|
+
HighLine.track_eof = false
|
5
|
+
|
6
|
+
module Xambuild
|
7
|
+
class CommandsGenerator
|
8
|
+
include Commander::Methods
|
9
|
+
UI = FastlaneCore::UI
|
10
|
+
|
11
|
+
FastlaneCore::CommanderGenerator.new.generate(Xambuild::Options.available_options)
|
12
|
+
|
13
|
+
def self.start
|
14
|
+
new.run
|
15
|
+
end
|
16
|
+
|
17
|
+
def convert_options(options)
|
18
|
+
o = options.__hash__.dup
|
19
|
+
o.delete(:verbose)
|
20
|
+
o
|
21
|
+
end
|
22
|
+
|
23
|
+
def run
|
24
|
+
program :version, Xambuild::VERSION
|
25
|
+
program :description, Xambuild::DESCRIPTION
|
26
|
+
program :help, "Author", "Jake Barnby <jakeb994@gmail.com>"
|
27
|
+
program :help_formatter, :compact
|
28
|
+
|
29
|
+
global_option("--verbose") { $verbose = true }
|
30
|
+
|
31
|
+
command :build do |c|
|
32
|
+
c.syntax = "xambuild"
|
33
|
+
c.description = "Just builds your app"
|
34
|
+
c.action do |_args, options|
|
35
|
+
config = FastlaneCore::Configuration.create(Xambuild::Options.available_options,
|
36
|
+
convert_options(options))
|
37
|
+
Xambuild::Manager.new.work(config)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
default_command :build
|
42
|
+
|
43
|
+
run!
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require "nokogiri"
|
2
|
+
require "fastlane/plugin/xambuild/helpers/msbuild/project"
|
3
|
+
require "fastlane/plugin/xambuild/helpers/msbuild/solution_parser"
|
4
|
+
|
5
|
+
module Xambuild
|
6
|
+
class DetectValues
|
7
|
+
|
8
|
+
def self.set_additional_default_values
|
9
|
+
config = Xambuild.config
|
10
|
+
|
11
|
+
# TODO: detect_platform automatically for :platform config
|
12
|
+
|
13
|
+
if config[:platform] == Platform::ANDROID
|
14
|
+
config[:build_platform] = "AnyCPU"
|
15
|
+
end
|
16
|
+
|
17
|
+
# Detect the project
|
18
|
+
Xambuild.project = Msbuild::Project.new(config)
|
19
|
+
detect_solution
|
20
|
+
detect_project
|
21
|
+
|
22
|
+
doc_csproj = get_parser_handle config[:project_path]
|
23
|
+
|
24
|
+
detect_output_path doc_csproj
|
25
|
+
detect_manifest doc_csproj
|
26
|
+
detect_info_plist
|
27
|
+
detect_assembly_name doc_csproj
|
28
|
+
|
29
|
+
config
|
30
|
+
end
|
31
|
+
|
32
|
+
# Helper Methods
|
33
|
+
|
34
|
+
def self.detect_solution
|
35
|
+
return if Xambuild.config[:solution_path]
|
36
|
+
|
37
|
+
sln = find_file("*.sln", 3)
|
38
|
+
UI.user_error! "Not able to find solution file automatically, try to specify it via `solution_path` parameter." unless sln
|
39
|
+
|
40
|
+
Xambuild.config[:solution_path] = abs_path sln
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.detect_project
|
44
|
+
return if Xambuild.config[:project_path]
|
45
|
+
|
46
|
+
path = Xambuild.config[:solution_path]
|
47
|
+
projects = Msbuild::SolutionParser.parse(path)
|
48
|
+
.get_platform Xambuild.config[:platform]
|
49
|
+
|
50
|
+
UI.user_error! "Not able to find any project in solution, that matches the platform `#{Xambuild.config[:platform]}`." unless projects.any?
|
51
|
+
|
52
|
+
project = projects.first
|
53
|
+
csproj = fix_path_relative project.project_path
|
54
|
+
|
55
|
+
UI.user_error! "Not able to find project file automatically, try to specify it via `project_path` parameter." unless csproj
|
56
|
+
|
57
|
+
Xambuild.config[:project_name] = project.project_name
|
58
|
+
Xambuild.config[:project_path] = abs_path csproj
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.detect_output_path(doc_csproj)
|
62
|
+
return if Xambuild.config[:output_path]
|
63
|
+
|
64
|
+
configuration = Xambuild.config[:build_configuration]
|
65
|
+
platform = Xambuild.config[:build_platform]
|
66
|
+
|
67
|
+
doc_node = doc_csproj.xpath("/*[local-name()='Project']/*[local-name()='PropertyGroup'][translate(@*[local-name() = 'Condition'],'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz') = \" '$(configuration)|$(platform)' == '#{configuration.downcase}|#{platform.downcase}' \"]/*[local-name()='OutputPath']/text()")
|
68
|
+
output_path = doc_node.text
|
69
|
+
UI.user_error! "Not able to find output path automatically, try to specify it via `output_path` parameter." unless output_path
|
70
|
+
|
71
|
+
Xambuild.config[:output_path] = abs_project_path output_path
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.detect_manifest(doc_csproj)
|
75
|
+
return if Xambuild.config[:manifest_path] || (Xambuild.config[:platform] != Platform::ANDROID)
|
76
|
+
|
77
|
+
doc_node = doc_csproj.css("AndroidManifest").first
|
78
|
+
|
79
|
+
Xambuild.config[:manifest_path] = abs_project_path doc_node.text
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.detect_info_plist
|
83
|
+
return if Xambuild.config[:plist_path] || (Xambuild.config[:platform] != Platform::IOS)
|
84
|
+
|
85
|
+
plist_path = find_file("Info.plist", 1)
|
86
|
+
UI.user_error! "Not able to find Info.plist automatically, try to specify it via `plist_path` parameter." unless plist_path
|
87
|
+
|
88
|
+
Xambuild.config[:plist_path] = abs_project_path plist_path
|
89
|
+
end
|
90
|
+
|
91
|
+
def self.detect_assembly_name(doc_csproj)
|
92
|
+
return if Xambuild.config[:assembly_name]
|
93
|
+
|
94
|
+
if [Platform::IOS, Platform::OSX].include? Xambuild.config[:platform]
|
95
|
+
Xambuild.config[:assembly_name] = doc_csproj.css("PropertyGroup > AssemblyName").text
|
96
|
+
elsif Xambuild.config[:platform] == Platform::ANDROID
|
97
|
+
doc = get_parser_handle Xambuild.config[:manifest_path]
|
98
|
+
Xambuild.config[:assembly_name] = doc.xpath("string(//manifest/@package)")
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
private_class_method
|
103
|
+
|
104
|
+
def self.find_file(query, depth)
|
105
|
+
itr = 0
|
106
|
+
files = []
|
107
|
+
|
108
|
+
loop do
|
109
|
+
files = Dir.glob(query)
|
110
|
+
query = "../#{query}"
|
111
|
+
itr += 1
|
112
|
+
break if files.any? || (itr > depth)
|
113
|
+
end
|
114
|
+
|
115
|
+
files.first
|
116
|
+
end
|
117
|
+
|
118
|
+
def self.get_parser_handle(filename)
|
119
|
+
f = File.open(filename)
|
120
|
+
doc = Nokogiri::XML(f)
|
121
|
+
f.close
|
122
|
+
|
123
|
+
doc
|
124
|
+
end
|
125
|
+
|
126
|
+
def self.fix_path_relative(path)
|
127
|
+
root = File.dirname Xambuild.config[:solution_path]
|
128
|
+
path = "#{root}/#{path}"
|
129
|
+
path
|
130
|
+
end
|
131
|
+
|
132
|
+
def self.abs_project_path(path)
|
133
|
+
path = path.tr('\\', "/")
|
134
|
+
platform_path = Xambuild.config[:project_path]
|
135
|
+
path = "#{File.dirname platform_path}/#{path}"
|
136
|
+
path
|
137
|
+
end
|
138
|
+
|
139
|
+
def self.abs_path(path)
|
140
|
+
path = path.tr('\\', "/")
|
141
|
+
path = File.expand_path(path)
|
142
|
+
path
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Xambuild
|
2
|
+
class AndroidZipalignCommandGenerator
|
3
|
+
class << self
|
4
|
+
def generate
|
5
|
+
parts = prefix
|
6
|
+
parts << zipalign_apk
|
7
|
+
parts += options
|
8
|
+
parts << Xambuild.cache[:signed_apk_path]
|
9
|
+
parts << Xambuild.cache[:build_apk_path]
|
10
|
+
parts += pipe
|
11
|
+
|
12
|
+
parts
|
13
|
+
end
|
14
|
+
|
15
|
+
def detect_build_tools
|
16
|
+
UI.user_error! "Please ensure that the Android SDK is installed and the ANDROID_HOME variable is set correctly" unless ENV["ANDROID_HOME"]
|
17
|
+
|
18
|
+
buildtools = File.join(ENV["ANDROID_HOME"], "build-tools")
|
19
|
+
version = Dir.entries(buildtools).max
|
20
|
+
|
21
|
+
UI.success "Using Buildtools Version: #{version}..."
|
22
|
+
|
23
|
+
[buildtools, version]
|
24
|
+
end
|
25
|
+
|
26
|
+
def zipalign_apk
|
27
|
+
buildtools, version = detect_build_tools
|
28
|
+
zipalign = ENV["ANDROID_HOME"] ? File.join(buildtools, version, "zipalign") : "zipalign"
|
29
|
+
|
30
|
+
zipalign
|
31
|
+
end
|
32
|
+
|
33
|
+
def options
|
34
|
+
options = []
|
35
|
+
options << "-v" if $verbose
|
36
|
+
options << "-f"
|
37
|
+
options << "4"
|
38
|
+
|
39
|
+
options
|
40
|
+
end
|
41
|
+
|
42
|
+
def prefix
|
43
|
+
[""]
|
44
|
+
end
|
45
|
+
|
46
|
+
def pipe
|
47
|
+
pipe = []
|
48
|
+
|
49
|
+
pipe
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Xambuild
|
2
|
+
class BuildCommandGenerator
|
3
|
+
class << self
|
4
|
+
def generate
|
5
|
+
parts = prefix
|
6
|
+
parts << compiler_bin
|
7
|
+
parts += options
|
8
|
+
parts += targets
|
9
|
+
parts += project
|
10
|
+
parts += pipe
|
11
|
+
|
12
|
+
parts
|
13
|
+
end
|
14
|
+
|
15
|
+
def prefix
|
16
|
+
[""]
|
17
|
+
end
|
18
|
+
|
19
|
+
def compiler_bin
|
20
|
+
Xambuild.config[:compiler_bin]
|
21
|
+
end
|
22
|
+
|
23
|
+
def options
|
24
|
+
config = Xambuild.config
|
25
|
+
|
26
|
+
options = []
|
27
|
+
options << config[:extra_build_options] if config[:extra_build_options]
|
28
|
+
options << "-p:Configuration=#{config[:build_configuration]}" if config[:build_configuration]
|
29
|
+
options << "-p:Platform=#{config[:build_platform]}" if Xambuild.project.ios? && config[:build_platform]
|
30
|
+
options << "-p:BuildIpa=true" if Xambuild.project.ios?
|
31
|
+
if config[:solution_path]
|
32
|
+
solution_dir = File.dirname(config[:solution_path])
|
33
|
+
options << "-p:SolutionDir=#{solution_dir}/"
|
34
|
+
end
|
35
|
+
|
36
|
+
options
|
37
|
+
end
|
38
|
+
|
39
|
+
def build_targets
|
40
|
+
Xambuild.config[:build_target].map! { |t| "-t:#{t}" }
|
41
|
+
end
|
42
|
+
|
43
|
+
def targets
|
44
|
+
targets = []
|
45
|
+
targets += build_targets
|
46
|
+
targets << "-t:SignAndroidPackage" if Xambuild.project.android?
|
47
|
+
|
48
|
+
targets
|
49
|
+
end
|
50
|
+
|
51
|
+
def project
|
52
|
+
path = []
|
53
|
+
|
54
|
+
path << Xambuild.config[:project_path]
|
55
|
+
|
56
|
+
path
|
57
|
+
end
|
58
|
+
|
59
|
+
def pipe
|
60
|
+
pipe = []
|
61
|
+
|
62
|
+
pipe
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Xambuild
|
2
|
+
class JavaSignCommandGenerator
|
3
|
+
class << self
|
4
|
+
def generate
|
5
|
+
build_apk_path = Xambuild.cache[:build_apk_path]
|
6
|
+
Xambuild.cache[:signed_apk_path] = "#{build_apk_path}-unaligned"
|
7
|
+
|
8
|
+
parts = prefix
|
9
|
+
parts << detect_jarsigner_executable
|
10
|
+
parts += options
|
11
|
+
parts << build_apk_path
|
12
|
+
parts << Xambuild.config[:keystore_alias]
|
13
|
+
parts += pipe
|
14
|
+
|
15
|
+
parts
|
16
|
+
end
|
17
|
+
|
18
|
+
def prefix
|
19
|
+
[""]
|
20
|
+
end
|
21
|
+
|
22
|
+
def detect_jarsigner_executable
|
23
|
+
jarsigner = ENV["JAVA_HOME"] ? File.join(ENV["JAVA_HOME"], "bin", "jarsigner") : "jarsigner"
|
24
|
+
|
25
|
+
jarsigner
|
26
|
+
end
|
27
|
+
|
28
|
+
def options
|
29
|
+
config = Xambuild.config
|
30
|
+
|
31
|
+
options = []
|
32
|
+
options << "-verbose" if $verbose
|
33
|
+
options << "-sigalg MD5withRSA"
|
34
|
+
options << "-digestalg SHA1"
|
35
|
+
options << "-storepass \"#{config[:keystore_password]}\""
|
36
|
+
options << "-keystore \"#{config[:keystore_path]}\""
|
37
|
+
options << "-tsa #{config[:keystore_tsa]}"
|
38
|
+
options << "-signedjar \"#{Xambuild.cache[:signed_apk_path]}\""
|
39
|
+
|
40
|
+
options
|
41
|
+
end
|
42
|
+
|
43
|
+
def pipe
|
44
|
+
pipe = []
|
45
|
+
|
46
|
+
pipe
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Xambuild
|
2
|
+
class ZipDsymCommandGenerator
|
3
|
+
class << self
|
4
|
+
def generate
|
5
|
+
parts = prefix
|
6
|
+
parts << detect_zip_executable
|
7
|
+
parts += options
|
8
|
+
parts += pipe
|
9
|
+
|
10
|
+
parts
|
11
|
+
end
|
12
|
+
|
13
|
+
def prefix
|
14
|
+
[""]
|
15
|
+
end
|
16
|
+
|
17
|
+
def detect_zip_executable
|
18
|
+
zip = ENV["XAMBUILD_ZIP_PATH"] || "zip"
|
19
|
+
|
20
|
+
zip
|
21
|
+
end
|
22
|
+
|
23
|
+
def options
|
24
|
+
build_dsym_path = Xambuild.cache[:build_dsym_path]
|
25
|
+
|
26
|
+
options = []
|
27
|
+
options << "-r"
|
28
|
+
options << "#{build_dsym_path}.zip"
|
29
|
+
options << build_dsym_path
|
30
|
+
|
31
|
+
options
|
32
|
+
end
|
33
|
+
|
34
|
+
def pipe
|
35
|
+
pipe = []
|
36
|
+
|
37
|
+
pipe
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|