travis 1.5.3 → 1.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +27 -2
- data/Rakefile +4 -69
- data/completion/travis.sh +126 -1375
- data/completion/travis.sh.erb +60 -0
- data/lib/travis/cli.rb +0 -3
- data/lib/travis/cli/accounts.rb +4 -2
- data/lib/travis/cli/api_command.rb +19 -1
- data/lib/travis/cli/command.rb +4 -4
- data/lib/travis/cli/endpoint.rb +13 -1
- data/lib/travis/cli/init.rb +26 -2
- data/lib/travis/cli/login.rb +24 -12
- data/lib/travis/cli/monitor.rb +25 -2
- data/lib/travis/cli/repo_command.rb +4 -4
- data/lib/travis/cli/setup.rb +27 -122
- data/lib/travis/cli/setup/cloud_control.rb +21 -0
- data/lib/travis/cli/setup/cloud_foundry.rb +23 -0
- data/lib/travis/cli/setup/engine_yard.rb +24 -0
- data/lib/travis/cli/setup/heroku.rb +20 -0
- data/lib/travis/cli/setup/nodejitsu.rb +27 -0
- data/lib/travis/cli/setup/open_shift.rb +20 -0
- data/lib/travis/cli/setup/ruby_gems.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/client/repository.rb +1 -1
- data/lib/travis/client/session.rb +27 -5
- data/lib/travis/tools/notification.rb +69 -0
- data/lib/travis/tools/system.rb +38 -0
- data/lib/travis/version.rb +1 -1
- data/spec/cli/endpoint_spec.rb +5 -0
- data/spec/cli/init_spec.rb +21 -19
- data/spec/client/repository_spec.rb +1 -0
- data/spec/client/session_spec.rb +16 -0
- data/spec/support/fake_api.rb +2 -1
- data/travis.gemspec +28 -14
- metadata +46 -19
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'travis/cli/setup'
|
2
|
+
|
3
|
+
module Travis
|
4
|
+
module CLI
|
5
|
+
class Setup
|
6
|
+
class CloudControl < Service
|
7
|
+
description "automatic deployment to cloudControl"
|
8
|
+
|
9
|
+
def run
|
10
|
+
deploy 'cloudcontrol' do |config|
|
11
|
+
config['email'] = ask("cloudControl email: ").to_s
|
12
|
+
config['password'] = ask("cloudControl password: ") { |q| q.echo = "*" }.to_s
|
13
|
+
app = ask("cloudControl application: ") { |q| q.default = repository.name }.to_s
|
14
|
+
dep = ask("cloudControl deployment: ") { |q| q.default = "default" }.to_s
|
15
|
+
config['deployment'] = "#{app}/#{dep}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'travis/cli/setup'
|
2
|
+
|
3
|
+
module Travis
|
4
|
+
module CLI
|
5
|
+
class Setup
|
6
|
+
class CloudFoundry < Service
|
7
|
+
description "automatic deployment to Cloud Foundry"
|
8
|
+
|
9
|
+
def run
|
10
|
+
deploy 'provider' => 'cloudfoundry' do |config|
|
11
|
+
target_file = File.expand_path('.cf/target', Dir.home)
|
12
|
+
config['target'] ||= File.read(target_file).chomp if File.exist? target_file
|
13
|
+
config['target'] ||= ask("Cloud Foundry target: ").to_s
|
14
|
+
config['username'] ||= ask("Cloud Foundry user name: ").to_s
|
15
|
+
config['password'] ||= ask("Cloud Foundry password: ") { |q| q.echo = "*" }.to_s
|
16
|
+
config['organization'] ||= ask("Cloud Foundry organization: ").to_s
|
17
|
+
config['space'] ||= ask("Cloud Foundry space: ").to_s
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'travis/cli/setup'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
module Travis
|
5
|
+
module CLI
|
6
|
+
class Setup
|
7
|
+
class EngineYard < Service
|
8
|
+
description "automatic deployment to Engine Yard"
|
9
|
+
|
10
|
+
def run
|
11
|
+
deploy 'provider' => 'engineyard' do |config|
|
12
|
+
eyrc = File.expand_path(".eyrc", Dir.home)
|
13
|
+
config['api_key'] = YAML.load_file(eyrc)["api_token"] if File.exists?(eyrc)
|
14
|
+
config['api_key'] = ask("API token: ") { |q| q.echo = "*" }.to_s unless config['api_key']
|
15
|
+
env = ask("Environment (optional): ").to_s
|
16
|
+
config['environment'] = env unless env.empty?
|
17
|
+
migrate = agree("Run migrations on deploy? ") { |q| q.default = 'yes' }
|
18
|
+
config['migrate'] = ask("Migration command: ") { |q| q.default = "rake db:migrate" } if migrate
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'travis/cli/setup'
|
2
|
+
|
3
|
+
module Travis
|
4
|
+
module CLI
|
5
|
+
class Setup
|
6
|
+
class Heroku < Service
|
7
|
+
description "automatic deployment to Heroku"
|
8
|
+
|
9
|
+
def run
|
10
|
+
deploy 'heroku' do |config|
|
11
|
+
config['api_key'] = `heroku auth:token 2>/dev/null`.strip
|
12
|
+
config['api_key'] = ask("Heroku API token: ") { |q| q.echo = "*" }.to_s if config['api_key'].empty?
|
13
|
+
config['app'] = `heroku apps:info 2>/dev/null`.scan(/^=== (.+)$/).flatten.first
|
14
|
+
config['app'] = ask("Heroku application name: ") { |q| q.default = repository.name }.to_s if config['app'].nil?
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'travis/cli/setup'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Travis
|
5
|
+
module CLI
|
6
|
+
class Setup
|
7
|
+
class Nodejitsu < Service
|
8
|
+
description "automatic deployment to Nodejitsu"
|
9
|
+
|
10
|
+
def run
|
11
|
+
deploy 'nodejitsu' do |config|
|
12
|
+
jitsu_file = File.expand_path('.jitsuconf', ENV['HOME'])
|
13
|
+
|
14
|
+
if File.exist? jitsu_file
|
15
|
+
jitsu_conf = JSON.parse(File.read(jitsu_file))
|
16
|
+
config['user'] = jitsu_conf['username']
|
17
|
+
config['api_key'] = jitsu_conf['apiToken']
|
18
|
+
end
|
19
|
+
|
20
|
+
config['user'] ||= ask("Nodejitsu user: ").to_s
|
21
|
+
config['api_key'] ||= ask("Nodejitsu API token: ") { |q| q.echo = "*" }.to_s
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'travis/cli/setup'
|
2
|
+
|
3
|
+
module Travis
|
4
|
+
module CLI
|
5
|
+
class Setup
|
6
|
+
class OpenShift < Service
|
7
|
+
description "automatic deployment to OpenShfit"
|
8
|
+
|
9
|
+
def run
|
10
|
+
deploy 'openshift' do |config|
|
11
|
+
config['user'] = ask("OpenShift user: ").to_s
|
12
|
+
config['password'] = ask("OpenShift password: ") { |q| q.echo = "*" }.to_s
|
13
|
+
config['app'] = ask("OpenShift application name: ") { |q| q.default = repository.name }.to_s
|
14
|
+
config['domain'] = ask("OpenShift domain: ").to_s
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'travis/cli/setup'
|
2
|
+
|
3
|
+
module Travis
|
4
|
+
module CLI
|
5
|
+
class Setup
|
6
|
+
class RubyGems < Service
|
7
|
+
description "automatic release to RubyGems"
|
8
|
+
|
9
|
+
def run
|
10
|
+
deploy 'rubygems', 'release' do |config|
|
11
|
+
authorization_file = File.expand_path('.rubygems/authorization', ENV['HOME'])
|
12
|
+
credentials_file = File.expand_path('.gem/credentials', ENV['HOME'])
|
13
|
+
|
14
|
+
config['api_key'] ||= File.read(authorization_file) if File.exist? authorization_file
|
15
|
+
config['api_key'] ||= YAML.load_file(credentials_file)[:rubygems_api_key] if File.exist? credentials_file
|
16
|
+
config['api_key'] ||= ask("RubyGems API token: ") { |q| q.echo = "*" }.to_s
|
17
|
+
config['gem'] ||= ask("Gem name: ") { |q| q.default = repository.name }.to_s
|
18
|
+
|
19
|
+
on("Release only tagged commits? ", config, 'tags' => true)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'travis/cli/setup'
|
2
|
+
|
3
|
+
module Travis
|
4
|
+
module CLI
|
5
|
+
class Setup
|
6
|
+
class SauceConnect < Service
|
7
|
+
description "Sauce Connet addon for Sauce Labs integration"
|
8
|
+
service_name "sauce_connect"
|
9
|
+
|
10
|
+
def run
|
11
|
+
travis_config['addons'] ||= {}
|
12
|
+
configure 'sauce_connect', {}, travis_config['addons'] do |config|
|
13
|
+
config['username'] = ask("Sauce Labs user: ").to_s
|
14
|
+
config['access_key'] = ask("Sauce Labs access key: ") { |q| q.echo = "*" }.to_s
|
15
|
+
encrypt(config, 'access_key') if agree("Encrypt access key? ") { |q| q.default = 'yes' }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'travis/cli/setup'
|
2
|
+
|
3
|
+
module Travis
|
4
|
+
module CLI
|
5
|
+
class Setup
|
6
|
+
class Service
|
7
|
+
def self.normalized_name(string)
|
8
|
+
string.to_s.downcase.gsub(/[^a-z]/, '')
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.description(description = nil)
|
12
|
+
@description ||= ""
|
13
|
+
@description = description if description
|
14
|
+
@description
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.service_name(service_name = nil)
|
18
|
+
@service_name ||= normalized_name(name[/[^:]+$/])
|
19
|
+
@service_name = service_name if service_name
|
20
|
+
@service_name
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.known_as?(name)
|
24
|
+
normalized_name(service_name) == normalized_name(name)
|
25
|
+
end
|
26
|
+
|
27
|
+
attr_accessor :command
|
28
|
+
|
29
|
+
def initialize(command)
|
30
|
+
@command = command
|
31
|
+
end
|
32
|
+
|
33
|
+
def method_missing(*args, &block)
|
34
|
+
@command.send(*args, &block)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def on(question, config, condition)
|
40
|
+
return unless agree(question) { |q| q.default = 'yes' }
|
41
|
+
config['on'] ||= {}
|
42
|
+
config['on'].merge! condition
|
43
|
+
end
|
44
|
+
|
45
|
+
def encrypt(config, key)
|
46
|
+
encrypted = repository.encrypt(config.fetch(key))
|
47
|
+
config[key] = { 'secure' => encrypted }
|
48
|
+
end
|
49
|
+
|
50
|
+
def configure(key, value = {}, config = travis_config)
|
51
|
+
error "#{key} section already exists in .travis.yml, run with --force to override" if config.include? key and not force?
|
52
|
+
yield(config[key] = value)
|
53
|
+
end
|
54
|
+
|
55
|
+
def branch
|
56
|
+
@branch ||= `git rev-parse --symbolic-full-name --abbrev-ref HEAD`.chomp
|
57
|
+
end
|
58
|
+
|
59
|
+
def deploy(provider, verb = "deploy")
|
60
|
+
configure('deploy', 'provider' => provider) do |config|
|
61
|
+
yield config
|
62
|
+
|
63
|
+
on("#{verb.capitalize} only from #{repository.slug}? ", config, 'repo' => repository.slug)
|
64
|
+
on("#{verb.capitalize} from #{branch} branch? ", config, 'branch' => branch) if branch != 'master' and branch != 'HEAD'
|
65
|
+
|
66
|
+
encrypt(config, 'password') if config['password'] and agree("Encrypt Password? ") { |q| q.default = 'yes' }
|
67
|
+
encrypt(config, 'api_key') if config['api_key'] and agree("Encrypt API key? ") { |q| q.default = 'yes' }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -48,7 +48,7 @@ module Travis
|
|
48
48
|
include States
|
49
49
|
|
50
50
|
# @!parse attr_reader :slug, :description
|
51
|
-
attributes :slug, :description, :last_build_id, :last_build_number, :last_build_state, :last_build_duration, :last_build_started_at, :last_build_finished_at
|
51
|
+
attributes :slug, :description, :last_build_id, :last_build_number, :last_build_state, :last_build_duration, :last_build_started_at, :last_build_finished_at, :github_language
|
52
52
|
inspect_info :slug
|
53
53
|
|
54
54
|
time :last_build_finished_at, :last_build_started_at
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'travis/client'
|
2
|
+
require 'travis/version'
|
2
3
|
|
3
4
|
require 'faraday'
|
4
5
|
require 'faraday_middleware'
|
@@ -16,12 +17,13 @@ module Travis
|
|
16
17
|
class Session
|
17
18
|
SSL_OPTIONS = { :ca_file => File.expand_path("../../cacert.pem", __FILE__) }
|
18
19
|
include Methods
|
19
|
-
attr_reader :connection, :headers, :access_token, :instruments, :faraday_adapter
|
20
|
+
attr_reader :connection, :headers, :access_token, :instruments, :faraday_adapter, :agent_info
|
20
21
|
|
21
22
|
def initialize(options = Travis::Client::ORG_URI)
|
22
23
|
@headers = {}
|
23
24
|
@cache = {}
|
24
25
|
@instruments = []
|
26
|
+
@agent_info = []
|
25
27
|
@config = nil
|
26
28
|
@faraday_adapter = defined?(Typhoeus) ? :typhoeus : :net_http
|
27
29
|
|
@@ -29,20 +31,26 @@ module Travis
|
|
29
31
|
options.each_pair { |key, value| public_send("#{key}=", value) }
|
30
32
|
|
31
33
|
raise ArgumentError, "neither :uri nor :connection specified" unless connection
|
32
|
-
headers['Accept']
|
34
|
+
headers['Accept'] = 'application/json; version=2'
|
35
|
+
set_user_agent
|
33
36
|
end
|
34
37
|
|
35
38
|
def uri
|
36
39
|
connection.url_prefix.to_s if connection
|
37
40
|
end
|
38
41
|
|
42
|
+
def agent_info=(info)
|
43
|
+
@agent_info = [info].flatten.freeze
|
44
|
+
set_user_agent
|
45
|
+
end
|
46
|
+
|
39
47
|
def uri=(uri)
|
40
48
|
clear_cache!
|
41
49
|
self.connection = Faraday.new(:url => uri, :ssl => SSL_OPTIONS) do |faraday|
|
42
50
|
faraday.request :url_encoded
|
43
|
-
faraday.response
|
44
|
-
faraday.response
|
45
|
-
faraday.response
|
51
|
+
faraday.response :json
|
52
|
+
faraday.response :follow_redirects
|
53
|
+
faraday.response :raise_error
|
46
54
|
faraday.adapter(*faraday_adapter)
|
47
55
|
end
|
48
56
|
end
|
@@ -50,6 +58,7 @@ module Travis
|
|
50
58
|
def faraday_adapter=(adapter)
|
51
59
|
@faraday_adapter = adapter
|
52
60
|
self.uri &&= uri
|
61
|
+
set_user_agent
|
53
62
|
end
|
54
63
|
|
55
64
|
def access_token=(token)
|
@@ -181,6 +190,19 @@ module Travis
|
|
181
190
|
|
182
191
|
private
|
183
192
|
|
193
|
+
def set_user_agent
|
194
|
+
adapter = Array === faraday_adapter ? faraday_adapter.first : faraday_adapter
|
195
|
+
adapter = adapter.to_s.capitalize.gsub(/_http_(.)/) { "::HTTP::#{$1.upcase}" }.gsub(/_http/, '::HTTP')
|
196
|
+
headers['User-Agent'] = "Travis/#{Travis::VERSION} (#{Travis::Tools::System.description(agent_info)}) Faraday/#{Faraday::VERSION} #{adapter}/#{adapter_version(adapter)}"
|
197
|
+
end
|
198
|
+
|
199
|
+
def adapter_version(adapter)
|
200
|
+
version = Object.const_get(adapter).const_get("VERSION")
|
201
|
+
[*version].join('.')
|
202
|
+
rescue Exception
|
203
|
+
"unknown"
|
204
|
+
end
|
205
|
+
|
184
206
|
def instrumented(name, *args)
|
185
207
|
name = [name, *args.map(&:inspect)].join(" ") if args.any?
|
186
208
|
result = nil
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require "travis/tools/system"
|
2
|
+
require "terminal-notifier"
|
3
|
+
require "cgi"
|
4
|
+
|
5
|
+
module Travis
|
6
|
+
module Tools
|
7
|
+
module Notification
|
8
|
+
extend self
|
9
|
+
DEFAULT = [:osx, :growl, :libnotify]
|
10
|
+
|
11
|
+
def new(*list)
|
12
|
+
list.concat(DEFAULT) if list.empty?
|
13
|
+
notification = list.map { |n| get(n) }.detect { |n| n.available? }
|
14
|
+
raise ArgumentError, "no notification system found (looked for #{list.join(", ")})" unless notification
|
15
|
+
notification
|
16
|
+
end
|
17
|
+
|
18
|
+
def get(name)
|
19
|
+
const = constants.detect { |c| c.to_s[/[^:]+$/].downcase == name.to_s }
|
20
|
+
raise ArgumentError, "unknown notifications type %p" % name unless const
|
21
|
+
const_get(const).new
|
22
|
+
end
|
23
|
+
|
24
|
+
class Dummy
|
25
|
+
def notify(title, body)
|
26
|
+
end
|
27
|
+
|
28
|
+
def available?
|
29
|
+
true
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class OSX
|
34
|
+
def notify(title, body)
|
35
|
+
TerminalNotifier.notify(body, :title => title)
|
36
|
+
end
|
37
|
+
|
38
|
+
def available?
|
39
|
+
System.mac? and TerminalNotifier.available?
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class Growl
|
44
|
+
def initialize
|
45
|
+
@command = "growlnotify"
|
46
|
+
end
|
47
|
+
|
48
|
+
def notify(title, body)
|
49
|
+
system "%s -m %p %p >/dev/null" [@command, title, body]
|
50
|
+
end
|
51
|
+
|
52
|
+
def available?
|
53
|
+
system "which #{@command} >/dev/null 2>/dev/null" unless System.windows?
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class LibNotify < Growl
|
58
|
+
def initialize
|
59
|
+
@command = "notify-send"
|
60
|
+
@expire_time = 10_000
|
61
|
+
end
|
62
|
+
|
63
|
+
def notify(title, body)
|
64
|
+
system @command, "--expire-time=#{@expire_time}", title, CGI.escapeHTML(body)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/lib/travis/tools/system.rb
CHANGED
@@ -6,6 +6,44 @@ module Travis
|
|
6
6
|
def windows?
|
7
7
|
File::ALT_SEPARATOR == "\\"
|
8
8
|
end
|
9
|
+
|
10
|
+
def mac?
|
11
|
+
RUBY_PLATFORM =~ /darwin/i
|
12
|
+
end
|
13
|
+
|
14
|
+
def linux?
|
15
|
+
RUBY_PLATFORM =~ /linux/i
|
16
|
+
end
|
17
|
+
|
18
|
+
def os
|
19
|
+
@os ||= windows? ? "Windows" : `uname`.chomp
|
20
|
+
end
|
21
|
+
|
22
|
+
def ruby_engine
|
23
|
+
defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby'
|
24
|
+
end
|
25
|
+
|
26
|
+
def ruby_version
|
27
|
+
"%s-p%s" % [RUBY_VERSION, RUBY_PATCHLEVEL]
|
28
|
+
end
|
29
|
+
|
30
|
+
def ruby
|
31
|
+
case ruby_engine
|
32
|
+
when 'ruby' then "Ruby #{ruby_version}"
|
33
|
+
when 'jruby' then "JRuby #{JRUBY_VERSION} like Ruby #{ruby_version}"
|
34
|
+
when 'rbx' then "Rubinius #{Rubinius.version[/\d\S+/]} like Ruby #{ruby_version}"
|
35
|
+
else "#{ruby_engine} like Ruby #{ruby_version}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def rubygems
|
40
|
+
return "no RubyGems" unless defined? Gem
|
41
|
+
"RubyGems #{Gem::VERSION}"
|
42
|
+
end
|
43
|
+
|
44
|
+
def description(*args)
|
45
|
+
[ os, ruby, rubygems, *args.flatten].compact.uniq.join("; ")
|
46
|
+
end
|
9
47
|
end
|
10
48
|
end
|
11
49
|
end
|