deploygate 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 26683c6657e25c8cfc28d840870758aff18b911d
4
- data.tar.gz: c6aab88672af12a275b127b604454a077416f78c
3
+ metadata.gz: 425bc06e86baa217dc4261b4c11f68649be3db03
4
+ data.tar.gz: fd3b686f1d21df377ce9d0649edff68789df5622
5
5
  SHA512:
6
- metadata.gz: 79894fef7c9c59ca78ba2535eebe1bc31f3af01bb776f21c17835e8ca6eb4e7cdfadb60fa9c2b27d4d0ac0327ca5d6c1579f739e27a7bf431820f253f15bd2fd
7
- data.tar.gz: a318661ff7a3991e289e76f8d96fa63e711a45f855b633468b5fa921165858f9f129506f77276e0c9ea706cad75f7f9e7e440c98f890546c0168f4f1e657c792
6
+ metadata.gz: c35ebded8826cbabc698c7ddb2c7522c1d20bdf3be3134975db1851c84fc3546d480c9b5f3689c0eb418a93001ee6b45ab80c28c1fc2f971a708f395e9656a7f
7
+ data.tar.gz: 9cd0682d323368b3bed67439271894e69295dea387a0e26701c5d303a593cb36b82b906832303b4d5171847c339473c3a7f4ab2a212f900579ef29eb06cc6cd2
data/README.md CHANGED
@@ -1,4 +1,5 @@
1
1
  # deploygate-cli
2
+ [![Build Status](https://travis-ci.org/DeployGate/deploygate-cli.svg?branch=master)](https://travis-ci.org/DeployGate/deploygate-cli)
2
3
 
3
4
  ## This is a beta version
4
5
 
@@ -25,12 +26,19 @@ $ gem install deploygate
25
26
  ```
26
27
 
27
28
  ## Usage
28
- Upload an application:
29
+
30
+ ### Upload apps
29
31
 
30
32
  ```
31
33
  $ dg deploy [package file path]
32
34
  ```
33
35
 
36
+ ### iOS build and upload
37
+
38
+ ```
39
+ $ dg deploy [ios project path]
40
+ ```
41
+
34
42
  ## License
35
43
 
36
44
  Copyright (C) 2015 DeployGate All rights reserved.
data/deploygate.gemspec CHANGED
@@ -16,7 +16,7 @@ Gem::Specification.new do |spec|
16
16
 
17
17
  dg installed! To get started fast:
18
18
 
19
- $ dg deploy [app_file_path]
19
+ $ dg deploy
20
20
 
21
21
  Or see the docs at:
22
22
 
@@ -28,6 +28,15 @@ POST_INSTALL_MESSAGE
28
28
  spec.add_dependency 'httpclient', '~> 2.2.5'
29
29
  spec.add_dependency 'commander', '~> 4.3.5'
30
30
  spec.add_dependency 'color_echo', '~> 2.0.1'
31
+ spec.add_dependency 'plist', '~> 3.1.0'
32
+ spec.add_dependency 'xcodeproj', '~> 0.28.2'
33
+ spec.add_dependency 'github_issue_request', '~> 0.0.2'
34
+ spec.add_dependency 'highline', '~> 1.7.8'
35
+
36
+ # ios build
37
+ spec.add_dependency 'gym', '~> 1.0.0'
38
+ spec.add_dependency 'spaceship', '~> 0.12.3'
39
+ spec.add_dependency 'sigh', '~> 1.1.0'
31
40
 
32
41
  spec.add_development_dependency "bundler", "~> 1.3"
33
42
  spec.add_development_dependency "rake"
@@ -0,0 +1,18 @@
1
+ module DeployGate
2
+ class Build
3
+ class << self
4
+
5
+ # @param [String] path
6
+ # @return [Boolean]
7
+ def ios?(path)
8
+ DeployGate::Builds::Ios.workspace?(path) || DeployGate::Builds::Ios.project?(path) || DeployGate::Builds::Ios.ios_root?(path)
9
+ end
10
+
11
+ # @param [String] path
12
+ # @return [Boolean]
13
+ def android?(path)
14
+ false # TODO: support android build
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,94 @@
1
+ module DeployGate
2
+ module Builds
3
+ module Ios
4
+ class Analyze
5
+ attr_reader :workspaces, :scheme_workspace, :build_workspace, :scheme
6
+
7
+ class NotLocalProvisioningProfileError < StandardError
8
+ end
9
+
10
+ BASE_WORK_DIR_NAME = 'project.xcworkspace'
11
+ BUILD_CONFIGRATION = 'Release'
12
+
13
+ # @param [Array] workspaces
14
+ # @return [DeployGate::Builds::Ios::Analyze]
15
+ def initialize(workspaces)
16
+ @workspaces = workspaces
17
+ @scheme_workspace = find_scheme_workspace(workspaces)
18
+ @build_workspace = find_build_workspace(workspaces)
19
+ @xcodeproj = File.dirname(@scheme_workspace)
20
+
21
+ config = FastlaneCore::Configuration.create(Gym::Options.available_options, {:workspace => @scheme_workspace})
22
+ project = FastlaneCore::Project.new(config)
23
+ if project.schemes.empty?
24
+ config = FastlaneCore::Configuration.create(Gym::Options.available_options, {:workspace => @build_workspace})
25
+ project = FastlaneCore::Project.new(config)
26
+ end
27
+ project.select_scheme
28
+ @scheme = project.options[:scheme]
29
+ end
30
+
31
+ # @return [String]
32
+ def target_bundle_identifier
33
+ scheme_file = find_xcschemes
34
+ xs = Xcodeproj::XCScheme.new(scheme_file)
35
+ target_name = xs.profile_action.buildable_product_runnable.buildable_reference.target_name
36
+
37
+ project = Xcodeproj::Project.open(@xcodeproj)
38
+ target = project.native_targets.reject{|target| target.name != target_name}.first
39
+ product_name = target.product_name
40
+ conf = target.build_configuration_list.build_configurations.reject{|conf| conf.name != BUILD_CONFIGRATION}.first
41
+ identifier = conf.build_settings['PRODUCT_BUNDLE_IDENTIFIER']
42
+ identifier.gsub!(/\$\(PRODUCT_NAME:.+\)/, product_name)
43
+
44
+ identifier
45
+ end
46
+
47
+ private
48
+
49
+ def find_xcschemes
50
+ shared_schemes = Dir[File.join(@xcodeproj, 'xcshareddata', 'xcschemes', '*.xcscheme')].reject do |scheme|
51
+ @scheme != File.basename(scheme, '.xcscheme')
52
+ end
53
+ user_schemes = Dir[File.join(@xcodeproj, 'xcuserdata', '*.xcuserdatad', 'xcschemes', '*.xcscheme')].reject do |scheme|
54
+ @scheme != File.basename(scheme, '.xcscheme')
55
+ end
56
+
57
+ shared_schemes.concat(user_schemes).first
58
+ end
59
+
60
+ # @param [Array] workspaces
61
+ # @return [String]
62
+ def find_scheme_workspace(workspaces)
63
+ return nil if workspaces.empty?
64
+ return workspaces.first if workspaces.count == 1
65
+
66
+ select = nil
67
+ workspaces.each do |workspace|
68
+ if BASE_WORK_DIR_NAME == File.basename(workspace)
69
+ select = workspace
70
+ end
71
+ end
72
+
73
+ select
74
+ end
75
+
76
+ # @param [Array] workspaces
77
+ # @return [String]
78
+ def find_build_workspace(workspaces)
79
+ return nil if workspaces.empty?
80
+ return workspaces.first if workspaces.count == 1
81
+
82
+ select = nil
83
+ workspaces.each do |workspace|
84
+ if BASE_WORK_DIR_NAME != File.basename(workspace)
85
+ select = workspace
86
+ end
87
+ end
88
+
89
+ select
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,144 @@
1
+ module DeployGate
2
+ module Builds
3
+ module Ios
4
+ class Export
5
+ AD_HOC = 'ad-hoc'
6
+ ENTERPRISE = 'enterprise'
7
+ SUPPORT_EXPORT_METHOD = [AD_HOC, ENTERPRISE]
8
+ PROFILE_EXTNAME = '.mobileprovision'
9
+
10
+ class << self
11
+ # @param [String] bundle_identifier
12
+ # @return [Hash]
13
+ def find_local_data(bundle_identifier)
14
+ result_profiles = {}
15
+ teams = {}
16
+ profiles.each do |profile_path|
17
+ plist = analyze_profile(profile_path)
18
+ entities = plist['Entitlements']
19
+ unless entities['get-task-allow']
20
+ team = entities['com.apple.developer.team-identifier']
21
+ application_id = entities['application-identifier']
22
+ application_id.slice!(/^#{team}\./)
23
+ application_id = '.' + application_id if application_id == '*'
24
+ if bundle_identifier.match(application_id) &&
25
+ DateTime.now < plist['ExpirationDate'] &&
26
+ installed_certificate?(profile_path)
27
+
28
+ teams[team] = plist['TeamName'] if teams[team].nil?
29
+ result_profiles[team] = [] if result_profiles[team].nil?
30
+ result_profiles[team].push(profile_path)
31
+ end
32
+ end
33
+ end
34
+
35
+ {
36
+ :teams => teams,
37
+ :profiles => result_profiles
38
+ }
39
+ end
40
+
41
+ # @param [String] profile_path
42
+ # @return [Boolean]
43
+ def installed_certificate?(profile_path)
44
+ plist = analyze_profile(profile_path)
45
+ certificate_str = plist['DeveloperCertificates'].first.read
46
+ certificate = OpenSSL::X509::Certificate.new certificate_str
47
+ id = OpenSSL::Digest::SHA1.new(certificate.to_der).to_s.upcase!
48
+ installed_identies.include?(id)
49
+ end
50
+
51
+ # @return [Array]
52
+ def installed_identies
53
+ available = `security find-identity -v -p codesigning`
54
+ ids = []
55
+ available.split("\n").each do |current|
56
+ next if current.include? "REVOKED"
57
+ begin
58
+ (ids << current.match(/.*\) (.*) \".*/)[1])
59
+ rescue
60
+ # the last line does not match
61
+ end
62
+ end
63
+
64
+ ids
65
+ end
66
+
67
+ # @param [Array] profiles
68
+ # @return [String]
69
+ def select_profile(profiles)
70
+ select = nil
71
+
72
+ profiles.each do |profile|
73
+ select = profile if adhoc?(profile) && select.nil?
74
+ select = profile if inhouse?(profile)
75
+ end
76
+ select
77
+ end
78
+
79
+ # @param [String] profile_path
80
+ # @return [String]
81
+ def codesigning_identity(profile_path)
82
+ plist = analyze_profile(profile_path)
83
+ method = method(profile_path)
84
+ identity = "iPhone Distribution: #{plist['TeamName']}"
85
+ identity += " (#{plist['Entitlements']['com.apple.developer.team-identifier']})" if method == AD_HOC
86
+
87
+ identity
88
+ end
89
+
90
+ # @param [String] profile_path
91
+ # @return [String]
92
+ def method(profile_path)
93
+ adhoc?(profile_path) ? AD_HOC : ENTERPRISE
94
+ end
95
+
96
+ # @param [String] profile_path
97
+ # @return [Boolean]
98
+ def adhoc?(profile_path)
99
+ plist = analyze_profile(profile_path)
100
+ !plist['Entitlements']['get-task-allow'] && plist['ProvisionsAllDevices'].nil?
101
+ end
102
+
103
+ # @param [String] profile_path
104
+ # @return [Boolean]
105
+ def inhouse?(profile_path)
106
+ plist = analyze_profile(profile_path)
107
+ !plist['Entitlements']['get-task-allow'] && !plist['ProvisionsAllDevices'].nil?
108
+ end
109
+
110
+ # @param [String] profile_path
111
+ # @return [Hash]
112
+ def analyze_profile(profile_path)
113
+ plist = nil
114
+ File.open(profile_path) do |profile|
115
+ asn1 = OpenSSL::ASN1.decode(profile.read)
116
+ plist_str = asn1.value[1].value[0].value[2].value[1].value[0].value
117
+ plist = Plist.parse_xml plist_str.force_encoding('UTF-8')
118
+ end
119
+ plist
120
+ end
121
+
122
+ # @return [Array]
123
+ def profiles
124
+ profiles = []
125
+ Find.find(profile_dir_path) do |path|
126
+ next if path == profile_dir_path
127
+ Find.prune if FileTest.directory?(path)
128
+ if File.extname(path) == PROFILE_EXTNAME
129
+ profiles.push(path)
130
+ end
131
+ end
132
+
133
+ profiles
134
+ end
135
+
136
+ # @return [String]
137
+ def profile_dir_path
138
+ File.join(ENV['HOME'], 'Library/MobileDevice/Provisioning Profiles')
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,82 @@
1
+ module DeployGate
2
+ module Builds
3
+ module Ios
4
+ class SetProfile
5
+ attr_reader :method
6
+
7
+ OUTPUT_PATH = '/tmp/dg/provisioning_profile/'
8
+ CERTIFICATE_OUTPUT_PATH = '/tmp/dg/certificate/'
9
+
10
+ # @param [String] username
11
+ # @param [String] identifier
12
+ # @return [DeployGate::Builds::Ios::SetProfile]
13
+ def initialize(username, identifier)
14
+ @username = username
15
+ @identifier = identifier
16
+ Spaceship.login(username)
17
+ Spaceship.select_team
18
+ if Spaceship.client.in_house?
19
+ @method = Export::ENTERPRISE
20
+ else
21
+ @method = Export::AD_HOC
22
+ end
23
+ end
24
+
25
+ # @return [Boolean]
26
+ def app_id_create
27
+ app_created = false
28
+ Spaceship.app.all.collect do |app|
29
+ if app.bundle_id == @identifier
30
+ app_created = true
31
+ break
32
+ end
33
+ end
34
+ unless app_created
35
+ Spaceship.app.create!(:bundle_id => @identifier, :name => "#{@identifier.split('.').join(' ')}")
36
+ return true
37
+ end
38
+
39
+ false
40
+ end
41
+
42
+ # @return [Array]
43
+ def create_provisioning
44
+ if @method == Export::AD_HOC
45
+ prod_certs = Spaceship.certificate.production.all
46
+ else
47
+ prod_certs = Spaceship.certificate.all.reject{|cert| cert.class != Spaceship::Portal::Certificate::InHouse}
48
+ end
49
+
50
+ # check local install certificate
51
+ FileUtils.mkdir_p(CERTIFICATE_OUTPUT_PATH)
52
+ distribution_cert_ids = []
53
+ prod_certs.each do |cert|
54
+ path = File.join(CERTIFICATE_OUTPUT_PATH, "#{cert.id}.cer")
55
+ raw_data = cert.download_raw
56
+ File.write(path, raw_data)
57
+ distribution_cert_ids.push(cert.id) if FastlaneCore::CertChecker.installed?(path)
58
+ end
59
+ raise 'Not local install certificate' if distribution_cert_ids.empty?
60
+
61
+ FileUtils.mkdir_p(OUTPUT_PATH)
62
+ provisionings = []
63
+ distribution_cert_ids.each do |cert_id|
64
+ values = {
65
+ :adhoc => @method == Export::AD_HOC ? true : false,
66
+ :app_identifier => @identifier,
67
+ :username => @username,
68
+ :output_path => OUTPUT_PATH,
69
+ :cert_id => cert_id,
70
+ :team_id => Spaceship.client.team_id
71
+ }
72
+ v = FastlaneCore::Configuration.create(Sigh::Options.available_options, values)
73
+ Sigh.config = v
74
+ provisionings.push(Sigh::Manager.start)
75
+ end
76
+
77
+ provisionings
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,81 @@
1
+ module DeployGate
2
+ module Builds
3
+ module Ios
4
+ WORK_DIR_EXTNAME = '.xcworkspace'
5
+ PROJECT_DIR_EXTNAME = '.xcodeproj'
6
+
7
+ class NotSupportExportMethodError < StandardError
8
+ end
9
+
10
+ class << self
11
+ # @param [Analyze] ios_analyze
12
+ # @param [String] target_scheme
13
+ # @param [String] codesigning_identity
14
+ # @param [String] export_method
15
+ # @return [String]
16
+ def build(ios_analyze, target_scheme, codesigning_identity, export_method = Export::AD_HOC)
17
+ raise NotSupportExportMethodError, 'Not support export' unless Export::SUPPORT_EXPORT_METHOD.include?(export_method)
18
+
19
+ values = {
20
+ :export_method => export_method,
21
+ :workspace => ios_analyze.build_workspace,
22
+ :configuration => Analyze::BUILD_CONFIGRATION,
23
+ :scheme => target_scheme,
24
+ :codesigning_identity => codesigning_identity
25
+ }
26
+ v = FastlaneCore::Configuration.create(Gym::Options.available_options, values)
27
+ absolute_ipa_path = File.expand_path(Gym::Manager.new.work(v))
28
+ absolute_dsym_path = absolute_ipa_path.gsub(".ipa", ".app.dSYM.zip") # TODO: upload to deploygate
29
+
30
+ absolute_ipa_path
31
+ end
32
+
33
+ # @param [String] path
34
+ # @return [Boolean]
35
+ def workspace?(path)
36
+ WORK_DIR_EXTNAME == File.extname(path)
37
+ end
38
+
39
+ # @param [String] path
40
+ # @return [Boolean]
41
+ def project?(path)
42
+ PROJECT_DIR_EXTNAME == File.extname(path)
43
+ end
44
+
45
+ def ios_root?(base_path)
46
+ Find.find(base_path) do |path|
47
+ next if path == base_path
48
+ return true if workspace?(path) || project?(path)
49
+ Find.prune if FileTest.directory?(path)
50
+ end
51
+ false
52
+ end
53
+
54
+ # @param [String] base_path
55
+ # @param [Boolean] current_only
56
+ # @return [Array]
57
+ def find_workspaces(base_path)
58
+ projects = []
59
+ Find.find(base_path) do |path|
60
+ next if path == base_path
61
+ if File.extname(path) == WORK_DIR_EXTNAME
62
+ projects.push(path)
63
+ end
64
+ end
65
+
66
+ projects
67
+ end
68
+
69
+ # @param [String] path
70
+ # @return [String]
71
+ def project_root_path(path)
72
+ result = path
73
+ if workspace?(path) || project?(path)
74
+ result = project_root_path(File.dirname(path))
75
+ end
76
+ result
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -4,6 +4,8 @@ module DeployGate
4
4
  attr_reader :arguments
5
5
 
6
6
  def run
7
+ GithubIssueRequest::Url.config('deploygate', 'deploygate-cli')
8
+
7
9
  program :name, 'dg'
8
10
  program :version, VERSION
9
11
  program :description, 'You can control to DeployGate in your terminal.'
@@ -12,7 +14,12 @@ module DeployGate
12
14
  c.syntax = 'dg init'
13
15
  c.description = 'project initial command'
14
16
  c.action do |args, options|
15
- Commands::Init.run
17
+ begin
18
+ Commands::Init.run
19
+ rescue => e
20
+ error_handling("Commands::Init Error: #{e.class}", e.message, ['bug', 'Init'])
21
+ raise e
22
+ end
16
23
  end
17
24
  end
18
25
 
@@ -25,7 +32,12 @@ module DeployGate
25
32
  c.option '--disable_notify', 'disable notify via email (iOS app only)'
26
33
  c.action do |args, options|
27
34
  options.default :message => '', :user => nil, :open => false, 'disable_notify' => false
28
- Commands::Deploy.run(args, options)
35
+ begin
36
+ Commands::Deploy.run(args, options)
37
+ rescue => e
38
+ error_handling("Commands::Deploy Error: #{e.class}", e.message, ['bug', 'Deploy'])
39
+ raise e
40
+ end
29
41
  end
30
42
  end
31
43
  alias_command :'push', :deploy
@@ -34,11 +46,31 @@ module DeployGate
34
46
  c.syntax = 'dg logout'
35
47
  c.description = 'logout'
36
48
  c.action do |args, options|
37
- Commands::Logout.run
49
+ begin
50
+ Commands::Logout.run
51
+ rescue => e
52
+ error_handling("Commands::Logout Error: #{e.class}", e.message, ['bug', 'Logout'])
53
+ raise e
54
+ end
38
55
  end
39
56
  end
40
57
 
41
58
  run!
42
59
  end
60
+
61
+ def error_handling(title, body, labels)
62
+ options = {
63
+ :title => title,
64
+ :body => body,
65
+ :labels => labels.push("v#{DeployGate::VERSION}")
66
+ }
67
+ url = GithubIssueRequest::Url.new(options).to_s
68
+ puts ''
69
+ if HighLine.agree('Do you want to report this issue on GitHub? (y/n) ') {|q| q.default = "n"}
70
+ puts "Please open github issue: #{url}"
71
+ system('open', url) if Commands::Deploy::Push.openable?
72
+ end
73
+ puts ''
74
+ end
43
75
  end
44
76
  end
@@ -0,0 +1,146 @@
1
+ module DeployGate
2
+ module Commands
3
+ module Deploy
4
+ class Build
5
+ class << self
6
+
7
+ # @param [Array] args
8
+ # @param [Hash] options
9
+ # @return [void]
10
+ def run(args, options)
11
+ # android/ios build
12
+ work_dir = args.first
13
+
14
+ if DeployGate::Build.ios?(work_dir)
15
+ root_path = DeployGate::Builds::Ios.project_root_path(work_dir)
16
+ workspaces = DeployGate::Builds::Ios.find_workspaces(root_path)
17
+ ios(workspaces, options)
18
+ elsif DeployGate::Build.android?(work_dir)
19
+ # TODO: support android build
20
+ end
21
+ end
22
+
23
+ # @param [Array] workspaces
24
+ # @param [Hash] options
25
+ # @return [void]
26
+ def ios(workspaces, options)
27
+ analyze = DeployGate::Builds::Ios::Analyze.new(workspaces)
28
+ target_scheme = analyze.scheme
29
+ begin
30
+ identifier = analyze.target_bundle_identifier
31
+ rescue
32
+ # not found bundle identifier
33
+ puts 'Please input bundle identifier'
34
+ puts 'Example: com.example.ios'
35
+ identifier = input_bundle_identifier
36
+ end
37
+
38
+ data = DeployGate::Builds::Ios::Export.find_local_data(identifier)
39
+ profiles = data[:profiles]
40
+ teams = data[:teams]
41
+
42
+ target_provisioning_profile = nil
43
+ if teams.empty?
44
+ target_provisioning_profile = create_provisioning(identifier)
45
+ elsif teams.count == 1
46
+ target_provisioning_profile = DeployGate::Builds::Ios::Export.select_profile(profiles[teams.keys.first])
47
+ elsif teams.count >= 2
48
+ target_provisioning_profile = select_teams(teams, profiles)
49
+ end
50
+ method = DeployGate::Builds::Ios::Export.method(target_provisioning_profile)
51
+ codesigning_identity = DeployGate::Builds::Ios::Export.codesigning_identity(target_provisioning_profile)
52
+
53
+ begin
54
+ ipa_path = DeployGate::Builds::Ios.build(analyze, target_scheme, codesigning_identity, method)
55
+ rescue => e
56
+ # TODO: build error handling
57
+ raise e
58
+ end
59
+
60
+ Push.upload([ipa_path], options)
61
+ end
62
+
63
+ def input_bundle_identifier
64
+ print 'bundle identifier: '
65
+ identifier = STDIN.gets.chop
66
+
67
+ if identifier == '' || identifier.nil?
68
+ puts 'You must input bundle identifier'
69
+ return input_bundle_identifier
70
+ end
71
+
72
+ identifier
73
+ end
74
+
75
+ # @param [Hash] teams
76
+ # @param [Hash] profiles
77
+ # @return [String]
78
+ def select_teams(teams, profiles)
79
+ result = nil
80
+ puts 'Select team:'
81
+ teams.each_with_index do |team, index|
82
+ puts "#{index + 1}. #{team[1]} (#{team[0]})"
83
+ end
84
+ print '? '
85
+ select = STDIN.gets.chop
86
+ begin
87
+ team = teams.keys[Integer(select) - 1]
88
+ team_profiles = profiles[team].first
89
+ raise 'not select' if team_profiles.nil?
90
+
91
+ result = DeployGate::Builds::Ios::Export.select_profile(profiles[team])
92
+ rescue => e
93
+ puts 'Please select team number'
94
+ return select_teams(teams, profiles)
95
+ end
96
+
97
+ result
98
+ end
99
+
100
+ # @param [String] identifier
101
+ # @return [String]
102
+ def create_provisioning(identifier)
103
+ puts <<EOF
104
+
105
+ No suitable provisioning profile found to export the app.
106
+
107
+ Please enter your email and password for Apple Developer Center
108
+ to set up/download provisioning profile automatically so you can
109
+ export the app without any extra steps.
110
+
111
+ Note: Your password will be stored to Keychain and never be sent to DeployGate.
112
+
113
+ EOF
114
+ print 'Email: '
115
+ username = STDIN.gets.chop
116
+
117
+ begin
118
+ set_profile = DeployGate::Builds::Ios::SetProfile.new(username, identifier)
119
+ rescue => e
120
+ DeployGate::Message::Error.print("Error: Please try login again")
121
+ raise e
122
+ end
123
+
124
+ begin
125
+ if set_profile.app_id_create
126
+ puts "App ID #{identifier} was created"
127
+ end
128
+ rescue => e
129
+ DeployGate::Message::Error.print("Error: Failed to create App ID")
130
+ raise e
131
+ end
132
+
133
+ begin
134
+ provisioning_profiles = set_profile.create_provisioning
135
+ rescue => e
136
+ DeployGate::Message::Error.print("Error: Failed to create provisioning profile")
137
+ raise e
138
+ end
139
+
140
+ DeployGate::Builds::Ios::Export.select_profile(provisioning_profiles)
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
@@ -6,8 +6,18 @@ module DeployGate
6
6
  # @param [Array] args
7
7
  # @param [Commander::Command::Options] options
8
8
  def run(args, options)
9
+ Init.login unless DeployGate::Session.new.login?
10
+
9
11
  # push or build(android/ios)
10
- Push.upload(args, options)
12
+ args.push(Dir.pwd) if args.empty?
13
+
14
+ work_file_path = args.first
15
+ if File.directory?(work_file_path)
16
+ Build.run(args, options)
17
+ else
18
+ # file upload
19
+ Push.upload(args, options)
20
+ end
11
21
  end
12
22
  end
13
23
  end
@@ -1,3 +1,3 @@
1
1
  module DeployGate
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
data/lib/deploygate.rb CHANGED
@@ -4,6 +4,17 @@ require "httpclient"
4
4
  require "io/console"
5
5
  require "rbconfig"
6
6
  require "color_echo"
7
+ require "openssl"
8
+ require "plist"
9
+ require "find"
10
+ require "github_issue_request"
11
+ require "highline"
12
+
13
+ # ios build
14
+ require "gym"
15
+ require "spaceship"
16
+ require "sigh"
17
+ require "xcodeproj"
7
18
 
8
19
  module DeployGate
9
20
  end
@@ -16,9 +27,15 @@ require "deploygate/commands/init"
16
27
  require "deploygate/commands/logout"
17
28
  require "deploygate/commands/deploy"
18
29
  require "deploygate/commands/deploy/push"
30
+ require "deploygate/commands/deploy/build"
19
31
  require "deploygate/config"
20
32
  require "deploygate/session"
21
33
  require "deploygate/deploy"
34
+ require "deploygate/build"
35
+ require "deploygate/builds/ios"
36
+ require "deploygate/builds/ios/export"
37
+ require "deploygate/builds/ios/analyze"
38
+ require "deploygate/builds/ios/set_profile"
22
39
  require "deploygate/message/error"
23
40
  require "deploygate/message/success"
24
41
  require "deploygate/version"
@@ -21,7 +21,7 @@ describe DeployGate::API::V1::Push do
21
21
  to_return(:body => response.to_json)
22
22
 
23
23
  call_process_block = false
24
- results = DeployGate::API::V1::Push.upload(test_file_path, target_user, token, message) {call_process_block = true}
24
+ results = DeployGate::API::V1::Push.upload(test_file_path, target_user, token, message)
25
25
  expect(results).to eq ({
26
26
  :error => response[:error],
27
27
  :message => response[:because],
@@ -31,7 +31,6 @@ describe DeployGate::API::V1::Push do
31
31
  :revision => response[:results][:revision],
32
32
  :web_url => DeployGate::API::V1::Base::BASE_URL + response[:results][:path]
33
33
  })
34
- expect(call_process_block).to be_truthy
35
34
  end
36
35
 
37
36
  it "failed" do
@@ -0,0 +1,37 @@
1
+ describe DeployGate::Build do
2
+ describe "#ios?" do
3
+ it "when select workspace" do
4
+ allow(DeployGate::Builds::Ios).to receive(:ios_root?).and_return(false)
5
+ allow(DeployGate::Builds::Ios).to receive(:workspace?).and_return(true)
6
+ allow(DeployGate::Builds::Ios).to receive(:project?).and_return(false)
7
+
8
+ result = DeployGate::Build.ios?('path')
9
+ expect(result).to be_truthy
10
+ end
11
+
12
+ it "when workspaces" do
13
+ allow(DeployGate::Builds::Ios).to receive(:ios_root?).and_return(false)
14
+ allow(DeployGate::Builds::Ios).to receive(:workspace?).and_return(false)
15
+ allow(DeployGate::Builds::Ios).to receive(:project?).and_return(true)
16
+
17
+ result = DeployGate::Build.ios?('path')
18
+ expect(result).to be_truthy
19
+ end
20
+
21
+ it "not ios" do
22
+ allow(DeployGate::Builds::Ios).to receive(:ios_root?).and_return(false)
23
+ allow(DeployGate::Builds::Ios).to receive(:workspace?).and_return(false)
24
+ allow(DeployGate::Builds::Ios).to receive(:project?).and_return(false)
25
+
26
+ result = DeployGate::Build.ios?('path')
27
+ expect(result).to be_falsey
28
+ end
29
+ end
30
+
31
+ describe "#android?" do
32
+ it "android not support" do
33
+ result = DeployGate::Build.android?('path')
34
+ expect(result).to be_falsey
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,3 @@
1
+ describe DeployGate::Builds::Ios::Analyze do
2
+ # TODO: add test
3
+ end
@@ -0,0 +1,59 @@
1
+ describe DeployGate::Builds::Ios::Export do
2
+ describe "#adhoc?" do
3
+ it "when adhoc plist" do
4
+ plist = {
5
+ 'ProvisionsAllDevices' => nil,
6
+ 'Entitlements' => {'get-task-allow' => false}
7
+ }
8
+ allow(DeployGate::Builds::Ios::Export).to receive(:analyze_profile).and_return(plist)
9
+ expect(DeployGate::Builds::Ios::Export.adhoc?('path')).to be_truthy
10
+ end
11
+
12
+ it "when inhouse plist" do
13
+ plist = {
14
+ 'ProvisionsAllDevices' => true,
15
+ 'Entitlements' => {'get-task-allow' => false}
16
+ }
17
+ allow(DeployGate::Builds::Ios::Export).to receive(:analyze_profile).and_return(plist)
18
+ expect(DeployGate::Builds::Ios::Export.adhoc?('path')).to be_falsey
19
+ end
20
+
21
+ it "when not distribution plist" do
22
+ plist = {
23
+ 'ProvisionsAllDevices' => nil,
24
+ 'Entitlements' => {'get-task-allow' => true}
25
+ }
26
+ allow(DeployGate::Builds::Ios::Export).to receive(:analyze_profile).and_return(plist)
27
+ expect(DeployGate::Builds::Ios::Export.adhoc?('path')).to be_falsey
28
+ end
29
+ end
30
+
31
+ describe "#inhouse?" do
32
+ it "when adhoc plist" do
33
+ plist = {
34
+ 'ProvisionsAllDevices' => nil,
35
+ 'Entitlements' => {'get-task-allow' => false}
36
+ }
37
+ allow(DeployGate::Builds::Ios::Export).to receive(:analyze_profile).and_return(plist)
38
+ expect(DeployGate::Builds::Ios::Export.inhouse?('path')).to be_falsey
39
+ end
40
+
41
+ it "when inhouse plist" do
42
+ plist = {
43
+ 'ProvisionsAllDevices' => true,
44
+ 'Entitlements' => {'get-task-allow' => false}
45
+ }
46
+ allow(DeployGate::Builds::Ios::Export).to receive(:analyze_profile).and_return(plist)
47
+ expect(DeployGate::Builds::Ios::Export.inhouse?('path')).to be_truthy
48
+ end
49
+
50
+ it "when not distribution plist" do
51
+ plist = {
52
+ 'ProvisionsAllDevices' => nil,
53
+ 'Entitlements' => {'get-task-allow' => true}
54
+ }
55
+ allow(DeployGate::Builds::Ios::Export).to receive(:analyze_profile).and_return(plist)
56
+ expect(DeployGate::Builds::Ios::Export.inhouse?('path')).to be_falsey
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,3 @@
1
+ describe DeployGate::Builds::Ios::SetProfile do
2
+ # TODO: add test
3
+ end
@@ -0,0 +1,95 @@
1
+ describe DeployGate::Builds::Ios do
2
+ before do
3
+ class ProjectMock
4
+ def schemes
5
+ []
6
+ end
7
+ end
8
+ class AnalyzeMock
9
+ def build_workspace
10
+ ''
11
+ end
12
+ end
13
+ end
14
+
15
+ describe "#build" do
16
+ it "should call Gym Manager" do
17
+ call_gym_manager = false
18
+ allow(FastlaneCore::Configuration).to receive(:create) {}
19
+ allow_any_instance_of(Gym::Manager).to receive(:work) { call_gym_manager = true }
20
+ allow(File).to receive(:exist?).and_return(true)
21
+ allow(File).to receive(:expand_path).and_return('path')
22
+ allow(FastlaneCore::Project).to receive(:new).and_return(ProjectMock.new)
23
+
24
+ DeployGate::Builds::Ios.build(AnalyzeMock.new, '', '')
25
+ expect(call_gym_manager).to be_truthy
26
+ end
27
+
28
+ it "raise not support export" do
29
+ allow(FastlaneCore::Configuration).to receive(:create) {}
30
+ allow_any_instance_of(Gym::Manager).to receive(:work) {}
31
+ allow(File).to receive(:exist?).and_return(true)
32
+ allow(File).to receive(:expand_path).and_return('path')
33
+ allow(FastlaneCore::Project).to receive(:new).and_return(ProjectMock.new)
34
+
35
+ expect {
36
+ DeployGate::Builds::Ios.build(AnalyzeMock.new, '', '', 'not support export method')
37
+ }.to raise_error DeployGate::Builds::Ios::NotSupportExportMethodError
38
+ end
39
+ end
40
+
41
+ describe "#workspace?" do
42
+ it "pod workspace" do
43
+ allow(File).to receive(:extname).and_return('.xcworkspace')
44
+
45
+ result = DeployGate::Builds::Ios.workspace?('path')
46
+ expect(result).to be_truthy
47
+ end
48
+
49
+ it "xcode project" do
50
+ allow(File).to receive(:extname).and_return('.xcodeproj')
51
+
52
+ result = DeployGate::Builds::Ios.workspace?('path')
53
+ expect(result).to be_falsey
54
+ end
55
+ end
56
+
57
+ describe "#project?" do
58
+ it "pod workspace" do
59
+ allow(File).to receive(:extname).and_return('.xcworkspace')
60
+
61
+ result = DeployGate::Builds::Ios.project?('path')
62
+ expect(result).to be_falsey
63
+ end
64
+
65
+ it "xcode project" do
66
+ allow(File).to receive(:extname).and_return('.xcodeproj')
67
+
68
+ result = DeployGate::Builds::Ios.project?('path')
69
+ expect(result).to be_truthy
70
+ end
71
+ end
72
+
73
+ describe "#find_workspaces" do
74
+ # TODO: add test
75
+ end
76
+
77
+ describe "#project_root_path" do
78
+ let(:root_path) {'test'}
79
+ it "when test/test.xcodeproj/project.xcworkspace" do
80
+ expect(DeployGate::Builds::Ios.project_root_path('test/test.xcodeproj/project.xcworkspace')).to eq root_path
81
+ end
82
+
83
+ it "when test/test.xcodeproj" do
84
+ expect(DeployGate::Builds::Ios.project_root_path('test/test.xcodeproj')).to eq root_path
85
+ end
86
+
87
+ it "when test/test.xcworkspace" do
88
+ expect(DeployGate::Builds::Ios.project_root_path('test/test.xcworkspace')).to eq root_path
89
+ end
90
+
91
+ it "when test/" do
92
+ expect(DeployGate::Builds::Ios.project_root_path('test/')).to eq root_path + '/'
93
+ end
94
+ end
95
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: deploygate
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - deploygate
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-10-20 00:00:00.000000000 Z
11
+ date: 2015-10-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
@@ -66,6 +66,104 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: 2.0.1
69
+ - !ruby/object:Gem::Dependency
70
+ name: plist
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 3.1.0
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 3.1.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: xcodeproj
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 0.28.2
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 0.28.2
97
+ - !ruby/object:Gem::Dependency
98
+ name: github_issue_request
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 0.0.2
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 0.0.2
111
+ - !ruby/object:Gem::Dependency
112
+ name: highline
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 1.7.8
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 1.7.8
125
+ - !ruby/object:Gem::Dependency
126
+ name: gym
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: 1.0.0
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: 1.0.0
139
+ - !ruby/object:Gem::Dependency
140
+ name: spaceship
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: 0.12.3
146
+ type: :runtime
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: 0.12.3
153
+ - !ruby/object:Gem::Dependency
154
+ name: sigh
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: 1.1.0
160
+ type: :runtime
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - "~>"
165
+ - !ruby/object:Gem::Version
166
+ version: 1.1.0
69
167
  - !ruby/object:Gem::Dependency
70
168
  name: bundler
71
169
  requirement: !ruby/object:Gem::Requirement
@@ -143,8 +241,14 @@ files:
143
241
  - lib/deploygate/api/v1/base.rb
144
242
  - lib/deploygate/api/v1/push.rb
145
243
  - lib/deploygate/api/v1/session.rb
244
+ - lib/deploygate/build.rb
245
+ - lib/deploygate/builds/ios.rb
246
+ - lib/deploygate/builds/ios/analyze.rb
247
+ - lib/deploygate/builds/ios/export.rb
248
+ - lib/deploygate/builds/ios/set_profile.rb
146
249
  - lib/deploygate/command_builder.rb
147
250
  - lib/deploygate/commands/deploy.rb
251
+ - lib/deploygate/commands/deploy/build.rb
148
252
  - lib/deploygate/commands/deploy/push.rb
149
253
  - lib/deploygate/commands/init.rb
150
254
  - lib/deploygate/commands/logout.rb
@@ -156,6 +260,11 @@ files:
156
260
  - lib/deploygate/version.rb
157
261
  - spec/deploygate/api/v1/push_spec.rb
158
262
  - spec/deploygate/api/v1/session_spec.rb
263
+ - spec/deploygate/build_spec.rb
264
+ - spec/deploygate/builds/ios/analyze_spec.rb
265
+ - spec/deploygate/builds/ios/export_spec.rb
266
+ - spec/deploygate/builds/ios/set_profile_spec.rb
267
+ - spec/deploygate/builds/ios_spec.rb
159
268
  - spec/deploygate/config_spec.rb
160
269
  - spec/deploygate/deploy_spec.rb
161
270
  - spec/deploygate/session_spec.rb
@@ -169,7 +278,7 @@ post_install_message: |2+
169
278
 
170
279
  dg installed! To get started fast:
171
280
 
172
- $ dg deploy [app_file_path]
281
+ $ dg deploy
173
282
 
174
283
  Or see the docs at:
175
284
 
@@ -197,6 +306,11 @@ summary: A command-line interface for DeployGate
197
306
  test_files:
198
307
  - spec/deploygate/api/v1/push_spec.rb
199
308
  - spec/deploygate/api/v1/session_spec.rb
309
+ - spec/deploygate/build_spec.rb
310
+ - spec/deploygate/builds/ios/analyze_spec.rb
311
+ - spec/deploygate/builds/ios/export_spec.rb
312
+ - spec/deploygate/builds/ios/set_profile_spec.rb
313
+ - spec/deploygate/builds/ios_spec.rb
200
314
  - spec/deploygate/config_spec.rb
201
315
  - spec/deploygate/deploy_spec.rb
202
316
  - spec/deploygate/session_spec.rb