travis 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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