travis-async-listener 1.8.3
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/LICENSE +22 -0
- data/README.md +2486 -0
- data/Rakefile +63 -0
- data/assets/cacert.pem +69 -0
- data/assets/init/c.yml +4 -0
- data/assets/init/clojure.yml +1 -0
- data/assets/init/cpp.yml +4 -0
- data/assets/init/erlang.yml +3 -0
- data/assets/init/go.yml +4 -0
- data/assets/init/groovy.yml +1 -0
- data/assets/init/haskell.yml +1 -0
- data/assets/init/java.yml +4 -0
- data/assets/init/node_js.yml +4 -0
- data/assets/init/objective-c.yml +1 -0
- data/assets/init/perl.yml +4 -0
- data/assets/init/php.yml +4 -0
- data/assets/init/python.yml +5 -0
- data/assets/init/ruby.yml +6 -0
- data/assets/init/scala.yml +4 -0
- data/assets/notifications/Travis CI.app/Contents/Info.plist +52 -0
- data/assets/notifications/Travis CI.app/Contents/MacOS/Travis CI +0 -0
- data/assets/notifications/Travis CI.app/Contents/PkgInfo +1 -0
- data/assets/notifications/Travis CI.app/Contents/Resources/Travis CI.icns +0 -0
- data/assets/notifications/Travis CI.app/Contents/Resources/en.lproj/Credits.rtf +29 -0
- data/assets/notifications/Travis CI.app/Contents/Resources/en.lproj/InfoPlist.strings +0 -0
- data/assets/notifications/Travis CI.app/Contents/Resources/en.lproj/MainMenu.nib +0 -0
- data/assets/notifications/Travis CI.app/Contents/_CodeSignature/CodeResources +173 -0
- data/assets/notifications/Travis CI.app/Contents/embedded.provisionprofile +0 -0
- data/assets/notifications/icon.png +0 -0
- data/assets/travis.sh +163 -0
- data/assets/travis.sh.erb +64 -0
- data/bin/travis +18 -0
- data/examples/org_overview.rb +3 -0
- data/examples/pro_auth.rb +23 -0
- data/examples/stream.rb +6 -0
- data/lib/travis.rb +8 -0
- data/lib/travis/auto_login.rb +3 -0
- data/lib/travis/cli.rb +126 -0
- data/lib/travis/cli/accounts.rb +31 -0
- data/lib/travis/cli/api_command.rb +180 -0
- data/lib/travis/cli/branches.rb +25 -0
- data/lib/travis/cli/cache.rb +76 -0
- data/lib/travis/cli/cancel.rb +18 -0
- data/lib/travis/cli/command.rb +422 -0
- data/lib/travis/cli/console.rb +31 -0
- data/lib/travis/cli/disable.rb +15 -0
- data/lib/travis/cli/enable.rb +31 -0
- data/lib/travis/cli/encrypt.rb +108 -0
- data/lib/travis/cli/encrypt_file.rb +140 -0
- data/lib/travis/cli/endpoint.rb +35 -0
- data/lib/travis/cli/env.rb +66 -0
- data/lib/travis/cli/help.rb +23 -0
- data/lib/travis/cli/history.rb +49 -0
- data/lib/travis/cli/init.rb +82 -0
- data/lib/travis/cli/lint.rb +49 -0
- data/lib/travis/cli/login.rb +76 -0
- data/lib/travis/cli/logout.rb +14 -0
- data/lib/travis/cli/logs.rb +65 -0
- data/lib/travis/cli/monitor.rb +110 -0
- data/lib/travis/cli/open.rb +39 -0
- data/lib/travis/cli/parser.rb +43 -0
- data/lib/travis/cli/pubkey.rb +30 -0
- data/lib/travis/cli/raw.rb +20 -0
- data/lib/travis/cli/repo_command.rb +154 -0
- data/lib/travis/cli/report.rb +101 -0
- data/lib/travis/cli/repos.rb +53 -0
- data/lib/travis/cli/requests.rb +47 -0
- data/lib/travis/cli/restart.rb +18 -0
- data/lib/travis/cli/settings.rb +77 -0
- data/lib/travis/cli/setup.rb +66 -0
- data/lib/travis/cli/setup/anynines.rb +21 -0
- data/lib/travis/cli/setup/appfog.rb +19 -0
- data/lib/travis/cli/setup/artifacts.rb +23 -0
- data/lib/travis/cli/setup/biicode.rb +19 -0
- data/lib/travis/cli/setup/cloud_66.rb +20 -0
- data/lib/travis/cli/setup/cloud_control.rb +21 -0
- data/lib/travis/cli/setup/cloud_files.rb +20 -0
- data/lib/travis/cli/setup/cloud_foundry.rb +23 -0
- data/lib/travis/cli/setup/code_deploy.rb +55 -0
- data/lib/travis/cli/setup/deis.rb +20 -0
- data/lib/travis/cli/setup/divshot.rb +18 -0
- data/lib/travis/cli/setup/elastic_beanstalk.rb +23 -0
- data/lib/travis/cli/setup/engine_yard.rb +24 -0
- data/lib/travis/cli/setup/gcs.rb +22 -0
- data/lib/travis/cli/setup/hackage.rb +18 -0
- data/lib/travis/cli/setup/heroku.rb +20 -0
- data/lib/travis/cli/setup/modulus.rb +18 -0
- data/lib/travis/cli/setup/ninefold.rb +20 -0
- data/lib/travis/cli/setup/nodejitsu.rb +27 -0
- data/lib/travis/cli/setup/npm.rb +20 -0
- data/lib/travis/cli/setup/open_shift.rb +20 -0
- data/lib/travis/cli/setup/opsworks.rb +22 -0
- data/lib/travis/cli/setup/pypi.rb +22 -0
- data/lib/travis/cli/setup/releases.rb +35 -0
- data/lib/travis/cli/setup/ruby_gems.rb +25 -0
- data/lib/travis/cli/setup/s3.rb +25 -0
- data/lib/travis/cli/setup/sauce_connect.rb +21 -0
- data/lib/travis/cli/setup/service.rb +73 -0
- data/lib/travis/cli/show.rb +69 -0
- data/lib/travis/cli/sshkey.rb +118 -0
- data/lib/travis/cli/status.rb +19 -0
- data/lib/travis/cli/sync.rb +30 -0
- data/lib/travis/cli/token.rb +14 -0
- data/lib/travis/cli/version.rb +17 -0
- data/lib/travis/cli/whatsup.rb +30 -0
- data/lib/travis/cli/whoami.rb +15 -0
- data/lib/travis/client.rb +39 -0
- data/lib/travis/client/account.rb +56 -0
- data/lib/travis/client/annotation.rb +21 -0
- data/lib/travis/client/artifact.rb +88 -0
- data/lib/travis/client/auto_login.rb +45 -0
- data/lib/travis/client/broadcast.rb +14 -0
- data/lib/travis/client/build.rb +47 -0
- data/lib/travis/client/cache.rb +25 -0
- data/lib/travis/client/commit.rb +28 -0
- data/lib/travis/client/entity.rb +238 -0
- data/lib/travis/client/env_var.rb +102 -0
- data/lib/travis/client/error.rb +38 -0
- data/lib/travis/client/has_uuid.rb +13 -0
- data/lib/travis/client/job.rb +61 -0
- data/lib/travis/client/lint_result.rb +25 -0
- data/lib/travis/client/listener.rb +184 -0
- data/lib/travis/client/methods.rb +104 -0
- data/lib/travis/client/namespace.rb +85 -0
- data/lib/travis/client/not_loadable.rb +13 -0
- data/lib/travis/client/repository.rb +224 -0
- data/lib/travis/client/request.rb +36 -0
- data/lib/travis/client/restartable.rb +23 -0
- data/lib/travis/client/session.rb +339 -0
- data/lib/travis/client/settings.rb +25 -0
- data/lib/travis/client/singleton_setting.rb +36 -0
- data/lib/travis/client/ssh_key.rb +11 -0
- data/lib/travis/client/states.rb +98 -0
- data/lib/travis/client/user.rb +67 -0
- data/lib/travis/client/weak_entity.rb +26 -0
- data/lib/travis/pro.rb +5 -0
- data/lib/travis/pro/auto_login.rb +3 -0
- data/lib/travis/tools/assets.rb +21 -0
- data/lib/travis/tools/completion.rb +54 -0
- data/lib/travis/tools/formatter.rb +50 -0
- data/lib/travis/tools/github.rb +279 -0
- data/lib/travis/tools/notification.rb +69 -0
- data/lib/travis/tools/safe_string.rb +22 -0
- data/lib/travis/tools/ssl_key.rb +48 -0
- data/lib/travis/tools/system.rb +88 -0
- data/lib/travis/version.rb +3 -0
- data/spec/cli/cancel_spec.rb +15 -0
- data/spec/cli/encrypt_spec.rb +43 -0
- data/spec/cli/endpoint_spec.rb +34 -0
- data/spec/cli/help_spec.rb +33 -0
- data/spec/cli/history_spec.rb +38 -0
- data/spec/cli/init_spec.rb +226 -0
- data/spec/cli/login_spec.rb +13 -0
- data/spec/cli/logs_spec.rb +8 -0
- data/spec/cli/open_spec.rb +33 -0
- data/spec/cli/repo_command_spec.rb +25 -0
- data/spec/cli/restart_spec.rb +15 -0
- data/spec/cli/setup_spec.rb +5 -0
- data/spec/cli/show_spec.rb +9 -0
- data/spec/cli/status_spec.rb +28 -0
- data/spec/cli/token_spec.rb +22 -0
- data/spec/cli/version_spec.rb +18 -0
- data/spec/cli/whoami_spec.rb +34 -0
- data/spec/client/account_spec.rb +32 -0
- data/spec/client/annotation_spec.rb +14 -0
- data/spec/client/broadcast_spec.rb +10 -0
- data/spec/client/build_spec.rb +31 -0
- data/spec/client/commit_spec.rb +22 -0
- data/spec/client/job_spec.rb +30 -0
- data/spec/client/methods_spec.rb +15 -0
- data/spec/client/namespace_spec.rb +19 -0
- data/spec/client/repository_spec.rb +39 -0
- data/spec/client/session_spec.rb +165 -0
- data/spec/client/user_spec.rb +16 -0
- data/spec/client_spec.rb +17 -0
- data/spec/pro_spec.rb +10 -0
- data/spec/spec_helper.rb +29 -0
- data/spec/support/fake_api.rb +738 -0
- data/spec/support/fake_github.rb +24 -0
- data/spec/support/helpers.rb +45 -0
- data/spec/travis_spec.rb +10 -0
- data/travis.gemspec +323 -0
- metadata +489 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
require 'travis/cli'
|
|
2
|
+
|
|
3
|
+
module Travis
|
|
4
|
+
module CLI
|
|
5
|
+
class ApiCommand < Command
|
|
6
|
+
include Travis::Client::Methods
|
|
7
|
+
attr_accessor :enterprise_name
|
|
8
|
+
attr_reader :session
|
|
9
|
+
abstract
|
|
10
|
+
|
|
11
|
+
on('-e', '--api-endpoint URL', 'Travis API server to talk to')
|
|
12
|
+
on('-I', '--[no-]insecure', 'do not verify SSL certificate of API endpoint')
|
|
13
|
+
on('--pro', "short-cut for --api-endpoint '#{Travis::Client::PRO_URI}'") { |c,_| c.api_endpoint = Travis::Client::PRO_URI }
|
|
14
|
+
on('--org', "short-cut for --api-endpoint '#{Travis::Client::ORG_URI}'") { |c,_| c.api_endpoint = Travis::Client::ORG_URI }
|
|
15
|
+
on('--staging', 'talks to staging system') { |c,_| c.api_endpoint = c.api_endpoint.gsub(/api/, 'api-staging') }
|
|
16
|
+
on('-t', '--token [ACCESS_TOKEN]', 'access token to use') { |c, t| c.access_token = t }
|
|
17
|
+
|
|
18
|
+
on('--debug', 'show API requests') do |c,_|
|
|
19
|
+
c.debug = true
|
|
20
|
+
c.session.instrument do |info, request|
|
|
21
|
+
c.time(info, request)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
on('--debug-http', 'show HTTP(S) exchange') do |c,_|
|
|
26
|
+
c.session.debug_http = true
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
on('-X', '--enterprise [NAME]', 'use enterprise setup (optionally takes name for multiple setups)') do |c, name|
|
|
30
|
+
c.enterprise_name = name || 'default'
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
on('--adapter ADAPTER', 'Faraday adapter to use for HTTP requests') do |c, adapter|
|
|
34
|
+
adapter.gsub! '-', '_'
|
|
35
|
+
require "faraday/adapter/#{adapter}"
|
|
36
|
+
require 'typhoeus/adapters/faraday' if adapter == 'typhoeus'
|
|
37
|
+
c.session.faraday_adapter = adapter.to_sym
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def initialize(*)
|
|
41
|
+
@session = Travis::Client.new(:agent_info => "command #{command_name}")
|
|
42
|
+
super
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def endpoint_config
|
|
46
|
+
config['endpoints'] ||= {}
|
|
47
|
+
config['endpoints'][api_endpoint] ||= {}
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def setup
|
|
51
|
+
setup_enterprise
|
|
52
|
+
self.api_endpoint = default_endpoint if default_endpoint and not explicit_api_endpoint?
|
|
53
|
+
self.access_token ||= fetch_token
|
|
54
|
+
endpoint_config['access_token'] ||= access_token
|
|
55
|
+
endpoint_config['insecure'] = insecure unless insecure.nil?
|
|
56
|
+
self.insecure = endpoint_config['insecure']
|
|
57
|
+
session.ssl = { :verify => false } if insecure?
|
|
58
|
+
authenticate if pro? or enterprise?
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def enterprise?
|
|
62
|
+
!!endpoint_config['enterprise']
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def pro?
|
|
66
|
+
api_endpoint == Travis::Client::PRO_URI
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def org?
|
|
70
|
+
api_endpoint == Travis::Client::ORG_URI
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def detected_endpoint?
|
|
74
|
+
api_endpoint == detected_endpoint
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def authenticate
|
|
78
|
+
error "not logged in, please run #{command("login#{endpoint_option}")}" if access_token.nil?
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def sync(block = true, dot = '.')
|
|
82
|
+
user.sync
|
|
83
|
+
|
|
84
|
+
steps = count = 1
|
|
85
|
+
while block and user.reload.syncing?
|
|
86
|
+
count += 1
|
|
87
|
+
sleep(1)
|
|
88
|
+
|
|
89
|
+
if count % steps == 0
|
|
90
|
+
steps = count/10 + 1
|
|
91
|
+
output.print dot
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
private
|
|
97
|
+
|
|
98
|
+
def setup_enterprise
|
|
99
|
+
return unless setup_enterprise?
|
|
100
|
+
c = config['enterprise'] ||= {}
|
|
101
|
+
c[enterprise_name] = api_endpoint if explicit_api_endpoint?
|
|
102
|
+
c[enterprise_name] ||= write_to($stderr) do
|
|
103
|
+
error "enterprise setup not configured" unless interactive?
|
|
104
|
+
user_input = ask(color("Enterprise domain: ", :bold)).to_s
|
|
105
|
+
domain = user_input[%r{^(?:https?://)?(.*?)/?(?:/api/?)?$}, 1]
|
|
106
|
+
endpoint = "https://#{domain}/api"
|
|
107
|
+
config['default_endpoint'] = endpoint if agree("Use #{color domain, :bold} as default endpoint? ") { |q| q.default = 'yes' }
|
|
108
|
+
endpoint
|
|
109
|
+
end
|
|
110
|
+
self.api_endpoint = c[enterprise_name]
|
|
111
|
+
self.insecure = true if insecure.nil?
|
|
112
|
+
endpoint_config['enterprise'] = true
|
|
113
|
+
@setup_ennterpise = true
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def setup_enterprise?
|
|
117
|
+
@setup_ennterpise ||= false
|
|
118
|
+
!!enterprise_name and not @setup_ennterpise
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def load_gh
|
|
122
|
+
return if defined? GH
|
|
123
|
+
debug "Loading gh"
|
|
124
|
+
require 'gh'
|
|
125
|
+
|
|
126
|
+
gh_config = session.config['github']
|
|
127
|
+
gh_config &&= gh_config.inject({}) { |h,(k,v)| h.update(k.to_sym => v) }
|
|
128
|
+
gh_config ||= {}
|
|
129
|
+
gh_config[:ssl] = Travis::Client::Session::SSL_OPTIONS
|
|
130
|
+
gh_config[:ssl] = { :verify => false } if gh_config[:api_url] and gh_config[:api_url] != "https://api.github.com"
|
|
131
|
+
gh_config.delete :scopes
|
|
132
|
+
|
|
133
|
+
gh_config[:instrumenter] = proc do |type, payload, &block|
|
|
134
|
+
next block.call unless type == 'http.gh'
|
|
135
|
+
time("GitHub API: #{payload[:verb].to_s.upcase} #{payload[:url]}", block)
|
|
136
|
+
end if debug?
|
|
137
|
+
|
|
138
|
+
GH.set(gh_config)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def github_endpoint
|
|
142
|
+
load_gh
|
|
143
|
+
GH.with({}).api_host
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def listen(*args)
|
|
147
|
+
super(*args) do |listener|
|
|
148
|
+
on_signal { listener.disconnect }
|
|
149
|
+
yield listener
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def default_endpoint
|
|
154
|
+
ENV['TRAVIS_ENDPOINT'] || config['default_endpoint']
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def detected_endpoint
|
|
158
|
+
default_endpoint || Travis::Client::ORG_URI
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def endpoint_option
|
|
162
|
+
return "" if org? and detected_endpoint?
|
|
163
|
+
return " --org" if org?
|
|
164
|
+
return " --pro" if pro?
|
|
165
|
+
|
|
166
|
+
if config['enterprise']
|
|
167
|
+
key, _ = config['enterprise'].detect { |k,v| v.start_with? api_endpoint }
|
|
168
|
+
return " -X" if key == "default"
|
|
169
|
+
return " -X #{key}" if key
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
" -e %p" % api_endpoint
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def fetch_token
|
|
176
|
+
ENV['TRAVIS_TOKEN'] || endpoint_config['access_token']
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
require 'travis/cli'
|
|
2
|
+
|
|
3
|
+
module Travis
|
|
4
|
+
module CLI
|
|
5
|
+
class Branches < RepoCommand
|
|
6
|
+
description "displays the most recent build for each branch"
|
|
7
|
+
|
|
8
|
+
def run
|
|
9
|
+
repository.last_on_branch.each do |build|
|
|
10
|
+
say [
|
|
11
|
+
color("#{build.branch_info}:".ljust(longest + 2), [:info, :bold]),
|
|
12
|
+
color("##{build.number.to_s.ljust(4)} #{build.state}".ljust(16), build.color),
|
|
13
|
+
build.commit.subject
|
|
14
|
+
].join(" ").strip + "\n"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def longest
|
|
21
|
+
repository.branches.keys.map { |b| b.size }.max
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
require 'travis/cli'
|
|
2
|
+
|
|
3
|
+
module Travis
|
|
4
|
+
module CLI
|
|
5
|
+
class Cache < RepoCommand
|
|
6
|
+
description 'lists or deletes repository caches'
|
|
7
|
+
on '-d', '--delete', 'delete listed caches'
|
|
8
|
+
on '-b', '--branch BRANCH', 'only list/delete caches on given branch'
|
|
9
|
+
on '-m', '--match STRING', 'only list/delete caches where slug matches given string'
|
|
10
|
+
on '-f', '--force', 'do not ask user to confirm deleting the caches'
|
|
11
|
+
|
|
12
|
+
def run
|
|
13
|
+
error "not allowed to access caches for #{color(repository.slug, :bold)}" unless repository.push?
|
|
14
|
+
branches = caches.group_by(&:branch)
|
|
15
|
+
check_caches
|
|
16
|
+
|
|
17
|
+
warn "Deleted the following caches:\n" if delete?
|
|
18
|
+
branches.each { |name, list| display_branch(name, list) }
|
|
19
|
+
size = caches.inject(0) { |s,c| s + c.size }
|
|
20
|
+
say "Overall size of above caches: " << formatter.file_size(size)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def check_caches
|
|
26
|
+
return if caches.any?
|
|
27
|
+
say "no caches found"
|
|
28
|
+
exit
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def display_branch(name, list)
|
|
32
|
+
say color(name ? "On branch #{name}:" : "Global:", :important)
|
|
33
|
+
list.each { |c| display_cache(c) }
|
|
34
|
+
puts
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def display_cache(cache)
|
|
38
|
+
say [
|
|
39
|
+
color(cache.slug.ljust(space), :bold),
|
|
40
|
+
"last modified: " << formatter.time(cache.last_modified),
|
|
41
|
+
"size: " << formatter.file_size(cache.size)
|
|
42
|
+
].join(" ") << "\n"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def params
|
|
46
|
+
params = {}
|
|
47
|
+
params[:branch] = branch if branch?
|
|
48
|
+
params[:match] = match if match?
|
|
49
|
+
params
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def caches
|
|
53
|
+
@caches ||= drop? ? repository.delete_caches(params) : repository.caches(params)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def space
|
|
57
|
+
@space ||= caches.map(&:slug).map(&:size).max
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def drop?
|
|
61
|
+
return false unless delete?
|
|
62
|
+
return true if force?
|
|
63
|
+
error "not deleting caches without --force" unless interactive?
|
|
64
|
+
error "aborted" unless danger_zone? "Do you really want to delete #{description}?"
|
|
65
|
+
true
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def description
|
|
69
|
+
description = color("all caches", :important)
|
|
70
|
+
description << " on branch #{color(branch, :important)}" if branch?
|
|
71
|
+
description << " that match #{color(match, :important)}" if match?
|
|
72
|
+
description
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require 'travis/cli'
|
|
2
|
+
|
|
3
|
+
module Travis
|
|
4
|
+
module CLI
|
|
5
|
+
class Cancel < RepoCommand
|
|
6
|
+
description "cancels a job or build"
|
|
7
|
+
|
|
8
|
+
def run(number = last_build.number)
|
|
9
|
+
authenticate
|
|
10
|
+
entity = job(number) || build(number)
|
|
11
|
+
error "could not find job or build #{repository.slug}##{number}" unless entity
|
|
12
|
+
entity.cancel
|
|
13
|
+
|
|
14
|
+
say "canceled", "#{entity.class.one} ##{entity.number} has been %s"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
require 'travis/cli'
|
|
2
|
+
require 'travis/tools/system'
|
|
3
|
+
require 'travis/tools/formatter'
|
|
4
|
+
require 'travis/tools/assets'
|
|
5
|
+
require 'travis/tools/completion'
|
|
6
|
+
require 'travis/version'
|
|
7
|
+
|
|
8
|
+
require 'highline'
|
|
9
|
+
require 'forwardable'
|
|
10
|
+
require 'yaml'
|
|
11
|
+
require 'timeout'
|
|
12
|
+
|
|
13
|
+
module Travis
|
|
14
|
+
module CLI
|
|
15
|
+
class Command
|
|
16
|
+
MINUTE = 60
|
|
17
|
+
HOUR = 3600
|
|
18
|
+
DAY = 86400
|
|
19
|
+
WEEK = 604800
|
|
20
|
+
|
|
21
|
+
include Tools::Assets
|
|
22
|
+
extend Parser, Forwardable, Tools::Assets
|
|
23
|
+
def_delegators :terminal, :agree, :ask, :choose
|
|
24
|
+
|
|
25
|
+
HighLine.use_color = Tools::System.unix? && $stdout.tty?
|
|
26
|
+
HighLine.color_scheme = HighLine::ColorScheme.new do |cs|
|
|
27
|
+
cs[:command] = [ :bold ]
|
|
28
|
+
cs[:error] = [ :red ]
|
|
29
|
+
cs[:important] = [ :bold, :underline ]
|
|
30
|
+
cs[:success] = [ :green ]
|
|
31
|
+
cs[:info] = [ :yellow ]
|
|
32
|
+
cs[:debug] = [ :magenta ]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
on('-h', '--help', 'Display help') do |c, _|
|
|
36
|
+
c.say c.help
|
|
37
|
+
exit
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
on('-i', '--[no-]interactive', "be interactive and colorful") do |c, v|
|
|
41
|
+
HighLine.use_color = v if Tools::System.unix?
|
|
42
|
+
c.force_interactive = v
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
on('-E', '--[no-]explode', "don't rescue exceptions")
|
|
46
|
+
on('--skip-version-check', "don't check if travis client is up to date")
|
|
47
|
+
on('--skip-completion-check', "don't check if auto-completion is set up")
|
|
48
|
+
|
|
49
|
+
def self.command_name
|
|
50
|
+
name[/[^:]*$/].split(/(?=[A-Z])/).map(&:downcase).join('-')
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
@@abstract ||= [Command] # ignore the man behind the courtains!
|
|
54
|
+
def self.abstract?
|
|
55
|
+
@@abstract.include? self
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def self.abstract
|
|
59
|
+
@@abstract << self
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def self.skip(*names)
|
|
63
|
+
names.each { |n| define_method(n) {} }
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def self.description(description = nil)
|
|
67
|
+
@description = description if description
|
|
68
|
+
@description ||= ""
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def self.subcommands(*list)
|
|
72
|
+
return @subcommands ||= [] if list.empty?
|
|
73
|
+
@subcommands = list
|
|
74
|
+
|
|
75
|
+
define_method :run do |subcommand, *args|
|
|
76
|
+
error "Unknown subcommand. Available: #{list.join(', ')}." unless list.include? subcommand.to_sym
|
|
77
|
+
send(subcommand, *args)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
define_method :usage do
|
|
81
|
+
usages = list.map { |c| color(usage_for("#{command_name} #{c}", c), :command) }
|
|
82
|
+
"\nUsage: #{usages.join("\n ")}\n\n"
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
attr_accessor :arguments, :config, :force_interactive, :formatter, :debug
|
|
87
|
+
attr_reader :input, :output
|
|
88
|
+
alias_method :debug?, :debug
|
|
89
|
+
|
|
90
|
+
def initialize(options = {})
|
|
91
|
+
@on_signal = []
|
|
92
|
+
@formatter = Travis::Tools::Formatter.new
|
|
93
|
+
self.output = $stdout
|
|
94
|
+
self.input = $stdin
|
|
95
|
+
options.each do |key, value|
|
|
96
|
+
public_send("#{key}=", value) if respond_to? "#{key}="
|
|
97
|
+
end
|
|
98
|
+
@arguments ||= []
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def terminal
|
|
102
|
+
@terminal ||= HighLine.new(input, output)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def input=(io)
|
|
106
|
+
@terminal = nil
|
|
107
|
+
@input = io
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def output=(io)
|
|
111
|
+
@terminal = nil
|
|
112
|
+
@output = io
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def write_to(io)
|
|
116
|
+
io_was, self.output = output, io
|
|
117
|
+
yield
|
|
118
|
+
ensure
|
|
119
|
+
self.output = io_was if io_was
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def parse(args)
|
|
123
|
+
rest = parser.parse(args)
|
|
124
|
+
arguments.concat(rest)
|
|
125
|
+
rescue OptionParser::ParseError => e
|
|
126
|
+
error e.message
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def setup
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def last_check
|
|
133
|
+
config['last_check'] ||= {
|
|
134
|
+
# migrate from old values
|
|
135
|
+
'at' => config.delete('last_version_check'),
|
|
136
|
+
'etag' => config.delete('etag')
|
|
137
|
+
}
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def check_version
|
|
141
|
+
last_check.clear if last_check['version'] != Travis::VERSION
|
|
142
|
+
seconds_since = Time.now.to_i - last_check['at'].to_i
|
|
143
|
+
|
|
144
|
+
return if skip_version_check?
|
|
145
|
+
return if seconds_since < MINUTE
|
|
146
|
+
|
|
147
|
+
case seconds_since
|
|
148
|
+
when MINUTE .. HOUR then timeout = 0.5
|
|
149
|
+
when HOUR .. DAY then timeout = 1.0
|
|
150
|
+
when DAY .. WEEK then timeout = 2.0
|
|
151
|
+
else timeout = 10.0
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
Timeout.timeout(timeout) do
|
|
155
|
+
response = Faraday.get('https://rubygems.org/api/v1/gems/travis.json', {}, 'If-None-Match' => last_check['etag'].to_s)
|
|
156
|
+
last_check['etag'] = response.headers['etag']
|
|
157
|
+
last_check['version'] = JSON.parse(response.body)['version'] if response.status == 200
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
last_check['at'] = Time.now.to_i
|
|
161
|
+
unless Tools::System.recent_version? Travis::VERSION, last_check['version']
|
|
162
|
+
warn "Outdated CLI version, run `gem install travis`."
|
|
163
|
+
end
|
|
164
|
+
rescue Timeout::Error, Faraday::Error::ClientError => error
|
|
165
|
+
debug "#{error.class}: #{error.message}"
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def check_completion
|
|
169
|
+
return if skip_completion_check? or !interactive?
|
|
170
|
+
|
|
171
|
+
if config['checked_completion']
|
|
172
|
+
Tools::Completion.update_completion if config['completion_version'] != Travis::VERSION
|
|
173
|
+
else
|
|
174
|
+
write_to($stderr) do
|
|
175
|
+
next Tools::Completion.update_completion if Tools::Completion.completion_installed?
|
|
176
|
+
next unless agree('Shell completion not installed. Would you like to install it now? ') { |q| q.default = "y" }
|
|
177
|
+
Tools::Completion.install_completion
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
config['checked_completion'] = true
|
|
182
|
+
config['completion_version'] = Travis::VERSION
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def check_ruby
|
|
186
|
+
return if RUBY_VERSION > '1.9.2' or skip_version_check?
|
|
187
|
+
warn "Your Ruby version is outdated, please consider upgrading, as we will drop support for #{RUBY_VERSION} soon!"
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def execute
|
|
191
|
+
setup_trap
|
|
192
|
+
check_ruby
|
|
193
|
+
check_arity(method(:run), *arguments)
|
|
194
|
+
load_config
|
|
195
|
+
check_version
|
|
196
|
+
check_completion
|
|
197
|
+
setup
|
|
198
|
+
run(*arguments)
|
|
199
|
+
clear_error
|
|
200
|
+
store_config
|
|
201
|
+
rescue Travis::Client::NotLoggedIn => e
|
|
202
|
+
raise(e) if explode?
|
|
203
|
+
error "#{e.message} - try running #{command("login#{endpoint_option}")}"
|
|
204
|
+
rescue Travis::Client::NotFound => e
|
|
205
|
+
raise(e) if explode?
|
|
206
|
+
error "resource not found (#{e.message})"
|
|
207
|
+
rescue Travis::Client::Error => e
|
|
208
|
+
raise(e) if explode?
|
|
209
|
+
error e.message
|
|
210
|
+
rescue StandardError => e
|
|
211
|
+
raise(e) if explode?
|
|
212
|
+
message = e.message
|
|
213
|
+
message += color("\nfor a full error report, run #{command("report#{endpoint_option}")}", :error) if interactive?
|
|
214
|
+
store_error(e)
|
|
215
|
+
error(message)
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def command_name
|
|
219
|
+
self.class.command_name
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def usage
|
|
223
|
+
"Usage: " << color(usage_for(command_name, :run), :command)
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def usage_for(prefix, method)
|
|
227
|
+
usage = "travis #{prefix}"
|
|
228
|
+
method = method(method)
|
|
229
|
+
if method.respond_to? :parameters
|
|
230
|
+
method.parameters.each do |type, name|
|
|
231
|
+
name = name.upcase
|
|
232
|
+
name = "[#{name}]" if type == :opt
|
|
233
|
+
name = "[#{name}..]" if type == :rest
|
|
234
|
+
usage << " #{name}"
|
|
235
|
+
end
|
|
236
|
+
elsif method.arity != 0
|
|
237
|
+
usage << " ..."
|
|
238
|
+
end
|
|
239
|
+
usage << " [OPTIONS]"
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def help(info = "")
|
|
243
|
+
parser.banner = usage
|
|
244
|
+
self.class.description.sub(/./) { |c| c.upcase } + ".\n" + info + parser.to_s
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
def say(data, format = nil, style = nil)
|
|
248
|
+
terminal.say format(data, format, style)
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def debug(line)
|
|
252
|
+
return unless debug?
|
|
253
|
+
write_to($stderr) do
|
|
254
|
+
say color("** #{line}", :debug)
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def time(info, callback = Proc.new)
|
|
259
|
+
return callback.call unless debug?
|
|
260
|
+
start = Time.now
|
|
261
|
+
debug(info)
|
|
262
|
+
callback.call
|
|
263
|
+
duration = Time.now - start
|
|
264
|
+
debug(" took %.2g seconds" % duration)
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def info(line)
|
|
268
|
+
write_to($stderr) do
|
|
269
|
+
say color(line, :info)
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def on_signal(&block)
|
|
274
|
+
@on_signal << block
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
private
|
|
278
|
+
|
|
279
|
+
def store_error(exception)
|
|
280
|
+
message = "An error occurred running `travis %s%s`:\n %p: %s\n" % [command_name, endpoint_option, exception.class, exception.message]
|
|
281
|
+
exception.backtrace.each { |l| message << " from #{l}\n" }
|
|
282
|
+
save_file("error.log", message)
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
def clear_error
|
|
286
|
+
delete_file("error.log")
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def setup_trap
|
|
290
|
+
[:INT, :TERM].each do |signal|
|
|
291
|
+
trap signal do
|
|
292
|
+
@on_signal.each { |c| c.call }
|
|
293
|
+
exit 1
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def format(data, format = nil, style = nil)
|
|
299
|
+
style ||= :important
|
|
300
|
+
data = format % color(data, style) if format and interactive?
|
|
301
|
+
data = data.gsub(/<\[\[/, '<%=').gsub(/\]\]>/, '%>')
|
|
302
|
+
data.encode! 'utf-8' if data.respond_to? :encode!
|
|
303
|
+
data
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
def template(*args)
|
|
307
|
+
File.read(*args).split('__END__', 2)[1].strip
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
def color(line, style)
|
|
311
|
+
return line.to_s unless interactive?
|
|
312
|
+
terminal.color(line || '???', Array(style).map(&:to_sym))
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
def interactive?(io = output)
|
|
316
|
+
return io.tty? if force_interactive.nil?
|
|
317
|
+
force_interactive
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
def empty_line
|
|
321
|
+
say "\n"
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
def warn(message)
|
|
325
|
+
write_to($stderr) do
|
|
326
|
+
say color(message, :error)
|
|
327
|
+
yield if block_given?
|
|
328
|
+
end
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
def error(message, &block)
|
|
332
|
+
warn(message, &block)
|
|
333
|
+
exit 1
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
def command(name)
|
|
337
|
+
color("#{File.basename($0)} #{name}", :command)
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
def success(line)
|
|
341
|
+
say color(line, :success) if interactive?
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
def config_path(name)
|
|
345
|
+
path = ENV.fetch('TRAVIS_CONFIG_PATH') { File.expand_path('.travis', Dir.home) }
|
|
346
|
+
Dir.mkdir(path, 0700) unless File.directory? path
|
|
347
|
+
File.join(path, name)
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
def load_file(name, default = nil)
|
|
351
|
+
return default unless path = config_path(name) and File.exist? path
|
|
352
|
+
debug "Loading %p" % path
|
|
353
|
+
File.read(path)
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
def delete_file(name)
|
|
357
|
+
return unless path = config_path(name) and File.exist? path
|
|
358
|
+
debug "Deleting %p" % path
|
|
359
|
+
File.delete(path)
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
def save_file(name, content, read_only = false)
|
|
363
|
+
path = config_path(name)
|
|
364
|
+
debug "Storing %p" % path
|
|
365
|
+
File.open(path, 'w') do |file|
|
|
366
|
+
file.write(content.to_s)
|
|
367
|
+
file.chmod(0600) if read_only
|
|
368
|
+
end
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
YAML_ERROR = defined?(Psych::SyntaxError) ? Psych::SyntaxError : ArgumentError
|
|
372
|
+
def load_config
|
|
373
|
+
@config = YAML.load load_file('config.yml', '{}')
|
|
374
|
+
@config ||= {}
|
|
375
|
+
@original_config = @config.dup
|
|
376
|
+
rescue YAML_ERROR => error
|
|
377
|
+
raise error if explode?
|
|
378
|
+
warn "Broken config file: #{color config_path('config.yml'), :bold}"
|
|
379
|
+
exit 1 unless interactive? and agree("Remove config file? ") { |q| q.default = "no" }
|
|
380
|
+
@original_config, @config = {}, {}
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
def store_config
|
|
384
|
+
save_file('config.yml', @config.to_yaml, true)
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
def check_arity(method, *args)
|
|
388
|
+
return unless method.respond_to? :parameters
|
|
389
|
+
method.parameters.each do |type, name|
|
|
390
|
+
return if type == :rest
|
|
391
|
+
wrong_args("few") unless args.shift or type == :opt or type == :block
|
|
392
|
+
end
|
|
393
|
+
wrong_args("many") if args.any?
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
def danger_zone?(message)
|
|
397
|
+
agree(color("DANGER ZONE: ", [:red, :bold]) << message << " ") { |q| q.default = "no" }
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
def write_file(file, content, force = false)
|
|
401
|
+
error "#{file} already exists" unless write_file?(file, force)
|
|
402
|
+
File.write(file, content)
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
def write_file?(file, force)
|
|
406
|
+
return true if force or not File.exist?(file)
|
|
407
|
+
return false unless interactive?
|
|
408
|
+
danger_zone? "Override existing #{color(file, :info)}?"
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
def wrong_args(quantity)
|
|
412
|
+
error "too #{quantity} arguments" do
|
|
413
|
+
say help
|
|
414
|
+
end
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
def endpoint_option
|
|
418
|
+
""
|
|
419
|
+
end
|
|
420
|
+
end
|
|
421
|
+
end
|
|
422
|
+
end
|