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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +172 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/CONTRIBUTING.md +392 -0
- data/Gemfile +32 -0
- data/Gemfile.lock +611 -0
- data/LICENSE +19 -0
- data/README.md +2744 -0
- data/Rakefile +210 -0
- data/bin/dpl +11 -0
- data/config/transliterate.yml +733 -0
- data/dpl.gemspec +23 -0
- data/lib/dpl/assets/atlas/install +19 -0
- data/lib/dpl/assets/convox/install +11 -0
- data/lib/dpl/assets/dpl/README.erb.md +138 -0
- data/lib/dpl/assets/dpl/git_ssh +8 -0
- data/lib/dpl/assets/git/detect_private_key +8 -0
- data/lib/dpl/assets/hephy/filter_log +3 -0
- data/lib/dpl/assets/pypi/install +4 -0
- data/lib/dpl/assets/scalingo/install +6 -0
- data/lib/dpl/cli.rb +100 -0
- data/lib/dpl/ctx/bash.rb +549 -0
- data/lib/dpl/ctx/test.rb +255 -0
- data/lib/dpl/ctx.rb +4 -0
- data/lib/dpl/helper/assets.rb +38 -0
- data/lib/dpl/helper/cmd.rb +169 -0
- data/lib/dpl/helper/config_file.rb +49 -0
- data/lib/dpl/helper/cookbook_site_streaming_uploader.rb +249 -0
- data/lib/dpl/helper/env.rb +92 -0
- data/lib/dpl/helper/github.rb +22 -0
- data/lib/dpl/helper/interpolate.rb +160 -0
- data/lib/dpl/helper/memoize.rb +23 -0
- data/lib/dpl/helper/squiggle.rb +24 -0
- data/lib/dpl/helper/transliterate.rb +13 -0
- data/lib/dpl/helper/wrap.rb +11 -0
- data/lib/dpl/helper/zip.rb +71 -0
- data/lib/dpl/provider/dsl.rb +410 -0
- data/lib/dpl/provider/examples.rb +132 -0
- data/lib/dpl/provider/status.rb +61 -0
- data/lib/dpl/provider.rb +651 -0
- data/lib/dpl/providers/anynines.rb +71 -0
- data/lib/dpl/providers/azure_web_apps.rb +63 -0
- data/lib/dpl/providers/bintray.rb +324 -0
- data/lib/dpl/providers/bluemixcloudfoundry.rb +98 -0
- data/lib/dpl/providers/boxfuse.rb +52 -0
- data/lib/dpl/providers/cargo.rb +32 -0
- data/lib/dpl/providers/chef_supermarket.rb +132 -0
- data/lib/dpl/providers/cloud66.rb +46 -0
- data/lib/dpl/providers/cloudfiles.rb +62 -0
- data/lib/dpl/providers/cloudformation.rb +281 -0
- data/lib/dpl/providers/cloudfoundry.rb +89 -0
- data/lib/dpl/providers/codedeploy.rb +190 -0
- data/lib/dpl/providers/convox.rb +130 -0
- data/lib/dpl/providers/datica.rb +64 -0
- data/lib/dpl/providers/ecr.rb +129 -0
- data/lib/dpl/providers/elasticbeanstalk.rb +207 -0
- data/lib/dpl/providers/engineyard.rb +113 -0
- data/lib/dpl/providers/firebase.rb +45 -0
- data/lib/dpl/providers/flynn.rb +35 -0
- data/lib/dpl/providers/gae.rb +78 -0
- data/lib/dpl/providers/gcs.rb +132 -0
- data/lib/dpl/providers/git_push.rb +273 -0
- data/lib/dpl/providers/gleis.rb +74 -0
- data/lib/dpl/providers/hackage.rb +53 -0
- data/lib/dpl/providers/hephy.rb +107 -0
- data/lib/dpl/providers/heroku/api.rb +123 -0
- data/lib/dpl/providers/heroku/git.rb +54 -0
- data/lib/dpl/providers/heroku.rb +111 -0
- data/lib/dpl/providers/lambda.rb +211 -0
- data/lib/dpl/providers/launchpad.rb +80 -0
- data/lib/dpl/providers/netlify.rb +38 -0
- data/lib/dpl/providers/npm.rb +130 -0
- data/lib/dpl/providers/nuget.rb +41 -0
- data/lib/dpl/providers/openshift.rb +52 -0
- data/lib/dpl/providers/opsworks.rb +146 -0
- data/lib/dpl/providers/packagecloud.rb_ +194 -0
- data/lib/dpl/providers/pages/api.rb +106 -0
- data/lib/dpl/providers/pages/git.rb +262 -0
- data/lib/dpl/providers/pages.rb +18 -0
- data/lib/dpl/providers/puppetforge.rb +50 -0
- data/lib/dpl/providers/pypi.rb +125 -0
- data/lib/dpl/providers/releases.rb +234 -0
- data/lib/dpl/providers/rubygems.rb +97 -0
- data/lib/dpl/providers/s3.rb +251 -0
- data/lib/dpl/providers/scalingo.rb +69 -0
- data/lib/dpl/providers/script.rb +32 -0
- data/lib/dpl/providers/snap.rb +68 -0
- data/lib/dpl/providers/surge.rb +59 -0
- data/lib/dpl/providers/testfairy.rb +101 -0
- data/lib/dpl/providers/transifex.rb +72 -0
- data/lib/dpl/providers.rb +48 -0
- data/lib/dpl/string_ext.rb +23 -0
- data/lib/dpl/support/aws_sdk_patch.rb +26 -0
- data/lib/dpl/support/gems.rb +73 -0
- data/lib/dpl/support/gstore_patch.rb +8 -0
- data/lib/dpl/support/version.rb +84 -0
- data/lib/dpl/version.rb +5 -0
- data/lib/dpl.rb +23 -0
- data/status.json +237 -0
- metadata +161 -0
@@ -0,0 +1,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
|