travis 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. data/LICENSE +22 -0
  2. data/README.md +154 -0
  3. data/Rakefile +38 -0
  4. data/bin/travis +4 -0
  5. data/lib/travis.rb +8 -0
  6. data/lib/travis/cacert.pem +3895 -0
  7. data/lib/travis/cli.rb +83 -0
  8. data/lib/travis/cli/api_command.rb +65 -0
  9. data/lib/travis/cli/command.rb +212 -0
  10. data/lib/travis/cli/encrypt.rb +57 -0
  11. data/lib/travis/cli/endpoint.rb +13 -0
  12. data/lib/travis/cli/help.rb +21 -0
  13. data/lib/travis/cli/login.rb +57 -0
  14. data/lib/travis/cli/parser.rb +43 -0
  15. data/lib/travis/cli/raw.rb +16 -0
  16. data/lib/travis/cli/repo_command.rb +54 -0
  17. data/lib/travis/cli/version.rb +12 -0
  18. data/lib/travis/cli/whoami.rb +12 -0
  19. data/lib/travis/client.rb +20 -0
  20. data/lib/travis/client/entity.rb +139 -0
  21. data/lib/travis/client/error.rb +11 -0
  22. data/lib/travis/client/methods.rb +45 -0
  23. data/lib/travis/client/namespace.rb +78 -0
  24. data/lib/travis/client/repository.rb +66 -0
  25. data/lib/travis/client/session.rb +191 -0
  26. data/lib/travis/client/user.rb +20 -0
  27. data/lib/travis/pro.rb +5 -0
  28. data/lib/travis/tools/token_finder.rb +51 -0
  29. data/lib/travis/version.rb +3 -0
  30. data/spec/cli/encrypt_spec.rb +18 -0
  31. data/spec/cli/endpoint_spec.rb +23 -0
  32. data/spec/cli/help_spec.rb +33 -0
  33. data/spec/cli/login_spec.rb +13 -0
  34. data/spec/cli/version_spec.rb +18 -0
  35. data/spec/cli/whoami_spec.rb +27 -0
  36. data/spec/client/methods_spec.rb +15 -0
  37. data/spec/client/namespace_spec.rb +19 -0
  38. data/spec/client/repository_spec.rb +15 -0
  39. data/spec/client/session_spec.rb +145 -0
  40. data/spec/client/user_spec.rb +16 -0
  41. data/spec/client_spec.rb +5 -0
  42. data/spec/pro_spec.rb +10 -0
  43. data/spec/spec_helper.rb +16 -0
  44. data/spec/support/fake_api.rb +89 -0
  45. data/spec/support/fake_github.rb +20 -0
  46. data/spec/support/helpers.rb +43 -0
  47. data/spec/travis_spec.rb +9 -0
  48. data/travis.gemspec +84 -0
  49. metadata +240 -0
@@ -0,0 +1,57 @@
1
+ require 'travis/cli'
2
+ require 'travis/tools/token_finder'
3
+ require 'json'
4
+
5
+ module Travis
6
+ module CLI
7
+ class Login < ApiCommand
8
+ skip :authenticate
9
+ attr_accessor :github_login, :github_password, :github_token, :callback
10
+
11
+ on('--github-token TOKEN', 'identify by GitHub token')
12
+
13
+ on('--auto', 'try to figure out who you are automatically (might send another apps token to Travis, token will not be stored)') do |c|
14
+ c.github_token ||= Travis::Tools::TokenFinder.find(:explode => c.explode?)
15
+ end
16
+
17
+ def run
18
+ generate_github_token unless github_token
19
+ endpoint_config['access_token'] = github_auth(github_token)
20
+ success("Successfully logged in!")
21
+ ensure
22
+ callback.call if callback
23
+ end
24
+
25
+ private
26
+
27
+ def generate_github_token
28
+ ask_info
29
+
30
+ gh = GH.with(:username => github_login, :password => github_password)
31
+ reply = gh.post('/authorizations', :scopes => github_scopes, :note => "temporary token to identify on #{api_endpoint}")
32
+
33
+ self.github_token = reply['token']
34
+ self.callback = proc { gh.delete reply['_links']['self']['href'] }
35
+ rescue GH::Error => e
36
+ raise e if explode?
37
+ error JSON.parse(e.info[:response_body])["message"]
38
+ end
39
+
40
+ def github_scopes
41
+ ['user:email', org? ? 'public_repo' : 'repo']
42
+ end
43
+
44
+ def ask_info
45
+ say "We need your #{color("GitHub login", :important)} to identify you."
46
+ say "This information will #{color("not be sent to Travis CI", :important)}, only to GitHub."
47
+ say "The password will not be displayed."
48
+ empty_line
49
+ say "Try running with #{color("--github-token", :info)} or #{color("--auto", :info)} if you don't want to enter your password anyways."
50
+ empty_line
51
+ self.github_login = ask("Username: ")
52
+ self.github_password = ask("Password: ") { |q| q.echo = "*" }
53
+ empty_line
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,43 @@
1
+ require 'travis/cli'
2
+ require 'optparse'
3
+
4
+ module Travis
5
+ module CLI
6
+ module Parser
7
+ def on_initialize(&block)
8
+ @on_initialize ||= []
9
+ @on_initialize << block if block
10
+ if superclass.respond_to? :on_initialize
11
+ superclass.on_initialize + @on_initialize
12
+ else
13
+ @on_initialize
14
+ end
15
+ end
16
+
17
+ def on(*args, &block)
18
+ block ||= begin
19
+ full_arg = args.detect { |a| a.start_with? '--' }
20
+ name = full_arg.gsub(/^--(\[no-\])?(\S+).*$/, '\2').gsub('-', '_')
21
+ attr_reader(name) unless method_defined? name
22
+ attr_writer(name) unless method_defined? "#{name}="
23
+ alias_method("#{name}?", name) if full_arg.start_with? '--[no-]' and not method_defined? "#{name}?"
24
+ proc { |instance, value| instance.public_send("#{name}=", value) }
25
+ end
26
+
27
+ on_initialize do |instance|
28
+ instance.parser.on(*args) do |value|
29
+ block.call(instance, value)
30
+ end
31
+ end
32
+ end
33
+
34
+ def new(*)
35
+ attr_accessor :parser unless method_defined? :parser
36
+ result = super
37
+ result.parser = OptionParser.new
38
+ on_initialize.each { |b| b[result] }
39
+ result
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,16 @@
1
+ require 'travis/cli'
2
+ require 'pp'
3
+
4
+ module Travis
5
+ module CLI
6
+ class Raw < ApiCommand
7
+ skip :authenticate
8
+ on('--[no-]json', 'display as json')
9
+
10
+ def run(resource)
11
+ reply = session.get_raw(resource)
12
+ json? ? say(reply.to_json) : pp(reply)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,54 @@
1
+ require 'travis/cli'
2
+
3
+ module Travis
4
+ module CLI
5
+ class RepoCommand < ApiCommand
6
+ GIT_REGEX = %r{Fetch URL: (?:https://|git://|git@)github\.com[:/](.*/.+?)(\.git)?$}
7
+ on('-r', '--repo SLUG') { |c| c.slug = slug }
8
+
9
+ attr_accessor :slug
10
+ abstract
11
+
12
+ def setup
13
+ error "Can't figure out GitHub repo name. Are you in the right directory?" unless self.slug ||= find_slug
14
+ self.api_endpoint = detect_api_endpoint
15
+ super
16
+ end
17
+
18
+ def repository
19
+ repo(slug)
20
+ rescue Travis::Client::NotFound
21
+ error "repository not known to travis: #{color(slug, :important)}"
22
+ end
23
+
24
+ private
25
+
26
+ def detected_endpoint?
27
+ !explicit_api_endpoint?
28
+ end
29
+
30
+ def find_slug
31
+ git_info = `git remote show origin 2>&1`
32
+ $1 if git_info =~ GIT_REGEX
33
+ end
34
+
35
+ def repo_config
36
+ config['repos'] ||= {}
37
+ config['repos'][slug] ||= {}
38
+ end
39
+
40
+ def detect_api_endpoint
41
+ if explicit_api_endpoint?
42
+ repo_config['endpoint'] = api_endpoint
43
+ else
44
+ repo_config['endpoint'] ||= begin
45
+ GH.head("/repos/#{slug}")
46
+ Travis::Client::ORG_URI
47
+ rescue GH::Error
48
+ Travis::Client::PRO_URI
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,12 @@
1
+ require 'travis/cli'
2
+ require 'travis/version'
3
+
4
+ module Travis
5
+ module CLI
6
+ class Version < Command
7
+ def run
8
+ say Travis::VERSION
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ require 'travis/cli'
2
+
3
+ module Travis
4
+ module CLI
5
+ class Whoami < ApiCommand
6
+ def run
7
+ authenticate
8
+ say user.login, "You are %s (#{user.name})"
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,20 @@
1
+ require 'backports/1.9.3' if RUBY_VERSION < '1.9.3'
2
+ require 'travis/client/error'
3
+ require 'travis/client/methods'
4
+ require 'travis/client/session'
5
+ require 'travis/client/entity'
6
+ require 'travis/client/user'
7
+ require 'travis/client/repository'
8
+ require 'travis/client/namespace'
9
+
10
+ module Travis
11
+ module Client
12
+ ORG_URI = 'https://api.travis-ci.org/'
13
+ PRO_URI = 'https://api.travis-ci.com/'
14
+
15
+ def self.new(options = {})
16
+ options['uri'] ||= ORG_URI if options.is_a? Hash
17
+ Session.new(options)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,139 @@
1
+ require 'travis/client'
2
+ require 'time'
3
+
4
+ module Travis
5
+ module Client
6
+ class Entity
7
+ attr_reader :attributes, :id, :session
8
+ attr_accessor :curry
9
+
10
+ MAP = {}
11
+
12
+ def self.subclasses
13
+ MAP.values.uniq
14
+ end
15
+
16
+ def self.subclass_for(key)
17
+ MAP.fetch(key)
18
+ end
19
+
20
+ def self.one(key = nil)
21
+ MAP[key.to_s] = self if key
22
+ @one ||= key.to_s
23
+ end
24
+
25
+ def self.many(key = nil)
26
+ MAP[key.to_s] = self if key
27
+ @many ||= key.to_s
28
+ end
29
+
30
+ def self.attributes(*list)
31
+ @attributes ||= []
32
+ list.each do |name|
33
+ name = name.to_s
34
+ @attributes << name
35
+ define_method(name) { load_attribute(name) }
36
+ define_method("#{name}=") { |value| set_attribute(name, value) }
37
+ define_method("#{name}?") { !!send(name) }
38
+ end
39
+ @attributes
40
+ end
41
+
42
+ def self.inspect_info(name)
43
+ alias_method(:inspect_info, name)
44
+ private(:inspect_info)
45
+ end
46
+
47
+ def initialize(session, id)
48
+ @attributes = {}
49
+ @session = session
50
+ @id = Integer(id)
51
+ end
52
+
53
+ def update_attributes(data)
54
+ data.each_pair do |key, value|
55
+ self[key] = value
56
+ end
57
+ end
58
+
59
+ def attribute_names
60
+ self.class.attributes
61
+ end
62
+
63
+ def [](key)
64
+ send(key) if include? key
65
+ end
66
+
67
+ def []=(key, value)
68
+ send("#{key}=", value) if include? key
69
+ end
70
+
71
+ def include?(key)
72
+ attributes.include? key or attribute_names.include? key.to_s
73
+ end
74
+
75
+ def reload
76
+ session.reload(self)
77
+ end
78
+
79
+ def load
80
+ reload unless complete?
81
+ end
82
+
83
+ def missing?(key)
84
+ return false unless include? key
85
+ !attributes.include?(key.to_s)
86
+ end
87
+
88
+ def complete?
89
+ attribute_names.all? { |key| attributes.include? key }
90
+ end
91
+
92
+ def inspect
93
+ klass = self.class
94
+ klass = curry if curry and curry.name and curry.to_s.size < klass.to_s.size
95
+ "#<#{klass}: #{inspect_info}>"
96
+ end
97
+
98
+ private
99
+
100
+ def inspect_info
101
+ id
102
+ end
103
+
104
+ def set_attribute(name, value)
105
+ attributes[name.to_s] = value
106
+ end
107
+
108
+ def load_attribute(name)
109
+ reload if missing? name
110
+ attributes[name.to_s]
111
+ end
112
+
113
+ # shamelessly stolen from sinatra
114
+ def time(value)
115
+ if value.respond_to? :to_time
116
+ value.to_time
117
+ elsif value.is_a? Time
118
+ value
119
+ elsif value.respond_to? :new_offset
120
+ d = value.new_offset 0
121
+ t = Time.utc d.year, d.mon, d.mday, d.hour, d.min, d.sec + d.sec_fraction
122
+ t.getlocal
123
+ elsif value.respond_to? :mday
124
+ Time.local(value.year, value.mon, value.mday)
125
+ elsif value.is_a? Numeric
126
+ Time.at value
127
+ elsif value.nil? or value.empty?
128
+ nil
129
+ else
130
+ Time.parse value.to_s
131
+ end
132
+ rescue ArgumentError => boom
133
+ raise boom
134
+ rescue Exception
135
+ raise ArgumentError, "unable to convert #{value.inspect} to a Time object"
136
+ end
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,11 @@
1
+ require 'travis/client'
2
+
3
+ module Travis
4
+ module Client
5
+ class Error < StandardError
6
+ end
7
+
8
+ class NotFound < Error
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,45 @@
1
+ require 'travis/client'
2
+
3
+ module Travis
4
+ module Client
5
+ module Methods
6
+ def access_token
7
+ session.access_token
8
+ end
9
+
10
+ def access_token=(token)
11
+ session.access_token = token
12
+ end
13
+
14
+ def api_endpoint
15
+ session.uri
16
+ end
17
+
18
+ def github_auth(github_token)
19
+ reply = session.post_raw("/auth/github?github_token=#{github_token}")
20
+ session.access_token = reply["access_token"]
21
+ end
22
+
23
+ def explicit_api_endpoint?
24
+ @explicit_api_endpoint ||= false
25
+ end
26
+
27
+ def api_endpoint=(uri)
28
+ @explicit_api_endpoint = true
29
+ session.uri = uri
30
+ end
31
+
32
+ def repos(params = {})
33
+ session.find_many(Repository, params)
34
+ end
35
+
36
+ def repo(id_or_slug)
37
+ session.find_one(Repository, id_or_slug)
38
+ end
39
+
40
+ def user
41
+ session.find_one(User)
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,78 @@
1
+ require 'travis/client'
2
+
3
+ module Travis
4
+ module Client
5
+ class Namespace < Module
6
+ class Curry < Module
7
+ attr_accessor :namespace, :type
8
+
9
+ def initialize(namespace, type)
10
+ @namespace, @type = namespace, type
11
+ end
12
+
13
+ def find_one(id = nil)
14
+ result = session.find_one(type, id)
15
+ result.curry = self
16
+ result
17
+ end
18
+
19
+ def current
20
+ result = session.find_one_or_many(type)
21
+ Array(result).each { |e| e.curry = self }
22
+ result
23
+ end
24
+
25
+ def find_many(params = {})
26
+ session.find_many(type, params).each do |entity|
27
+ entity.curry = self
28
+ end
29
+ end
30
+
31
+ alias find find_one
32
+ alias find_all find_many
33
+
34
+ private
35
+
36
+ def session
37
+ namespace.session
38
+ end
39
+ end
40
+
41
+
42
+ include Methods
43
+ attr_accessor :session
44
+
45
+ def initialize(session = nil)
46
+ session = Travis::Client.new(session || {}) unless session.is_a? Session
47
+ @session = session
48
+
49
+ Entity.subclasses.each do |subclass|
50
+ name = subclass.name[/[^:]+$/]
51
+ const_set(name, Curry.new(self, subclass))
52
+ end
53
+ end
54
+
55
+ def included(klass)
56
+ fix_names(klass)
57
+ delegate_session(klass)
58
+ end
59
+
60
+ private
61
+
62
+ def fix_names(klass)
63
+ constants.each do |name|
64
+ const = klass.const_get(name)
65
+ klass.const_set(name, const) if const == const_get(name)
66
+ end
67
+ end
68
+
69
+ def delegate_session(klass)
70
+ return if klass == Object or klass == Kernel
71
+ klass.extend(Methods)
72
+ namespace = self
73
+ klass.define_singleton_method(:session) { namespace.session }
74
+ klass.define_singleton_method(:session=) { |value| namespace.session = value }
75
+ end
76
+ end
77
+ end
78
+ end