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