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.
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