deploygate 0.0.1 → 0.0.2

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 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