travis_dpl_test 2.0.3.beta.4.ror
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 +7 -0
- data/CHANGELOG.md +172 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/CONTRIBUTING.md +392 -0
- data/Gemfile +32 -0
- data/Gemfile.lock +611 -0
- data/LICENSE +19 -0
- data/README.md +2744 -0
- data/Rakefile +210 -0
- data/bin/dpl +11 -0
- data/config/transliterate.yml +733 -0
- data/dpl.gemspec +23 -0
- data/lib/dpl/assets/atlas/install +19 -0
- data/lib/dpl/assets/convox/install +11 -0
- data/lib/dpl/assets/dpl/README.erb.md +138 -0
- data/lib/dpl/assets/dpl/git_ssh +8 -0
- data/lib/dpl/assets/git/detect_private_key +8 -0
- data/lib/dpl/assets/hephy/filter_log +3 -0
- data/lib/dpl/assets/pypi/install +4 -0
- data/lib/dpl/assets/scalingo/install +6 -0
- data/lib/dpl/cli.rb +100 -0
- data/lib/dpl/ctx/bash.rb +549 -0
- data/lib/dpl/ctx/test.rb +255 -0
- data/lib/dpl/ctx.rb +4 -0
- data/lib/dpl/helper/assets.rb +38 -0
- data/lib/dpl/helper/cmd.rb +169 -0
- data/lib/dpl/helper/config_file.rb +49 -0
- data/lib/dpl/helper/cookbook_site_streaming_uploader.rb +249 -0
- data/lib/dpl/helper/env.rb +92 -0
- data/lib/dpl/helper/github.rb +22 -0
- data/lib/dpl/helper/interpolate.rb +160 -0
- data/lib/dpl/helper/memoize.rb +23 -0
- data/lib/dpl/helper/squiggle.rb +24 -0
- data/lib/dpl/helper/transliterate.rb +13 -0
- data/lib/dpl/helper/wrap.rb +11 -0
- data/lib/dpl/helper/zip.rb +71 -0
- data/lib/dpl/provider/dsl.rb +410 -0
- data/lib/dpl/provider/examples.rb +132 -0
- data/lib/dpl/provider/status.rb +61 -0
- data/lib/dpl/provider.rb +651 -0
- data/lib/dpl/providers/anynines.rb +71 -0
- data/lib/dpl/providers/azure_web_apps.rb +63 -0
- data/lib/dpl/providers/bintray.rb +324 -0
- data/lib/dpl/providers/bluemixcloudfoundry.rb +98 -0
- data/lib/dpl/providers/boxfuse.rb +52 -0
- data/lib/dpl/providers/cargo.rb +32 -0
- data/lib/dpl/providers/chef_supermarket.rb +132 -0
- data/lib/dpl/providers/cloud66.rb +46 -0
- data/lib/dpl/providers/cloudfiles.rb +62 -0
- data/lib/dpl/providers/cloudformation.rb +281 -0
- data/lib/dpl/providers/cloudfoundry.rb +89 -0
- data/lib/dpl/providers/codedeploy.rb +190 -0
- data/lib/dpl/providers/convox.rb +130 -0
- data/lib/dpl/providers/datica.rb +64 -0
- data/lib/dpl/providers/ecr.rb +129 -0
- data/lib/dpl/providers/elasticbeanstalk.rb +207 -0
- data/lib/dpl/providers/engineyard.rb +113 -0
- data/lib/dpl/providers/firebase.rb +45 -0
- data/lib/dpl/providers/flynn.rb +35 -0
- data/lib/dpl/providers/gae.rb +78 -0
- data/lib/dpl/providers/gcs.rb +132 -0
- data/lib/dpl/providers/git_push.rb +273 -0
- data/lib/dpl/providers/gleis.rb +74 -0
- data/lib/dpl/providers/hackage.rb +53 -0
- data/lib/dpl/providers/hephy.rb +107 -0
- data/lib/dpl/providers/heroku/api.rb +123 -0
- data/lib/dpl/providers/heroku/git.rb +54 -0
- data/lib/dpl/providers/heroku.rb +111 -0
- data/lib/dpl/providers/lambda.rb +211 -0
- data/lib/dpl/providers/launchpad.rb +80 -0
- data/lib/dpl/providers/netlify.rb +38 -0
- data/lib/dpl/providers/npm.rb +130 -0
- data/lib/dpl/providers/nuget.rb +41 -0
- data/lib/dpl/providers/openshift.rb +52 -0
- data/lib/dpl/providers/opsworks.rb +146 -0
- data/lib/dpl/providers/packagecloud.rb_ +194 -0
- data/lib/dpl/providers/pages/api.rb +106 -0
- data/lib/dpl/providers/pages/git.rb +262 -0
- data/lib/dpl/providers/pages.rb +18 -0
- data/lib/dpl/providers/puppetforge.rb +50 -0
- data/lib/dpl/providers/pypi.rb +125 -0
- data/lib/dpl/providers/releases.rb +234 -0
- data/lib/dpl/providers/rubygems.rb +97 -0
- data/lib/dpl/providers/s3.rb +251 -0
- data/lib/dpl/providers/scalingo.rb +69 -0
- data/lib/dpl/providers/script.rb +32 -0
- data/lib/dpl/providers/snap.rb +68 -0
- data/lib/dpl/providers/surge.rb +59 -0
- data/lib/dpl/providers/testfairy.rb +101 -0
- data/lib/dpl/providers/transifex.rb +72 -0
- data/lib/dpl/providers.rb +48 -0
- data/lib/dpl/string_ext.rb +23 -0
- data/lib/dpl/support/aws_sdk_patch.rb +26 -0
- data/lib/dpl/support/gems.rb +73 -0
- data/lib/dpl/support/gstore_patch.rb +8 -0
- data/lib/dpl/support/version.rb +84 -0
- data/lib/dpl/version.rb +5 -0
- data/lib/dpl.rb +23 -0
- data/status.json +237 -0
- metadata +161 -0
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'uri'
|
|
4
|
+
|
|
5
|
+
# we want this, don't we?
|
|
6
|
+
Thread.abort_on_exception = true
|
|
7
|
+
|
|
8
|
+
module Dpl
|
|
9
|
+
module Providers
|
|
10
|
+
class S3 < Provider
|
|
11
|
+
register :s3
|
|
12
|
+
|
|
13
|
+
status :stable
|
|
14
|
+
|
|
15
|
+
full_name 'AWS S3'
|
|
16
|
+
|
|
17
|
+
description sq(<<-STR)
|
|
18
|
+
tbd
|
|
19
|
+
STR
|
|
20
|
+
|
|
21
|
+
gem 'aws-sdk-s3', '~> 1'
|
|
22
|
+
gem 'mime-types', '~> 3.4.1'
|
|
23
|
+
|
|
24
|
+
env :aws, :s3
|
|
25
|
+
config '~/.aws/credentials', '~/.aws/config', prefix: 'aws'
|
|
26
|
+
|
|
27
|
+
opt '--access_key_id ID', 'AWS access key id', required: true, secret: true
|
|
28
|
+
opt '--secret_access_key KEY', 'AWS secret key', required: true, secret: true
|
|
29
|
+
opt '--bucket BUCKET', 'S3 bucket', required: true
|
|
30
|
+
opt '--region REGION', 'S3 region', default: 'us-east-1'
|
|
31
|
+
opt '--endpoint URL', 'S3 endpoint'
|
|
32
|
+
opt '--upload_dir DIR', 'S3 directory to upload to'
|
|
33
|
+
opt '--local_dir DIR', 'Local directory to upload from', default: '.', example: '~/travis/build (absolute path) or ./build (relative path)'
|
|
34
|
+
opt '--glob GLOB', 'Files to upload', default: '**/*'
|
|
35
|
+
opt '--dot_match', 'Upload hidden files starting with a dot'
|
|
36
|
+
opt '--acl ACL', 'Access control for the uploaded objects', default: 'private', enum: %w[private public_read public_read_write authenticated_read bucket_owner_read bucket_owner_full_control]
|
|
37
|
+
opt '--detect_encoding', 'HTTP header Content-Encoding for files compressed with gzip and compress utilities'
|
|
38
|
+
opt '--cache_control STR', 'HTTP header Cache-Control to suggest that the browser cache the file', type: :array, default: 'no-cache', enum: [/^no-cache.*/, /^no-store.*/, /^max-age=\d+.*/, /^s-maxage=\d+.*/, /^no-transform/, /^public/, /^private/], note: 'accepts mapping values to globs', eg: 'public: *.css,*.js'
|
|
39
|
+
opt '--expires DATE', 'Date and time that the cached object expires', type: :array, format: /^"?\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} .+"?.*$/, note: 'accepts mapping values to globs', eg: '2020-01-01 00:00:00 UTC: *.css,*.js'
|
|
40
|
+
opt '--default_text_charset CHARSET', 'Default character set to append to the content-type of text files'
|
|
41
|
+
opt '--storage_class CLASS', 'S3 storage class to upload as', default: 'STANDARD', enum: %w[STANDARD STANDARD_IA REDUCED_REDUNDANCY]
|
|
42
|
+
opt '--server_side_encryption', 'Use S3 Server Side Encryption (SSE-AES256)'
|
|
43
|
+
opt '--index_document_suffix SUFFIX', 'Index document suffix of a S3 website'
|
|
44
|
+
opt '--overwrite', 'Whether or not to overwrite existing files', default: true
|
|
45
|
+
opt '--force_path_style', 'Whether to force keeping the bucket name on the path'
|
|
46
|
+
opt '--max_threads NUM', 'The number of threads to use for S3 file uploads', default: 5, max: 15, type: :integer
|
|
47
|
+
opt '--verbose', 'Be verbose about uploading files'
|
|
48
|
+
|
|
49
|
+
msgs login: 'Using Access Key: %{access_key_id}',
|
|
50
|
+
default_uri_schema: 'S3 endpoint does not specify a scheme; defaulting to https',
|
|
51
|
+
access_denied: 'It looks like you tried to write to a bucket that is not yours or does not exist. Please create the bucket before trying to write to it.',
|
|
52
|
+
checksum_error: 'AWS secret key does not match the access key id',
|
|
53
|
+
invalid_access_key_id: 'Invalid S3 access key id',
|
|
54
|
+
upload: 'Uploading %s files with up to %s threads ...',
|
|
55
|
+
upload_file: 'Uploading %s to %s with %s',
|
|
56
|
+
upload_skipped: 'Skipping %{file}, already exists',
|
|
57
|
+
upload_failed: 'Failed to upload %s',
|
|
58
|
+
index_document_suffix: 'Setting index document suffix to %s'
|
|
59
|
+
|
|
60
|
+
DEFAULT_CONTENT_TYPE = 'application/octet-stream'
|
|
61
|
+
|
|
62
|
+
def setup
|
|
63
|
+
@cwd = Dir.pwd
|
|
64
|
+
Dir.chdir(local_dir)
|
|
65
|
+
# Aws.eager_autoload!(services: ['S3'])
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def login
|
|
69
|
+
info :login
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def deploy
|
|
73
|
+
upload
|
|
74
|
+
index_document_suffix if index_document_suffix?
|
|
75
|
+
rescue Aws::S3::Errors::ServiceError => e
|
|
76
|
+
handle_error(e)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def finish
|
|
80
|
+
Dir.chdir(@cwd) if @cwd
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
private
|
|
84
|
+
|
|
85
|
+
def upload
|
|
86
|
+
info :upload, files.length, max_threads
|
|
87
|
+
threads = max_threads.times.map { |_i| Thread.new(&method(:upload_files)) }
|
|
88
|
+
threads.each(&:join)
|
|
89
|
+
info "\n" unless verbose?
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def upload_files
|
|
93
|
+
while file = files.pop
|
|
94
|
+
opts = upload_opts(file)
|
|
95
|
+
progress(file, opts)
|
|
96
|
+
upload_file(file, opts)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def progress(file, data)
|
|
101
|
+
if verbose?
|
|
102
|
+
info :upload_file, file, upload_dir || '/', to_pairs(data)
|
|
103
|
+
else
|
|
104
|
+
print '.'
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def upload_file(file, opts)
|
|
109
|
+
object = bucket.object(upload_path(file))
|
|
110
|
+
return warn :upload_skipped, file: file if !overwrite && object.exists?
|
|
111
|
+
|
|
112
|
+
info :upload_file, file, upload_dir || '/', to_pairs(opts)
|
|
113
|
+
object.upload_file(file, opts) || warn(:upload_failed, file)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def index_document_suffix
|
|
117
|
+
info :index_document_suffix, super
|
|
118
|
+
body = { website_configuration: { index_document: { suffix: super } } }
|
|
119
|
+
bucket.website.put(body)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def upload_path(file)
|
|
123
|
+
[upload_dir, file].compact.join('/')
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def upload_opts(file)
|
|
127
|
+
compact(
|
|
128
|
+
acl:,
|
|
129
|
+
content_type: content_type(file),
|
|
130
|
+
content_encoding: detect_encoding? ? encoding(file) : nil,
|
|
131
|
+
cache_control: match_opt(cache_control, file),
|
|
132
|
+
expires: match_opt(expires, file),
|
|
133
|
+
storage_class:,
|
|
134
|
+
server_side_encryption:
|
|
135
|
+
)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def files
|
|
139
|
+
@files ||= Dir.glob(*glob).reject { |path| File.directory?(path) }
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def glob
|
|
143
|
+
[super, dot_match? ? File::FNM_DOTMATCH : nil].compact
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def acl
|
|
147
|
+
super.gsub(/_/, '-') if acl?
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def server_side_encryption
|
|
151
|
+
'AES256' if server_side_encryption?
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def content_type(file)
|
|
155
|
+
return DEFAULT_CONTENT_TYPE unless type = MIME::Types.type_for(file).first
|
|
156
|
+
|
|
157
|
+
type = "#{type}; charset=#{default_text_charset}" if encoding(file) == 'text' && default_text_charset?
|
|
158
|
+
type.to_s
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def compact(hash)
|
|
162
|
+
hash.reject { |_, value| value.nil? }.to_h
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def endpoint
|
|
166
|
+
@endpoint ||= normalize_endpoint(super) if endpoint?
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def normalize_endpoint(url)
|
|
170
|
+
uri = URI.parse(url)
|
|
171
|
+
return uri if uri.scheme
|
|
172
|
+
|
|
173
|
+
info :default_uri_scheme
|
|
174
|
+
URI.parse("https://#{url}")
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def handle_error(err)
|
|
178
|
+
case err
|
|
179
|
+
when Aws::S3::Errors::InvalidAccessKeyId
|
|
180
|
+
error :invalid_access_key_id
|
|
181
|
+
when Aws::S3::Errors::ChecksumError
|
|
182
|
+
error :checksum_error
|
|
183
|
+
when Aws::S3::Errors::AccessDenied
|
|
184
|
+
error :access_denied
|
|
185
|
+
else
|
|
186
|
+
error err.message
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def bucket
|
|
191
|
+
@bucket ||= Aws::S3::Resource.new(client:).bucket(super)
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def client
|
|
195
|
+
Aws::S3::Client.new(s3_opts)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def s3_opts
|
|
199
|
+
compact(
|
|
200
|
+
region:,
|
|
201
|
+
credentials:,
|
|
202
|
+
endpoint:,
|
|
203
|
+
force_path_style: force_path_style?
|
|
204
|
+
)
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def credentials
|
|
208
|
+
Aws::Credentials.new(access_key_id, secret_access_key)
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def to_pairs(hash)
|
|
212
|
+
hash.map { |pair| pair.join('=') }.join(' ')
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def match_opt(strs, file)
|
|
216
|
+
maps = Array(strs).map { |str| Mapping.new(str, file) }
|
|
217
|
+
maps.map(&:value).compact.first
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
class Mapping < Struct.new(:str, :file)
|
|
221
|
+
MATCH = File::FNM_DOTMATCH | File::FNM_EXTGLOB
|
|
222
|
+
|
|
223
|
+
def value
|
|
224
|
+
str, glob = parse
|
|
225
|
+
unquote(str) if match?(glob)
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
private
|
|
229
|
+
|
|
230
|
+
def unquote(str)
|
|
231
|
+
str =~ /^"(.*)"$/ && ::Regexp.last_match(1) || str
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def match?(glob)
|
|
235
|
+
glob.nil? || File.fnmatch?(normalize(glob), file, MATCH)
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def normalize(glob)
|
|
239
|
+
return glob if glob.include?('{')
|
|
240
|
+
|
|
241
|
+
"{#{glob.split(',').map(&:strip).join(',')}}"
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def parse
|
|
245
|
+
parts = str.split(': ')
|
|
246
|
+
parts.size > 1 ? [parts[0..-2].join(': '), parts.last] : parts
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dpl
|
|
4
|
+
module Providers
|
|
5
|
+
class Scalingo < Provider
|
|
6
|
+
register :scalingo
|
|
7
|
+
|
|
8
|
+
status :alpha
|
|
9
|
+
|
|
10
|
+
description sq(<<-STR)
|
|
11
|
+
tbd
|
|
12
|
+
STR
|
|
13
|
+
|
|
14
|
+
env :scalingo
|
|
15
|
+
|
|
16
|
+
required :api_token, %i[username password]
|
|
17
|
+
|
|
18
|
+
opt '--app APP', default: :repo_name
|
|
19
|
+
opt '--api_token TOKEN', 'Scalingo API token', alias: :api_key, deprecated: :api_key
|
|
20
|
+
opt '--username NAME', 'Scalingo username'
|
|
21
|
+
opt '--password PASS', 'Scalingo password', secret: true
|
|
22
|
+
opt '--region REGION', 'Scalingo region', default: 'agora-fr1', enum: %w[agora-fr1 osc-fr1]
|
|
23
|
+
opt '--remote REMOTE', 'Git remote name', default: 'scalingo-dpl'
|
|
24
|
+
opt '--branch BRANCH', 'Git branch', default: 'master'
|
|
25
|
+
opt '--timeout SEC', 'Timeout for Scalingo CLI commands', default: 60, type: :integer
|
|
26
|
+
|
|
27
|
+
needs :git, :ssh_key
|
|
28
|
+
|
|
29
|
+
cmds login_key: 'timeout %{timeout} ./scalingo login --api-token %{api_token} > /dev/null',
|
|
30
|
+
login_creds: 'echo -e \"%{username}\n%{password}\" | timeout %{timeout} ./scalingo login > /dev/null',
|
|
31
|
+
add_key: 'timeout %{timeout} ./scalingo keys-add dpl_tmp_key %{key}',
|
|
32
|
+
remove_key: 'timeout %{timeout} ./scalingo keys-remove dpl_tmp_key',
|
|
33
|
+
git_setup: './scalingo --app %{app} git-setup --remote %{remote}',
|
|
34
|
+
push: 'git push %{remote} HEAD:%{branch} -f'
|
|
35
|
+
|
|
36
|
+
errs install: 'Failed to install the Scalingo CLI.',
|
|
37
|
+
login: 'Failed to authenticate with the Scalingo API.',
|
|
38
|
+
add_key: 'Failed to add the ssh key.',
|
|
39
|
+
remove_key: 'Failed to remove the ssh key.',
|
|
40
|
+
git_setup: 'Failed to add the git remote.',
|
|
41
|
+
push: 'Failed to push the app.'
|
|
42
|
+
|
|
43
|
+
def install
|
|
44
|
+
script :install
|
|
45
|
+
ENV['SCALINGO_REGION'] = region if region?
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def login
|
|
49
|
+
shell api_token ? :login_key : :login_creds, assert: err(:login)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def add_key(key, _type = nil)
|
|
53
|
+
shell :add_key, key:
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def setup
|
|
57
|
+
shell :git_setup
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def deploy
|
|
61
|
+
shell :push
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def remove_key
|
|
65
|
+
shell :remove_key
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dpl
|
|
4
|
+
module Providers
|
|
5
|
+
class Script < Provider
|
|
6
|
+
register :script
|
|
7
|
+
|
|
8
|
+
status :stable
|
|
9
|
+
|
|
10
|
+
summary 'Minimal provider that executes a custom command'
|
|
11
|
+
|
|
12
|
+
description sq(<<-STR)
|
|
13
|
+
This deployment provider executes a single, custom command. This is
|
|
14
|
+
usually a script that is contained in your repository, but it can be
|
|
15
|
+
any command executable in the build environment.
|
|
16
|
+
|
|
17
|
+
It is possible to pass arguments to a script deployment like so:
|
|
18
|
+
|
|
19
|
+
dpl script -s './scripts/deploy.sh production --verbose'
|
|
20
|
+
|
|
21
|
+
Deployment will be marked a failure if the script exits with nonzero
|
|
22
|
+
status.
|
|
23
|
+
STR
|
|
24
|
+
|
|
25
|
+
opt '-s', '--script SCRIPT', 'The script to execute', required: true
|
|
26
|
+
|
|
27
|
+
def deploy
|
|
28
|
+
shell script, assert: 'Script failed with status %{status}'
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dpl
|
|
4
|
+
module Providers
|
|
5
|
+
class Snap < Provider
|
|
6
|
+
register :snap
|
|
7
|
+
|
|
8
|
+
status :stable
|
|
9
|
+
|
|
10
|
+
description sq(<<-STR)
|
|
11
|
+
tbd
|
|
12
|
+
STR
|
|
13
|
+
|
|
14
|
+
env :snap
|
|
15
|
+
|
|
16
|
+
opt '--token TOKEN', 'Snap API token', required: true, secret: true
|
|
17
|
+
opt '--snap STR', 'Path to the snap to be pushed (can be a glob)', default: '**/*.snap'
|
|
18
|
+
opt '--channel CHAN', 'Channel into which the snap will be released', default: 'edge'
|
|
19
|
+
|
|
20
|
+
apt 'snapd', 'snap'
|
|
21
|
+
|
|
22
|
+
cmds apt_get_update: 'sudo apt-get update -qq',
|
|
23
|
+
update_snapd: 'sudo apt-get install snapd',
|
|
24
|
+
install: 'sudo snap install snapcraft --classic',
|
|
25
|
+
login: 'echo "%{token}" | snapcraft login --with -',
|
|
26
|
+
deploy: 'snapcraft push %{snap_path} --release=%{channel}'
|
|
27
|
+
|
|
28
|
+
msgs login: 'Attemping to login ...',
|
|
29
|
+
no_snaps: 'No snap found matching %{snap}',
|
|
30
|
+
multiple_snaps: 'Multiple snaps found matching %{snap}: %{snap_paths}',
|
|
31
|
+
deploy: 'Pushing snap %{snap_path}'
|
|
32
|
+
|
|
33
|
+
def install
|
|
34
|
+
return if which 'snapcraft'
|
|
35
|
+
|
|
36
|
+
shell :apt_get_update
|
|
37
|
+
shell :update_snapd
|
|
38
|
+
shell :install
|
|
39
|
+
ENV['PATH'] += ':/snap/bin'
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def login
|
|
43
|
+
shell :login, assert: 'Failed to authenticate: %{err}', success: '%{out}', capture: true
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def validate
|
|
47
|
+
error :no_snaps if snaps.empty?
|
|
48
|
+
error :multiple_snaps if snaps.size > 1
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def deploy
|
|
52
|
+
shell :deploy
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def snap_path
|
|
56
|
+
snaps.first
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def snap_paths
|
|
60
|
+
snaps.join(', ')
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def snaps
|
|
64
|
+
@snaps ||= Dir[snap].sort
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'open-uri'
|
|
4
|
+
|
|
5
|
+
module Dpl
|
|
6
|
+
module Providers
|
|
7
|
+
class Surge < Provider
|
|
8
|
+
register :surge
|
|
9
|
+
|
|
10
|
+
status :stable
|
|
11
|
+
|
|
12
|
+
description sq(<<-STR)
|
|
13
|
+
tbd
|
|
14
|
+
STR
|
|
15
|
+
|
|
16
|
+
node_js '>= 8.8.1'
|
|
17
|
+
|
|
18
|
+
gem 'json'
|
|
19
|
+
npm :surge
|
|
20
|
+
env :surge
|
|
21
|
+
|
|
22
|
+
opt '--login EMAIL', 'Surge login (the email address you use with Surge)', required: true
|
|
23
|
+
opt '--token TOKEN', 'Surge login token (can be retrieved with `surge token`)', required: true, secret: true
|
|
24
|
+
opt '--domain NAME', 'Domain to publish to. Not required if the domain is set in the CNAME file in the project folder.'
|
|
25
|
+
opt '--project PATH', 'Path to project directory relative to repo root', default: '.'
|
|
26
|
+
|
|
27
|
+
cmds deploy: 'surge %{project} %{domain}'
|
|
28
|
+
|
|
29
|
+
msgs invalid_project: '%{project} is not a directory',
|
|
30
|
+
missing_domain: 'Please set the domain in .travis.yml or in a CNAME file in the project directory'
|
|
31
|
+
|
|
32
|
+
def login
|
|
33
|
+
ENV['SURGE_LOGIN'] ||= opts[:login]
|
|
34
|
+
ENV['SURGE_TOKEN'] ||= opts[:token]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def validate
|
|
38
|
+
error :invalid_project if invalid_project?
|
|
39
|
+
error :missing_domain if missing_domain?
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def deploy
|
|
43
|
+
shell :deploy
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def invalid_project?
|
|
47
|
+
!File.directory?(project)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def missing_domain?
|
|
51
|
+
!domain && !File.exist?("#{project}/CNAME")
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def project
|
|
55
|
+
expand(super, build_dir)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'dpl/version'
|
|
4
|
+
require 'net/http'
|
|
5
|
+
require 'securerandom'
|
|
6
|
+
|
|
7
|
+
module Dpl
|
|
8
|
+
module Providers
|
|
9
|
+
class Testfairy < Provider
|
|
10
|
+
register :testfairy
|
|
11
|
+
|
|
12
|
+
status :alpha
|
|
13
|
+
|
|
14
|
+
full_name 'TestFairy'
|
|
15
|
+
|
|
16
|
+
description sq(<<-STR)
|
|
17
|
+
tbd
|
|
18
|
+
STR
|
|
19
|
+
|
|
20
|
+
gem 'json'
|
|
21
|
+
gem 'multipart-post', '~> 2.0.0', require: 'net/http/post/multipart'
|
|
22
|
+
|
|
23
|
+
env :testfairy
|
|
24
|
+
|
|
25
|
+
opt '--api_key KEY', 'TestFairy API key', required: true, secret: true
|
|
26
|
+
opt '--app_file FILE', 'Path to the app file that will be generated after the build (APK/IPA)', required: true
|
|
27
|
+
opt '--symbols_file FILE', 'Path to the symbols file'
|
|
28
|
+
opt '--testers_groups GROUPS', 'Tester groups to be notified about this build', example: 'e.g. group1,group1'
|
|
29
|
+
opt '--notify', 'Send an email with a changelog to your users'
|
|
30
|
+
opt '--auto_update', 'Automaticall upgrade all the previous installations of this app this version'
|
|
31
|
+
opt '--advanced_options OPTS', 'Comma_separated list of advanced options', example: 'option1,option2'
|
|
32
|
+
|
|
33
|
+
URL = 'https://upload.testfairy.com/api/upload'
|
|
34
|
+
UA = "Travis CI dpl version=#{Dpl::VERSION}".freeze
|
|
35
|
+
|
|
36
|
+
msgs deploy: 'Uploading to TestFairy: %s',
|
|
37
|
+
done: 'Done. Check your build at %s'
|
|
38
|
+
|
|
39
|
+
def deploy
|
|
40
|
+
info :deploy, pretty_print(params)
|
|
41
|
+
body = JSON.parse(http.request(request).body)
|
|
42
|
+
error body['message'] if body['status'] == 'fail'
|
|
43
|
+
info :done, body['build_url']
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def params
|
|
49
|
+
@params ||= compact(
|
|
50
|
+
'api_key': api_key,
|
|
51
|
+
'apk_file': file(app_file),
|
|
52
|
+
'symbols_file': file(symbols_file),
|
|
53
|
+
'testers-groups': testers_groups,
|
|
54
|
+
'notify': bool(notify),
|
|
55
|
+
'auto-update': bool(auto_update),
|
|
56
|
+
'advanced-options': advanced_options,
|
|
57
|
+
'changelog': changelog
|
|
58
|
+
)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def changelog
|
|
62
|
+
git_log "--pretty=oneline --abbrev-commit #{commits}" if commits
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def commits
|
|
66
|
+
ENV['TRAVIS_COMMIT_RANGE']
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def request
|
|
70
|
+
Net::HTTP::Post::Multipart.new(uri.path, params, 'User-Agent' => UA)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def http
|
|
74
|
+
Net::HTTP.start(uri.host, uri.port, use_ssl: true)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def uri
|
|
78
|
+
@uri ||= URI.parse(URL)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def file(path)
|
|
82
|
+
UploadIO.new(path, '', File.basename(path)) if path
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def bool(obj)
|
|
86
|
+
unless obj.nil?
|
|
87
|
+
obj ? 'on' : 'off'
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def pretty_print(params)
|
|
92
|
+
params = params.map do |key, value|
|
|
93
|
+
value = obfuscate(value) if key == :api_key
|
|
94
|
+
value = value.path if value.respond_to?(:path)
|
|
95
|
+
[key, value]
|
|
96
|
+
end
|
|
97
|
+
JSON.pretty_generate(params.to_h)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dpl
|
|
4
|
+
module Providers
|
|
5
|
+
class Transifex < Provider
|
|
6
|
+
register :transifex
|
|
7
|
+
|
|
8
|
+
status :alpha
|
|
9
|
+
|
|
10
|
+
description sq(<<-STR)
|
|
11
|
+
tbd
|
|
12
|
+
STR
|
|
13
|
+
|
|
14
|
+
python '>= 2.7', '!= 3.0', '!= 3.1', '!= 3.2', '!= 3.3', '< 3.8'
|
|
15
|
+
|
|
16
|
+
required :api_token, %i[username password]
|
|
17
|
+
|
|
18
|
+
env :transifex
|
|
19
|
+
|
|
20
|
+
opt '--api_token TOKEN', 'Transifex API token', secret: true
|
|
21
|
+
opt '--username NAME', 'Transifex username'
|
|
22
|
+
opt '--password PASS', 'Transifex password', secret: true
|
|
23
|
+
opt '--hostname NAME', 'Transifex hostname', default: 'www.transifex.com'
|
|
24
|
+
opt '--cli_version VER', 'CLI version to install', default: '>=0.11'
|
|
25
|
+
|
|
26
|
+
cmds status: 'tx status',
|
|
27
|
+
push: 'tx push --source --no-interactive'
|
|
28
|
+
|
|
29
|
+
msgs login: 'Writing ~/.transifexrc (user: %{username}, password: %{password})'
|
|
30
|
+
errs push: 'Failure pushing to Transifex'
|
|
31
|
+
|
|
32
|
+
RC = sq(<<-RC)
|
|
33
|
+
[%{url}]
|
|
34
|
+
hostname = %{url}
|
|
35
|
+
username = %{username}
|
|
36
|
+
password = %{password}
|
|
37
|
+
RC
|
|
38
|
+
|
|
39
|
+
def install
|
|
40
|
+
pip_install 'transifex-client', 'tx', cli_version
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def login
|
|
44
|
+
info :login
|
|
45
|
+
write_rc
|
|
46
|
+
shell :status
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def deploy
|
|
50
|
+
shell :push, retry: true
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
def write_rc
|
|
56
|
+
write_file '~/.transifexrc', interpolate(RC, opts, secure: true)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def username
|
|
60
|
+
super || 'api'
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def password
|
|
64
|
+
super || api_token
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def url
|
|
68
|
+
hostname.start_with?('https://') ? hostname : "https://#{hostname}"
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|