flappy-cli 0.3.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/.gitignore +10 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +12 -0
- data/LICENSE.txt +21 -0
- data/README.md +41 -0
- data/Rakefile +2 -0
- data/bin/console +14 -0
- data/bin/flappy +14 -0
- data/bin/setup +8 -0
- data/flappy-cli.gemspec +50 -0
- data/lib/flappy/api.yml +7 -0
- data/lib/flappy/cli.rb +145 -0
- data/lib/flappy/patches/blank.rb +131 -0
- data/lib/flappy/patches/concern.rb +146 -0
- data/lib/flappy/patches/default_headers.rb +9 -0
- data/lib/flappy/patches/hash.rb +79 -0
- data/lib/flappy/patches/instance_variables.rb +30 -0
- data/lib/flappy/patches/native_patch.rb +28 -0
- data/lib/flappy/patches/os_patch.rb +28 -0
- data/lib/flappy/patches/try.rb +102 -0
- data/lib/flappy/patches.rb +12 -0
- data/lib/flappy/util/archive_ipa.rb +55 -0
- data/lib/flappy/util/build_apk.rb +104 -0
- data/lib/flappy/util/build_common.rb +90 -0
- data/lib/flappy/util/build_ipa.rb +331 -0
- data/lib/flappy/util/config.rb +41 -0
- data/lib/flappy/util/http.rb +30 -0
- data/lib/flappy/util/iOS_check.rb +29 -0
- data/lib/flappy/util/iOS_env_config.rb +54 -0
- data/lib/flappy/util/iOS_logger.rb +113 -0
- data/lib/flappy/util/info.rb +39 -0
- data/lib/flappy/util/ipa_info.rb +198 -0
- data/lib/flappy/util/mapping.rb +84 -0
- data/lib/flappy/util/multi_io.rb +27 -0
- data/lib/flappy/util/parser/apk.rb +42 -0
- data/lib/flappy/util/parser/bin/pngcrush +0 -0
- data/lib/flappy/util/parser/common.rb +24 -0
- data/lib/flappy/util/parser/pngcrush.rb +23 -0
- data/lib/flappy/util/publish.rb +185 -0
- data/lib/flappy/util/update_pod.rb +264 -0
- data/lib/flappy/util.rb +105 -0
- data/lib/flappy/version.rb +3 -0
- data/lib/flappy-cli.rb +3 -0
- data/lib/flappy.rb +28 -0
- data/output/Flappy-Archives/iOS/WorkspaceForPackageTest/20170103000308/20170103000308_WorkspaceForPackageTest.json +1 -0
- data/output/Flappy-Archives/iOS/WorkspaceForPackageTest/20170103000308/20170103000308_WorkspaceForPackageTest_Podfile +16 -0
- data/output/Flappy-Archives/iOS/WorkspaceForPackageTest/20170103000308/20170103000308_WorkspaceForPackageTest_Podfile_lock +40 -0
- data/output/Flappy-Archives/iOS/WorkspaceForPackageTest/20170103000314/20170103000314_WorkspaceForPackageTest.json +1 -0
- data/output/Flappy-Archives/iOS/WorkspaceForPackageTest/20170103000314/20170103000314_WorkspaceForPackageTest_Podfile +16 -0
- data/output/Flappy-Archives/iOS/WorkspaceForPackageTest/20170103000314/20170103000314_WorkspaceForPackageTest_Podfile_lock +40 -0
- metadata +223 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module ActiveSupport
|
|
4
|
+
# A typical module looks like this:
|
|
5
|
+
#
|
|
6
|
+
# module M
|
|
7
|
+
# def self.included(base)
|
|
8
|
+
# base.extend ClassMethods
|
|
9
|
+
# base.class_eval do
|
|
10
|
+
# scope :disabled, -> { where(disabled: true) }
|
|
11
|
+
# end
|
|
12
|
+
# end
|
|
13
|
+
#
|
|
14
|
+
# module ClassMethods
|
|
15
|
+
# ...
|
|
16
|
+
# end
|
|
17
|
+
# end
|
|
18
|
+
#
|
|
19
|
+
# By using <tt>ActiveSupport::Concern</tt> the above module could instead be
|
|
20
|
+
# written as:
|
|
21
|
+
#
|
|
22
|
+
# require 'active_support/concern'
|
|
23
|
+
#
|
|
24
|
+
# module M
|
|
25
|
+
# extend ActiveSupport::Concern
|
|
26
|
+
#
|
|
27
|
+
# included do
|
|
28
|
+
# scope :disabled, -> { where(disabled: true) }
|
|
29
|
+
# end
|
|
30
|
+
#
|
|
31
|
+
# class_methods do
|
|
32
|
+
# ...
|
|
33
|
+
# end
|
|
34
|
+
# end
|
|
35
|
+
#
|
|
36
|
+
# Moreover, it gracefully handles module dependencies. Given a +Foo+ module
|
|
37
|
+
# and a +Bar+ module which depends on the former, we would typically write the
|
|
38
|
+
# following:
|
|
39
|
+
#
|
|
40
|
+
# module Foo
|
|
41
|
+
# def self.included(base)
|
|
42
|
+
# base.class_eval do
|
|
43
|
+
# def self.method_injected_by_foo
|
|
44
|
+
# ...
|
|
45
|
+
# end
|
|
46
|
+
# end
|
|
47
|
+
# end
|
|
48
|
+
# end
|
|
49
|
+
#
|
|
50
|
+
# module Bar
|
|
51
|
+
# def self.included(base)
|
|
52
|
+
# base.method_injected_by_foo
|
|
53
|
+
# end
|
|
54
|
+
# end
|
|
55
|
+
#
|
|
56
|
+
# class Host
|
|
57
|
+
# include Foo # We need to include this dependency for Bar
|
|
58
|
+
# include Bar # Bar is the module that Host really needs
|
|
59
|
+
# end
|
|
60
|
+
#
|
|
61
|
+
# But why should +Host+ care about +Bar+'s dependencies, namely +Foo+? We
|
|
62
|
+
# could try to hide these from +Host+ directly including +Foo+ in +Bar+:
|
|
63
|
+
#
|
|
64
|
+
# module Bar
|
|
65
|
+
# include Foo
|
|
66
|
+
# def self.included(base)
|
|
67
|
+
# base.method_injected_by_foo
|
|
68
|
+
# end
|
|
69
|
+
# end
|
|
70
|
+
#
|
|
71
|
+
# class Host
|
|
72
|
+
# include Bar
|
|
73
|
+
# end
|
|
74
|
+
#
|
|
75
|
+
# Unfortunately this won't work, since when +Foo+ is included, its <tt>base</tt>
|
|
76
|
+
# is the +Bar+ module, not the +Host+ class. With <tt>ActiveSupport::Concern</tt>,
|
|
77
|
+
# module dependencies are properly resolved:
|
|
78
|
+
#
|
|
79
|
+
# require 'active_support/concern'
|
|
80
|
+
#
|
|
81
|
+
# module Foo
|
|
82
|
+
# extend ActiveSupport::Concern
|
|
83
|
+
# included do
|
|
84
|
+
# def self.method_injected_by_foo
|
|
85
|
+
# ...
|
|
86
|
+
# end
|
|
87
|
+
# end
|
|
88
|
+
# end
|
|
89
|
+
#
|
|
90
|
+
# module Bar
|
|
91
|
+
# extend ActiveSupport::Concern
|
|
92
|
+
# include Foo
|
|
93
|
+
#
|
|
94
|
+
# included do
|
|
95
|
+
# self.method_injected_by_foo
|
|
96
|
+
# end
|
|
97
|
+
# end
|
|
98
|
+
#
|
|
99
|
+
# class Host
|
|
100
|
+
# include Bar # It works, now Bar takes care of its dependencies
|
|
101
|
+
# end
|
|
102
|
+
module Concern
|
|
103
|
+
class MultipleIncludedBlocks < StandardError #:nodoc:
|
|
104
|
+
def initialize
|
|
105
|
+
super "Cannot define multiple 'included' blocks for a Concern"
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def self.extended(base) #:nodoc:
|
|
110
|
+
base.instance_variable_set(:@_dependencies, [])
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def append_features(base)
|
|
114
|
+
if base.instance_variable_defined?(:@_dependencies)
|
|
115
|
+
base.instance_variable_get(:@_dependencies) << self
|
|
116
|
+
return false
|
|
117
|
+
else
|
|
118
|
+
return false if base < self
|
|
119
|
+
@_dependencies.each { |dep| base.send(:include, dep) }
|
|
120
|
+
super
|
|
121
|
+
base.extend const_get(:ClassMethods) if const_defined?(:ClassMethods)
|
|
122
|
+
base.class_eval(&@_included_block) if instance_variable_defined?(:@_included_block)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def included(base = nil, &block)
|
|
127
|
+
if base.nil?
|
|
128
|
+
fail MultipleIncludedBlocks if instance_variable_defined?(:@_included_block)
|
|
129
|
+
|
|
130
|
+
@_included_block = block
|
|
131
|
+
else
|
|
132
|
+
super
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def class_methods(&class_methods_module_definition)
|
|
137
|
+
if const_defined?(:ClassMethods, false)
|
|
138
|
+
mod = const_get(:ClassMethods)
|
|
139
|
+
else
|
|
140
|
+
mod = const_set(:ClassMethods, Module.new)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
mod.module_eval(&class_methods_module_definition)
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
class Hash
|
|
4
|
+
# Returns a copy of self with all blank keys removed.
|
|
5
|
+
#
|
|
6
|
+
# hash = { name: 'Rob', age: '', title: nil }
|
|
7
|
+
#
|
|
8
|
+
# hash.compact
|
|
9
|
+
# # => { name: 'Rob' }
|
|
10
|
+
def compact
|
|
11
|
+
delete_if { |_, v| v.is_a?(FalseClass) ? false : v.blank? }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Returns a new hash with all keys converted using the block operation.
|
|
15
|
+
#
|
|
16
|
+
# hash = { name: 'Rob', age: '28' }
|
|
17
|
+
#
|
|
18
|
+
# hash.transform_keys{ |key| key.to_s.upcase }
|
|
19
|
+
# # => {"NAME"=>"Rob", "AGE"=>"28"}
|
|
20
|
+
def transform_keys
|
|
21
|
+
return enum_for(:transform_keys) unless block_given?
|
|
22
|
+
result = self.class.new
|
|
23
|
+
each_key do |key|
|
|
24
|
+
result[yield(key)] = self[key]
|
|
25
|
+
end
|
|
26
|
+
result
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Returns a new hash with all keys converted to symbols, as long as
|
|
30
|
+
# they respond to +to_sym+.
|
|
31
|
+
#
|
|
32
|
+
# hash = { 'name' => 'Rob', 'age' => '28' }
|
|
33
|
+
#
|
|
34
|
+
# hash.symbolize_keys
|
|
35
|
+
# # => {:name=>"Rob", :age=>"28"}
|
|
36
|
+
def symbolize_keys
|
|
37
|
+
transform_keys { |key| key.to_sym rescue key }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Returns a new hash with all keys converted by the block operation.
|
|
41
|
+
# This includes the keys from the root hash and from all
|
|
42
|
+
# nested hashes and arrays.
|
|
43
|
+
#
|
|
44
|
+
# hash = { person: { name: 'Rob', age: '28' } }
|
|
45
|
+
#
|
|
46
|
+
# hash.deep_transform_keys{ |key| key.to_s.upcase }
|
|
47
|
+
# # => {"PERSON"=>{"NAME"=>"Rob", "AGE"=>"28"}}
|
|
48
|
+
def deep_transform_keys(&block)
|
|
49
|
+
_deep_transform_keys_in_object(self, &block)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Returns a new hash with all keys converted to symbols, as long as
|
|
53
|
+
# they respond to +to_sym+. This includes the keys from the root hash
|
|
54
|
+
# and from all nested hashes and arrays.
|
|
55
|
+
#
|
|
56
|
+
# hash = { 'person' => { 'name' => 'Rob', 'age' => '28' } }
|
|
57
|
+
#
|
|
58
|
+
# hash.deep_symbolize_keys
|
|
59
|
+
# # => {:person=>{:name=>"Rob", :age=>"28"}}
|
|
60
|
+
def deep_symbolize_keys
|
|
61
|
+
deep_transform_keys { |key| key.to_sym rescue key }
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
private
|
|
65
|
+
|
|
66
|
+
# support methods for deep transforming nested hashes and arrays
|
|
67
|
+
def _deep_transform_keys_in_object(object, &block)
|
|
68
|
+
case object
|
|
69
|
+
when Hash
|
|
70
|
+
object.each_with_object({}) do |(key, value), result|
|
|
71
|
+
result[yield(key)] = _deep_transform_keys_in_object(value, &block)
|
|
72
|
+
end
|
|
73
|
+
when Array
|
|
74
|
+
object.map { |e| _deep_transform_keys_in_object(e, &block) }
|
|
75
|
+
else
|
|
76
|
+
object
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
class Object
|
|
4
|
+
# Returns a hash with string keys that maps instance variable names without "@" to their
|
|
5
|
+
# corresponding values.
|
|
6
|
+
#
|
|
7
|
+
# class C
|
|
8
|
+
# def initialize(x, y)
|
|
9
|
+
# @x, @y = x, y
|
|
10
|
+
# end
|
|
11
|
+
# end
|
|
12
|
+
#
|
|
13
|
+
# C.new(0, 1).instance_values # => {"x" => 0, "y" => 1}
|
|
14
|
+
def instance_values
|
|
15
|
+
Hash[instance_variables.map { |name| [name[1..-1], instance_variable_get(name)] }]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Returns an array of instance variable names as strings including "@".
|
|
19
|
+
#
|
|
20
|
+
# class C
|
|
21
|
+
# def initialize(x, y)
|
|
22
|
+
# @x, @y = x, y
|
|
23
|
+
# end
|
|
24
|
+
# end
|
|
25
|
+
#
|
|
26
|
+
# C.new(0, 1).instance_variable_names # => ["@y", "@x"]
|
|
27
|
+
def instance_variable_names
|
|
28
|
+
instance_variables.map { |var| var.to_s }
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
class File
|
|
4
|
+
class << self
|
|
5
|
+
# A binary file is Mach-O dSYM
|
|
6
|
+
#
|
|
7
|
+
# @return [true, false]
|
|
8
|
+
def dsym?(file_path)
|
|
9
|
+
!(`file -b #{file_path}` =~ /dSYM/).nil?
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# A file is ASCII text
|
|
13
|
+
#
|
|
14
|
+
# @return [true, false]
|
|
15
|
+
def text?(file_path)
|
|
16
|
+
!(`file -b #{file_path}` =~ /text/).nil?
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class String
|
|
22
|
+
# Convert String encoding to UTF-8
|
|
23
|
+
#
|
|
24
|
+
# @return string
|
|
25
|
+
def to_utf8
|
|
26
|
+
encode(Encoding.find('UTF-8'), invalid: :replace, undef: :replace, replace: '')
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module OS
|
|
4
|
+
class << self
|
|
5
|
+
|
|
6
|
+
def windows?
|
|
7
|
+
!(/cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM).nil?
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def mac?
|
|
11
|
+
!(/darwin/ =~ RUBY_PLATFORM).nil?
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def unix?
|
|
15
|
+
!OS.windows?
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def linux?
|
|
19
|
+
OS.unix? && !OS.mac?
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def set_locale
|
|
23
|
+
system 'export LC_ALL=en_US.UTF-8'
|
|
24
|
+
system 'export LC_CTYPE=en_US.UTF-8'
|
|
25
|
+
system 'export LANG=en_US.UTF-8'
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
class Object
|
|
4
|
+
# Invokes the public method whose name goes as first argument just like
|
|
5
|
+
# +public_send+ does, except that if the receiver does not respond to it the
|
|
6
|
+
# call returns +nil+ rather than raising an exception.
|
|
7
|
+
#
|
|
8
|
+
# This method is defined to be able to write
|
|
9
|
+
#
|
|
10
|
+
# @person.try(:name)
|
|
11
|
+
#
|
|
12
|
+
# instead of
|
|
13
|
+
#
|
|
14
|
+
# @person.name if @person
|
|
15
|
+
#
|
|
16
|
+
# +try+ calls can be chained:
|
|
17
|
+
#
|
|
18
|
+
# @person.try(:spouse).try(:name)
|
|
19
|
+
#
|
|
20
|
+
# instead of
|
|
21
|
+
#
|
|
22
|
+
# @person.spouse.name if @person && @person.spouse
|
|
23
|
+
#
|
|
24
|
+
# +try+ will also return +nil+ if the receiver does not respond to the method:
|
|
25
|
+
#
|
|
26
|
+
# @person.try(:non_existing_method) #=> nil
|
|
27
|
+
#
|
|
28
|
+
# instead of
|
|
29
|
+
#
|
|
30
|
+
# @person.non_existing_method if @person.respond_to?(:non_existing_method) #=> nil
|
|
31
|
+
#
|
|
32
|
+
# +try+ returns +nil+ when called on +nil+ regardless of whether it responds
|
|
33
|
+
# to the method:
|
|
34
|
+
#
|
|
35
|
+
# nil.try(:to_i) # => nil, rather than 0
|
|
36
|
+
#
|
|
37
|
+
# Arguments and blocks are forwarded to the method if invoked:
|
|
38
|
+
#
|
|
39
|
+
# @posts.try(:each_slice, 2) do |a, b|
|
|
40
|
+
# ...
|
|
41
|
+
# end
|
|
42
|
+
#
|
|
43
|
+
# The number of arguments in the signature must match. If the object responds
|
|
44
|
+
# to the method the call is attempted and +ArgumentError+ is still raised
|
|
45
|
+
# in case of argument mismatch.
|
|
46
|
+
#
|
|
47
|
+
# If +try+ is called without arguments it yields the receiver to a given
|
|
48
|
+
# block unless it is +nil+:
|
|
49
|
+
#
|
|
50
|
+
# @person.try do |p|
|
|
51
|
+
# ...
|
|
52
|
+
# end
|
|
53
|
+
#
|
|
54
|
+
# You can also call try with a block without accepting an argument, and the block
|
|
55
|
+
# will be instance_eval'ed instead:
|
|
56
|
+
#
|
|
57
|
+
# @person.try { upcase.truncate(50) }
|
|
58
|
+
#
|
|
59
|
+
# Please also note that +try+ is defined on +Object+. Therefore, it won't work
|
|
60
|
+
# with instances of classes that do not have +Object+ among their ancestors,
|
|
61
|
+
# like direct subclasses of +BasicObject+. For example, using +try+ with
|
|
62
|
+
# +SimpleDelegator+ will delegate +try+ to the target instead of calling it on
|
|
63
|
+
# the delegator itself.
|
|
64
|
+
def try(*a, &b)
|
|
65
|
+
try!(*a, &b) if a.empty? || respond_to?(a.first)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Same as #try, but will raise a NoMethodError exception if the receiver is not +nil+ and
|
|
69
|
+
# does not implement the tried method.
|
|
70
|
+
|
|
71
|
+
def try!(*a, &b)
|
|
72
|
+
if a.empty? && block_given?
|
|
73
|
+
if b.arity.zero?
|
|
74
|
+
instance_eval(&b)
|
|
75
|
+
else
|
|
76
|
+
yield self
|
|
77
|
+
end
|
|
78
|
+
else
|
|
79
|
+
public_send(*a, &b)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
class NilClass
|
|
85
|
+
# Calling +try+ on +nil+ always returns +nil+.
|
|
86
|
+
# It becomes especially helpful when navigating through associations that may return +nil+.
|
|
87
|
+
#
|
|
88
|
+
# nil.try(:name) # => nil
|
|
89
|
+
#
|
|
90
|
+
# Without +try+
|
|
91
|
+
# @person && @person.children.any? && @person.children.first.name
|
|
92
|
+
#
|
|
93
|
+
# With +try+
|
|
94
|
+
# @person.try(:children).try(:first).try(:name)
|
|
95
|
+
def try(*args)
|
|
96
|
+
nil
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def try!(*args)
|
|
100
|
+
nil
|
|
101
|
+
end
|
|
102
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require_relative './patches/blank'
|
|
4
|
+
require_relative './patches/concern'
|
|
5
|
+
require_relative './patches/hash'
|
|
6
|
+
require_relative './patches/instance_variables'
|
|
7
|
+
require_relative './patches/native_patch'
|
|
8
|
+
require_relative './patches/os_patch'
|
|
9
|
+
require_relative './patches/try'
|
|
10
|
+
require_relative './patches/default_headers'
|
|
11
|
+
|
|
12
|
+
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module Flappy
|
|
4
|
+
module ArchiveIpa
|
|
5
|
+
def archive_ipa(*args, options)
|
|
6
|
+
|
|
7
|
+
build_dir = Dir.pwd
|
|
8
|
+
ipa_dir = ''
|
|
9
|
+
|
|
10
|
+
if args.count == 2
|
|
11
|
+
build_dir = args.first.to_s
|
|
12
|
+
ipa_dir = args.last.to_s
|
|
13
|
+
elsif args.count == 1
|
|
14
|
+
build_dir = args.first.to_s
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
check_condition(build_dir, 'build dir can not be empty!')
|
|
18
|
+
build_dir = File.expand_path(build_dir) unless build_dir.blank?
|
|
19
|
+
|
|
20
|
+
unless ipa_dir.blank?
|
|
21
|
+
ipa_path = File.join(ipa_dir, Time.now.strftime('%Y%m%d%H%M%S') + '.ipa')
|
|
22
|
+
else
|
|
23
|
+
ipa_path = File.join(build_dir, Time.now.strftime('%Y%m%d%H%M%S') + '.ipa')
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
puts "build_dir: #{build_dir}\nipa_path: #{ipa_path}"
|
|
27
|
+
|
|
28
|
+
xcrun_cmd = archive_ipa_with_path(build_dir, ipa_path)
|
|
29
|
+
|
|
30
|
+
puts '>>>>>>>>>>>>>>>>>>>>>>>>>>>> Archiving......'
|
|
31
|
+
puts "archive cmd: #{xcrun_cmd}"
|
|
32
|
+
|
|
33
|
+
system(xcrun_cmd) unless xcrun_cmd.blank?
|
|
34
|
+
|
|
35
|
+
if $?.to_i != 0
|
|
36
|
+
puts '>>>>>>>>>>>>>>>>>>>>>>>>>>>> Archive failed'
|
|
37
|
+
exit 1
|
|
38
|
+
else
|
|
39
|
+
puts '>>>>>>>>>>>>>>>>>>>>>>>>>>>> Archive succeed'
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def archive_ipa_with_path(build_dir, ipa_path)
|
|
44
|
+
check_condition(build_dir, 'build dir is empty!')
|
|
45
|
+
check_condition(ipa_path, 'ipa_path dir is empty!')
|
|
46
|
+
|
|
47
|
+
apps = Dir["#{build_dir}/**/*.app"].sort_by(&:size)
|
|
48
|
+
|
|
49
|
+
log_iOS("Found .app in build dir: #{apps}")
|
|
50
|
+
check_no_output_app(apps)
|
|
51
|
+
|
|
52
|
+
xcrun_cmd = "xcrun --sdk iphoneos PackageApplication -v #{apps.join(' ')} -o #{ipa_path}"
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module Flappy
|
|
4
|
+
module BuildApk
|
|
5
|
+
|
|
6
|
+
def build_apk(*args, options)
|
|
7
|
+
|
|
8
|
+
set_flavor(options)
|
|
9
|
+
check_not_set_flavor
|
|
10
|
+
|
|
11
|
+
initialize_build_common_options(args, options)
|
|
12
|
+
|
|
13
|
+
Dir.chdir(@build_dir)
|
|
14
|
+
File.new(File.join("#{@build_dir}","dependencies.txt"), "w")
|
|
15
|
+
@build_cmd = initialize_apk_build_cmd
|
|
16
|
+
|
|
17
|
+
logger_info_and_run_build_command
|
|
18
|
+
|
|
19
|
+
temp_cmd = initialize_get_dependencies_cmd;
|
|
20
|
+
logger_info_and_get_dependencies_command temp_cmd
|
|
21
|
+
|
|
22
|
+
output_apk
|
|
23
|
+
publish_build_app(options) if options.publish?
|
|
24
|
+
|
|
25
|
+
logger_info_blank_line
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def set_flavor(options)
|
|
31
|
+
unless options.flavor.blank?
|
|
32
|
+
@flavor = options.flavor
|
|
33
|
+
build_type = options[:debug] ? 'Debug' : 'Release'
|
|
34
|
+
unless @flavor =~ /^assemble(.+)/
|
|
35
|
+
@flavor = "assemble#{@flavor}#{build_type}"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def initialize_apk_build_cmd
|
|
41
|
+
check_build_gradle_exist
|
|
42
|
+
|
|
43
|
+
cmd = "./gradlew build"
|
|
44
|
+
cmd = "./gradlew #{@flavor}" unless @flavor.blank?
|
|
45
|
+
cmd
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def initialize_get_dependencies_cmd
|
|
49
|
+
#create dependencies.txt
|
|
50
|
+
cmd = "./gradlew -q app:dependencies --configuration compile > #{@build_dir}/dependencies.txt"
|
|
51
|
+
cmd
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def gradle_build_path
|
|
55
|
+
"#{@build_dir}/build/outputs/apk"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def prefix_gradle_build_path
|
|
59
|
+
"#{@build_dir}/app/build/outputs/apk"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def cheniu_prefix_gradle_build_path
|
|
63
|
+
"#{@build_dir}/cheniu/build/outputs/apk"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def output_apk
|
|
67
|
+
@builded_apk ||= Dir["#{gradle_build_path}/*.apk"].find { |i| i =~ /release/ ||i =~ /debug/}
|
|
68
|
+
@builded_apk ||= Dir["#{prefix_gradle_build_path}/*.apk"].find { |i| i =~ /release/ || i =~ /debug/}
|
|
69
|
+
@builded_apk ||= Dir["#{@build_dir}/*.apk"].find { |i| i =~ /release/ || i =~ /debug/ }
|
|
70
|
+
@builded_apk ||= Dir["#{cheniu_prefix_gradle_build_path}/*.apk"].find { |i| i =~ /release/ || i =~ /debug/}
|
|
71
|
+
|
|
72
|
+
check_no_output_apk
|
|
73
|
+
|
|
74
|
+
apk_info = Flappy.apk_info(@builded_apk)
|
|
75
|
+
|
|
76
|
+
# binding.pry
|
|
77
|
+
@apk_name = @name.blank? ? "#{apk_info[:name]}-#{apk_info[:version]}" : @name
|
|
78
|
+
|
|
79
|
+
@builded_app_path = "#{@build_dir}/#{@apk_name}.apk"
|
|
80
|
+
FileUtils.cp(@builded_apk, @builded_app_path)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def check_no_output_apk
|
|
84
|
+
unless @builded_apk
|
|
85
|
+
logger.error 'Builded has no output apk'
|
|
86
|
+
exit 1
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def check_not_set_flavor
|
|
91
|
+
unless @flavor
|
|
92
|
+
logger.error 'please set flavor'
|
|
93
|
+
exit 1
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def check_build_gradle_exist
|
|
98
|
+
return if File.exist?("#{@build_dir}/build.gradle")
|
|
99
|
+
|
|
100
|
+
logger.error "The build.gradle isn't exit"
|
|
101
|
+
exit 1
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
module Flappy
|
|
2
|
+
module BuildCommon
|
|
3
|
+
|
|
4
|
+
def initialize_build_common_options(args, options)
|
|
5
|
+
@build_dir = initialize_build_dir(args, options)
|
|
6
|
+
@output_path = initialize_output_path(options)
|
|
7
|
+
@token = options[:firToken] || current_token
|
|
8
|
+
@changelog = options[:changelog].to_s
|
|
9
|
+
@short = options[:short].to_s
|
|
10
|
+
@name = options[:name].to_s
|
|
11
|
+
@proj = options[:proj].to_s
|
|
12
|
+
@export_qrcode = options[:qrcode]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def initialize_build_dir(args, options)
|
|
16
|
+
build_dir = args.first.to_s
|
|
17
|
+
if File.extname(build_dir) == '.git'
|
|
18
|
+
args.shift && git_clone_build_dir(build_dir, options)
|
|
19
|
+
elsif build_dir.blank? || !File.exist?(build_dir)
|
|
20
|
+
Dir.pwd
|
|
21
|
+
else
|
|
22
|
+
args.shift && File.absolute_path(build_dir)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def git_clone_build_dir(ssh_url, options)
|
|
27
|
+
# project_name = File.basename(ssh_url, '.git')
|
|
28
|
+
# now_time = Time.now.strftime('%Y%m%d%H%M%S')
|
|
29
|
+
# work_space = "Flappy/Android/#{project_name}/#{now_time}"
|
|
30
|
+
# code_name = "#{now_time}_#{project_name}_store";
|
|
31
|
+
#
|
|
32
|
+
# repo_name = work_space + "/#{code_name}"
|
|
33
|
+
|
|
34
|
+
repo_name = File.basename(ssh_url, '.git') + "_#{Time.now.strftime('%Y%m%dT%H%M%S')}"
|
|
35
|
+
branch = options[:branch].blank? ? 'master' : options[:branch]
|
|
36
|
+
git_cmd = "git clone --depth=50 --branch=#{branch} #{ssh_url} #{repo_name}"
|
|
37
|
+
|
|
38
|
+
logger.info git_cmd
|
|
39
|
+
logger_info_dividing_line
|
|
40
|
+
|
|
41
|
+
if system(git_cmd)
|
|
42
|
+
File.absolute_path(repo_name)
|
|
43
|
+
else
|
|
44
|
+
logger.error 'Git clone failed'
|
|
45
|
+
exit 1
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def initialize_output_path(options)
|
|
50
|
+
if options[:output].blank?
|
|
51
|
+
output_path = "#{@build_dir}"
|
|
52
|
+
FileUtils.mkdir_p(output_path) unless File.exist?(output_path)
|
|
53
|
+
output_path
|
|
54
|
+
else
|
|
55
|
+
output_path = options[:output].to_s
|
|
56
|
+
unless File.exist?(output_path)
|
|
57
|
+
logger.warn "The output path not exist and flappy-cli will autocreate it..."
|
|
58
|
+
end
|
|
59
|
+
File.absolute_path(output_path)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def publish_build_app(options)
|
|
64
|
+
logger_info_blank_line
|
|
65
|
+
publish @builded_app_path, options
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def logger_info_and_run_build_command
|
|
69
|
+
puts @build_cmd if $DEBUG
|
|
70
|
+
|
|
71
|
+
logger.info 'Building......'
|
|
72
|
+
logger_info_dividing_line
|
|
73
|
+
|
|
74
|
+
logger.info `#{@build_cmd}`
|
|
75
|
+
|
|
76
|
+
if $?.to_i != 0
|
|
77
|
+
logger.error 'Build failed'
|
|
78
|
+
exit 1
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def logger_info_and_get_dependencies_command(cmd)
|
|
83
|
+
logger.info cmd
|
|
84
|
+
logger_info_dividing_line
|
|
85
|
+
logger.info `#{cmd}`
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
|