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,146 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dpl
4
+ module Providers
5
+ class Opsworks < Provider
6
+ register :opsworks
7
+
8
+ status :stable
9
+
10
+ full_name 'AWS OpsWorks'
11
+
12
+ description sq(<<-STR)
13
+ tbd
14
+ STR
15
+
16
+ gem 'aws-sdk-opsworks', '~> 1.0'
17
+
18
+ env :aws, :opsworks
19
+ config '~/.aws/credentials', '~/.aws/config', prefix: 'aws'
20
+
21
+ opt '--access_key_id ID', 'AWS access key id', required: true, secret: true
22
+ opt '--secret_access_key KEY', 'AWS secret key', required: true, secret: true
23
+ opt '--app_id APP', 'The app id', required: true
24
+ opt '--region REGION', 'AWS region', default: 'us-east-1'
25
+ opt '--instance_ids ID', 'An instance id', type: :array
26
+ opt '--layer_ids ID', 'A layer id', type: :array
27
+ opt '--migrate', 'Migrate the database.'
28
+ opt '--wait_until_deployed', 'Wait until the app is deployed and return the deployment status.'
29
+ opt '--update_on_success', 'When wait-until-deployed and updated-on-success are both not given, application source is updated to the current SHA. Ignored when wait-until-deployed is not given.', alias: :update_app_on_success
30
+ opt '--custom_json JSON', 'Custom json options override (overwrites default configuration)'
31
+
32
+ msgs login: 'Using Access Key: %{access_key_id}',
33
+ create_deploy: 'Creating deployment ... ',
34
+ done: 'Done: %s',
35
+ waiting: 'Deploying ',
36
+ failed: 'Failed.',
37
+ success: 'Success.',
38
+ update_app: 'Updating application source branch/revision setting.',
39
+ app_not_found: 'App %s not found.',
40
+ timeout: 'Timeout: failed to finish deployment within 10 minutes.',
41
+ service_error: 'Deployment failed. OpsWorks service error: %s',
42
+ comment: 'Deploy build %{build_number} via Travis CI'
43
+
44
+ def login
45
+ info :login
46
+ end
47
+
48
+ def deploy
49
+ timeout(600) { create_deployment }
50
+ rescue Aws::Errors::ServiceError => e
51
+ error :service_error, e.message
52
+ end
53
+
54
+ def create_deployment
55
+ print :create_deploy
56
+ id = opsworks.create_deployment(deploy_config)[:deployment_id]
57
+ info :done, id
58
+ wait_until_deployed(id) if wait_until_deployed?
59
+ end
60
+
61
+ def deploy_config
62
+ compact(
63
+ stack_id:,
64
+ app_id:,
65
+ command: { name: 'deploy' },
66
+ comment:,
67
+ custom_json:,
68
+ instance_ids:,
69
+ layer_ids:
70
+ )
71
+ end
72
+
73
+ def wait_until_deployed(id)
74
+ print :waiting
75
+ depl = poll_deployment(id) while depl.nil? || depl[:status] == 'running'
76
+ error :failed if depl[:status] != 'successful'
77
+ info :success
78
+ update_app if update_on_success?
79
+ end
80
+
81
+ def poll_deployment(id)
82
+ print '.'
83
+ sleep 5
84
+ describe_deployments(id)[:deployments].first
85
+ end
86
+
87
+ def update_app
88
+ info :update_app
89
+ opsworks.update_app(update_config)
90
+ end
91
+
92
+ def update_config
93
+ {
94
+ app_id:,
95
+ app_source: {
96
+ revision: git_sha
97
+ }
98
+ }
99
+ end
100
+
101
+ def custom_json
102
+ super || { deploy: { app[:shortname] => { migrate: migrate?, scm: { revision: git_sha } } } }.to_json
103
+ end
104
+
105
+ def stack_id
106
+ app[:stack_id]
107
+ end
108
+
109
+ def app
110
+ @app ||= describe_app
111
+ end
112
+
113
+ def comment
114
+ interpolate(msg(:comment))
115
+ end
116
+
117
+ def build_number
118
+ super || sha
119
+ end
120
+
121
+ def describe_app
122
+ data = opsworks.describe_apps(app_ids: [app_id])
123
+ error :app_not_found, app_id unless data[:apps]&.any?
124
+ data[:apps].first
125
+ end
126
+
127
+ def describe_deployments(id)
128
+ opsworks.describe_deployments(deployment_ids: [id])
129
+ end
130
+
131
+ def opsworks
132
+ @opsworks ||= Aws::OpsWorks::Client.new(region:, credentials:)
133
+ end
134
+
135
+ def credentials
136
+ Aws::Credentials.new(access_key_id, secret_access_key)
137
+ end
138
+
139
+ def timeout(sec, &block)
140
+ Timeout.timeout(sec, &block)
141
+ rescue Timeout::Error
142
+ error :timeout
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,194 @@
1
+ # talk to previous contributors about the logic in source_files
2
+ #
3
+ # the docs recommend to just `packagecloud push src target` https://packagecloud.io/docs#travis
4
+
5
+ module Dpl
6
+ module Providers
7
+ class Packagecloud < Provider
8
+ register :packagecloud
9
+
10
+ status :alpha
11
+
12
+ description sq(<<-str)
13
+ tbd
14
+ str
15
+
16
+ gem 'packagecloud-ruby', '~> 1.0.8', require: 'packagecloud'
17
+
18
+ env :packagecloud
19
+
20
+ opt '--username USER', 'The packagecloud.io username.', required: true
21
+ opt '--token TOKEN', 'The packagecloud.io api token.', required: true, secret: true
22
+ opt '--repository REPO', 'The repository to push to.', required: true
23
+ opt '--local_dir DIR', 'The sub-directory of the built assets for deployment.', default: '.'
24
+ opt '--dist DIST', 'Required for debian, rpm, and node.js packages (use "node" for node.js packages). The complete list of supported strings can be found on the packagecloud.io docs.'
25
+ opt '--force', 'Whether package has to be (re)uploaded / deleted before upload'
26
+ opt '--connect_timeout SEC', type: :integer, default: 60
27
+ opt '--read_timeout SEC', type: :integer, default: 60
28
+ opt '--write_timeout SEC', type: :integer, default: 180
29
+ opt '--package_glob GLOB', type: :array, default: ['**/*']
30
+
31
+ # previous implementation checks against all of these in dist_required? but
32
+ # then the error message only lists rmp, deb, python, and dsc. i think the
33
+ # error message might be out of date? so is the option description in the
34
+ # readme (and thus above).
35
+ NEED_DIST = %w(rpm deb dsc whl egg egg-info gz zip tar bz2 z tgz)
36
+
37
+ msgs authenticate: 'Logging in to https://packagecloud.io with %{username}:%{token}',
38
+ timeouts: 'Timeouts: %{timeout_info}',
39
+ unauthenticated: 'Could not authenticate to https://packagecloud.io, please check the credentials',
40
+ supported_packages: 'Supported packages: %s',
41
+ source_packages: 'Source packages: %s',
42
+ delete_package: 'Deleting package: %s on %s',
43
+ push_package: 'Pushing package: %s to %s/%s',
44
+ source_fragment: 'Found source fragment: %s for %s',
45
+ missing_packages: 'No supported packages found',
46
+ missing_dist: 'Distribution needed for rpm, deb, python, and dsc packages (e.g. dist: ubuntu/breezy)',
47
+ unknown_dist: 'Failed to find distribution %{dist}',
48
+ error: 'Error: %s'
49
+
50
+ def install
51
+ @cwd = Dir.pwd
52
+ Dir.chdir(local_dir)
53
+ end
54
+
55
+ def login
56
+ info :authenticate
57
+ info :timeouts
58
+ client
59
+ rescue ::Packagecloud::UnauthenticatedException
60
+ error :unauthenticated
61
+ end
62
+
63
+ def timeout_info
64
+ to_pairs(timeouts)
65
+ end
66
+
67
+ def validate
68
+ error :missing_packages if paths.empty?
69
+ error :missing_dist if missing_dist?
70
+ info :supported_packages, paths.join(', ')
71
+ info :source_packages, source_paths.join(', ') if source_paths.any?
72
+ end
73
+
74
+ def deploy
75
+ packages.each do |package|
76
+ delete(package) if force?
77
+ push(package)
78
+ end
79
+ end
80
+
81
+ def finish
82
+ Dir.chdir(@cwd)
83
+ end
84
+
85
+ private
86
+
87
+ def delete(package)
88
+ info :delete_package, package.filename, dist
89
+ result = client.delete_package(repository, *dist.split('/'), package.filename)
90
+ error "Error #{result.response}" unless result.succeeded
91
+ end
92
+
93
+ def push(package)
94
+ info :push_package, package.filename, username, repository
95
+ args = [repository, package]
96
+ args << distro if dist_required?(package.filename)
97
+ result = client.put_package(*args)
98
+ error :error, result.response unless result.succeeded
99
+ end
100
+
101
+ def packages
102
+ paths.map { |path| package(path) }
103
+ end
104
+
105
+ def package(path)
106
+ opts = { file: path }
107
+ opts[:source_files] = source_files(path) if source?(path)
108
+ ::Packagecloud::Package.new(opts)
109
+ end
110
+
111
+ def paths
112
+ @paths ||= glob(package_glob).select { |path| supported?(path) }
113
+ end
114
+
115
+ def source_paths
116
+ paths.select { |path| source?(path) }
117
+ end
118
+
119
+ # I believe this resembles the logic in the previous version, but it seems
120
+ # very odd to me. Files are considered source files only if they are already
121
+ # part of the package (it looks up the package from the client, and extracts
122
+ # the file names), and if they are not located in sub directories (it uses
123
+ # File.basename to filter). Not sure that's how it's supposed to work?
124
+ def source_files(path)
125
+ files = contents(path)
126
+ paths = glob('**/*').select { |path| files.include?(path) }
127
+ paths = paths.map { |path| [File.basename(path), path] }
128
+ paths.each { |name, _| info(:source_fragement, name, path) }
129
+ paths.map { |name, path| [name, open(path)] }.to_h
130
+ end
131
+
132
+ def contents(path)
133
+ package = ::Packagecloud::Package.new(file: path)
134
+ result = client.package_contents(repository, package, distro)
135
+ error :error, result.response unless result.succeeded
136
+ result.response['files'].map { |file| file['filename'] }
137
+ end
138
+
139
+ def distro
140
+ @distro ||= client.find_distribution_id(dist) || error(:unknown_dist)
141
+ rescue ArgumentError => e
142
+ error :error, e.message
143
+ end
144
+
145
+ def supported?(path)
146
+ ::Packagecloud::SUPPORTED_EXTENSIONS.include?(ext(path))
147
+ end
148
+
149
+ def source?(path)
150
+ ext(path) == 'dsc'
151
+ end
152
+
153
+ def dist_required?(path)
154
+ NEED_DIST.include?(ext(path))
155
+ end
156
+
157
+ def package_glob
158
+ "{#{super.join(',')}}"
159
+ end
160
+
161
+ def timeouts
162
+ only(opts, :connect_timeout, :read_timeout, :write_timeout)
163
+ end
164
+
165
+ def ext(path)
166
+ File.extname(path).to_s.gsub('.','').downcase
167
+ end
168
+
169
+ def glob(glob)
170
+ Dir.glob(glob).reject { |path| File.directory?(path) }
171
+ end
172
+
173
+ def missing_dist?
174
+ !dist? && paths.any? { |path| dist_required?(path) }
175
+ end
176
+
177
+ def client
178
+ @client ||= ::Packagecloud::Client.new(credentials, "travis-ci dpl #{Dpl::VERSION}", connection)
179
+ end
180
+
181
+ def credentials
182
+ ::Packagecloud::Credentials.new(username, token)
183
+ end
184
+
185
+ def connection
186
+ ::Packagecloud::Connection.new('https', 'packagecloud.io', '443', timeouts)
187
+ end
188
+
189
+ def to_pairs(hash)
190
+ hash.map { |pair| pair.join('=') }.join(' ')
191
+ end
192
+ end
193
+ end
194
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'timeout'
4
+
5
+ module Dpl
6
+ module Providers
7
+ class Pages
8
+ class Api < Pages
9
+ register :'pages:api'
10
+
11
+ status :dev
12
+
13
+ PAGES_PREVIEW_MEDIA_TYPE = 'application/vnd.github.mister-fantastic-preview+json'
14
+
15
+ # suppress warnings about preview API
16
+ ENV['OCTOKIT_SILENT'] = 'true'
17
+
18
+ TIMEOUTS = {
19
+ timeout: 180,
20
+ open_timeout: 180
21
+ }.freeze
22
+
23
+ gem 'octokit', '~> 7'
24
+
25
+ full_name 'GitHub Pages (API)'
26
+
27
+ description sq(<<-STR)
28
+ This provider requests GitHub Pages build for the repository given by
29
+ the `--repo` flag, or the current one, if the flag is not given.
30
+ Note that `dpl` does not perform any check about the fitness of the request;
31
+ it is assumed that the target repository (and the branch that GitHub Pages is
32
+ configured to use) is ready for building.
33
+ For example, if your GitHub Pages is configured to use `gh-pages` but the
34
+ deployment is run on the `master` branch, you would have to ensure that the
35
+ `gh-pages` would be updated accordingly during the build.
36
+ STR
37
+
38
+ opt '--repo SLUG', 'GitHub repo slug', default: :repo_slug
39
+ opt '--token TOKEN', 'GitHub oauth token with repo permission', required: true, secret: true, alias: :github_token
40
+
41
+ msgs not_found: sq(<<-MSG),
42
+ GitHub Pages not found for %{slug}.
43
+ Either the given token has insufficient scope (repo or public_repo), or
44
+ GitHub Pages is not enabled for this repo (see https://github.com/%{slug}/settings)'
45
+ MSG
46
+ timeout: 'GitHub Pages build request timed out',
47
+ deploy: 'Requesting GitHub Pages build using API'
48
+
49
+ def validate
50
+ error :not_found unless pages_enabled?
51
+ end
52
+
53
+ def deploy
54
+ info :deploy
55
+
56
+ api.request_page_build slug
57
+
58
+ response = api.pages slug
59
+ logger.debug response
60
+
61
+ Timeout.timeout(30) do
62
+ until response.status == 'built'
63
+ response = api.pages slug
64
+ logger.debug response
65
+ sleep 1
66
+ end
67
+ end
68
+
69
+ latest_pages_build = api.latest_pages_build slug
70
+ if msg = latest_pages_build.error.message
71
+ error "Build failed: #{msg}"
72
+ end
73
+
74
+ info "Pages deployed to #{response.html_url}, using commit #{latest_pages_build.commit}"
75
+ logger.debug latest_pages_build
76
+ rescue Octokit::Forbidden => e
77
+ error e.message
78
+ rescue Timeout::Error
79
+ error :timeout
80
+ end
81
+
82
+ private
83
+
84
+ def slug
85
+ repo || repo_slug
86
+ end
87
+
88
+ def api
89
+ ::Octokit.default_media_type = PAGES_PREVIEW_MEDIA_TYPE unless @api
90
+
91
+ @api ||= Octokit::Client.new(**creds, auto_paginate: true, connection_options: { request: TIMEOUTS })
92
+ end
93
+
94
+ def creds
95
+ { access_token: token }
96
+ end
97
+
98
+ def pages_enabled?
99
+ api.pages slug
100
+ rescue Octokit::NotFound
101
+ false
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,262 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dpl
4
+ module Providers
5
+ class Pages
6
+ class Git < Pages
7
+ register :'pages:git'
8
+
9
+ status :stable
10
+
11
+ full_name 'GitHub Pages'
12
+
13
+ description sq(<<-STR)
14
+ tbd
15
+ STR
16
+
17
+ gem 'octokit', '~> 7'
18
+ gem 'public_suffix', '~> 5'
19
+
20
+ required :token, :deploy_key
21
+
22
+ opt '--repo SLUG', 'Repo slug', default: :repo_slug
23
+ opt '--token TOKEN', 'GitHub token with repo permission', secret: true, alias: :github_token
24
+ opt '--deploy_key PATH', 'Path to a file containing a private deploy key with write access to the repository', see: 'https://developer.github.com/v3/guides/managing-deploy-keys/#deploy-keys'
25
+ opt '--target_branch BRANCH', 'Branch to push force to', default: 'gh-pages'
26
+ opt '--keep_history', 'Create incremental commit instead of doing push force', default: true
27
+ opt '--commit_message MSG', default: 'Deploy %{project_name} to %{url}:%{target_branch}', interpolate: true
28
+ opt '--allow_empty_commit', 'Allow an empty commit to be created', requires: :keep_history
29
+ opt '--verbose', 'Be verbose about the deploy process'
30
+ opt '--local_dir DIR', 'Directory to push to GitHub Pages', default: '.'
31
+ opt '--fqdn FQDN', 'Write the given domain name to the CNAME file'
32
+ opt '--project_name NAME', 'Used in the commit message only (defaults to fqdn or the current repo slug)'
33
+ opt '--name NAME', 'Committer name', note: 'defaults to the current git commit author name'
34
+ opt '--email EMAIL', 'Committer email', note: 'defaults to the current git commit author email'
35
+ opt '--committer_from_gh', 'Use the token\'s owner name and email for the commit', requires: :token
36
+ opt '--deployment_file', 'Enable creation of a deployment-info file'
37
+ opt '--url URL', default: 'github.com', alias: :github_url
38
+
39
+ needs :git
40
+
41
+ msgs login: 'Authenticated as %s',
42
+ invalid_token: 'The provided GitHub token is invalid (error: %s)',
43
+ insufficient_scopes: 'Dpl does not have permission to access %{url} using the provided GitHub token. Please make sure the token have the repo or public_repo scope.',
44
+ setup_deploy_key: 'Moving deploy key %{deploy_key} to %{path}',
45
+ check_deploy_key: 'Checking deploy key',
46
+ deploy: 'Deploying branch %{target_branch} to %{url}',
47
+ keep_history: 'The deployment is configured to preserve the target branch if it exists on remote.',
48
+ work_dir: 'Using temporary work directory %{work_dir}',
49
+ committer_from_gh: 'The repo is configured to use committer user and email.',
50
+ setup_dir: 'The source dir for deployment is %s',
51
+ git_clone: 'Cloning the branch %{target_branch} from the remote repo',
52
+ git_init: 'Initializing local git repo',
53
+ git_checkout: 'Checking out orphan branch %{target_branch}',
54
+ copy_files: 'Copying %{src_dir} contents to %{work_dir}',
55
+ git_config: 'Configuring git committer to be %{name} <%{email}>',
56
+ prepare: 'Preparing to deploy %{target_branch} branch to gh-pages',
57
+ git_push: 'Pushing to %{url}',
58
+ stop: 'There are no changes to commit, stopping.'
59
+
60
+ cmds git_clone: 'git clone --quiet --branch="%{target_branch}" --depth=1 "%{remote_url}" . > /dev/null 2>&1',
61
+ git_init: 'git init .',
62
+ git_checkout: 'git checkout --orphan "%{target_branch}"',
63
+ check_deploy_key: 'ssh -i %{key} -T git@%{url} 2>&1 | grep successful > /dev/null',
64
+ copy_files: 'rsync -rl --exclude .git --delete "%{src_dir}/" .',
65
+ git_config_email: 'git config user.email %{quoted_email}',
66
+ git_config_name: 'git config user.name %{quoted_name}',
67
+ deployment_file: 'touch "deployed at %{now} by %{name}"',
68
+ cname: 'echo "%{fqdn}" > CNAME',
69
+ git_add: 'git add -A .',
70
+ git_commit_hook: 'cp %{path} .git/hooks/pre-commit',
71
+ git_commit: 'git commit %{git_commit_opts} -q %{git_commit_msg_opts}',
72
+ git_show: 'git show --stat-count=10 HEAD',
73
+ git_push: 'git push%{git_push_opts} --quiet "%{remote_url}" "%{target_branch}":"%{target_branch}" > /dev/null 2>&1'
74
+
75
+ errs copy_files: 'Failed to copy %{src_dir}.',
76
+ check_deploy_key: 'Failed to authenticate using the deploy key',
77
+ git_init: 'Failed to create new git repo',
78
+ git_checkout: 'Failed to create the orphan branch',
79
+ git_push: 'Failed to push the build to %{url}:%{target_branch}'
80
+
81
+ def login
82
+ token? ? login_token : setup_deploy_key
83
+ end
84
+
85
+ def setup
86
+ info :setup_dir, src_dir
87
+ info :committer_from_gh if committer_from_gh?
88
+ info :git_config
89
+ end
90
+
91
+ def prepare
92
+ info :deploy
93
+ info :keep_history if keep_history?
94
+ debug :work_dir
95
+ Dir.chdir(work_dir)
96
+ end
97
+
98
+ def deploy
99
+ git_clone? ? git_clone : git_init
100
+ copy_files
101
+ return info :stop if git_clone? && !git_dirty?
102
+
103
+ git_config
104
+ git_commit
105
+ git_push
106
+ git_status if verbose?
107
+ end
108
+
109
+ private
110
+
111
+ def login_token
112
+ user.login
113
+ info :login, user.login
114
+ error :insufficient_scopes unless sufficient_scopes?
115
+ rescue Octokit::Unauthorized => e
116
+ error :invalid_token, e.message
117
+ end
118
+
119
+ def setup_deploy_key
120
+ path = '~/.dpl/deploy_key'
121
+ info(:setup_deploy_key, path:)
122
+ mv deploy_key, path
123
+ chmod 0600, path
124
+ setup_git_ssh path
125
+ shell :check_deploy_key, key: path, url: opts[:url]
126
+ end
127
+
128
+ def git_clone?
129
+ keep_history? && git_branch_exists?
130
+ end
131
+
132
+ def git_clone
133
+ shell :git_clone, echo: false
134
+ end
135
+
136
+ def git_init
137
+ shell :git_init
138
+ shell :git_checkout
139
+ end
140
+
141
+ def copy_files
142
+ shell :copy_files
143
+ end
144
+
145
+ def git_config
146
+ shell :git_config_name, echo: false
147
+ shell :git_config_email, echo: false
148
+ end
149
+
150
+ def git_commit
151
+ info :prepare
152
+ shell :git_commit_hook, path: asset(:git, :detect_private_key).path, echo: false if deploy_key?
153
+ shell :deployment_file if deployment_file?
154
+ shell :cname if fqdn?
155
+ shell :git_add
156
+ shell :git_commit
157
+ shell :git_show
158
+ end
159
+
160
+ def git_push
161
+ shell :git_push, echo: false
162
+ end
163
+
164
+ def git_status
165
+ shell 'git status'
166
+ end
167
+
168
+ def git_branch_exists?
169
+ git_ls_remote?(remote_url, target_branch)
170
+ end
171
+
172
+ def git_commit_opts
173
+ ' --allow-empty' if allow_empty_commit?
174
+ end
175
+
176
+ def git_commit_msg_opts
177
+ msg = interpolate(commit_message, vars:)
178
+ msg.split("\n").reject(&:empty?).map { |message| %(-m #{quote(message)}) }
179
+ end
180
+
181
+ def git_push_opts
182
+ ' --force' unless keep_history?
183
+ end
184
+
185
+ def name
186
+ str = super if name?
187
+ str ||= user.name if committer_from_gh?
188
+ str ||= git_author_name
189
+ str = "#{str} (via Travis CI)" if ENV['TRAVIS'] && !name?
190
+ str
191
+ end
192
+ memoize :name
193
+
194
+ def email
195
+ str = super if email?
196
+ str ||= user.email if committer_from_gh?
197
+ str || git_author_email
198
+ end
199
+ memoize :email
200
+
201
+ def project_name
202
+ super || fqdn || repo_slug
203
+ end
204
+
205
+ def sufficient_scopes?
206
+ api.scopes.include?('public_repo') || api.scopes.include?('repo')
207
+ end
208
+
209
+ def remote_url
210
+ token? ? https_url_with_token : git_url
211
+ end
212
+
213
+ def https_url_with_token
214
+ "https://#{token}@#{url}"
215
+ end
216
+
217
+ def git_url
218
+ "git@#{opts[:url]}:#{slug}.git"
219
+ end
220
+
221
+ def url
222
+ "#{opts[:url]}/#{slug}.git"
223
+ end
224
+
225
+ def slug
226
+ repo || repo_slug
227
+ end
228
+
229
+ def user
230
+ @user ||= api.user
231
+ rescue StandardError => e
232
+ puts "ERR: #{e.inspect}"
233
+ puts e.backtrace
234
+ end
235
+
236
+ def src_dir
237
+ @src_dir ||= expand(local_dir)
238
+ end
239
+
240
+ def work_dir
241
+ @work_dir ||= tmp_dir
242
+ end
243
+
244
+ def api
245
+ @api ||= Octokit::Client.new(access_token: token, api_endpoint:)
246
+ end
247
+
248
+ def api_endpoint
249
+ opts[:url] == 'github.com' ? 'https://api.github.com/' : "https://#{opts[:url]}/api/v3/"
250
+ end
251
+
252
+ def now
253
+ Time.now
254
+ end
255
+
256
+ def debug(*args)
257
+ info(*args) if verbose?
258
+ end
259
+ end
260
+ end
261
+ end
262
+ end