dpl 1.10.17.travis.6637.6 → 2.0.0.alpha.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +74 -0
- data/CONTRIBUTING.md +392 -0
- data/Gemfile +17 -3
- data/Gemfile.lock +373 -0
- data/LICENSE +16 -19
- data/NOTES.md +275 -0
- data/README.md +1977 -707
- data/Rakefile +2 -2
- data/bin/dpl +7 -3
- data/lib/dpl.rb +20 -0
- data/lib/dpl/assets/atlas/install +19 -0
- data/lib/dpl/assets/dpl/README.erb.md +133 -0
- data/lib/dpl/assets/dpl/git_ssh +2 -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 +36 -48
- data/lib/dpl/ctx.rb +2 -0
- data/lib/dpl/ctx/bash.rb +543 -0
- data/lib/dpl/ctx/test.rb +242 -0
- data/lib/dpl/helper/assets.rb +36 -0
- data/lib/dpl/helper/cmd.rb +167 -0
- data/lib/dpl/helper/config_file.rb +47 -0
- data/lib/dpl/helper/env.rb +39 -0
- data/lib/dpl/helper/interpolate.rb +126 -0
- data/lib/dpl/helper/memoize.rb +20 -0
- data/lib/dpl/helper/squiggle.rb +22 -0
- data/lib/dpl/helper/zip.rb +69 -0
- data/lib/dpl/provider.rb +562 -234
- data/lib/dpl/provider/dsl.rb +369 -0
- data/lib/dpl/provider/examples.rb +128 -0
- data/lib/dpl/provider/status.rb +59 -0
- data/lib/dpl/providers.rb +40 -0
- data/lib/dpl/providers/anynines.rb +65 -0
- data/lib/dpl/providers/atlas.rb +49 -0
- data/lib/dpl/providers/azure_web_apps.rb +59 -0
- data/lib/dpl/providers/bintray.rb +313 -0
- data/lib/dpl/providers/bluemixcloudfoundry.rb +92 -0
- data/lib/dpl/providers/boxfuse.rb +48 -0
- data/lib/dpl/providers/cargo.rb +19 -0
- data/lib/dpl/providers/chef_supermarket.rb +128 -0
- data/lib/dpl/providers/cloud66.rb +40 -0
- data/lib/dpl/providers/cloudfiles.rb +56 -0
- data/lib/dpl/providers/cloudfoundry.rb +81 -0
- data/lib/dpl/providers/codedeploy.rb +179 -0
- data/lib/dpl/providers/datica.rb +60 -0
- data/lib/dpl/providers/elasticbeanstalk.rb +195 -0
- data/lib/dpl/providers/engineyard.rb +107 -0
- data/lib/dpl/providers/firebase.rb +41 -0
- data/lib/dpl/providers/gae.rb +74 -0
- data/lib/dpl/providers/gcs.rb +105 -0
- data/lib/dpl/providers/hackage.rb +47 -0
- data/lib/dpl/providers/hephy.rb +101 -0
- data/lib/dpl/providers/heroku.rb +111 -0
- data/lib/dpl/providers/heroku/api.rb +119 -0
- data/lib/dpl/providers/heroku/git.rb +50 -0
- data/lib/dpl/providers/lambda.rb +202 -0
- data/lib/dpl/providers/launchpad.rb +74 -0
- data/lib/dpl/providers/netlify.rb +30 -0
- data/lib/dpl/providers/npm.rb +88 -0
- data/lib/dpl/providers/openshift.rb +46 -0
- data/lib/dpl/providers/opsworks.rb +142 -0
- data/lib/dpl/providers/packagecloud.rb +190 -0
- data/lib/dpl/providers/pages.rb +17 -0
- data/lib/dpl/providers/pages/api.rb +102 -0
- data/lib/dpl/providers/pages/git.rb +251 -0
- data/lib/dpl/providers/puppetforge.rb +44 -0
- data/lib/dpl/providers/pypi.rb +120 -0
- data/lib/dpl/providers/releases.rb +214 -0
- data/lib/dpl/providers/rubygems.rb +89 -0
- data/lib/dpl/providers/s3.rb +243 -0
- data/lib/dpl/providers/scalingo.rb +63 -0
- data/lib/dpl/providers/script.rb +28 -0
- data/lib/dpl/providers/snap.rb +59 -0
- data/lib/dpl/providers/surge.rb +55 -0
- data/lib/dpl/providers/testfairy.rb +93 -0
- data/lib/dpl/providers/transifex.rb +66 -0
- data/lib/dpl/support/aws_sdk_patch.rb +23 -0
- data/lib/dpl/support/gems.rb +69 -0
- data/lib/dpl/support/gstore_patch.rb +6 -0
- data/lib/dpl/support/version.rb +83 -0
- data/lib/dpl/version.rb +2 -2
- metadata +98 -169
- data/.coveralls.yml +0 -1
- data/.github/CONTRIBUTING.md +0 -173
- data/.github/stale.yml +0 -53
- data/.gitignore +0 -13
- data/.rspec +0 -2
- data/.travis.yml +0 -56
- data/dpl-anynines.gemspec +0 -3
- data/dpl-atlas.gemspec +0 -3
- data/dpl-azure_webapps.gemspec +0 -3
- data/dpl-bintray.gemspec +0 -3
- data/dpl-bitballoon.gemspec +0 -3
- data/dpl-bluemix_cloud_foundry.gemspec +0 -3
- data/dpl-boxfuse.gemspec +0 -3
- data/dpl-cargo.gemspec +0 -3
- data/dpl-catalyze.gemspec +0 -3
- data/dpl-chef_supermarket.gemspec +0 -20
- data/dpl-cloud66.gemspec +0 -3
- data/dpl-cloud_files.gemspec +0 -3
- data/dpl-cloud_foundry.gemspec +0 -3
- data/dpl-code_deploy.gemspec +0 -3
- data/dpl-deis.gemspec +0 -3
- data/dpl-elastic_beanstalk.gemspec +0 -3
- data/dpl-engine_yard.gemspec +0 -3
- data/dpl-firebase.gemspec +0 -3
- data/dpl-gae.gemspec +0 -3
- data/dpl-gcs.gemspec +0 -3
- data/dpl-hackage.gemspec +0 -3
- data/dpl-hephy.gemspec +0 -3
- data/dpl-heroku.gemspec +0 -3
- data/dpl-lambda.gemspec +0 -3
- data/dpl-launchpad.gemspec +0 -3
- data/dpl-npm.gemspec +0 -3
- data/dpl-openshift.gemspec +0 -3
- data/dpl-ops_works.gemspec +0 -3
- data/dpl-packagecloud.gemspec +0 -3
- data/dpl-pages.gemspec +0 -3
- data/dpl-puppet_forge.gemspec +0 -3
- data/dpl-pypi.gemspec +0 -3
- data/dpl-releases.gemspec +0 -8
- data/dpl-rubygems.gemspec +0 -3
- data/dpl-s3.gemspec +0 -3
- data/dpl-scalingo.gemspec +0 -3
- data/dpl-script.gemspec +0 -3
- data/dpl-snap.gemspec +0 -3
- data/dpl-surge.gemspec +0 -3
- data/dpl-testfairy.gemspec +0 -3
- data/dpl-transifex.gemspec +0 -3
- data/dpl.gemspec +0 -3
- data/gemspec_helper.rb +0 -51
- data/lib/dpl/error.rb +0 -3
- data/notes/engine_yard.md +0 -1
- data/notes/heroku.md +0 -3
- data/spec/cli_spec.rb +0 -36
- data/spec/provider_spec.rb +0 -191
- data/spec/spec_helper.rb +0 -20
@@ -0,0 +1,59 @@
|
|
1
|
+
module Dpl
|
2
|
+
class Provider < Cl::Cmd
|
3
|
+
class Status < Struct.new(:provider, :status, :info)
|
4
|
+
STATUS = %i(dev alpha beta stable deprecated)
|
5
|
+
|
6
|
+
MSG = {
|
7
|
+
dev: 'Support for deployments to %s is in development',
|
8
|
+
alpha: 'Support for deployments to %s is in alpha',
|
9
|
+
beta: 'Support for deployments to %s is in beta',
|
10
|
+
deprecated: 'Support for deployments to %s is deprecated',
|
11
|
+
pre_stable: 'Please see here: %s'
|
12
|
+
}
|
13
|
+
|
14
|
+
URL = 'https://github.com/travis-ci/dpl/#maturity-levels'
|
15
|
+
|
16
|
+
def initialize(provider, status, info)
|
17
|
+
unknown!(status) unless known?(status)
|
18
|
+
super
|
19
|
+
end
|
20
|
+
|
21
|
+
def announce?
|
22
|
+
!stable?
|
23
|
+
end
|
24
|
+
|
25
|
+
def msg
|
26
|
+
msg = "#{MSG[status] % name}"
|
27
|
+
msg << "(#{info})" if info
|
28
|
+
msg << ". #{MSG[:pre_stable] % URL}" if pre_stable?
|
29
|
+
"\n#{msg}\n"
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def name
|
35
|
+
provider.full_name
|
36
|
+
end
|
37
|
+
|
38
|
+
def pre_stable?
|
39
|
+
STATUS.index(status) < STATUS.index(:stable)
|
40
|
+
end
|
41
|
+
|
42
|
+
def stable?
|
43
|
+
status == :stable
|
44
|
+
end
|
45
|
+
|
46
|
+
def deprecated?
|
47
|
+
status == :deprecated
|
48
|
+
end
|
49
|
+
|
50
|
+
def known?(status)
|
51
|
+
STATUS.include?(status)
|
52
|
+
end
|
53
|
+
|
54
|
+
def unknown!(status)
|
55
|
+
raise "Unknown status: #{status.inspect}. Known statuses are: #{STATUS.map(&:inspect).join(', ')}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'dpl/providers/anynines'
|
2
|
+
require 'dpl/providers/atlas'
|
3
|
+
require 'dpl/providers/azure_web_apps'
|
4
|
+
require 'dpl/providers/bintray'
|
5
|
+
require 'dpl/providers/boxfuse'
|
6
|
+
require 'dpl/providers/bluemixcloudfoundry'
|
7
|
+
require 'dpl/providers/cargo'
|
8
|
+
require 'dpl/providers/datica'
|
9
|
+
require 'dpl/providers/chef_supermarket'
|
10
|
+
require 'dpl/providers/cloud66'
|
11
|
+
require 'dpl/providers/cloudfiles'
|
12
|
+
require 'dpl/providers/cloudfoundry'
|
13
|
+
require 'dpl/providers/codedeploy'
|
14
|
+
require 'dpl/providers/elasticbeanstalk'
|
15
|
+
require 'dpl/providers/engineyard'
|
16
|
+
require 'dpl/providers/firebase'
|
17
|
+
require 'dpl/providers/gae'
|
18
|
+
require 'dpl/providers/gcs'
|
19
|
+
require 'dpl/providers/hackage'
|
20
|
+
require 'dpl/providers/hephy'
|
21
|
+
require 'dpl/providers/heroku'
|
22
|
+
require 'dpl/providers/lambda'
|
23
|
+
require 'dpl/providers/launchpad'
|
24
|
+
require 'dpl/providers/netlify'
|
25
|
+
require 'dpl/providers/npm'
|
26
|
+
require 'dpl/providers/openshift'
|
27
|
+
require 'dpl/providers/opsworks'
|
28
|
+
require 'dpl/providers/packagecloud'
|
29
|
+
require 'dpl/providers/pages'
|
30
|
+
require 'dpl/providers/puppetforge'
|
31
|
+
require 'dpl/providers/pypi'
|
32
|
+
require 'dpl/providers/releases'
|
33
|
+
require 'dpl/providers/rubygems'
|
34
|
+
require 'dpl/providers/s3'
|
35
|
+
require 'dpl/providers/scalingo'
|
36
|
+
require 'dpl/providers/script'
|
37
|
+
require 'dpl/providers/snap'
|
38
|
+
require 'dpl/providers/surge'
|
39
|
+
require 'dpl/providers/testfairy'
|
40
|
+
require 'dpl/providers/transifex'
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Dpl
|
2
|
+
module Providers
|
3
|
+
class Anynines < Provider
|
4
|
+
status :alpha
|
5
|
+
|
6
|
+
description sq(<<-str)
|
7
|
+
tbd
|
8
|
+
str
|
9
|
+
|
10
|
+
opt '--username USER', 'anynines username', required: true
|
11
|
+
opt '--password PASS', 'anynines password', required: true, secret: true
|
12
|
+
opt '--organization ORG', 'anynines target organization', required: true
|
13
|
+
opt '--space SPACE', 'anynines target space', required: true
|
14
|
+
opt '--app_name APP', 'Application name'
|
15
|
+
opt '--buildpack PACK', 'Custom buildpack name or Git URL'
|
16
|
+
opt '--manifest FILE', 'Path to the manifest'
|
17
|
+
opt '--logout', default: true, internal: true
|
18
|
+
|
19
|
+
API = 'https://api.de.a9s.eu'
|
20
|
+
|
21
|
+
cmds install: 'test $(uname) = "Linux" && rel="linux64-binary" || rel="macosx64"; wget "https://cli.run.pivotal.io/stable?release=${rel}&source=github" -qO cf.tgz && tar -zxvf cf.tgz && rm cf.tgz',
|
22
|
+
api: './cf api %{url}',
|
23
|
+
login: './cf login -u %{username} -p %{password} -o %{organization} -s %{space}',
|
24
|
+
push: './cf push %{args}',
|
25
|
+
logout: './cf logout'
|
26
|
+
|
27
|
+
errs install: 'Failed to install CLI tools',
|
28
|
+
api: 'Failed to set api',
|
29
|
+
login: 'Failed to login',
|
30
|
+
target: 'Failed to target organization %{organization}, space %{space}',
|
31
|
+
push: 'Failed to push app',
|
32
|
+
logout: 'Failed to logout'
|
33
|
+
|
34
|
+
def install
|
35
|
+
shell :install
|
36
|
+
end
|
37
|
+
|
38
|
+
def login
|
39
|
+
shell :api
|
40
|
+
shell :login
|
41
|
+
end
|
42
|
+
|
43
|
+
def deploy
|
44
|
+
shell :push
|
45
|
+
end
|
46
|
+
|
47
|
+
def finish
|
48
|
+
shell :logout if logout?
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def url
|
54
|
+
API
|
55
|
+
end
|
56
|
+
|
57
|
+
def args
|
58
|
+
args = []
|
59
|
+
args << quote(app_name) if app_name?
|
60
|
+
args << "-f #{manifest}" if manifest?
|
61
|
+
args.join(' ')
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Dpl
|
2
|
+
module Providers
|
3
|
+
class Atlas < Provider
|
4
|
+
status :alpha
|
5
|
+
|
6
|
+
description sq(<<-str)
|
7
|
+
tbd
|
8
|
+
str
|
9
|
+
|
10
|
+
opt '--app APP', 'The Atlas application to upload to', required: true
|
11
|
+
opt '--token TOKEN', 'The Atlas API token', required: true, secret: true
|
12
|
+
opt '--paths PATH', 'Files or directories to upload', type: :array, default: ['.']
|
13
|
+
opt '--address ADDR', 'The address of the Atlas server'
|
14
|
+
opt '--include GLOB', 'Glob pattern of files or directories to include', type: :array
|
15
|
+
opt '--exclude GLOB', 'Glob pattern of files or directories to exclude', type: :array
|
16
|
+
opt '--metadata DATA', 'Arbitrary key=value (string) metadata to be sent with the upload', type: :array
|
17
|
+
opt '--vcs', 'Get lists of files to exclude and include from a VCS (Git, Mercurial or SVN)'
|
18
|
+
opt '--args ARGS', 'Args to pass to the atlas-upload CLI'
|
19
|
+
opt '--debug', 'Turn on debug output'
|
20
|
+
|
21
|
+
cmds upload: 'atlas-upload %{args} %{app} %{path}'
|
22
|
+
|
23
|
+
def setup
|
24
|
+
ENV['ATLAS_TOKEN'] = token
|
25
|
+
end
|
26
|
+
|
27
|
+
def install
|
28
|
+
script :install
|
29
|
+
end
|
30
|
+
|
31
|
+
def deploy
|
32
|
+
paths.each { |path| upload(path) }
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def upload(path)
|
38
|
+
shell :upload, path: path
|
39
|
+
# shell ['atlas-upload', args, app, path].compact.join(' ')
|
40
|
+
end
|
41
|
+
|
42
|
+
ARGS = %i(address exclude include metadata vcs debug)
|
43
|
+
|
44
|
+
def args
|
45
|
+
super || opts_for(ARGS, prefix: '-')
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Dpl
|
2
|
+
module Providers
|
3
|
+
class AzureWebApps < Provider
|
4
|
+
status :alpha
|
5
|
+
|
6
|
+
full_name 'Azure Web Apps'
|
7
|
+
|
8
|
+
description sq(<<-str)
|
9
|
+
tbd
|
10
|
+
str
|
11
|
+
|
12
|
+
env :AZURE_WA
|
13
|
+
|
14
|
+
opt '--site SITE', 'Web App name (e.g. myapp in myapp.azurewebsites.net)', required: true
|
15
|
+
opt '--username NAME', 'Web App Deployment Username', required: true
|
16
|
+
opt '--password PASS', 'Web App Deployment Password', required: true, secret: true
|
17
|
+
opt '--slot SLOT', 'Slot name (if your app uses staging deployment)'
|
18
|
+
opt '--verbose', 'Print deployment output from Azure. Warning: If authentication fails, Git prints credentials in clear text. Correct credentials remain hidden.'
|
19
|
+
|
20
|
+
needs :git
|
21
|
+
|
22
|
+
cmds checkout: 'git checkout HEAD',
|
23
|
+
add: 'git add . --all --force',
|
24
|
+
commit: 'git commit -m "Cleanup commit"',
|
25
|
+
deploy: 'git push --force --quiet %{url} HEAD:refs/heads/master'
|
26
|
+
|
27
|
+
msgs commit: 'Committing changes to git',
|
28
|
+
deploy: 'Deploying to Azure Web App: %{site}'
|
29
|
+
|
30
|
+
errs push: 'Failed pushing to Azure Web Apps'
|
31
|
+
|
32
|
+
URL = 'https://%s:%s@%s.scm.azurewebsites.net:443/%s.git'
|
33
|
+
|
34
|
+
def setup
|
35
|
+
commit if git_dirty? && !cleanup?
|
36
|
+
end
|
37
|
+
|
38
|
+
def deploy
|
39
|
+
shell :deploy, silence: !verbose?
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def url
|
45
|
+
URL % [username, password, target, site]
|
46
|
+
end
|
47
|
+
|
48
|
+
def target
|
49
|
+
slot || site
|
50
|
+
end
|
51
|
+
|
52
|
+
def commit
|
53
|
+
shell :checkout
|
54
|
+
shell :add
|
55
|
+
shell :commit
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,313 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'uri'
|
3
|
+
require 'find'
|
4
|
+
|
5
|
+
module Dpl
|
6
|
+
module Providers
|
7
|
+
class Bintray < Provider
|
8
|
+
status :alpha
|
9
|
+
|
10
|
+
description sq(<<-str)
|
11
|
+
tbd
|
12
|
+
str
|
13
|
+
|
14
|
+
gem 'json'
|
15
|
+
|
16
|
+
opt '--user USER', 'Bintray user', required: true
|
17
|
+
opt '--key KEY', 'Bintray API key', required: true, secret: true
|
18
|
+
opt '--file FILE', 'Path to a descriptor file for the Bintray upload', required: true
|
19
|
+
opt '--passphrase PHRASE', 'Passphrase as configured on Bintray (if GPG signing is used)'
|
20
|
+
opt '--url URL', default: 'https://api.bintray.com', internal: true
|
21
|
+
|
22
|
+
msgs missing_file: 'Missing descriptor file: %{file}',
|
23
|
+
invalid_file: 'Failed to parse descriptor file %{file}',
|
24
|
+
create_package: 'Creating package %{package_name}',
|
25
|
+
package_attrs: 'Adding attributes for package %{package_name}',
|
26
|
+
create_version: 'Creating version %{version_name}',
|
27
|
+
version_attrs: 'Adding attributes for version %{version_name}',
|
28
|
+
upload_file: 'Uploading file %{source} to %{target}',
|
29
|
+
sign_version: 'Signing version %s passphrase',
|
30
|
+
publish_version: 'Publishing version %{version_name} of package %{package_name}',
|
31
|
+
missing_path: 'Path: %{path} does not exist.',
|
32
|
+
list_download: 'Listing %{path} in downloads',
|
33
|
+
retrying: '%{code} response from Bintray. It may take some time for a version to be published, retrying in %{pause} sec ... (%{count}/%{max})',
|
34
|
+
giveup_retries: 'Too many retries failed, giving up, something went wrong.',
|
35
|
+
unexpected_code: 'Unexpected HTTP response code %s while checking if the %s exists' ,
|
36
|
+
request_failed: '%s %s returned unexpected HTTP response code %s',
|
37
|
+
request_success: 'Bintray response: %s %s. %s'
|
38
|
+
|
39
|
+
PATHS = {
|
40
|
+
packages: '/packages/%{subject}/%{repo}',
|
41
|
+
package: '/packages/%{subject}/%{repo}/%{package_name}',
|
42
|
+
package_attrs: '/packages/%{subject}/%{repo}/%{package_name}/attributes',
|
43
|
+
versions: '/packages/%{subject}/%{repo}/%{package_name}/versions',
|
44
|
+
version: '/packages/%{subject}/%{repo}/%{package_name}/versions/%{version_name}',
|
45
|
+
version_attrs: '/packages/%{subject}/%{repo}/%{package_name}/versions/%{version_name}/attributes',
|
46
|
+
version_sign: '/gpg/%{subject}/%{repo}/%{package_name}/versions/%{version_name}',
|
47
|
+
version_publish: '/content/%{subject}/%{repo}/%{package_name}/%{version_name}/publish',
|
48
|
+
version_file: '/content/%{subject}/%{repo}/%{package_name}/%{version_name}/%{target}',
|
49
|
+
file_metadata: '/file_metadata/%{subject}/%{repo}/%{target}'
|
50
|
+
}
|
51
|
+
|
52
|
+
MAP = {
|
53
|
+
package: %i(name desc licenses labels vcs_url website_url
|
54
|
+
issue_tracker_url public_download_numbers public_stats),
|
55
|
+
version: %i(name desc released vcs_tag github_release_notes_file
|
56
|
+
github_use_tag_release_notes attributes)
|
57
|
+
}
|
58
|
+
|
59
|
+
def install
|
60
|
+
require 'json'
|
61
|
+
end
|
62
|
+
|
63
|
+
def validate
|
64
|
+
error :missing_file unless File.exist?(file)
|
65
|
+
# validate that the repo exists, and we have access
|
66
|
+
end
|
67
|
+
|
68
|
+
def deploy
|
69
|
+
create_package unless package_exists?
|
70
|
+
create_version unless version_exists?
|
71
|
+
upload_files
|
72
|
+
sign_version if sign_version?
|
73
|
+
publish_version && update_files if publish_version?
|
74
|
+
end
|
75
|
+
|
76
|
+
def package_exists?
|
77
|
+
exists?(:package)
|
78
|
+
end
|
79
|
+
|
80
|
+
def create_package
|
81
|
+
info :create_package
|
82
|
+
post(path(:packages), compact(only(package, *MAP[:package])))
|
83
|
+
return unless package_attrs
|
84
|
+
info :package_attrs
|
85
|
+
post(path(:package_attrs), package_attrs)
|
86
|
+
end
|
87
|
+
|
88
|
+
def version_exists?
|
89
|
+
exists?(:version)
|
90
|
+
end
|
91
|
+
|
92
|
+
def create_version
|
93
|
+
info :create_version
|
94
|
+
post(path(:versions), compact(only(version, *MAP[:version])))
|
95
|
+
return unless version_attrs
|
96
|
+
info :version_attrs
|
97
|
+
post(path(:version_attrs), version_attrs)
|
98
|
+
end
|
99
|
+
|
100
|
+
def upload_files
|
101
|
+
files.each do |file|
|
102
|
+
info :upload_file, source: file.source, target: file.target
|
103
|
+
put(path(:version_file, target: file.target), file.read, file.params)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def sign_version
|
108
|
+
body = compact(passphrase: passphrase)
|
109
|
+
info :sign_version, (passphrase? ? 'with' : 'without')
|
110
|
+
post(path(:version_sign), body)
|
111
|
+
end
|
112
|
+
|
113
|
+
def publish_version
|
114
|
+
info :publish_version
|
115
|
+
post(path(:version_publish))
|
116
|
+
end
|
117
|
+
|
118
|
+
def update_files
|
119
|
+
files.select(&:download).each do |file|
|
120
|
+
info :list_download, path: file.target
|
121
|
+
update_file(file)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def update_file(file)
|
126
|
+
retrying(max: 10, pause: 5) do
|
127
|
+
body = { list_in_downloads: file.download }.to_json
|
128
|
+
headers = { 'Content-Type': 'application/json' }
|
129
|
+
put(path(:file_metadata, target: file.target), body, {}, headers)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def retrying(opts, &block)
|
134
|
+
1.upto(opts[:max]) do |count|
|
135
|
+
code = yield
|
136
|
+
return if code < 400
|
137
|
+
info :retrying, opts.merge(count: count, code: code)
|
138
|
+
sleep opts[:pause]
|
139
|
+
end
|
140
|
+
error :giveup_retries
|
141
|
+
end
|
142
|
+
|
143
|
+
def files
|
144
|
+
return {} unless files = descriptor[:files]
|
145
|
+
return @files if @files
|
146
|
+
keys = %i(path includePattern excludePattern uploadPattern matrixParams listInDownloads)
|
147
|
+
files = files.map { |file| file if file[:path] = path_for(file[:includePattern]) }
|
148
|
+
@files = files.compact.map { |file| find(*file.values_at(*keys)) }.flatten
|
149
|
+
end
|
150
|
+
|
151
|
+
def find(path, includes, excludes, uploads, params, download)
|
152
|
+
paths = Find.find(path).select { |path| File.file?(path) }
|
153
|
+
paths = paths.reject { |path| excluded?(path, excludes) }
|
154
|
+
paths = paths.map { |path| [path, path.match(/#{includes}/)] }
|
155
|
+
paths = paths.select(&:last)
|
156
|
+
paths.map { |path, match| Upload.new(path, fmt(uploads, match.captures), params, download) }
|
157
|
+
end
|
158
|
+
|
159
|
+
def fmt(pattern, captures)
|
160
|
+
captures.each.with_index.inject(pattern) do |pattern, (capture, ix)|
|
161
|
+
pattern.gsub("$#{ix + 1}", capture)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def excluded?(path, pattern)
|
166
|
+
!pattern.to_s.empty? && path.match(/#{pattern}/)
|
167
|
+
end
|
168
|
+
|
169
|
+
def path_for(str)
|
170
|
+
ix = str.index('(')
|
171
|
+
path = ix.to_i == 0 ? str : str[0, ix]
|
172
|
+
return path if File.exist?(path)
|
173
|
+
warn :missing_path, path: path
|
174
|
+
nil
|
175
|
+
end
|
176
|
+
|
177
|
+
def exists?(type)
|
178
|
+
case code = head(path(type), raise: false, silent: true)
|
179
|
+
when 200, 201 then true
|
180
|
+
when 404 then false
|
181
|
+
else error :unexpected_code, code, type
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def head(path, opts = {})
|
186
|
+
req = Net::HTTP::Head.new(path)
|
187
|
+
req.basic_auth(user, key)
|
188
|
+
request(req, opts)
|
189
|
+
end
|
190
|
+
|
191
|
+
def post(path, body = nil)
|
192
|
+
req = Net::HTTP::Post.new(path)
|
193
|
+
req.add_field('Content-Type', 'application/json')
|
194
|
+
req.basic_auth(user, key)
|
195
|
+
req.body = JSON.dump(body) if body
|
196
|
+
request(req)
|
197
|
+
end
|
198
|
+
|
199
|
+
def put(path, body, params = {}, headers = {})
|
200
|
+
req = Net::HTTP::Put.new(append_params(path, params))
|
201
|
+
headers.each { |key, value| req.add_field(key.to_s, value) }
|
202
|
+
req.basic_auth(user, key)
|
203
|
+
req.body = body
|
204
|
+
request(req)
|
205
|
+
end
|
206
|
+
|
207
|
+
def request(req, opts = {})
|
208
|
+
res = http.request(req)
|
209
|
+
handle(req, res, opts)
|
210
|
+
res.code.to_i
|
211
|
+
end
|
212
|
+
|
213
|
+
def http
|
214
|
+
http = Net::HTTP.new(url.host, url.port)
|
215
|
+
http.use_ssl = true
|
216
|
+
http
|
217
|
+
end
|
218
|
+
|
219
|
+
def append_params(path, params)
|
220
|
+
[path, *Array(params).map { |pair| pair.join('=') }].join(';')
|
221
|
+
end
|
222
|
+
|
223
|
+
def handle(req, res, opts = { raise: true })
|
224
|
+
error :request_failed, req.method, req.uri, res.code if opts[:raise] && !success?(res.code)
|
225
|
+
info :request_success, res.code, res.message, parse(res)['message'] unless opts[:silent]
|
226
|
+
res.code.to_i
|
227
|
+
end
|
228
|
+
|
229
|
+
def success?(code)
|
230
|
+
code.to_s[0].to_i == 2
|
231
|
+
end
|
232
|
+
|
233
|
+
def descriptor
|
234
|
+
@descriptor ||= symbolize(JSON.parse(File.read(file)))
|
235
|
+
rescue => e
|
236
|
+
error :invalid_file
|
237
|
+
end
|
238
|
+
|
239
|
+
def url
|
240
|
+
@url ||= URI.parse(super || URL)
|
241
|
+
end
|
242
|
+
|
243
|
+
def package
|
244
|
+
descriptor[:package]
|
245
|
+
end
|
246
|
+
|
247
|
+
def package_name
|
248
|
+
package[:name]
|
249
|
+
end
|
250
|
+
|
251
|
+
def package_attrs
|
252
|
+
package[:attributes]
|
253
|
+
end
|
254
|
+
|
255
|
+
def subject
|
256
|
+
package[:subject]
|
257
|
+
end
|
258
|
+
|
259
|
+
def repo
|
260
|
+
package[:repo]
|
261
|
+
end
|
262
|
+
|
263
|
+
def version
|
264
|
+
descriptor[:version]
|
265
|
+
end
|
266
|
+
|
267
|
+
def version_name
|
268
|
+
version[:name]
|
269
|
+
end
|
270
|
+
|
271
|
+
def version_attrs
|
272
|
+
version[:attributes]
|
273
|
+
end
|
274
|
+
|
275
|
+
def sign_version?
|
276
|
+
version[:gpgSign]
|
277
|
+
end
|
278
|
+
|
279
|
+
def publish_version?
|
280
|
+
descriptor[:publish]
|
281
|
+
end
|
282
|
+
|
283
|
+
def path(resource, args = {})
|
284
|
+
interpolate(PATHS[resource], args, secure: true)
|
285
|
+
end
|
286
|
+
|
287
|
+
def parse(json)
|
288
|
+
hash = JSON.parse(json)
|
289
|
+
hash.is_a?(Hash) ? hash : {}
|
290
|
+
rescue
|
291
|
+
{}
|
292
|
+
end
|
293
|
+
|
294
|
+
def compact(hash)
|
295
|
+
hash.reject { |_, value| value.nil? }
|
296
|
+
end
|
297
|
+
|
298
|
+
def only(hash, *keys)
|
299
|
+
hash.select { |key, _| keys.include?(key) }
|
300
|
+
end
|
301
|
+
|
302
|
+
class Upload < Struct.new(:source, :target, :params, :download)
|
303
|
+
def read
|
304
|
+
IO.read(source)
|
305
|
+
end
|
306
|
+
|
307
|
+
def eql?(other)
|
308
|
+
source == other.source
|
309
|
+
end
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|
313
|
+
end
|