nixenvironment 0.0.70 → 0.0.71

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,24 +1,87 @@
1
1
  module Nixenvironment
2
2
  class BuildEnvVarsLoader
3
3
  def self.load
4
- cmd_output = %x[ source \"#{LOAD_BUILD_ENV_VARS_SCRIPT_PATH}\" ].strip!
4
+ build_env_vars = load_last_revision.merge(load_last_build_vars)
5
+
6
+ built_products_dir = build_env_vars[BUILT_PRODUCTS_DIR_KEY]
7
+ app_product = build_env_vars[APP_PRODUCT_KEY]
8
+ ipa_product = build_env_vars[IPA_PRODUCT_KEY]
9
+ app_infoplist_file = build_env_vars['APP_INFOPLIST_FILE']
10
+ embedded_profile = build_env_vars['EMBEDDED_PROFILE']
11
+ monotonic_revision = build_env_vars['MONOTONIC_REVISION']
12
+ sdk = build_env_vars[SDK_NAME_KEY]
13
+
14
+ raise "error: directory '#{built_products_dir}' must exist (build products dir)!" unless File.exist?(built_products_dir)
15
+ raise "error: app product directory '#{app_product}' or ipa file '#{ipa_product}' must exist!" unless File.exist?(app_product) && File.exist?(ipa_product)
16
+ raise "error: file '#{app_infoplist_file}' must exist (app info plist)!" unless File.exist?(app_infoplist_file)
17
+
18
+ if sdk.include?('ios')
19
+ embedded_profile_exist = embedded_profile.present? && File.exist?(embedded_profile)
20
+ embedded_profile_name = embedded_profile_exist ? %x[ /usr/libexec/PlistBuddy -c 'Print :Name' /dev/stdin <<< $(security cms -D -i '#{embedded_profile}') ].strip : 'SIMULATOR'
21
+ end
22
+
23
+ current_app_version = %x[ /usr/libexec/PlistBuddy -c 'Print CFBundleShortVersionString' '#{app_infoplist_file}' ].strip
24
+
25
+ build_env_vars[EMBEDDED_PROFILE_NAME_KEY] = embedded_profile_name
26
+ build_env_vars[CURRENT_APP_VERSION_KEY] = current_app_version
27
+ build_env_vars[CURRENT_BUILD_VERSION_KEY] = monotonic_revision
28
+
29
+ puts
30
+ puts "#{EMBEDDED_PROFILE_NAME_KEY} = #{embedded_profile_name}"
31
+ puts "#{CURRENT_APP_VERSION_KEY} = #{current_app_version}"
32
+ puts "#{CURRENT_BUILD_VERSION_KEY} = #{monotonic_revision}"
33
+ puts
34
+
35
+ build_env_vars
36
+ end
37
+
38
+ def self.load_last_revision
39
+ load_internal(AUTOGENERATED_LAST_REVISION, false)
40
+ end
41
+
42
+ def self.load_last_build_vars
43
+ load_internal(AUTOGENERATED_LAST_BUILD_VARS, false)
44
+ end
45
+
46
+ def self.working_copy_is_clean?
47
+ working_copy_is_clean = load_last_revision['WORKING_COPY_IS_CLEAN'] == '1'
48
+
49
+ if working_copy_is_clean
50
+ puts 'Working copy is clean. Continuing ...'
51
+ else
52
+ puts 'error: working copy must not have local modifications.'
53
+ puts 'You must add following files and folders to .gitignore:'
54
+ Git.status(:porcelain => true)
55
+ end
56
+
57
+ working_copy_is_clean
58
+ end
59
+
60
+ def self.load_internal(file, print_log = true)
61
+ raise "error: file '#{file}' must exist!" unless File.exist?(file)
62
+
5
63
  build_env_vars = {}
6
64
 
7
- vars_list = cmd_output.split(/\n/).reject(&:empty?)
65
+ vars_list = File.read(file).lines.reject(&:empty?)
8
66
  if vars_list.present?
67
+ puts if print_log
68
+
9
69
  vars_to_strip = Hash[vars_list.map { |it| it.split('=', 2) }]
10
70
  vars_to_strip.each do |key, value|
11
- if key.present? && value.present?
71
+ if key.present? && !key.include?(AUTOGENERATED_FILES_HEADER)
12
72
  stripped_key = key.strip
13
- stripped_value = value.strip
14
-
15
- puts "#{stripped_key} : #{stripped_value}"
73
+ stripped_value = value ? value.tr("'", '').strip : 'unknown'
74
+ puts "#{stripped_key} = #{stripped_value}" if print_log
16
75
  build_env_vars[stripped_key] = stripped_value
17
76
  end
18
77
  end
78
+
79
+ puts if print_log
19
80
  end
20
81
 
21
82
  build_env_vars
22
83
  end
84
+
85
+ private_class_method :load_internal
23
86
  end
24
87
  end
@@ -12,15 +12,10 @@ module Nixenvironment
12
12
 
13
13
  # ninbas legacy
14
14
  BUILD_SCRIPTS_PATH = File.expand_path('../../../legacy', __FILE__)
15
- SAVE_REVISION_SCRIPT_PATH = File.join(BUILD_SCRIPTS_PATH, 'SaveRevision.sh')
16
15
  UNITY_BUILD_ANDROID_SCRIPT_PATH = File.join(BUILD_SCRIPTS_PATH, 'UnityBuildAndroid.py')
17
- LOAD_BUILD_ENV_VARS_SCRIPT_PATH = File.join(BUILD_SCRIPTS_PATH, 'LoadBuildEnvVars.sh')
18
16
  CODE_COVERAGE_REPORT_SCRIPT_PATH = File.join(BUILD_SCRIPTS_PATH, 'GenerateCodeCoverageForXCTests.sh')
19
17
  CODE_DUPLICATION_REPORT_SCRIPT_PATH = File.join(BUILD_SCRIPTS_PATH, 'GenerateCodeDuplicationReport.sh')
20
- MAKE_TAG_SCRIPT_PATH = File.join(BUILD_SCRIPTS_PATH, 'MakeTag.sh')
21
- CLEAN_WORKING_COPY_SCRIPT_PATH = File.join(BUILD_SCRIPTS_PATH, 'CleanWorkingCopy.sh')
22
18
  DEPLOY_SCRIPT_PATH = File.join(BUILD_SCRIPTS_PATH, 'Deploy.sh')
23
- DEPLOY_IPA_SCRIPT_PATH = File.join(BUILD_SCRIPTS_PATH, 'DeployIPA.sh')
24
19
  DEPLOY_APK_SCRIPT_PATH = File.join(BUILD_SCRIPTS_PATH, 'DeployAPK.py')
25
20
 
26
21
  # utils
@@ -34,11 +29,20 @@ module Nixenvironment
34
29
 
35
30
  # file extensions
36
31
  APP_EXT = '.app'
32
+ IPA_EXT = '.ipa'
37
33
  APPEX_EXT = '.appex'
38
34
  DSYM_EXT = '.dSYM'
39
35
  ZIP_EXT = '.zip'
40
36
  GIT_EXT = '.git'
41
37
 
38
+ # autogenerated
39
+ AUTOGENERATED_FILES_HEADER = '### AUTOGENERATED BY Nixenvironment; DO NOT EDIT ###'
40
+ AUTOGENERATED_LAST_REVISION = '_last_revision.sh'
41
+ AUTOGENERATED_LAST_BUILD_VARS = '_last_build_vars.sh'
42
+ AUTOGENERATED_COVERAGE = 'coverage.xml'
43
+ AUTOGENERATED_DUPLICATION = 'duplication.xml'
44
+ AUTOGENERATED_TEST_RESULTS = 'test-results/'
45
+
42
46
  # watch, extensions, widgets
43
47
  WATCHKIT_APP_KEY = 'WatchKit App'
44
48
  WATCHKIT_EXTENSION_KEY = 'WatchKit Extension'
@@ -0,0 +1,129 @@
1
+ require 'net/scp'
2
+ require 'colorize'
3
+
4
+ module Nixenvironment
5
+ class Deployer
6
+ def self.deploy(deploy_host, deploy_dir, deploy_user, deploy_password, deploy_itunesconnect_user, deliver_deploy)
7
+ build_env_vars = BuildEnvVarsLoader.load
8
+ raise 'Error! Working copy is not clean!' unless BuildEnvVarsLoader.working_copy_is_clean?
9
+
10
+ ipa_product = build_env_vars[IPA_PRODUCT_KEY]
11
+ ipa_product_resigned_device = build_env_vars['IPA_PRODUCT_RESIGNED_DEVICE']
12
+ ipa_product_resigned_adhoc = build_env_vars['IPA_PRODUCT_RESIGNED_ADHOC']
13
+ ipa_product_resigned_appstore = build_env_vars['IPA_PRODUCT_RESIGNED_APPSTORE']
14
+
15
+ ipa_bundle_id = build_env_vars[IPA_BUNDLE_ID_KEY]
16
+ ipa_bundle_id_resigned_device = build_env_vars['IPA_BUNDLE_ID_RESIGNED_DEVICE']
17
+ ipa_bundle_id_resigned_adhoc = build_env_vars['IPA_BUNDLE_ID_RESIGNED_ADHOC']
18
+ ipa_bundle_id_resigned_appstore = build_env_vars['IPA_BUNDLE_ID_RESIGNED_APPSTORE']
19
+
20
+ current_app_version = build_env_vars[CURRENT_APP_VERSION_KEY]
21
+ current_build_version = build_env_vars[CURRENT_BUILD_VERSION_KEY]
22
+
23
+ name_for_deployment = build_env_vars[NAME_FOR_DEPLOYMENT_KEY]
24
+ name_for_deployment_resigned_device = build_env_vars['NAME_FOR_DEPLOYMENT_RESIGNED_DEVICE']
25
+ name_for_deployment_resigned_adhoc = build_env_vars['NAME_FOR_DEPLOYMENT_RESIGNED_ADHOC']
26
+ name_for_deployment_resigned_appstore = build_env_vars['NAME_FOR_DEPLOYMENT_RESIGNED_APPSTORE']
27
+
28
+ executable_name = build_env_vars[EXECUTABLE_NAME_KEY]
29
+ app_dsym = build_env_vars[APP_DSYM_KEY]
30
+ sdk = build_env_vars[SDK_NAME_KEY]
31
+
32
+ ipa_count = 0
33
+
34
+ if ipa_product.present? && File.exist?(ipa_product)
35
+ ipa_count += 1
36
+ deploy_internal(deploy_host, deploy_dir, deploy_user, deploy_password, deploy_itunesconnect_user, deliver_deploy,
37
+ ipa_bundle_id, ipa_product, current_app_version, current_build_version, name_for_deployment, executable_name, app_dsym, sdk)
38
+ end
39
+
40
+ if ipa_product_resigned_device.present? && File.exist?(ipa_product_resigned_device)
41
+ ipa_count += 1
42
+ ipa_bundle_id_resigned_device = ipa_bundle_id if ipa_bundle_id_resigned_device.blank?
43
+ deploy_internal(deploy_host, deploy_dir, deploy_user, deploy_password, deploy_itunesconnect_user, deliver_deploy,
44
+ ipa_bundle_id_resigned_device, ipa_product_resigned_device, current_app_version, current_build_version, name_for_deployment_resigned_device,
45
+ executable_name, app_dsym, sdk)
46
+ end
47
+
48
+ if ipa_product_resigned_adhoc.present? && File.exist?(ipa_product_resigned_adhoc)
49
+ ipa_count += 1
50
+ ipa_bundle_id_resigned_adhoc = ipa_bundle_id if ipa_bundle_id_resigned_adhoc.blank?
51
+ deploy_internal(deploy_host, deploy_dir, deploy_user, deploy_password, deploy_itunesconnect_user, deliver_deploy,
52
+ ipa_bundle_id_resigned_adhoc, ipa_product_resigned_adhoc, current_app_version, current_build_version, name_for_deployment_resigned_adhoc,
53
+ executable_name, app_dsym, sdk)
54
+ end
55
+
56
+ if ipa_product_resigned_appstore.present? && File.exist?(ipa_product_resigned_appstore)
57
+ ipa_count += 1
58
+ ipa_bundle_id_resigned_appstore = ipa_bundle_id if ipa_bundle_id_resigned_appstore.blank?
59
+ deploy_internal(deploy_host, deploy_dir, deploy_user, deploy_password, deploy_itunesconnect_user, deliver_deploy,
60
+ ipa_bundle_id_resigned_appstore, ipa_product_resigned_appstore, current_app_version, current_build_version, name_for_deployment_resigned_appstore,
61
+ executable_name, app_dsym, sdk)
62
+ end
63
+
64
+ raise 'Nothing to upload!' if ipa_count == 0
65
+ end
66
+
67
+ def self.deploy_internal(deploy_host, deploy_dir, deploy_user, deploy_password, deploy_itunesconnect_user, deliver_deploy,
68
+ ipa_bundle_id, ipa_product, current_app_version, current_build_version, name_for_deployment, executable_name, app_dsym, sdk)
69
+ is_macos_build = sdk.include?('macos')
70
+
71
+ local_path_to_app = File.join(Dir.tmpdir, ipa_bundle_id)
72
+ local_path_to_build = File.join(local_path_to_app, "v.#{current_app_version}_#{current_build_version}")
73
+ configuration_full_path = File.join(local_path_to_build, name_for_deployment)
74
+ destination_ipa_product = File.join(configuration_full_path, executable_name + (is_macos_build ? ZIP_EXT : IPA_EXT))
75
+
76
+ FileUtils.rm_rf(local_path_to_build)
77
+ FileUtils.mkdir_p(configuration_full_path)
78
+ FileUtils.cp_r(ipa_product, destination_ipa_product)
79
+
80
+ unless is_macos_build
81
+ if deploy_itunesconnect_user.present?
82
+ puts
83
+ print 'Starting pilot ...'.blue
84
+ puts
85
+
86
+ skip_submission = deliver_deploy ? '' : '--skip_submission'
87
+ system("pilot upload -i '#{ipa_product}' -u '#{deploy_itunesconnect_user}' #{skip_submission}")
88
+ end
89
+
90
+ # zip dsym
91
+ dsym_path = executable_name + APP_EXT + DSYM_EXT
92
+ zipped_dsym_path = File.join(configuration_full_path, dsym_path + ZIP_EXT)
93
+
94
+ if File.exist?(app_dsym)
95
+ Dir.chdir(File.dirname(app_dsym)) do
96
+ system("/usr/bin/zip --symlinks --verbose --recurse-paths '#{zipped_dsym_path}' '#{File.basename(app_dsym)}'")
97
+ end
98
+ end
99
+ end
100
+
101
+ FileUtils.chmod_R(0775, local_path_to_app)
102
+
103
+ scp(deploy_host, deploy_dir, deploy_user, deploy_password, local_path_to_app)
104
+
105
+ FileUtils.rm_rf(local_path_to_app)
106
+ end
107
+
108
+ def self.scp(deploy_host, deploy_dir, deploy_user, deploy_password, local_path_to_app)
109
+ puts
110
+ puts "Uploading build from '#{local_path_to_app}' ...".blue
111
+ puts
112
+
113
+ max_percentage = 100
114
+ progress_step = 5
115
+ max_progress = max_percentage / progress_step
116
+
117
+ Net::SCP.upload!(deploy_host, deploy_user, local_path_to_app, deploy_dir, :ssh => { :password => deploy_password }, :recursive => true, :preserve => true) do |_ch, _name, sent, total|
118
+ percentage = (sent.to_f / total.to_f * max_percentage).round(1)
119
+ current_progress = (percentage / progress_step).floor
120
+ progress_bar = '|' + '#' * current_progress + '_' * (max_progress - current_progress) + '|'
121
+ print "#{percentage}% #{progress_bar}\r"
122
+ end
123
+
124
+ puts
125
+ end
126
+
127
+ private_class_method :deploy_internal, :scp
128
+ end
129
+ end
@@ -0,0 +1,179 @@
1
+ module Nixenvironment
2
+ class SCM
3
+ SCM_SVN = 'svn'
4
+ SCM_GIT = 'git'
5
+ SCM_MERCURIAL = 'mercurial'
6
+
7
+ def self.current
8
+ %x[ git status ]
9
+ return SCM_GIT if $?.success?
10
+
11
+ %x[ hg status ]
12
+ return SCM_MERCURIAL if $?.success?
13
+
14
+ %x[ svn info ]
15
+ return SCM_SVN if $?.success?
16
+
17
+ return 'undefined'
18
+ end
19
+
20
+ def self.save_revision
21
+ scm_type = current
22
+ case scm_type
23
+ when SCM_SVN
24
+ puts 'SVN working copy detected'
25
+ current_revision = %x[ svnversion ]
26
+ current_monotonic_revision = current_revision
27
+
28
+ # simply check that the SVN version is a digit
29
+ if Integer(current_revision)
30
+ working_copy_is_clean = 1
31
+ else
32
+ puts "Working copy is not clean because of svnversion returning #{current_revision}"
33
+ working_copy_is_clean = 0
34
+ end
35
+ when SCM_GIT
36
+ puts 'GIT working copy detected'
37
+ current_revision = %x[ git rev-parse HEAD ]
38
+ last_revision_in_repo = %x[ git rev-list --all -n 1 ]
39
+
40
+ # number of pushes before current
41
+ current_monotonic_revision = %x[ git rev-list --all --count ]
42
+
43
+ if current_revision == last_revision_in_repo
44
+ current_monotonic_revision = current_monotonic_revision.succ
45
+ end
46
+
47
+ status = %x[ git status --porcelain ]
48
+
49
+ if status == ''
50
+ working_copy_is_clean = 1
51
+ else
52
+ # TODO: check this out for Unity builds
53
+ # ignore changes to build folder, because it's created by Xcode before the script runs as a build phase
54
+ lines_count = status.lines.count
55
+
56
+ if lines_count != 1
57
+ puts "Working copy is not clean because of status:\n#{status}"
58
+ puts
59
+ working_copy_is_clean = 0
60
+ else
61
+ wc_root = %[ git rev-parse --show-toplevel ]
62
+ modified_path = status[3..-1]
63
+ abs_modified_path = File.join(wc_root, modified_path)
64
+
65
+ # compare path to Xcode env var
66
+ if abs_modified_path == ENV['BUILD_ROOT']
67
+ working_copy_is_clean = 1
68
+ else
69
+ puts "Working copy is not clean because of status:\n#{status}"
70
+ puts
71
+ working_copy_is_clean = 0
72
+ end
73
+ end
74
+ end
75
+ when SCM_MERCURIAL
76
+ puts 'Mercurial working copy detected'
77
+ current_revision = %x[ hg id -i ]
78
+ # number of pushes in current branch
79
+ # TODO: compare with: "$(hg log --template '{rev}:{node|short} {desc|firstline} ({author})\n' | wc -l | tr -d ' ')"
80
+ current_monotonic_revision = %x[ hg id --num --rev tip ]
81
+
82
+ status = %x[ hg status ]
83
+
84
+ if status == ''
85
+ working_copy_is_clean = 1
86
+ else
87
+ # TODO: check this out for Unity builds
88
+ # ignore changes to build folder, because it's created by Xcode before the script runs as a build phase
89
+ lines_count = status.lines.count
90
+
91
+ if lines_count != 1
92
+ puts "Working copy is not clean because of status:\n#{status}"
93
+ puts
94
+ working_copy_is_clean = 0
95
+ else
96
+ wc_root = %[ hg root ]
97
+ modified_path = status[3..-1]
98
+ abs_modified_path = File.join(wc_root, modified_path)
99
+
100
+ # compare path to Xcode env var
101
+ if abs_modified_path == ENV['BUILD_ROOT']
102
+ working_copy_is_clean = 1
103
+ else
104
+ puts "Working copy is not clean because of status:\n#{status}"
105
+ puts
106
+ working_copy_is_clean = 0
107
+ end
108
+ end
109
+ end
110
+ else
111
+ puts 'warning: script must be run from working copy. Revision is undefined.'
112
+ current_revision = 'undefined'
113
+ current_monotonic_revision = 'undefined'
114
+ working_copy_is_clean = 0
115
+ end
116
+
117
+ system("
118
+ echo \"#!/bin/sh\
119
+ #{AUTOGENERATED_FILES_HEADER}
120
+ REVISION='#{current_revision.strip!}'
121
+ MONOTONIC_REVISION='#{current_monotonic_revision.strip!}'
122
+ WORKING_COPY_IS_CLEAN='#{working_copy_is_clean}'\" > #{AUTOGENERATED_LAST_REVISION}
123
+ ")
124
+ end
125
+
126
+ # TODO: implement --all for svn
127
+ def self.clean_working_copy(all)
128
+ puts '--- Clean all specified' if all.present?
129
+ scm_type = current
130
+
131
+ case scm_type
132
+ when SCM_SVN
133
+ puts 'SVN working copy detected, cleaning ...'
134
+
135
+ # revert all changes
136
+ system('svn revert --depth=infinity .')
137
+
138
+ # revert all changes in external directories
139
+ # TODO: rework this for Windows
140
+ system("
141
+ for i in $(svn status | grep ^Performing | cut -d\' -f 2)
142
+ do
143
+ svn revert -R $i
144
+ done")
145
+
146
+ # Wipes out unversioned files from Subversion working copy
147
+ system('svn cleanup . --remove-unversioned')
148
+ when SCM_GIT
149
+ puts 'GIT working copy detected, cleaning ...'
150
+
151
+ # revert all changes
152
+ system('git reset --hard')
153
+
154
+ if all
155
+ # wipe out unversioned files (force, remove whole directories, remove ignored files)
156
+ system('git clean -fdx')
157
+ else
158
+ # wipe out unversioned files (force, remove whole directories)
159
+ system('git clean -fd')
160
+ end
161
+ when SCM_MERCURIAL
162
+ puts 'Mercurial working copy detected, cleaning ...'
163
+
164
+ # revert all changes
165
+ system('hg revert --all')
166
+
167
+ if all
168
+ # wipe out unversioned files
169
+ system('hg --config extensions.purge= clean --all')
170
+ else
171
+ # wipe out unversioned files (except ignored files)
172
+ system('hg --config extensions.purge= clean')
173
+ end
174
+ else
175
+ puts 'Error: script must be run from working copy!'
176
+ end
177
+ end
178
+ end
179
+ end
@@ -1,3 +1,3 @@
1
1
  module Nixenvironment
2
- VERSION = '0.0.70'
2
+ VERSION = '0.0.71'
3
3
  end
@@ -107,6 +107,8 @@ module Nixenvironment
107
107
  build_args << '-showBuildSettings'
108
108
 
109
109
  puts 'Reading config settings ...'
110
+ puts
111
+
110
112
  cmd_output = execute(nil, build_args, true)
111
113
 
112
114
  env_vars_list = cmd_output.split(/\n/).reject(&:empty?)
@@ -167,7 +169,11 @@ module Nixenvironment
167
169
 
168
170
  def self.read_info
169
171
  @info = {}
172
+
173
+ puts
170
174
  cmd_output = execute(nil, %w(-list 2>&1))
175
+ puts
176
+
171
177
  raise "Getting xcode_project_info error! #{cmd_output}" if cmd_output.include?('error')
172
178
 
173
179
  cmd_output = cmd_output.lines[1..-1].join.split(/\n\n/)