travis-artifacts 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/.rspec +1 -0
- data/.travis.yml +6 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +52 -0
- data/bin/travis-artifacts +8 -0
- data/lib/travis/artifacts.rb +25 -0
- data/lib/travis/artifacts/artifact.rb +17 -0
- data/lib/travis/artifacts/cli.rb +93 -0
- data/lib/travis/artifacts/logger.rb +17 -0
- data/lib/travis/artifacts/path.rb +15 -0
- data/lib/travis/artifacts/test.rb +27 -0
- data/lib/travis/artifacts/uploader.rb +109 -0
- data/lib/travis/client.rb +47 -0
- data/lib/travis/client_spec.rb +4 -0
- data/script/console +11 -0
- data/spec/fixtures/files/foo/bar/baz.txt +0 -0
- data/spec/fixtures/files/logs/bar.log +0 -0
- data/spec/fixtures/files/logs/foo.log +0 -0
- data/spec/fixtures/files/output.txt +0 -0
- data/spec/spec_helper.rb +9 -0
- data/spec/travis/artifacts/artifact_spec.rb +6 -0
- data/spec/travis/artifacts/cli_spec.rb +65 -0
- data/spec/travis/artifacts/uploader_spec.rb +137 -0
- data/travis-artifacts.gemspec +22 -0
- metadata +142 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
travis-artifacts (0.0.1)
|
5
|
+
faraday
|
6
|
+
faraday_middleware
|
7
|
+
fog
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: http://rubygems.org/
|
11
|
+
specs:
|
12
|
+
builder (3.1.4)
|
13
|
+
diff-lcs (1.1.3)
|
14
|
+
excon (0.16.8)
|
15
|
+
faraday (0.8.4)
|
16
|
+
multipart-post (~> 1.1)
|
17
|
+
faraday_middleware (0.9.0)
|
18
|
+
faraday (>= 0.7.4, < 0.9)
|
19
|
+
fog (1.7.0)
|
20
|
+
builder
|
21
|
+
excon (~> 0.14)
|
22
|
+
formatador (~> 0.2.0)
|
23
|
+
mime-types
|
24
|
+
multi_json (~> 1.0)
|
25
|
+
net-scp (~> 1.0.4)
|
26
|
+
net-ssh (>= 2.1.3)
|
27
|
+
nokogiri (~> 1.5.0)
|
28
|
+
ruby-hmac
|
29
|
+
formatador (0.2.4)
|
30
|
+
mime-types (1.19)
|
31
|
+
multi_json (1.3.7)
|
32
|
+
multipart-post (1.1.5)
|
33
|
+
net-scp (1.0.4)
|
34
|
+
net-ssh (>= 1.99.1)
|
35
|
+
net-ssh (2.6.1)
|
36
|
+
nokogiri (1.5.5)
|
37
|
+
rspec (2.12.0)
|
38
|
+
rspec-core (~> 2.12.0)
|
39
|
+
rspec-expectations (~> 2.12.0)
|
40
|
+
rspec-mocks (~> 2.12.0)
|
41
|
+
rspec-core (2.12.0)
|
42
|
+
rspec-expectations (2.12.0)
|
43
|
+
diff-lcs (~> 1.1.3)
|
44
|
+
rspec-mocks (2.12.0)
|
45
|
+
ruby-hmac (0.4.0)
|
46
|
+
|
47
|
+
PLATFORMS
|
48
|
+
ruby
|
49
|
+
|
50
|
+
DEPENDENCIES
|
51
|
+
rspec
|
52
|
+
travis-artifacts!
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Travis
|
2
|
+
autoload :Client, 'travis/client'
|
3
|
+
|
4
|
+
module Artifacts
|
5
|
+
autoload :Cli, 'travis/artifacts/cli'
|
6
|
+
autoload :Uploader, 'travis/artifacts/uploader'
|
7
|
+
autoload :Artifact, 'travis/artifacts/artifact'
|
8
|
+
autoload :Path, 'travis/artifacts/path'
|
9
|
+
autoload :Logger, 'travis/artifacts/logger'
|
10
|
+
autoload :Test, 'travis/artifacts/test'
|
11
|
+
autoload :ConfigParser, 'travis/artifacts/config_parser'
|
12
|
+
autoload :Prefix, 'travis/artifacts/prefix'
|
13
|
+
|
14
|
+
def self.aws_config
|
15
|
+
{ :aws_access_key_id => ENV['ARTIFACTS_AWS_ACCESS_KEY_ID'],
|
16
|
+
:aws_secret_access_key => ENV['ARTIFACTS_AWS_SECRET_ACCESS_KEY'],
|
17
|
+
:region => ENV['ARTIFACTS_AWS_REGION'] || 'us-east-1'
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.bucket_name
|
22
|
+
ENV['ARTIFACTS_S3_BUCKET']
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'mime/types'
|
2
|
+
|
3
|
+
module Travis::Artifacts
|
4
|
+
class Artifact < Struct.new(:source, :destination)
|
5
|
+
def content_type
|
6
|
+
if MIME::Types.type_for(source).any?
|
7
|
+
MIME::Types.type_for(source).first.content_type
|
8
|
+
else
|
9
|
+
'application/octet-stream'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def read
|
14
|
+
File.read(source)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
module Travis::Artifacts
|
4
|
+
class Cli
|
5
|
+
attr_reader :options, :argv, :paths
|
6
|
+
attr_accessor :command, :client
|
7
|
+
|
8
|
+
VALID_COMMANDS = ['upload']
|
9
|
+
|
10
|
+
def initialize(argv = nil)
|
11
|
+
@argv = argv || ARGV
|
12
|
+
@options = { :paths => [] }
|
13
|
+
@paths = []
|
14
|
+
@client = Travis::Client.new
|
15
|
+
end
|
16
|
+
|
17
|
+
# I would like to use option parser for now, to keep
|
18
|
+
# the code super simple and without too much external
|
19
|
+
# dependencies, if it grows too much, I may change it
|
20
|
+
def start
|
21
|
+
parse!
|
22
|
+
create_paths
|
23
|
+
|
24
|
+
execute_command
|
25
|
+
end
|
26
|
+
|
27
|
+
def root
|
28
|
+
options[:root] || Dir.pwd
|
29
|
+
end
|
30
|
+
|
31
|
+
def upload
|
32
|
+
Uploader.new(paths, options[:target_path]).upload
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def execute_command
|
38
|
+
if VALID_COMMANDS.include? command
|
39
|
+
send(command)
|
40
|
+
else
|
41
|
+
STDERR.puts 'Could not find command'
|
42
|
+
exit 1
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def fetch_paths
|
47
|
+
options[:paths]
|
48
|
+
end
|
49
|
+
|
50
|
+
def create_paths
|
51
|
+
fetch_paths.each do |path|
|
52
|
+
from, to = path.split(':')
|
53
|
+
paths << Path.new(from, to, root)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def parse!
|
58
|
+
self.command = argv[0]
|
59
|
+
parser.parse! argv
|
60
|
+
end
|
61
|
+
|
62
|
+
def parser
|
63
|
+
@opt_parser ||= begin
|
64
|
+
options = self.options
|
65
|
+
|
66
|
+
OptionParser.new do |opt|
|
67
|
+
opt.banner = 'Usage: travis-uploader COMMAND [OPTIONS]'
|
68
|
+
opt.separator ''
|
69
|
+
opt.separator 'Commands'
|
70
|
+
opt.separator ' upload: upload files to server'
|
71
|
+
opt.separator ''
|
72
|
+
opt.separator 'Options'
|
73
|
+
|
74
|
+
opt.on('--path PATH','path(s) to upload to a server') do |path|
|
75
|
+
options[:paths] << path
|
76
|
+
end
|
77
|
+
|
78
|
+
opt.on('--target-path TARGET_PATH', 'path to upload files to') do |target_path|
|
79
|
+
options[:target_path] = target_path
|
80
|
+
end
|
81
|
+
|
82
|
+
opt.on('--root ROOT', 'root directory for relative paths') do |root|
|
83
|
+
options[:root] = root
|
84
|
+
end
|
85
|
+
|
86
|
+
opt.on('-h','--help','help') do
|
87
|
+
puts @opt_parser
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module Travis::Artifacts
|
4
|
+
module Logger
|
5
|
+
class << self
|
6
|
+
attr_accessor :output
|
7
|
+
end
|
8
|
+
self.output = STDOUT
|
9
|
+
|
10
|
+
|
11
|
+
attr_reader :logger
|
12
|
+
|
13
|
+
def logger
|
14
|
+
@logger ||= ::Logger.new(Travis::Artifacts::Logger.output)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Travis::Artifacts
|
2
|
+
class Test
|
3
|
+
def success?
|
4
|
+
ENV['TRAVIS_TEST_RESULT'].to_i == 0
|
5
|
+
end
|
6
|
+
|
7
|
+
def failure?
|
8
|
+
!success?
|
9
|
+
end
|
10
|
+
|
11
|
+
def build_number
|
12
|
+
ENV['TRAVIS_BUILD_NUMBER']
|
13
|
+
end
|
14
|
+
|
15
|
+
def build_id
|
16
|
+
ENV['TRAVIS_BUILD_ID']
|
17
|
+
end
|
18
|
+
|
19
|
+
def job_number
|
20
|
+
ENV['TRAVIS_JOB_NUMBER']
|
21
|
+
end
|
22
|
+
|
23
|
+
def job_id
|
24
|
+
ENV['TRAVIS_JOB_ID']
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'find'
|
2
|
+
require 'fog'
|
3
|
+
|
4
|
+
module Travis::Artifacts
|
5
|
+
class Uploader
|
6
|
+
include Travis::Artifacts::Logger
|
7
|
+
|
8
|
+
attr_reader :paths, :target_path
|
9
|
+
|
10
|
+
def initialize(paths, target_path = nil)
|
11
|
+
@paths = paths
|
12
|
+
@test = Test.new
|
13
|
+
@target_path = target_path || "artifacts/#{@test.build_number}/#{@test.job_number}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def upload
|
17
|
+
files.each do |file|
|
18
|
+
upload_file(file)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def files
|
23
|
+
files = []
|
24
|
+
|
25
|
+
paths.each do |artifact_path|
|
26
|
+
to = artifact_path.to
|
27
|
+
from = artifact_path.from
|
28
|
+
root = artifact_path.root
|
29
|
+
|
30
|
+
if artifact_path.directory?
|
31
|
+
root = File.join(root, from)
|
32
|
+
root << '/' unless root =~ /\/$/
|
33
|
+
end
|
34
|
+
|
35
|
+
Find.find(artifact_path.fullpath) do |path|
|
36
|
+
next unless File.file? path
|
37
|
+
|
38
|
+
relative = path.sub(/#{root}\/?/, '')
|
39
|
+
|
40
|
+
destination = if to
|
41
|
+
artifact_path.directory? ? File.join(to, relative) : to
|
42
|
+
else
|
43
|
+
relative
|
44
|
+
end
|
45
|
+
|
46
|
+
files << Artifact.new(path, destination)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
files
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
def upload_file(file)
|
55
|
+
retries = 0
|
56
|
+
|
57
|
+
begin
|
58
|
+
_upload(file)
|
59
|
+
rescue StandardError => e
|
60
|
+
if retries < 2
|
61
|
+
logger.info "Attempt to upload failed, retrying"
|
62
|
+
retries += 1
|
63
|
+
retry
|
64
|
+
else
|
65
|
+
if e.respond_to?(:request)
|
66
|
+
# we don't want to display sensitive data, make the error message simpler
|
67
|
+
request = e.request
|
68
|
+
response = e.response
|
69
|
+
raise e
|
70
|
+
raise e.class.new("Expected(#{request[:expects].inspect}) <=> Actual(#{response.status})")
|
71
|
+
else
|
72
|
+
raise
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def _upload(file)
|
79
|
+
destination = File.join(target_path, file.destination)
|
80
|
+
|
81
|
+
logger.info "Uploading file #{file.source} to #{destination}"
|
82
|
+
|
83
|
+
bucket.files.create({
|
84
|
+
:key => destination,
|
85
|
+
:public => true,
|
86
|
+
:body => file.read,
|
87
|
+
:content_type => file.content_type,
|
88
|
+
:metadata => { "Cache-Control" => 'public, max-age=315360000'}
|
89
|
+
})
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def job_id
|
95
|
+
test.job_id
|
96
|
+
end
|
97
|
+
|
98
|
+
def bucket
|
99
|
+
@bucket ||= s3.directories.get(Travis::Artifacts.bucket_name)
|
100
|
+
end
|
101
|
+
|
102
|
+
def s3
|
103
|
+
@s3 ||= begin
|
104
|
+
config = { :provider => 'AWS' }.merge Travis::Artifacts.aws_config
|
105
|
+
Fog::Storage.new(config)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'faraday_middleware'
|
3
|
+
|
4
|
+
module Travis
|
5
|
+
class Client
|
6
|
+
attr_reader :connection
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@connection = create_connection
|
10
|
+
end
|
11
|
+
|
12
|
+
# TODO: this needs to be fixed before going too far, returning body without
|
13
|
+
# ability to inspect response or returning nil on any failure are not
|
14
|
+
# the best ways to deal with API client
|
15
|
+
def fetch_config(job_id)
|
16
|
+
response = get("/jobs/#{job_id}")
|
17
|
+
if (200..299).include?(response.status)
|
18
|
+
response.body['job']['config']
|
19
|
+
else
|
20
|
+
# TODO: I should probably raise here
|
21
|
+
nil
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
http_methods = [:get, :post, :put, :patch, :delete, :head]
|
28
|
+
http_methods.each do |method|
|
29
|
+
|
30
|
+
define_method(method) do |*args, &block|
|
31
|
+
path, params, headers = *args
|
32
|
+
connection.send(method, path, params, headers, &block)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def create_connection
|
37
|
+
# TODO: support for pro
|
38
|
+
Faraday.new(:url => 'https://api.travis-ci.org') do |faraday|
|
39
|
+
faraday.response :json, :content_type => /\bjson$/
|
40
|
+
|
41
|
+
faraday.adapter :net_http
|
42
|
+
end.tap do |connection|
|
43
|
+
connection.headers['Accept'] = 'application/vnd.travis-ci.2+json'
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/script/console
ADDED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Travis::Artifacts
|
4
|
+
describe Cli do
|
5
|
+
let(:cli) { described_class.new(argv) }
|
6
|
+
|
7
|
+
|
8
|
+
context 'with multiple paths' do
|
9
|
+
before { cli.stub(:upload) }
|
10
|
+
let(:argv) { ['upload', '--path', 'path/to/foo', '--path', 'path/to/bar:bar'] }
|
11
|
+
|
12
|
+
before { cli.start }
|
13
|
+
|
14
|
+
it 'parses them' do
|
15
|
+
cli.options[:paths].should == ['path/to/foo', 'path/to/bar:bar']
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'adds paths to paths array' do
|
19
|
+
cli.paths.map { |p| [p.from, p.to] }.should == [['path/to/foo', nil], ['path/to/bar', 'bar']]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe 'upload' do
|
24
|
+
let(:argv) { ['upload', '--path', 'foo', '--target-path', 'bar'] }
|
25
|
+
it 'calls Uploader with given paths and target_path' do
|
26
|
+
uploader = mock('uploader')
|
27
|
+
Uploader.should_receive(:new).with([Path.new('foo', nil, Dir.pwd)], 'bar').and_return(uploader)
|
28
|
+
uploader.should_receive(:upload)
|
29
|
+
|
30
|
+
cli.start
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe '#root' do
|
35
|
+
before { cli.stub(:upload) }
|
36
|
+
before { cli.start }
|
37
|
+
|
38
|
+
context 'with root passed as an argument' do
|
39
|
+
let(:argv) { ['upload', '--root', 'foo'] }
|
40
|
+
it 'returns passed value' do
|
41
|
+
cli.root.should == 'foo'
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'without root passed as an argument' do
|
46
|
+
before { cli.stub(:upload) }
|
47
|
+
let(:argv) { ['upload'] }
|
48
|
+
it 'returns cwd' do
|
49
|
+
cli.root.should == Dir.pwd
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'with a command' do
|
55
|
+
before { cli.stub(:upload) }
|
56
|
+
let(:argv) { ['upload'] }
|
57
|
+
|
58
|
+
before { cli.start }
|
59
|
+
|
60
|
+
it 'saves it' do
|
61
|
+
cli.command.should == 'upload'
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Travis::Artifacts
|
4
|
+
describe Uploader do
|
5
|
+
let(:uploader) { Uploader.new(paths, 'artifacts/1') }
|
6
|
+
let(:paths) { [] }
|
7
|
+
|
8
|
+
context 'without given target_path' do
|
9
|
+
let(:uploader) { Uploader.new(paths, nil) }
|
10
|
+
|
11
|
+
it 'sets a default' do
|
12
|
+
test = mock('test', :job_number => "10.1", :build_number => "10")
|
13
|
+
Test.stub(:new => test)
|
14
|
+
|
15
|
+
uploader.target_path.should == 'artifacts/10/10.1'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#upload_file' do
|
20
|
+
it 'retries 3 times before giving up' do
|
21
|
+
file = Artifact.new('source/file.png', 'destination/file.png')
|
22
|
+
|
23
|
+
uploader.should_receive(:_upload).exactly(3).times.and_raise(StandardError)
|
24
|
+
|
25
|
+
expect {
|
26
|
+
uploader.upload_file(file)
|
27
|
+
}.to raise_error(StandardError)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'simplifies excon error to not show request data' do
|
31
|
+
file = Artifact.new('source/file.png', 'destination/file.png')
|
32
|
+
|
33
|
+
request = { :expects => 200 }
|
34
|
+
response = mock('response', :status => 500)
|
35
|
+
error_class = Class.new(Excon::Errors::HTTPStatusError)
|
36
|
+
my_error = error_class.new('message', request, response)
|
37
|
+
uploader.should_receive(:_upload).exactly(4).times.and_raise(my_error)
|
38
|
+
|
39
|
+
expect {
|
40
|
+
uploader.upload_file(file)
|
41
|
+
}.to raise_error { |e|
|
42
|
+
e.message.should == 'Expected(200) <=> Actual(500)'
|
43
|
+
}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe '#upload' do
|
48
|
+
it 'uploads file to S3' do
|
49
|
+
files = [
|
50
|
+
Artifact.new('source/path.png', 'destination/path.png')
|
51
|
+
]
|
52
|
+
files[0].stub(:read => 'contents')
|
53
|
+
uploader.stub(:files => files)
|
54
|
+
|
55
|
+
bucket = mock('bucker')
|
56
|
+
bucket_files = mock('bucket_files')
|
57
|
+
uploader.stub(:bucket => bucket)
|
58
|
+
bucket.stub(:files => bucket_files)
|
59
|
+
|
60
|
+
bucket_files.should_receive(:create).with({
|
61
|
+
:key => 'artifacts/1/destination/path.png',
|
62
|
+
:public => true,
|
63
|
+
:body => 'contents',
|
64
|
+
:content_type => 'image/png',
|
65
|
+
:metadata => {'Cache-Control' => 'public, max-age=315360000'}
|
66
|
+
|
67
|
+
})
|
68
|
+
|
69
|
+
uploader.upload
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe '#files' do
|
74
|
+
let(:root) { File.expand_path('../../../fixtures', __FILE__) }
|
75
|
+
|
76
|
+
context 'with nested files from root' do
|
77
|
+
let(:paths) { [Path.new('.', nil, root)] }
|
78
|
+
|
79
|
+
it 'resolves paths into files to upload' do
|
80
|
+
files = [
|
81
|
+
'files/foo/bar/baz.txt',
|
82
|
+
'files/logs/bar.log',
|
83
|
+
'files/logs/foo.log',
|
84
|
+
'files/output.txt'
|
85
|
+
]
|
86
|
+
files.map! { |file| Artifact.new(File.join(root, '.', file), file) }
|
87
|
+
|
88
|
+
uploader.files.sort_by { |o| o.source }.should == files.sort_by { |o| o.source }
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context 'with destination path and directory' do
|
93
|
+
let(:paths) { [Path.new('files/logs', 'logs', root)] }
|
94
|
+
|
95
|
+
it 'resolves paths into files to upload' do
|
96
|
+
files = [
|
97
|
+
'logs/bar.log',
|
98
|
+
'logs/foo.log'
|
99
|
+
]
|
100
|
+
files.map! { |file| Artifact.new(File.join(root, 'files', file), "#{file}") }
|
101
|
+
|
102
|
+
uploader.files.sort_by { |o| o.source }.should == files.sort_by { |o| o.source }
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
context 'with individual files' do
|
107
|
+
let(:paths) {
|
108
|
+
[Path.new('files/logs/bar.log', nil, root),
|
109
|
+
Path.new('files/logs/foo.log', 'new/location/foo.log', root)]
|
110
|
+
|
111
|
+
}
|
112
|
+
|
113
|
+
it 'resolves paths into files to upload' do
|
114
|
+
file1 = 'files/logs/bar.log'
|
115
|
+
file2 = 'files/logs/foo.log'
|
116
|
+
|
117
|
+
files = [
|
118
|
+
Artifact.new(File.join(root, file1), 'files/logs/bar.log'),
|
119
|
+
Artifact.new(File.join(root, file2), 'new/location/foo.log')
|
120
|
+
]
|
121
|
+
|
122
|
+
uploader.files.should == files
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
context 'with multilevel directory and to' do
|
127
|
+
let(:paths) {
|
128
|
+
[Path.new('files/foo', 'foo1', root)]
|
129
|
+
}
|
130
|
+
|
131
|
+
it 'resolves paths into files to upload' do
|
132
|
+
uploader.files.should == [Artifact.new(File.join(root, 'files/foo/bar/baz.txt'), 'foo1/bar/baz.txt')]
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'travis-artifacts'
|
3
|
+
s.version = '0.0.1'
|
4
|
+
|
5
|
+
s.description = 'Travis build artifacts tools'
|
6
|
+
s.summary = s.description
|
7
|
+
|
8
|
+
s.homepage = 'https://github.com/travis-ci/travis-artifacts'
|
9
|
+
s.authors = ['admin@travis-ci.org']
|
10
|
+
s.email = 'admin@travis-ci.org'
|
11
|
+
|
12
|
+
s.add_dependency 'fog'
|
13
|
+
s.add_dependency 'faraday'
|
14
|
+
s.add_dependency 'faraday_middleware'
|
15
|
+
|
16
|
+
s.add_development_dependency 'rspec'
|
17
|
+
|
18
|
+
s.files = `git ls-files`.split($/)
|
19
|
+
s.executables = s.files.grep(/^bin/).map{|f| File.basename(f) }
|
20
|
+
s.test_files = s.files.grep(/^spec/)
|
21
|
+
s.require_paths = ['lib']
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: travis-artifacts
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- admin@travis-ci.org
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-11-30 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: fog
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: faraday
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: faraday_middleware
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: rspec
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
description: Travis build artifacts tools
|
79
|
+
email: admin@travis-ci.org
|
80
|
+
executables:
|
81
|
+
- travis-artifacts
|
82
|
+
extensions: []
|
83
|
+
extra_rdoc_files: []
|
84
|
+
files:
|
85
|
+
- .gitignore
|
86
|
+
- .rspec
|
87
|
+
- .travis.yml
|
88
|
+
- Gemfile
|
89
|
+
- Gemfile.lock
|
90
|
+
- bin/travis-artifacts
|
91
|
+
- lib/travis/artifacts.rb
|
92
|
+
- lib/travis/artifacts/artifact.rb
|
93
|
+
- lib/travis/artifacts/cli.rb
|
94
|
+
- lib/travis/artifacts/logger.rb
|
95
|
+
- lib/travis/artifacts/path.rb
|
96
|
+
- lib/travis/artifacts/test.rb
|
97
|
+
- lib/travis/artifacts/uploader.rb
|
98
|
+
- lib/travis/client.rb
|
99
|
+
- lib/travis/client_spec.rb
|
100
|
+
- script/console
|
101
|
+
- spec/fixtures/files/foo/bar/baz.txt
|
102
|
+
- spec/fixtures/files/logs/bar.log
|
103
|
+
- spec/fixtures/files/logs/foo.log
|
104
|
+
- spec/fixtures/files/output.txt
|
105
|
+
- spec/spec_helper.rb
|
106
|
+
- spec/travis/artifacts/artifact_spec.rb
|
107
|
+
- spec/travis/artifacts/cli_spec.rb
|
108
|
+
- spec/travis/artifacts/uploader_spec.rb
|
109
|
+
- travis-artifacts.gemspec
|
110
|
+
homepage: https://github.com/travis-ci/travis-artifacts
|
111
|
+
licenses: []
|
112
|
+
post_install_message:
|
113
|
+
rdoc_options: []
|
114
|
+
require_paths:
|
115
|
+
- lib
|
116
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
117
|
+
none: false
|
118
|
+
requirements:
|
119
|
+
- - ! '>='
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: '0'
|
122
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
123
|
+
none: false
|
124
|
+
requirements:
|
125
|
+
- - ! '>='
|
126
|
+
- !ruby/object:Gem::Version
|
127
|
+
version: '0'
|
128
|
+
requirements: []
|
129
|
+
rubyforge_project:
|
130
|
+
rubygems_version: 1.8.24
|
131
|
+
signing_key:
|
132
|
+
specification_version: 3
|
133
|
+
summary: Travis build artifacts tools
|
134
|
+
test_files:
|
135
|
+
- spec/fixtures/files/foo/bar/baz.txt
|
136
|
+
- spec/fixtures/files/logs/bar.log
|
137
|
+
- spec/fixtures/files/logs/foo.log
|
138
|
+
- spec/fixtures/files/output.txt
|
139
|
+
- spec/spec_helper.rb
|
140
|
+
- spec/travis/artifacts/artifact_spec.rb
|
141
|
+
- spec/travis/artifacts/cli_spec.rb
|
142
|
+
- spec/travis/artifacts/uploader_spec.rb
|