travis 1.0.3 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/README.md +786 -86
  2. data/Rakefile +3 -0
  3. data/lib/travis/cli/api_command.rb +22 -0
  4. data/lib/travis/cli/command.rb +36 -15
  5. data/lib/travis/cli/console.rb +13 -0
  6. data/lib/travis/cli/disable.rb +13 -0
  7. data/lib/travis/cli/enable.rb +29 -0
  8. data/lib/travis/cli/encrypt.rb +17 -4
  9. data/lib/travis/cli/history.rb +43 -0
  10. data/lib/travis/cli/logs.rb +18 -0
  11. data/lib/travis/cli/open.rb +36 -0
  12. data/lib/travis/cli/parser.rb +3 -3
  13. data/lib/travis/cli/repo_command.rb +16 -1
  14. data/lib/travis/cli/restart.rb +14 -0
  15. data/lib/travis/cli/show.rb +50 -0
  16. data/lib/travis/cli/status.rb +17 -0
  17. data/lib/travis/cli/sync.rb +28 -0
  18. data/lib/travis/cli/whatsup.rb +16 -0
  19. data/lib/travis/cli.rb +11 -0
  20. data/lib/travis/client/artifact.rb +25 -0
  21. data/lib/travis/client/build.rb +41 -0
  22. data/lib/travis/client/commit.rb +26 -0
  23. data/lib/travis/client/entity.rb +47 -3
  24. data/lib/travis/client/job.rb +52 -0
  25. data/lib/travis/client/methods.rb +22 -1
  26. data/lib/travis/client/repository.rb +70 -5
  27. data/lib/travis/client/session.rb +40 -8
  28. data/lib/travis/client/states.rb +85 -0
  29. data/lib/travis/client/user.rb +6 -0
  30. data/lib/travis/client.rb +5 -0
  31. data/lib/travis/tools/formatter.rb +38 -0
  32. data/lib/travis/version.rb +1 -1
  33. data/spec/cli/encrypt_spec.rb +16 -1
  34. data/spec/cli/history_spec.rb +28 -0
  35. data/spec/cli/logs_spec.rb +8 -0
  36. data/spec/cli/open_spec.rb +33 -0
  37. data/spec/cli/restart_spec.rb +15 -0
  38. data/spec/cli/show_spec.rb +9 -0
  39. data/spec/cli/status_spec.rb +28 -0
  40. data/spec/client/build_spec.rb +31 -0
  41. data/spec/client/commit_spec.rb +18 -0
  42. data/spec/client/job_spec.rb +30 -0
  43. data/spec/client/repository_spec.rb +15 -0
  44. data/spec/client/session_spec.rb +4 -0
  45. data/spec/spec_helper.rb +4 -1
  46. data/spec/support/fake_api.rb +616 -1
  47. data/travis.gemspec +37 -3
  48. metadata +66 -2
@@ -0,0 +1,26 @@
1
+ require 'travis/client'
2
+
3
+ module Travis
4
+ module Client
5
+ class Commit < Entity
6
+ # @!parse attr_reader :sha, :branch, :message, :committed_at, :author_name, :author_email, :committer_name, :committer_email, :compare_url
7
+ attributes :sha, :branch, :message, :committed_at, :author_name, :author_email, :committer_name, :committer_email, :compare_url
8
+ time :committed_at
9
+
10
+ one :commit
11
+ many :commits
12
+
13
+ def subject
14
+ message.to_s.lines.first.strip
15
+ end
16
+
17
+ def short_sha
18
+ sha.to_s[0..6]
19
+ end
20
+
21
+ def inspect_info
22
+ short_sha + " " + subject.inspect
23
+ end
24
+ end
25
+ end
26
+ end
@@ -9,6 +9,10 @@ module Travis
9
9
 
10
10
  MAP = {}
11
11
 
12
+ def self.relations
13
+ @relations ||= []
14
+ end
15
+
12
16
  def self.subclasses
13
17
  MAP.values.uniq
14
18
  end
@@ -17,6 +21,10 @@ module Travis
17
21
  MAP.fetch(key)
18
22
  end
19
23
 
24
+ def self.aka(name)
25
+ MAP[name.to_s] = self
26
+ end
27
+
20
28
  def self.one(key = nil)
21
29
  MAP[key.to_s] = self if key
22
30
  @one ||= key.to_s
@@ -29,16 +37,33 @@ module Travis
29
37
 
30
38
  def self.attributes(*list)
31
39
  @attributes ||= []
40
+
32
41
  list.each do |name|
33
42
  name = name.to_s
43
+ fail "can't call an attribute id" if name == "id"
44
+
34
45
  @attributes << name
35
46
  define_method(name) { load_attribute(name) }
36
47
  define_method("#{name}=") { |value| set_attribute(name, value) }
37
48
  define_method("#{name}?") { !!send(name) }
38
49
  end
50
+
39
51
  @attributes
40
52
  end
41
53
 
54
+ def self.time(*list)
55
+ list.each do |name|
56
+ define_method("#{name}=") { |value| set_attribute(name, time(value)) }
57
+ end
58
+ end
59
+
60
+ def self.has(*list)
61
+ list.each do |name|
62
+ relations << name
63
+ define_method(name) { relation(name.to_s) }
64
+ end
65
+ end
66
+
42
67
  def self.inspect_info(name)
43
68
  alias_method(:inspect_info, name)
44
69
  private(:inspect_info)
@@ -73,11 +98,12 @@ module Travis
73
98
  end
74
99
 
75
100
  def reload
76
- session.reload(self)
101
+ relations.each { |e| session.reset(e) }
102
+ session.reset(self)
77
103
  end
78
104
 
79
105
  def load
80
- reload unless complete?
106
+ session.reload(self) unless complete?
81
107
  end
82
108
 
83
109
  def missing?(key)
@@ -95,8 +121,26 @@ module Travis
95
121
  "#<#{klass}: #{inspect_info}>"
96
122
  end
97
123
 
124
+ def relations
125
+ self.class.relations.map { |r| public_send(r) }.flatten(1)
126
+ end
127
+
98
128
  private
99
129
 
130
+ def relation(name)
131
+ name = name.to_s
132
+ entity = Entity.subclass_for(name)
133
+
134
+ if entity.many == name
135
+ Array(send("#{entity.one}_ids")).map do |id|
136
+ session.find_one(entity, id)
137
+ end
138
+ else
139
+ id = send("#{name}_id")
140
+ session.find_one(entity, id) unless id.nil?
141
+ end
142
+ end
143
+
100
144
  def inspect_info
101
145
  id
102
146
  end
@@ -106,7 +150,7 @@ module Travis
106
150
  end
107
151
 
108
152
  def load_attribute(name)
109
- reload if missing? name
153
+ session.reload(self) if missing? name
110
154
  attributes[name.to_s]
111
155
  end
112
156
 
@@ -0,0 +1,52 @@
1
+ require 'travis/client'
2
+
3
+ module Travis
4
+ module Client
5
+ class Job < Entity
6
+ include States
7
+
8
+ # @!parse attr_reader :repository_id, :build_id, :commit_id, :log_id, :number, :config, :state, :started_at, :finished_at, :queue, :allow_failure, :tags
9
+ attributes :repository_id, :build_id, :commit_id, :log_id, :number, :config, :state, :started_at, :finished_at, :queue, :allow_failure, :tags
10
+ time :started_at, :finished_at
11
+
12
+ alias allow_failure? allow_failure
13
+
14
+ # @!parse attr_reader :commit, :repository, :build
15
+ has :commit, :repository, :build, :log
16
+
17
+ one :job
18
+ many :jobs
19
+
20
+ def restart
21
+ session.restart(self)
22
+ end
23
+
24
+ def pull_request?
25
+ build.pull_request?
26
+ end
27
+
28
+ def push?
29
+ build.push?
30
+ end
31
+
32
+ def allow_failures?
33
+ return false unless config.include? 'matrix' and config['matrix'].include? 'allow_failures'
34
+ config['matrix']['allow_failures'].any? do |allow|
35
+ allow.all? { |key, value| config[key] == value }
36
+ end
37
+ end
38
+
39
+ def duration
40
+ attributes['duration'] ||= begin
41
+ start = started_at || Time.now
42
+ finish = finished_at || Time.now
43
+ (finish - start).to_i
44
+ end
45
+ end
46
+
47
+ def inspect_info
48
+ "#{repository.slug}##{number}"
49
+ end
50
+ end
51
+ end
52
+ end
@@ -16,7 +16,7 @@ module Travis
16
16
  end
17
17
 
18
18
  def github_auth(github_token)
19
- reply = session.post_raw("/auth/github?github_token=#{github_token}")
19
+ reply = session.post_raw("/auth/github", :github_token => github_token)
20
20
  session.access_token = reply["access_token"]
21
21
  end
22
22
 
@@ -37,9 +37,30 @@ module Travis
37
37
  session.find_one(Repository, id_or_slug)
38
38
  end
39
39
 
40
+ def build(id)
41
+ session.find_one(Build, id)
42
+ end
43
+
44
+ def job(id)
45
+ session.find_one(Job, id)
46
+ end
47
+
48
+ def artifact(id)
49
+ session.find_one(Artifact, id)
50
+ end
51
+
52
+ alias log artifact
53
+
40
54
  def user
41
55
  session.find_one(User)
42
56
  end
57
+
58
+ def restart(entity)
59
+ # btw, internally we call this reset, not restart, as it resets the state machine
60
+ # but we thought that would be too confusing
61
+ session.post_raw('/requests', "#{entity.class.one}_id" => entity.id)
62
+ entity.reload
63
+ end
43
64
  end
44
65
  end
45
66
  end
@@ -29,11 +29,17 @@ module Travis
29
29
  end
30
30
  end
31
31
 
32
- attributes :slug, :description, :last_build_id, :last_build_number, :last_build_state, :last_build_duration, :last_build_language, :last_build_started_at, :last_build_finished_at
32
+ include States
33
+
34
+ # @!parse attr_reader :slug, :description
35
+ attributes :slug, :description, :last_build_id, :last_build_number, :last_build_state, :last_build_duration, :last_build_started_at, :last_build_finished_at
33
36
  inspect_info :slug
34
37
 
38
+ time :last_build_finished_at, :last_build_started_at
39
+
35
40
  one :repo
36
41
  many :repos
42
+ aka :repository
37
43
 
38
44
  def public_key
39
45
  attributes["public_key"] ||= begin
@@ -54,13 +60,72 @@ module Travis
54
60
  key.encrypt(value)
55
61
  end
56
62
 
57
- def last_build_started_at=(time)
58
- set_attribute(:last_build_started_at, time(time))
63
+ # @!parse attr_reader :last_build
64
+ def last_build
65
+ return unless last_build_id
66
+ attributes['last_build'] ||= begin
67
+ last_build = session.find_one(Build, last_build_id)
68
+ last_build.number = last_build_number
69
+ last_build.state = last_build_state
70
+ last_build.duration = last_build_duration
71
+ last_build.started_at = last_build_started_at
72
+ last_build.finished_at = last_build_finished_at
73
+ last_build.repository_id = id
74
+ last_build
75
+ end
76
+ end
77
+
78
+ def builds(params = nil)
79
+ return each_build unless params
80
+ session.find_many(Build, params.merge(:repository_id => id))
59
81
  end
60
82
 
61
- def last_build_finished_at=(time)
62
- set_attribute(:last_build_finished_at, time(time))
83
+ def build(number)
84
+ builds(:number => number.to_s).first
63
85
  end
86
+
87
+ def recent_builds
88
+ builds({})
89
+ end
90
+
91
+ def each_build(params = nil, &block)
92
+ return enum_for(__method__, params) unless block_given?
93
+ params ||= {}
94
+ chunk = builds(params)
95
+ until chunk.empty?
96
+ chunk.each(&block)
97
+ number = chunk.last.number
98
+ chunk = number == '1' ? [] : builds(params.merge(:after_number => number))
99
+ end
100
+ self
101
+ end
102
+
103
+ def job(number)
104
+ build_number = number.to_s[/^\d+/]
105
+ build = build(build_number)
106
+ job = build.jobs.detect { |j| j.number == number } if number != build_number
107
+ job ||= build.jobs.first if build.jobs.size == 1
108
+ job
109
+ end
110
+
111
+ def set_hook(flag)
112
+ result = session.put_raw('/hooks/', :hook => { :id => id, :active => flag })
113
+ result['result']
114
+ end
115
+
116
+ def disable
117
+ set_hook(false)
118
+ end
119
+
120
+ def enable
121
+ set_hook(true)
122
+ end
123
+
124
+ private
125
+
126
+ def state
127
+ last_build_state
128
+ end
64
129
  end
65
130
  end
66
131
  end
@@ -9,11 +9,13 @@ module Travis
9
9
  class Session
10
10
  SSL_OPTIONS = { :ca_file => File.expand_path("../../cacert.pem", __FILE__) }
11
11
  include Methods
12
- attr_reader :connection, :headers, :access_token
12
+ attr_reader :connection, :headers, :access_token, :instruments
13
13
 
14
14
  def initialize(options = Travis::Client::ORG_URI)
15
- @headers = {}
16
- @cache = {}
15
+ @headers = {}
16
+ @cache = {}
17
+ @instruments = []
18
+ @config = nil
17
19
 
18
20
  options = { :uri => options } unless options.respond_to? :each_pair
19
21
  options.each_pair { |key, value| public_send("#{key}=", value) }
@@ -29,7 +31,7 @@ module Travis
29
31
  def uri=(uri)
30
32
  clear_cache!
31
33
  self.connection = Faraday.new(:url => uri, :ssl => SSL_OPTIONS) do |faraday|
32
- faraday.request :json
34
+ faraday.request :url_encoded
33
35
  faraday.response :json
34
36
  faraday.response :follow_redirects
35
37
  faraday.response :raise_error
@@ -47,6 +49,7 @@ module Travis
47
49
  def connection=(connection)
48
50
  clear_cache!
49
51
  connection.headers.merge! headers
52
+ @config = nil
50
53
  @connection = connection
51
54
  @headers = connection.headers
52
55
  end
@@ -84,12 +87,22 @@ module Travis
84
87
  end
85
88
  end
86
89
 
90
+ def reset(entity)
91
+ entity.attributes.clear
92
+ entity
93
+ end
94
+
87
95
  def reload(entity)
96
+ reset(entity)
88
97
  result = fetch_one(entity.class, entity.id)
89
98
  entity.update_attributes(result.attributes) if result.attributes != entity.attributes
90
99
  result
91
100
  end
92
101
 
102
+ def config
103
+ @config ||= get_raw('/config')['config']
104
+ end
105
+
93
106
  def get(*args)
94
107
  result = {}
95
108
  get_raw(*args).each_pair do |key, value|
@@ -104,13 +117,19 @@ module Travis
104
117
  end
105
118
 
106
119
  def get_raw(*args)
107
- connection.get(*args).body
108
- rescue Faraday::Error::ClientError => e
109
- handle_error(e)
120
+ raw(:get, *args)
110
121
  end
111
122
 
112
123
  def post_raw(*args)
113
- connection.post(*args).body
124
+ raw(:post, *args)
125
+ end
126
+
127
+ def put_raw(*args)
128
+ raw(:put, *args)
129
+ end
130
+
131
+ def raw(verb, *args)
132
+ instrumented(verb.to_s.upcase, *args) { connection.public_send(verb, *args).body }
114
133
  rescue Faraday::Error::ClientError => e
115
134
  handle_error(e)
116
135
  end
@@ -135,8 +154,21 @@ module Travis
135
154
  self
136
155
  end
137
156
 
157
+ def instrument(&block)
158
+ instruments << block
159
+ end
160
+
138
161
  private
139
162
 
163
+ def instrumented(name, *args)
164
+ name = [name, *args.map(&:inspect)].join(" ") if args.any?
165
+ result = nil
166
+ chain = instruments + [proc { |n,l| result = yield }]
167
+ lift = proc { chain.shift.call(name, lift) }
168
+ lift.call
169
+ result
170
+ end
171
+
140
172
  def create_entity(type, data)
141
173
  id = Integer(data.fetch('id'))
142
174
  entity = cached(type, :id, id) { type.new(self, id) }
@@ -0,0 +1,85 @@
1
+ require 'travis/client'
2
+
3
+ module Travis
4
+ module Client
5
+ module States
6
+ STATES = %w[created queued started passed failed errored canceled]
7
+
8
+ def pending?
9
+ check_state
10
+ %w[created started queued].include? state
11
+ end
12
+
13
+ def started?
14
+ check_state
15
+ state != 'created' and state != 'queued'
16
+ end
17
+
18
+ def queued?
19
+ check_state
20
+ state != 'created'
21
+ end
22
+
23
+ def finished?
24
+ not pending?
25
+ end
26
+
27
+ def passed?
28
+ check_state
29
+ state == 'passed'
30
+ end
31
+
32
+ def errored?
33
+ check_state
34
+ state == 'errored'
35
+ end
36
+
37
+ def failed?
38
+ check_state
39
+ state == 'failed'
40
+ end
41
+
42
+ def canceled?
43
+ check_state
44
+ state == 'canceled'
45
+ end
46
+
47
+ def unsuccessful?
48
+ errored? or failed? or canceled?
49
+ end
50
+
51
+ def created?
52
+ check_state
53
+ !!state
54
+ end
55
+
56
+ def color
57
+ pending? ? 'yellow' : passed? ? 'green' : 'red'
58
+ end
59
+
60
+ def yellow?
61
+ color == 'yellow'
62
+ end
63
+
64
+ def green?
65
+ color == 'green'
66
+ end
67
+
68
+ def red?
69
+ color == 'red'
70
+ end
71
+
72
+ def running?
73
+ state == 'started'
74
+ end
75
+
76
+ alias successful? passed?
77
+
78
+ private
79
+
80
+ def check_state
81
+ raise Error, "unknown state %p for %p" % [state, self] unless STATES.include? state
82
+ end
83
+ end
84
+ end
85
+ end
@@ -3,6 +3,7 @@ require 'travis/client'
3
3
  module Travis
4
4
  module Client
5
5
  class User < Entity
6
+ # @!parse attr_reader :login, :name, :email, :gravatar_id, :locale, :is_syncing, :synced_at, :correct_scopes
6
7
  attributes :login, :name, :email, :gravatar_id, :locale, :is_syncing, :synced_at, :correct_scopes
7
8
  inspect_info :login
8
9
 
@@ -13,6 +14,11 @@ module Travis
13
14
  set_attribute(:synced_at, time(time))
14
15
  end
15
16
 
17
+ def sync
18
+ session.post_raw('/users/sync')
19
+ reload
20
+ end
21
+
16
22
  alias syncing? is_syncing
17
23
  alias correct_scopes? correct_scopes
18
24
  end
data/lib/travis/client.rb CHANGED
@@ -1,10 +1,15 @@
1
1
  require 'backports/1.9.3' if RUBY_VERSION < '1.9.3'
2
2
  require 'travis/client/error'
3
+ require 'travis/client/states'
3
4
  require 'travis/client/methods'
4
5
  require 'travis/client/session'
5
6
  require 'travis/client/entity'
6
7
  require 'travis/client/user'
7
8
  require 'travis/client/repository'
9
+ require 'travis/client/build'
10
+ require 'travis/client/artifact'
11
+ require 'travis/client/commit'
12
+ require 'travis/client/job'
8
13
  require 'travis/client/namespace'
9
14
 
10
15
  module Travis
@@ -0,0 +1,38 @@
1
+ require 'time'
2
+
3
+ module Travis
4
+ module Tools
5
+ class Formatter
6
+ DAY = 24 * 60 * 60
7
+ TIME_FORMAT = "%Y-%m-%d %H:%M:%S"
8
+ CONFIG_KEYS = ['rvm', 'gemfile', 'env', 'jdk', 'otp_release', 'php', 'node_js', 'perl', 'python', 'scala', 'compiler']
9
+
10
+ def duration(seconds, suffix = nil)
11
+ return "none" if seconds.nil?
12
+ seconds = (Time.now - seconds).to_i if seconds.is_a? Time
13
+ output = []
14
+ minutes, seconds = seconds.divmod(60)
15
+ hours, minutes = minutes.divmod(60)
16
+ output << "#{hours } hrs" if hours > 0
17
+ output << "#{minutes} min" if minutes > 0
18
+ output << "#{seconds} sec" if seconds > 0 or output.empty?
19
+ output << suffix if suffix
20
+ output.join(" ")
21
+ end
22
+
23
+ def time(time)
24
+ return "not yet" if time.nil? # or time > Time.now
25
+ #return duration(time, "ago") if Time.now - time < DAY
26
+ time.localtime.strftime(TIME_FORMAT)
27
+ end
28
+
29
+ def job_config(config)
30
+ output = []
31
+ config.each_pair do |key, value|
32
+ output << "#{key}: #{value}" if CONFIG_KEYS.include? key
33
+ end
34
+ output.join(", ")
35
+ end
36
+ end
37
+ end
38
+ end
@@ -1,3 +1,3 @@
1
1
  module Travis
2
- VERSION = '1.0.3'
2
+ VERSION = '1.1.0'
3
3
  end
@@ -18,6 +18,21 @@ describe Travis::CLI::Endpoint do
18
18
 
19
19
  example "cat foo | travis encrypt" do
20
20
  run_cli('encrypt') { |i| i.puts('foo') }
21
- stdout.should match(/^".{60,}"\n$/)
21
+ stdout.should match(/\A".{60,}"\n\Z/)
22
+ end
23
+
24
+ example "cat foo\\nbar | travis encrypt -s" do
25
+ run_cli('encrypt', '-s') { |i| i.puts("foo\nbar") }
26
+ stdout.should match(/\A(".{60,}"\n){2}\Z/)
27
+ end
28
+
29
+ example "cat foo\\nbar | travis encrypt" do
30
+ run_cli('encrypt') { |i| i.puts("foo\nbar") }
31
+ stdout.should match(/\A".{60,}"\n\Z/)
32
+ end
33
+
34
+ example "travis encrypt rails/rails foo" do
35
+ run_cli('encrypt', 'rails/rails', 'foo').should be_success
36
+ stderr.should match(/WARNING/)
22
37
  end
23
38
  end
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+
3
+ describe Travis::CLI::History do
4
+ example 'travis history' do
5
+ run_cli('history').should be_success
6
+ stdout.should be == "#6180 failed: master Associaton -> Association\n"
7
+ end
8
+
9
+ example 'travis history -a 6180' do
10
+ run_cli('history', '-a', '6180').should be_success
11
+ stdout.should be == ''
12
+ end
13
+
14
+ example 'travis history -b master' do
15
+ run_cli('history', '-b', 'master').should be_success
16
+ stdout.should be == "#6180 failed: master Associaton -> Association\n"
17
+ end
18
+
19
+ example 'travis history -b not-master' do
20
+ run_cli('history', '-b', 'not-master').should be_success
21
+ stdout.should be_empty
22
+ end
23
+
24
+ example 'travis history -p 5' do
25
+ run_cli('history', '-p', '5').should be_success
26
+ stdout.should be_empty
27
+ end
28
+ end
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+
3
+ describe Travis::CLI::Logs do
4
+ example 'logs 6180.1' do
5
+ run_cli('logs', '6180.1', '-E').should be_success
6
+ stdout.should be == "$ export GEM=railties\n"
7
+ end
8
+ end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ describe Travis::CLI::Open do
4
+ example 'travis open -p' do
5
+ run_cli('open', '-p').should be_success
6
+ stdout.should be == "https://travis-ci.org/travis-ci/travis\n"
7
+ end
8
+
9
+ example 'travis open 6180 -p' do
10
+ run_cli('open', '6180', '-p').should be_success
11
+ stdout.should be == "https://travis-ci.org/travis-ci/travis/builds/4125095\n"
12
+ end
13
+
14
+ example 'travis open 6180.1 -p' do
15
+ run_cli('open', '6180.1', '-p').should be_success
16
+ stdout.should be == "https://travis-ci.org/travis-ci/travis/jobs/4125096\n"
17
+ end
18
+
19
+ example 'travis open -pg' do
20
+ run_cli('open', '-pg').should be_success
21
+ stdout.should be == "https://github.com/travis-ci/travis\n"
22
+ end
23
+
24
+ example 'travis open 6180 -pg' do
25
+ run_cli('open', '6180', '-pg').should be_success
26
+ stdout.should be == "https://github.com/rails/rails/compare/6581d798e830...a0265b98f16c\n"
27
+ end
28
+
29
+ example 'travis open 6180.1 -pg' do
30
+ run_cli('open', '6180.1', '-pg').should be_success
31
+ stdout.should be == "https://github.com/rails/rails/compare/6581d798e830...a0265b98f16c\n"
32
+ end
33
+ end