fir-cli-xsl 1.0.3 → 1.0.4

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 (68) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +8 -0
  3. data/.dockerignore +2 -0
  4. data/.flow-plugin.yml +14 -0
  5. data/.gitignore +27 -0
  6. data/.travis.yml +23 -0
  7. data/CHANGELOG +194 -0
  8. data/Dockerfile +12 -0
  9. data/Gemfile +10 -0
  10. data/LICENSE.txt +22 -0
  11. data/README.md +67 -0
  12. data/Rakefile +10 -0
  13. data/bin/console +11 -0
  14. data/bin/fir +14 -0
  15. data/bin/setup +7 -0
  16. data/doc/help.md +34 -0
  17. data/doc/info.md +44 -0
  18. data/doc/install.md +67 -0
  19. data/doc/login.md +19 -0
  20. data/doc/publish.md +35 -0
  21. data/doc/upgrade.md +7 -0
  22. data/fir-cli.gemspec +52 -0
  23. data/fir.sh +46 -0
  24. data/install.sh +210 -0
  25. data/lib/fir-cli.rb +3 -0
  26. data/lib/fir.rb +28 -0
  27. data/lib/fir/api.yml +7 -0
  28. data/lib/fir/cli.rb +192 -0
  29. data/lib/fir/patches.rb +10 -0
  30. data/lib/fir/patches/blank.rb +131 -0
  31. data/lib/fir/patches/concern.rb +146 -0
  32. data/lib/fir/patches/default_headers.rb +9 -0
  33. data/lib/fir/patches/hash.rb +79 -0
  34. data/lib/fir/patches/instance_variables.rb +30 -0
  35. data/lib/fir/patches/native_patch.rb +28 -0
  36. data/lib/fir/patches/os_patch.rb +28 -0
  37. data/lib/fir/patches/try.rb +102 -0
  38. data/lib/fir/util.rb +86 -0
  39. data/lib/fir/util/build_apk.rb +77 -0
  40. data/lib/fir/util/build_common.rb +93 -0
  41. data/lib/fir/util/build_ipa.rb +11 -0
  42. data/lib/fir/util/config.rb +43 -0
  43. data/lib/fir/util/http.rb +23 -0
  44. data/lib/fir/util/info.rb +38 -0
  45. data/lib/fir/util/login.rb +17 -0
  46. data/lib/fir/util/mapping.rb +98 -0
  47. data/lib/fir/util/me.rb +19 -0
  48. data/lib/fir/util/parser/apk.rb +46 -0
  49. data/lib/fir/util/parser/bin/pngcrush +0 -0
  50. data/lib/fir/util/parser/common.rb +24 -0
  51. data/lib/fir/util/parser/ipa.rb +188 -0
  52. data/lib/fir/util/parser/pngcrush.rb +23 -0
  53. data/lib/fir/util/publish.rb +293 -0
  54. data/lib/fir/version.rb +5 -0
  55. data/lib/fir/xcode_wrapper.sh +29 -0
  56. data/lib/fir_cli.rb +3 -0
  57. data/test/build_ipa_test.rb +17 -0
  58. data/test/cases/test_apk.apk +0 -0
  59. data/test/cases/test_apk_txt +1 -0
  60. data/test/cases/test_ipa.ipa +0 -0
  61. data/test/cases/test_ipa_dsym +0 -0
  62. data/test/info_test.rb +36 -0
  63. data/test/login_test.rb +12 -0
  64. data/test/mapping_test.rb +18 -0
  65. data/test/me_test.rb +17 -0
  66. data/test/publish_test.rb +44 -0
  67. data/test/test_helper.rb +98 -0
  68. metadata +84 -4
@@ -0,0 +1,23 @@
1
+ # encoding: utf-8
2
+
3
+ module FIR
4
+ module Parser
5
+ module Pngcrush
6
+
7
+ class << self
8
+
9
+ def png_bin
10
+ @png_bin ||= File.expand_path('../bin/pngcrush', __FILE__)
11
+ end
12
+
13
+ def uncrush_icon crushed_icon_path, uncrushed_icon_path
14
+ system("#{png_bin} -revert-iphone-optimizations #{crushed_icon_path} #{uncrushed_icon_path} &> /dev/null")
15
+ end
16
+
17
+ def crush_icon uncrushed_icon_path, crushed_icon_path
18
+ system("#{png_bin} -iphone #{uncrushed_icon_path} #{crushed_icon_path} &> /dev/null")
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,293 @@
1
+ # frozen_string_literal: true
2
+ module FIR
3
+ module Publish
4
+ def dingtalk(*args, options)
5
+ initialize_dtalk_options
6
+ if @token
7
+ payload = {
8
+ "msgtype": 'markdown',
9
+ "markdown": {
10
+ "title": "ceshi",
11
+ "text": @content
12
+ }
13
+ }
14
+ url = "https://oapi.dingtalk.com/robot/send?access_token=#{@token}"
15
+ DefaultRest.post(url, payload)
16
+ end
17
+
18
+ def initialize_dtalk_options(args, options)
19
+ @token = options[:token] || current_token
20
+ @content = options[:content].to_s
21
+ end
22
+
23
+ def publish(*args, options)
24
+ initialize_publish_options(args, options)
25
+ check_supported_file_and_token
26
+
27
+ logger_info_publishing_message
28
+
29
+ @app_info = send("#{@file_type}_info", @file_path, full_info: true)
30
+ @user_info = fetch_user_info(@token)
31
+ @uploading_info = fetch_uploading_info
32
+ @app_id = @uploading_info[:id]
33
+
34
+ upload_app
35
+
36
+ logger_info_dividing_line
37
+ logger_info_app_short_and_qrcode(options)
38
+
39
+ dingtalk_notifier(options)
40
+ upload_mapping_file_with_publish(options)
41
+ logger_info_blank_line
42
+ clean_files
43
+ end
44
+
45
+ def logger_info_publishing_message
46
+ user_info = fetch_user_info(@token)
47
+
48
+ email = user_info.fetch(:email, '')
49
+ name = user_info.fetch(:name, '')
50
+
51
+ logger.info "Publishing app via #{name}<#{email}>......."
52
+ logger_info_dividing_line
53
+ end
54
+
55
+ def upload_app
56
+ @icon_cert = @uploading_info[:cert][:icon]
57
+ @binary_cert = @uploading_info[:cert][:binary]
58
+
59
+ fetch_release_info
60
+ upload_app_icon unless @app_info[:icons].blank?
61
+ @app_uploaded_callback_data = upload_app_binary
62
+ logger.info "App id is #{@app_id}"
63
+ logger.info "Release id is #{@app_uploaded_callback_data[:release_id]}"
64
+ upload_device_info
65
+ update_app_info
66
+ fetch_app_info
67
+ update_release_info
68
+ end
69
+
70
+ %w[binary icon].each do |word|
71
+ define_method("upload_app_#{word}") do
72
+ upload_file(word)
73
+ storage = ENV['SOTRAGE_NAME'] || 'qiniu'
74
+ post("#{fir_api[:base_url]}/auth/#{storage}/callback", send("#{word}_information"))
75
+ end
76
+ end
77
+
78
+ def upload_file(postfix)
79
+ logger.info "Uploading app #{postfix}......"
80
+ url = @uploading_info[:cert][postfix.to_sym][:upload_url]
81
+ info = send("uploading_#{postfix}_info")
82
+ logger.debug "url = #{url}, info = #{info}"
83
+ uploaded_info = post(url, info.merge(manual_callback: true),
84
+ params_to_json: false,
85
+ header: nil)
86
+ rescue StandardError
87
+ logger.error "Uploading app #{postfix} failed"
88
+ exit 1
89
+ end
90
+
91
+ def uploading_icon_info
92
+ large_icon_path = @app_info[:icons].max_by { |f| File.size(f) }
93
+ @uncrushed_icon_path = convert_icon(large_icon_path)
94
+ {
95
+ key: @icon_cert[:key],
96
+ token: @icon_cert[:token],
97
+ file: File.new(@uncrushed_icon_path, 'rb'),
98
+ 'x:is_converted' => '1'
99
+ }
100
+ end
101
+
102
+ def icon_information
103
+ {
104
+ key: @icon_cert[:key],
105
+ token: @icon_cert[:token],
106
+ origin: 'fir-cli',
107
+ parent_id: @app_id,
108
+ fsize: File.size(@uncrushed_icon_path),
109
+ fname: 'blob'
110
+ }
111
+ end
112
+
113
+ def binary_information
114
+ {
115
+ build: @app_info[:build],
116
+ fname: File.basename(@file_path),
117
+ key: @binary_cert[:key],
118
+ name: @app_info[:display_name] || @app_info[:name],
119
+ origin: 'fir-cli',
120
+ parent_id: @app_id,
121
+ release_tag: 'develop',
122
+ fsize: File.size(@file_path),
123
+ release_type: @app_info[:release_type],
124
+ distribution_name: @app_info[:distribution_name],
125
+ token: @binary_cert[:token],
126
+ version: @app_info[:version],
127
+ changelog: @changelog,
128
+ user_id: @user_info[:id]
129
+ }.reject { |x| x.nil? || x == '' }
130
+ end
131
+
132
+ def uploading_binary_info
133
+ {
134
+ key: @binary_cert[:key],
135
+ token: @binary_cert[:token],
136
+ file: File.new(@file_path, 'rb'),
137
+ # Custom variables
138
+ 'x:name' => @app_info[:display_name] || @app_info[:name],
139
+ 'x:build' => @app_info[:build],
140
+ 'x:version' => @app_info[:version],
141
+ 'x:changelog' => @changelog,
142
+ 'x:release_type' => @app_info[:release_type],
143
+ 'x:distribution_name' => @app_info[:distribution_name]
144
+ }
145
+ end
146
+
147
+ def upload_device_info
148
+ return if @app_info[:devices].blank?
149
+
150
+ logger.info 'Updating devices info......'
151
+
152
+ post fir_api[:udids_url], key: @binary_cert[:key],
153
+ udids: @app_info[:devices].join(','),
154
+ api_token: @token
155
+ end
156
+
157
+ def update_app_info
158
+ update_info = { short: @short, passwd: @passwd, is_opened: @is_opened }.compact
159
+
160
+ return if update_info.blank?
161
+
162
+ logger.info 'Updating app info......'
163
+
164
+ patch fir_api[:app_url] + "/#{@app_id}", update_info.merge(api_token: @token)
165
+ end
166
+
167
+ def fetch_uploading_info
168
+ logger.info "Fetching #{@app_info[:identifier]}@fir.im uploading info......"
169
+ logger.info "Uploading app: #{@app_info[:name]}-#{@app_info[:version]}(Build #{@app_info[:build]})"
170
+
171
+ post fir_api[:app_url], type: @app_info[:type],
172
+ bundle_id: @app_info[:identifier],
173
+ manual_callback: true,
174
+ api_token: @token
175
+ end
176
+
177
+ def fetch_release_id
178
+ get "#{fir_api[:base_url]}/apps/#{@app_id}/releases/find_release_by_key", api_token: @token, key: @binary_cert[:key]
179
+ end
180
+
181
+ def fetch_app_info
182
+ logger.info 'Fetch app info from fir.im'
183
+
184
+ @fir_app_info = get(fir_api[:app_url] + "/#{@app_id}", api_token: @token)
185
+ write_app_info(id: @fir_app_info[:id], short: @fir_app_info[:short], name: @fir_app_info[:name])
186
+ @fir_app_info
187
+ end
188
+
189
+
190
+ def fetch_release_info
191
+ logger.info 'Fetch release info from fir.im'
192
+
193
+ release_info = get(fir_api[:app_url] + "/#{@app_id}" + "/releases", api_token: @token, page: 1)
194
+ if release_info[:datas].count > 0
195
+ @release_id = release_info[:datas][0][:id]
196
+ end
197
+
198
+ end
199
+
200
+ def update_release_info
201
+ logger.info "Update release" ":#{@release_id}" + " info from fir.im"
202
+
203
+ if nil != @release_id
204
+ patch fir_api[:app_url] + "/#{@app_id}" + "/releases" + "/#{@release_id}", api_token: @token, is_history: true
205
+ end
206
+ end
207
+
208
+ def upload_mapping_file_with_publish(options)
209
+ return if !options[:mappingfile] || !options[:proj]
210
+
211
+ logger_info_blank_line
212
+
213
+ mapping options[:mappingfile], proj: options[:proj],
214
+ build: @app_info[:build],
215
+ version: @app_info[:version],
216
+ token: @token
217
+ end
218
+
219
+ def logger_info_app_short_and_qrcode(options)
220
+ @download_url = "#{fir_api[:domain]}/#{@fir_app_info[:short]}"
221
+ @download_url += "?release_id=#{@app_uploaded_callback_data[:release_id]}" if !!options[:need_release_id]
222
+
223
+ logger.info "Published succeed: #{@download_url}"
224
+
225
+ @qrcode_path = "#{File.dirname(@file_path)}/fir-#{@app_info[:name]}.png"
226
+ FIR.generate_rqrcode(@download_url, @qrcode_path)
227
+
228
+ logger.info "Local qrcode file: #{@qrcode_path}" if @export_qrcode
229
+ end
230
+
231
+ private
232
+
233
+ def clean_files
234
+ File.delete(@qrcode_path) unless @export_qrcode
235
+ end
236
+
237
+ def dingtalk_notifier(options)
238
+ if options[:dingtalk_access_token]
239
+ title = "#{@app_info[:name]}-#{@app_info[:version]}(Build #{@app_info[:build]})"
240
+ payload = {
241
+ "msgtype": 'markdown',
242
+ "markdown": {
243
+ "title": "#{title} uploaded",
244
+ "text": "#{title} uploaded at #{Time.now}\nurl: #{@download_url}\n ![app二维码](data:image/png;base64,#{Base64.strict_encode64(File.read(open(@qrcode_path)))})"
245
+ }
246
+ }
247
+ url = "https://oapi.dingtalk.com/robot/send?access_token=#{options[:dingtalk_access_token]}"
248
+ DefaultRest.post(url, payload)
249
+ end
250
+ rescue StandardError => e
251
+ logger.warn "Dingtalk send error #{e.message}"
252
+ end
253
+
254
+ def initialize_publish_options(args, options)
255
+ @file_path = File.absolute_path(args.first.to_s)
256
+ @file_type = File.extname(@file_path).delete('.')
257
+ @token = options[:token] || current_token
258
+ @changelog = read_changelog(options[:changelog]).to_s.to_utf8
259
+ @short = options[:short].to_s
260
+ @passwd = options[:password].to_s
261
+ @is_opened = @passwd.blank? ? options[:open] : false
262
+ @export_qrcode = !!options[:qrcode]
263
+ end
264
+
265
+ def read_changelog(changelog)
266
+ return if changelog.blank?
267
+
268
+ File.exist?(changelog) ? File.read(changelog) : changelog
269
+ end
270
+
271
+ def check_supported_file_and_token
272
+ check_file_exist(@file_path)
273
+ check_supported_file(@file_path)
274
+ check_token_cannot_be_blank(@token)
275
+ fetch_user_info(@token)
276
+ end
277
+
278
+ def convert_icon(origin_path)
279
+ # 兼容性不太好, 蔽掉转化图标
280
+ return origin_path
281
+
282
+ logger.info "Converting app's icon......"
283
+
284
+ if @app_info[:type] == 'ios'
285
+ output_path = Tempfile.new(['uncrushed_icon', '.png']).path
286
+ FIR::Parser::Pngcrush.uncrush_icon(origin_path, output_path)
287
+ origin_path = output_path if File.size(output_path) != 0
288
+ end
289
+
290
+ origin_path
291
+ end
292
+ end
293
+ end
@@ -0,0 +1,5 @@
1
+ # encoding: utf-8
2
+
3
+ module FIR
4
+ VERSION = '1.0.4'
5
+ end
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env bash
2
+
3
+ #!/bin/bash --login
4
+
5
+ which rvm > /dev/null
6
+
7
+ if [[ $? -eq 0 ]]; then
8
+ echo "RVM detected, forcing to use system ruby since xcodebuild cause error"
9
+ [[ -s "$HOME/.rvm/scripts/rvm" ]] && source "$HOME/.rvm/scripts/rvm"
10
+ rvm use system
11
+ fi
12
+
13
+ if which rbenv > /dev/null; then
14
+ echo "rbenv detected, removing env variables since xcodebuild cause error"
15
+ rbenv shell system
16
+ fi
17
+
18
+ shell_session_update() { :; }
19
+
20
+ unset RUBYLIB
21
+ unset RUBYOPT
22
+ unset BUNDLE_BIN_PATH
23
+ unset _ORIGINAL_GEM_PATH
24
+ unset BUNDLE_GEMFILE
25
+ unset GEM_HOME
26
+ unset GEM_PATH
27
+
28
+ set -x
29
+ xcodebuild "$@"
@@ -0,0 +1,3 @@
1
+ # encoding: utf-8
2
+ # This file exists for backward compatbility with require 'fir_cli'
3
+ require_relative './fir'
@@ -0,0 +1,17 @@
1
+ # encoding: utf-8
2
+
3
+ class BuildAppTest < Minitest::Test
4
+
5
+ def test_build_app
6
+ if ENV['BUILD_TEST']
7
+ options = OpenStruct.new
8
+ options.send('publish?=', true)
9
+
10
+ assert FIR.build_ipa(default_ipa_project, options)
11
+ assert FIR.build_apk(default_apk_project, options)
12
+
13
+ assert FIR.build_ipa(default_ipa_git_url, options)
14
+ assert FIR.build_apk(default_apk_git_url, options)
15
+ end
16
+ end
17
+ end
Binary file
@@ -0,0 +1 @@
1
+ this is a text
Binary file
Binary file
@@ -0,0 +1,36 @@
1
+ # encoding: utf-8
2
+
3
+ class InfoTest < Minitest::Test
4
+
5
+ def test_apk_info
6
+ info = FIR.apk_info(default_apk, full_info: true)
7
+
8
+ assert_equal 'android', info[:type]
9
+ assert_equal 'com.bughd.myapplication', info[:identifier]
10
+ assert_equal 'My Application', info[:name]
11
+ assert_equal '1', info[:build]
12
+ assert_equal '1.0', info[:version]
13
+
14
+ assert_equal true, File.exist?(info[:icons].first)
15
+
16
+ assert FIR.info(default_apk, {})
17
+ end
18
+
19
+ def test_ipa_info
20
+ info = FIR.ipa_info(default_ipa, full_info: true)
21
+
22
+ assert_equal 'ios', info[:type]
23
+ assert_equal 'im.fir.build-ipa', info[:identifier]
24
+ assert_equal 'build_ipa', info[:name]
25
+ assert_equal '1', info[:build]
26
+ assert_equal '1.0', info[:version]
27
+
28
+ # Only for OSX
29
+ # assert_equal nil, info[:display_name]
30
+ # assert_equal default_device_udid, info[:devices].first
31
+ # assert_equal 'adhoc', info[:release_type]
32
+ # assert_equal default_distribution_name, info[:distribution_name]
33
+
34
+ assert FIR.info(default_ipa, {})
35
+ end
36
+ end
@@ -0,0 +1,12 @@
1
+ # encoding: utf-8
2
+
3
+ class LoginTest < Minitest::Test
4
+
5
+ def test_login
6
+ user_info = FIR.fetch_user_info(default_token)
7
+
8
+ assert_equal default_email, user_info.fetch(:email, '')
9
+
10
+ assert FIR.login(default_token)
11
+ end
12
+ end
@@ -0,0 +1,18 @@
1
+ # encoding: utf-8
2
+
3
+ class MappingTest < Minitest::Test
4
+
5
+ def test_mapping
6
+ options = {
7
+ token: default_token,
8
+ version: '1.1',
9
+ build: '1'
10
+ }
11
+
12
+ if ENV['MAPPING_TEST']
13
+ assert FIR.mapping(default_dsym_mapping, options.merge(proj: default_bughd_project_ios_id))
14
+ assert FIR.mapping(default_txt_mapping, options.merge(proj: default_bughd_project_android_id))
15
+ assert FIR.mapping(bigger_txt_mapping, options.merge(proj: default_bughd_project_android_id))
16
+ end
17
+ end
18
+ end