fastlane-plugin-sapfire 1.0.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.
Files changed (29) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +135 -0
  4. data/lib/fastlane/plugin/sapfire/actions/app_certification_action.rb +79 -0
  5. data/lib/fastlane/plugin/sapfire/actions/associate_ms_store_action.rb +263 -0
  6. data/lib/fastlane/plugin/sapfire/actions/build_uwp_app_action.rb +55 -0
  7. data/lib/fastlane/plugin/sapfire/actions/dotnet_select_action.rb +46 -0
  8. data/lib/fastlane/plugin/sapfire/actions/ensure_dotnet_version_action.rb +123 -0
  9. data/lib/fastlane/plugin/sapfire/actions/ms_credentials_action.rb +91 -0
  10. data/lib/fastlane/plugin/sapfire/actions/msbuild_action.rb +45 -0
  11. data/lib/fastlane/plugin/sapfire/actions/msbuild_select_action.rb +53 -0
  12. data/lib/fastlane/plugin/sapfire/actions/nuget_pack_action.rb +46 -0
  13. data/lib/fastlane/plugin/sapfire/actions/update_uwp_signing_settings_action.rb +73 -0
  14. data/lib/fastlane/plugin/sapfire/actions/upload_ms_store_action.rb +218 -0
  15. data/lib/fastlane/plugin/sapfire/actions/upload_nuget_action.rb +95 -0
  16. data/lib/fastlane/plugin/sapfire/actions_base/msbuild_action_base.rb +42 -0
  17. data/lib/fastlane/plugin/sapfire/helper/ms_devcenter_helper.rb +292 -0
  18. data/lib/fastlane/plugin/sapfire/helper/sapfire_helper.rb +55 -0
  19. data/lib/fastlane/plugin/sapfire/msbuild/module.rb +34 -0
  20. data/lib/fastlane/plugin/sapfire/msbuild/options.rb +123 -0
  21. data/lib/fastlane/plugin/sapfire/msbuild/runner.rb +171 -0
  22. data/lib/fastlane/plugin/sapfire/sln_project/block.rb +30 -0
  23. data/lib/fastlane/plugin/sapfire/sln_project/global_block.rb +147 -0
  24. data/lib/fastlane/plugin/sapfire/sln_project/module.rb +14 -0
  25. data/lib/fastlane/plugin/sapfire/sln_project/project_block.rb +82 -0
  26. data/lib/fastlane/plugin/sapfire/sln_project/root_block.rb +72 -0
  27. data/lib/fastlane/plugin/sapfire/version.rb +5 -0
  28. data/lib/fastlane/plugin/sapfire.rb +16 -0
  29. metadata +182 -0
@@ -0,0 +1,171 @@
1
+ require "open3"
2
+ require "fastlane_core/print_table"
3
+ require_relative "module"
4
+ require_relative "options"
5
+ require_relative "../sln_project/module"
6
+ require_relative "../helper/sapfire_helper"
7
+
8
+ module Msbuild
9
+ class Runner
10
+ def run
11
+ params = Msbuild.config.params
12
+
13
+ FastlaneCore::PrintTable.print_values(config: params, title: "Summary for msbuild")
14
+ UI.user_error!("Can't find MSBuild") unless Fastlane::Helper::SapfireHelper.msbuild_specified?
15
+ UI.user_error!("Can't find dotnet. You selected MSBuild as a library, so dotnet is required to work with it. ") if
16
+ Msbuild.config.msbuild_type == Msbuild::MsbuildType::LIBRARY && !Fastlane::Helper::SapfireHelper.dotnet_specified?
17
+
18
+ prev_cwd = Dir.pwd
19
+ working_directory = File.dirname(File.expand_path(params[:project]))
20
+ UI.message("Change working directory to #{working_directory}")
21
+ Dir.chdir(working_directory)
22
+
23
+ params[:jobs] = 1 if params[:jobs].zero?
24
+ msbuild_path = Msbuild.config.msbuild_path
25
+ msbuild_args = get_msbuild_args(params, Msbuild.config.overwritten_props)
26
+ cmd = "#{msbuild_path} #{msbuild_args.join(" ")}"
27
+
28
+ check_configuration_platform(params)
29
+ UI.command(cmd)
30
+
31
+ Open3.popen2(cmd) do |_, stdout, wait_thr|
32
+ until stdout.eof?
33
+ stdout.each do |l|
34
+ line = l.force_encoding("utf-8").chomp
35
+ puts line
36
+ end
37
+ end
38
+
39
+ UI.user_error!("MSBuild execution failed. See the log above.") unless wait_thr.value.success?
40
+ UI.success("MSBuild has ended successfully") if wait_thr.value.success?
41
+ end
42
+
43
+ rename_package(params)
44
+
45
+ UI.message("Change working directory back to #{prev_cwd}")
46
+ Dir.chdir(prev_cwd)
47
+ end
48
+
49
+ def get_project_property_string(key, value)
50
+ "-p:#{key}=#{value}"
51
+ end
52
+
53
+ def get_msbuild_args(params, overwritten_props)
54
+ args = []
55
+
56
+ if params.values.include?(:properties)
57
+ params[:properties].each do |key, value|
58
+ unless overwritten_props.include?(key)
59
+ args.append(get_project_property_string(key, value))
60
+ next
61
+ end
62
+
63
+ # Remove properties that would be overwritten by this action
64
+ UI.important("Property #{key} will be ignored. Use `#{overwritten_props[key]}` option instead.")
65
+ end
66
+ end
67
+
68
+ configuration = params[:configuration] if
69
+ params.values.include?(:configuration) && !params[:configuration].empty?
70
+
71
+ platform = params[:platform] if
72
+ params.values.include?(:platform) && !params[:platform].empty?
73
+
74
+ need_restore = ([true].include?(params[:restore]) if params.values.include?(:restore)) || false
75
+
76
+ need_clean = ([true].include?(params[:clean]) if params.values.include?(:clean)) || false
77
+
78
+ appx_output_path = File.expand_path(params[:appx_output_path]) if
79
+ params.values.include?(:appx_output_path) && !params[:appx_output_path].empty?
80
+
81
+ appx_bundle_platforms = params[:appx_bundle_platforms] if
82
+ params.values.include?(:appx_bundle_platforms) && !params[:appx_bundle_platforms].empty?
83
+
84
+ build_mode = params[:build_mode] if
85
+ params.values.include?(:build_mode) && !params[:build_mode].empty?
86
+
87
+ certificate = Msbuild.config.certificate unless
88
+ Msbuild.config.certificate.nil? || Msbuild.config.certificate.empty?
89
+
90
+ certificate_password = Msbuild.config.certificate_password unless
91
+ Msbuild.config.certificate_password.nil? || Msbuild.config.certificate_password.empty?
92
+
93
+ certificate_thumbprint = Msbuild.config.certificate_thumbprint unless
94
+ Msbuild.config.certificate_thumbprint.nil? || Msbuild.config.certificate_thumbprint.empty?
95
+
96
+ signing_enabled = !params[:skip_codesigning] if params.values.include?(:skip_codesigning)
97
+
98
+ args.append("-p:Configuration=\"#{configuration}\"") unless configuration.nil?
99
+ args.append("-p:Platform=\"#{platform}\"") unless platform.nil?
100
+ args.append("-p:AppxPackageDir=\"#{appx_output_path}\"") unless appx_output_path.nil?
101
+ args.append("-p:AppxBundlePlatforms=\"#{appx_bundle_platforms}\"") unless appx_bundle_platforms.nil?
102
+ args.append("-p:AppxBundle=Always") if Msbuild.config.build_type == Msbuild::BuildType::UWP
103
+ args.append("-p:UapAppxPackageBuildMode=#{build_mode}") unless build_mode.nil?
104
+ args.append("-p:AppxPackageSigningEnabled=#{signing_enabled}") unless signing_enabled.nil?
105
+ args.append("-p:PackageCertificateKeyFile=\"#{certificate}\"") unless certificate.nil?
106
+ args.append("-p:PackageCertificatePassword=#{certificate_password}") unless certificate_password.nil?
107
+ args.append("-p:PackageCertificateThumbprint=#{certificate_thumbprint}") unless certificate_thumbprint.nil?
108
+ args.append("-m#{params[:jobs].positive? ? ":#{params[:jobs]}" : ""}")
109
+ args.append("-r") if need_restore || Msbuild.config.build_type == Msbuild::BuildType::NUGET
110
+
111
+ args.append("-t:Clean;Build") if need_clean
112
+ args.append("-t:Pack") if Msbuild.config.build_type == Msbuild::BuildType::NUGET
113
+
114
+ args
115
+ end
116
+
117
+ def check_configuration_platform(params)
118
+ configuration = params[:configuration] if
119
+ params.values.include?(:configuration) && !params[:configuration].empty?
120
+
121
+ platform = params[:platform] if
122
+ params.values.include?(:platform) && !params[:platform].empty?
123
+
124
+ root_block = SlnProject.open(params[:project])
125
+ platforms = root_block.global.solution_configuration_platforms.platforms
126
+
127
+ UI.user_error!("Configuration #{configuration} was not found in the solution") if
128
+ configuration.nil? || !platforms.key?(configuration)
129
+ UI.user_error!("Platform #{platform} for configuration #{configuration} was not found in the solution") if
130
+ platform.nil? || !platforms[configuration].include?(platform)
131
+
132
+ appx_bundle_platforms = params[:appx_bundle_platforms] if
133
+ params.values.include?(:appx_bundle_platforms) && !params[:appx_bundle_platforms].empty?
134
+ return if appx_bundle_platforms.nil?
135
+
136
+ appx_bundle_platforms.split("|").each do |x|
137
+ UI.user_error!("Platform #{x} for APPX bundle was not found in the solution") if
138
+ x.nil? || !platforms[configuration].include?(x)
139
+ end
140
+ end
141
+
142
+ def rename_package(params)
143
+ appx_output_name = params[:appx_output_name] if
144
+ params.values.include?(:appx_output_name) && !params[:appx_output_name].empty?
145
+
146
+ return if appx_output_name.nil? || appx_output_name.empty?
147
+
148
+ appx_output_path = File.expand_path(params[:appx_output_path]) if
149
+ params.values.include?(:appx_output_path) && !params[:appx_output_path].empty?
150
+ appx_output_path = "./" if appx_output_path.nil? || appx_output_path.empty?
151
+
152
+ Msbuild::Options::PACKAGE_FORMATS.each do |extension|
153
+ entries = Dir.entries(appx_output_path).find_all { |x| x.end_with?(extension) }
154
+ next if entries.nil? || entries.empty?
155
+
156
+ old_name = entries[0]
157
+ new_name = "#{appx_output_name}#{extension}"
158
+
159
+ UI.message("Rename #{old_name} to #{new_name}")
160
+ File.rename(File.join(appx_output_path, old_name), File.join(appx_output_path, new_name))
161
+ end
162
+
163
+ end
164
+
165
+ public(:run)
166
+ private(:get_project_property_string)
167
+ private(:get_msbuild_args)
168
+ private(:check_configuration_platform)
169
+ private(:rename_package)
170
+ end
171
+ end
@@ -0,0 +1,30 @@
1
+ module SlnProject
2
+ class Assignment
3
+ attr_accessor :key, :value
4
+
5
+ def initialize(line)
6
+ parts = line.split("=")
7
+
8
+ if parts.empty?
9
+ self.key = ""
10
+ self.value = ""
11
+ return
12
+ end
13
+
14
+ self.key = parts[0].strip
15
+ self.value = parts[1].strip
16
+ end
17
+ end
18
+
19
+ class InnerBlockResult
20
+ attr_accessor :block, :is_end
21
+ end
22
+
23
+ class BaseBlock
24
+ def parse(block_str)
25
+ nil
26
+ end
27
+
28
+ protected(:parse)
29
+ end
30
+ end
@@ -0,0 +1,147 @@
1
+ require_relative "block"
2
+
3
+ module SlnProject
4
+ class GlobalBlock < BaseBlock
5
+ START_TOKENS = {
6
+ GlobalSectionToken: "GlobalSection"
7
+ }.freeze
8
+
9
+ END_TOKENS = {
10
+ GlobalSectionToken: "EndGlobalSection"
11
+ }.freeze
12
+
13
+ attr_accessor :solution_configuration_platforms, :solution_properties
14
+
15
+ def parse(block_str)
16
+ inner_block = nil
17
+ inner_block_str = ""
18
+ content_reader = StringIO.new(block_str)
19
+
20
+ content_reader.each_line do |line|
21
+ trim_line = line.strip
22
+ inner_block = check_start_token(trim_line, inner_block)
23
+ inner_block = check_end_token(trim_line, inner_block, inner_block_str)
24
+
25
+ inner_block_str += "#{trim_line}\n" unless inner_block.nil?
26
+ inner_block_str = "" if inner_block.nil?
27
+ end
28
+ end
29
+
30
+ def check_start_token(line, inner_block)
31
+ inner_block = GlobalSectionFactory.create(line) if line.start_with?("#{START_TOKENS[:GlobalSectionToken]}(")
32
+ inner_block
33
+ end
34
+
35
+ def check_end_token(line, inner_block, inner_block_str)
36
+ return inner_block unless END_TOKENS.any? { |_, v| line == v }
37
+
38
+ inner_block.parse(inner_block_str)
39
+
40
+ case inner_block.type
41
+ when GlobalSectionBlock::TYPES[:SolutionConfigurationPlatforms]
42
+ self.solution_configuration_platforms = inner_block
43
+ when GlobalSectionBlock::TYPES[:SolutionProperties]
44
+ self.solution_properties = inner_block
45
+ end
46
+
47
+ nil
48
+ end
49
+
50
+ public(:parse)
51
+ private(:check_start_token)
52
+ private(:check_end_token)
53
+ private(:solution_configuration_platforms=)
54
+ private(:solution_properties=)
55
+ end
56
+
57
+ class GlobalSectionFactory
58
+ def self.create(line)
59
+ type = parse_header(line)
60
+
61
+ case type
62
+ when GlobalSectionBlock::TYPES[:SolutionConfigurationPlatforms]
63
+ SolutionConfigurationPlatformsBlock.new(type)
64
+ when GlobalSectionBlock::TYPES[:SolutionProperties]
65
+ SolutionPropertiesBlock.new(type)
66
+ else
67
+ GlobalSectionBlock.new(GlobalSectionBlock::TYPES[:Unknown])
68
+ end
69
+ end
70
+
71
+ def self.parse_header(line)
72
+ key = Assignment.new(line).key
73
+ pattern = /\((.*?)\)/
74
+ key.match(pattern)[1]
75
+ end
76
+
77
+ private_class_method(:parse_header, :new)
78
+ public_class_method(:create)
79
+ end
80
+
81
+ class GlobalSectionBlock < BaseBlock
82
+ TYPES = {
83
+ SolutionConfigurationPlatforms: "SolutionConfigurationPlatforms",
84
+ SolutionProperties: "SolutionProperties",
85
+ Unknown: "Unknown" # must be always the last
86
+ }.freeze
87
+
88
+ attr_accessor :type
89
+
90
+ def initialize(type)
91
+ self.type = type
92
+ end
93
+
94
+ private(:type=)
95
+ end
96
+
97
+ class SolutionConfigurationPlatformsBlock < GlobalSectionBlock
98
+ attr_accessor :platforms
99
+
100
+ def parse(block_str)
101
+ is_header = false
102
+ content_reader = StringIO.new(block_str)
103
+ self.platforms = {}
104
+
105
+ content_reader.each_line do |line|
106
+ unless is_header
107
+ is_header = true
108
+ next
109
+ end
110
+
111
+ assignment = Assignment.new(line).key
112
+ assignment_parts = assignment.split("|")
113
+ configuration = assignment_parts[0]
114
+ platform = assignment_parts[1]
115
+
116
+ self.platforms[configuration] = [] unless self.platforms[configuration].is_a?(Array) && !self.platforms[configuration].empty?
117
+ self.platforms[configuration].append(platform)
118
+ end
119
+ end
120
+
121
+ public(:parse)
122
+ private(:platforms=)
123
+ end
124
+
125
+ class SolutionPropertiesBlock < GlobalSectionBlock
126
+ attr_accessor :entries
127
+
128
+ def parse(block_str)
129
+ is_header = false
130
+ content_reader = StringIO.new(block_str)
131
+ self.entries = {}
132
+
133
+ content_reader.each_line do |line|
134
+ unless is_header
135
+ is_header = true
136
+ next
137
+ end
138
+
139
+ assignment = Assignment.new(line)
140
+ self.entries[assignment.key] = assignment.value
141
+ end
142
+ end
143
+
144
+ public(:parse)
145
+ private(:entries=)
146
+ end
147
+ end
@@ -0,0 +1,14 @@
1
+ require_relative "root_block"
2
+
3
+ module SlnProject
4
+ def self.open(path)
5
+ path = path.to_s
6
+ raise "Path to SLN-file can't be empty or null" unless path && !path.empty?
7
+ raise "The SLN-file at path #{path} doesn't exist" unless File.exist?(path)
8
+
9
+ content = File.read(path)
10
+ root_block = RootBlock.new
11
+ root_block.parse(content)
12
+ root_block
13
+ end
14
+ end
@@ -0,0 +1,82 @@
1
+ require_relative "block"
2
+
3
+ module SlnProject
4
+ class ProjectBlock < BaseBlock
5
+ START_TOKENS = {
6
+ ProjectSectionToken: "ProjectSection"
7
+ }.freeze
8
+
9
+ END_TOKENS = {
10
+ ProjectSectionToken: "EndProjectSection"
11
+ }.freeze
12
+
13
+ attr_accessor :guid, :name, :path
14
+
15
+ def parse(block_str)
16
+ is_header_passed = false
17
+ inner_block = nil
18
+ inner_block_str = ""
19
+ content_reader = StringIO.new(block_str)
20
+
21
+ content_reader.each_line do |line|
22
+ trim_line = line.strip
23
+
24
+ unless is_header_passed
25
+ is_header_passed = true if parse_header(trim_line)
26
+ end
27
+
28
+ inner_block = check_start_token(trim_line, inner_block)
29
+ inner_block = check_end_token(trim_line, inner_block, inner_block_str)
30
+
31
+ inner_block_str += "#{trim_line}\n" unless inner_block.nil?
32
+ inner_block_str = "" if inner_block.nil?
33
+ end
34
+ end
35
+
36
+ def to_s
37
+ name
38
+ end
39
+
40
+ def check_start_token(line, inner_block)
41
+ inner_block = ProjectSectionBlock.new if line.start_with?("#{START_TOKENS[:ProjectSectionToken]}(")
42
+ inner_block
43
+ end
44
+
45
+ def check_end_token(line, inner_block, inner_block_str)
46
+ return inner_block unless END_TOKENS.any? { |_, v| line == v }
47
+
48
+ inner_block.parse(inner_block_str)
49
+ nil
50
+ end
51
+
52
+ def parse_header(line)
53
+ assignment = Assignment.new(line)
54
+ parts = assignment.value.split(",")
55
+ return false if parts.empty?
56
+
57
+ self.name = parts[0].strip.delete("\"")
58
+ self.path = parts[1].strip.delete("\"")
59
+ self.guid = parts[2].strip
60
+ .delete("\"")
61
+ .delete("{")
62
+ .delete("}")
63
+
64
+ true
65
+ end
66
+
67
+ public(:parse)
68
+ public(:to_s)
69
+ private(:check_start_token)
70
+ private(:check_end_token)
71
+ private(:parse_header)
72
+ private(:guid=)
73
+ private(:name=)
74
+ private(:path=)
75
+ end
76
+
77
+ class ProjectSectionBlock < BaseBlock
78
+ def parse(block_str)
79
+ nil
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,72 @@
1
+ require_relative "project_block"
2
+ require_relative "global_block"
3
+ require_relative "block"
4
+
5
+ module SlnProject
6
+ class RootBlock < BaseBlock
7
+ TOKENS = {
8
+ VisualStudioVersionToken: "VisualStudioVersion",
9
+ MinimumVisualStudioVersionToken: "MinimumVisualStudioVersion"
10
+ }.freeze
11
+
12
+ START_TOKENS = {
13
+ ProjectToken: "Project",
14
+ GlobalToken: "Global"
15
+ }.freeze
16
+
17
+ END_TOKENS = {
18
+ ProjectToken: "EndProject",
19
+ GlobalToken: "EndGlobal"
20
+ }.freeze
21
+
22
+ attr_accessor :visual_studio_version, :min_visual_studio_version, :projects, :global
23
+
24
+ def parse(block_str)
25
+ inner_block = nil
26
+ inner_block_str = ""
27
+ content_reader = StringIO.new(block_str)
28
+
29
+ content_reader.each_line do |line|
30
+ trim_line = line.strip
31
+ inner_block = check_start_token(trim_line, inner_block)
32
+ inner_block = check_end_token(trim_line, inner_block, inner_block_str)
33
+
34
+ inner_block_str += "#{trim_line}\n" unless inner_block.nil?
35
+ inner_block_str = "" if inner_block.nil?
36
+ end
37
+ end
38
+
39
+ def check_start_token(line, inner_block)
40
+ self.visual_studio_version = Assignment.new(line).value if line.start_with?(TOKENS[:VisualStudioVersionToken])
41
+ self.min_visual_studio_version = Assignment.new(line).value if line.start_with?(TOKENS[:MinimumVisualStudioVersionToken])
42
+
43
+ inner_block = ProjectBlock.new if line.start_with?("#{START_TOKENS[:ProjectToken]}(")
44
+ inner_block = GlobalBlock.new if line.start_with?(START_TOKENS[:GlobalToken])
45
+
46
+ inner_block
47
+ end
48
+
49
+ def check_end_token(line, inner_block, inner_block_str)
50
+ return inner_block unless END_TOKENS.any? { |_, v| line == v }
51
+
52
+ inner_block.parse(inner_block_str)
53
+
54
+ if line.start_with?(END_TOKENS[:ProjectToken])
55
+ self.projects = [] if self.projects.nil?
56
+ self.projects.append(inner_block)
57
+ elsif line.start_with?(END_TOKENS[:GlobalToken])
58
+ self.global = inner_block
59
+ end
60
+
61
+ nil
62
+ end
63
+
64
+ public(:parse)
65
+ private(:check_start_token)
66
+ private(:check_end_token)
67
+ private(:visual_studio_version=)
68
+ private(:min_visual_studio_version=)
69
+ private(:projects=)
70
+ private(:global=)
71
+ end
72
+ end
@@ -0,0 +1,5 @@
1
+ module Fastlane
2
+ module Sapfire
3
+ VERSION = "1.0.0"
4
+ end
5
+ end
@@ -0,0 +1,16 @@
1
+ require "fastlane/plugin/sapfire/version"
2
+
3
+ module Fastlane
4
+ module Sapfire
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::Sapfire.all_classes.each do |current|
15
+ require current
16
+ end