travis_dpl_test 2.0.3.beta.4.ror

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +172 -0
  3. data/CODE_OF_CONDUCT.md +74 -0
  4. data/CONTRIBUTING.md +392 -0
  5. data/Gemfile +32 -0
  6. data/Gemfile.lock +611 -0
  7. data/LICENSE +19 -0
  8. data/README.md +2744 -0
  9. data/Rakefile +210 -0
  10. data/bin/dpl +11 -0
  11. data/config/transliterate.yml +733 -0
  12. data/dpl.gemspec +23 -0
  13. data/lib/dpl/assets/atlas/install +19 -0
  14. data/lib/dpl/assets/convox/install +11 -0
  15. data/lib/dpl/assets/dpl/README.erb.md +138 -0
  16. data/lib/dpl/assets/dpl/git_ssh +8 -0
  17. data/lib/dpl/assets/git/detect_private_key +8 -0
  18. data/lib/dpl/assets/hephy/filter_log +3 -0
  19. data/lib/dpl/assets/pypi/install +4 -0
  20. data/lib/dpl/assets/scalingo/install +6 -0
  21. data/lib/dpl/cli.rb +100 -0
  22. data/lib/dpl/ctx/bash.rb +549 -0
  23. data/lib/dpl/ctx/test.rb +255 -0
  24. data/lib/dpl/ctx.rb +4 -0
  25. data/lib/dpl/helper/assets.rb +38 -0
  26. data/lib/dpl/helper/cmd.rb +169 -0
  27. data/lib/dpl/helper/config_file.rb +49 -0
  28. data/lib/dpl/helper/cookbook_site_streaming_uploader.rb +249 -0
  29. data/lib/dpl/helper/env.rb +92 -0
  30. data/lib/dpl/helper/github.rb +22 -0
  31. data/lib/dpl/helper/interpolate.rb +160 -0
  32. data/lib/dpl/helper/memoize.rb +23 -0
  33. data/lib/dpl/helper/squiggle.rb +24 -0
  34. data/lib/dpl/helper/transliterate.rb +13 -0
  35. data/lib/dpl/helper/wrap.rb +11 -0
  36. data/lib/dpl/helper/zip.rb +71 -0
  37. data/lib/dpl/provider/dsl.rb +410 -0
  38. data/lib/dpl/provider/examples.rb +132 -0
  39. data/lib/dpl/provider/status.rb +61 -0
  40. data/lib/dpl/provider.rb +651 -0
  41. data/lib/dpl/providers/anynines.rb +71 -0
  42. data/lib/dpl/providers/azure_web_apps.rb +63 -0
  43. data/lib/dpl/providers/bintray.rb +324 -0
  44. data/lib/dpl/providers/bluemixcloudfoundry.rb +98 -0
  45. data/lib/dpl/providers/boxfuse.rb +52 -0
  46. data/lib/dpl/providers/cargo.rb +32 -0
  47. data/lib/dpl/providers/chef_supermarket.rb +132 -0
  48. data/lib/dpl/providers/cloud66.rb +46 -0
  49. data/lib/dpl/providers/cloudfiles.rb +62 -0
  50. data/lib/dpl/providers/cloudformation.rb +281 -0
  51. data/lib/dpl/providers/cloudfoundry.rb +89 -0
  52. data/lib/dpl/providers/codedeploy.rb +190 -0
  53. data/lib/dpl/providers/convox.rb +130 -0
  54. data/lib/dpl/providers/datica.rb +64 -0
  55. data/lib/dpl/providers/ecr.rb +129 -0
  56. data/lib/dpl/providers/elasticbeanstalk.rb +207 -0
  57. data/lib/dpl/providers/engineyard.rb +113 -0
  58. data/lib/dpl/providers/firebase.rb +45 -0
  59. data/lib/dpl/providers/flynn.rb +35 -0
  60. data/lib/dpl/providers/gae.rb +78 -0
  61. data/lib/dpl/providers/gcs.rb +132 -0
  62. data/lib/dpl/providers/git_push.rb +273 -0
  63. data/lib/dpl/providers/gleis.rb +74 -0
  64. data/lib/dpl/providers/hackage.rb +53 -0
  65. data/lib/dpl/providers/hephy.rb +107 -0
  66. data/lib/dpl/providers/heroku/api.rb +123 -0
  67. data/lib/dpl/providers/heroku/git.rb +54 -0
  68. data/lib/dpl/providers/heroku.rb +111 -0
  69. data/lib/dpl/providers/lambda.rb +211 -0
  70. data/lib/dpl/providers/launchpad.rb +80 -0
  71. data/lib/dpl/providers/netlify.rb +38 -0
  72. data/lib/dpl/providers/npm.rb +130 -0
  73. data/lib/dpl/providers/nuget.rb +41 -0
  74. data/lib/dpl/providers/openshift.rb +52 -0
  75. data/lib/dpl/providers/opsworks.rb +146 -0
  76. data/lib/dpl/providers/packagecloud.rb_ +194 -0
  77. data/lib/dpl/providers/pages/api.rb +106 -0
  78. data/lib/dpl/providers/pages/git.rb +262 -0
  79. data/lib/dpl/providers/pages.rb +18 -0
  80. data/lib/dpl/providers/puppetforge.rb +50 -0
  81. data/lib/dpl/providers/pypi.rb +125 -0
  82. data/lib/dpl/providers/releases.rb +234 -0
  83. data/lib/dpl/providers/rubygems.rb +97 -0
  84. data/lib/dpl/providers/s3.rb +251 -0
  85. data/lib/dpl/providers/scalingo.rb +69 -0
  86. data/lib/dpl/providers/script.rb +32 -0
  87. data/lib/dpl/providers/snap.rb +68 -0
  88. data/lib/dpl/providers/surge.rb +59 -0
  89. data/lib/dpl/providers/testfairy.rb +101 -0
  90. data/lib/dpl/providers/transifex.rb +72 -0
  91. data/lib/dpl/providers.rb +48 -0
  92. data/lib/dpl/string_ext.rb +23 -0
  93. data/lib/dpl/support/aws_sdk_patch.rb +26 -0
  94. data/lib/dpl/support/gems.rb +73 -0
  95. data/lib/dpl/support/gstore_patch.rb +8 -0
  96. data/lib/dpl/support/version.rb +84 -0
  97. data/lib/dpl/version.rb +5 -0
  98. data/lib/dpl.rb +23 -0
  99. data/status.json +237 -0
  100. 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