-
Notifications
You must be signed in to change notification settings - Fork 367
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
420 additions
and
241 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,249 +1,22 @@ | ||
module Dpl | ||
module Providers | ||
class Pages < Provider | ||
status :alpha | ||
|
||
full_name 'GitHub Pages' | ||
|
||
description sq(<<-str) | ||
tbd | ||
str | ||
|
||
gem 'octokit', '~> 4.14.0' | ||
gem 'public_suffix', '~> 3.0.3' | ||
|
||
required :github_token, :deploy_key | ||
|
||
opt '--github_token TOKEN', 'GitHub oauth token with repo permission', secret: true | ||
opt '--deploy_key KEY', 'A base64-encoded, private deploy key with write access to the repository', note: 'RSA keys are too long to fit into a Travis CI secure variable, but ECDSA-521 fits', see: 'https://developer.github.com/v3/guides/managing-deploy-keys/#deploy-keys' | ||
opt '--repo SLUG', 'Repo slug', default: :repo_slug | ||
opt '--target_branch BRANCH', 'Branch to push force to', default: 'gh-pages' | ||
opt '--keep_history', 'Create incremental commit instead of doing push force', default: true | ||
opt '--commit_message MSG', default: 'Deploy %{project_name} to %{url}:%{target_branch}' | ||
opt '--allow_empty_commit', 'Allow an empty commit to be created', requires: :keep_history | ||
opt '--committer_from_gh', 'Use the token\'s owner name and email for commit. Overrides the email and name options' | ||
opt '--verbose', 'Be verbose about the deploy process' | ||
opt '--local_dir DIR', 'Directory to push to GitHub Pages', default: '.' | ||
opt '--fqdn FQDN', 'Writes your website\'s domain name to the CNAME file' | ||
opt '--project_name NAME', 'Used in the commit message only (defaults to fqdn or the current repo slug)' | ||
opt '--email EMAIL', 'Committer email', default: 'deploy@travis-ci.org' | ||
opt '--name NAME', 'Committer name', default: 'Deploy Bot' | ||
opt '--deployment_file', 'Enable creation of a deployment-info file' | ||
opt '--github_url URL', default: 'github.com' | ||
|
||
needs :git | ||
|
||
msgs login: 'Authenticated as %s', | ||
invalid_token: 'The provided GitHub token is invalid (error: %s)', | ||
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.', | ||
setup_deploy_key: 'Setting up deploy key in %{path}', | ||
deploy: 'Deploying branch %{target_branch} to %{github_url}', | ||
keep_history: 'The deployment is configured to preserve the target branch if it exists on remote.', | ||
work_dir: 'Using temporary work directory %{work_dir}', | ||
committer_from_gh: 'The repo is configured to use committer user and email.', | ||
setup_dir: 'The source dir for deployment is %s', | ||
git_clone: 'Cloning the branch %{target_branch} from the remote repo', | ||
git_init: 'Initializing local git repo', | ||
git_checkout: 'Checking out orphan branch %{target_branch}', | ||
copy_files: 'Copying %{src_dir} contents to %{work_dir}', | ||
git_config: 'Configuring git committer to be %{committer_name} <%{committer_email}>', | ||
prepare: 'Preparing to deploy %{target_branch} branch to gh-pages', | ||
git_push: 'Pushing to %{url}' | ||
|
||
cmds git_clone: 'git clone --quiet --branch="%{target_branch}" --depth=1 "%{remote_url}" . > /dev/null 2>&1', | ||
git_init: 'git init .', | ||
git_checkout: 'git checkout --orphan "%{target_branch}"', | ||
check_deploy_key: 'ssh -i %{key} -T %{git_url}', | ||
copy_files: 'rsync -rl --exclude .git --delete "%{src_dir}/" .', | ||
git_config_email: 'git config user.email "%{committer_email}"', | ||
git_config_name: 'git config user.name "%{committer_name}"', | ||
deployment_file: 'touch "deployed at %{now} by %{committer_name}"', | ||
cname: 'echo "%{fqdn}" > CNAME', | ||
git_add: 'git add -A .', | ||
git_commit: 'git commit%{git_commit_opts} -qm "%{commit_message}"', | ||
git_show: 'git show --stat-count=10 HEAD', | ||
git_push: 'git push%{git_push_opts} --quiet "%{remote_url}" "%{target_branch}":"%{target_branch}" > /dev/null 2>&1' | ||
|
||
errs copy_files: 'Failed to copy %{src_dir}.', | ||
check_deploy_key: 'Failed to authenticate using the deploy key', | ||
git_init: 'Failed to create new git repo', | ||
git_checkout: 'Failed to create the orphan branch', | ||
git_push: 'Failed to push the build to %{url}:%{target_branch}' | ||
|
||
def login | ||
github_token? ? login_token : login_deploy_key | ||
end | ||
|
||
def setup | ||
info :setup_dir, src_dir | ||
info :committer_from_gh if committer_from_gh? | ||
end | ||
|
||
def prepare | ||
info :deploy | ||
info :keep_history if keep_history? | ||
debug :work_dir | ||
Dir.chdir(work_dir) | ||
end | ||
|
||
def deploy | ||
git_clone? ? git_clone : git_init | ||
copy_files | ||
git_config | ||
git_commit | ||
git_push | ||
git_status if verbose? | ||
end | ||
|
||
private | ||
|
||
def login_token | ||
user.login | ||
info :login, user.login | ||
error :insufficient_scopes unless sufficient_scopes? | ||
rescue Octokit::Unauthorized => e | ||
error :invalid_token, e.message | ||
end | ||
|
||
def login_deploy_key | ||
setup_deploy_key if deploy_key? | ||
end | ||
|
||
def git_clone? | ||
keep_history? && git_branch_exists? | ||
end | ||
|
||
def git_clone | ||
shell :git_clone, echo: false | ||
end | ||
|
||
def git_init | ||
shell :git_init | ||
shell :git_checkout | ||
end | ||
|
||
def copy_files | ||
shell :copy_files | ||
end | ||
|
||
def git_config | ||
info :git_config | ||
shell :git_config_name, echo: false | ||
shell :git_config_email, echo: false | ||
end | ||
|
||
def git_commit | ||
info :prepare | ||
shell :deployment_file if deployment_file? | ||
shell :cname if fqdn? | ||
shell :git_add | ||
shell :git_commit | ||
shell :git_show | ||
end | ||
|
||
def git_push | ||
shell :git_push, echo: false | ||
end | ||
|
||
def git_status | ||
shell 'git status' | ||
end | ||
|
||
def setup_deploy_key | ||
path = "~/.dpl/deploy_key" | ||
info :setup_deploy_key, path: path | ||
write_file path, decoded_deploy_key, 0600 | ||
setup_git_ssh path | ||
shell :check_deploy_key, key: path | ||
end | ||
|
||
def decoded_deploy_key | ||
Base64.decode64(deploy_key) | ||
def self.new(ctx, args) | ||
return super unless registry_key.to_sym == :pages | ||
arg = args.detect { |arg| arg.include? '--strategy' } | ||
strategy = arg ? arg.split('=', 2).last : 'git' | ||
Provider[:"pages:#{strategy}"].new(ctx, args) | ||
end | ||
|
||
def git_branch_exists? | ||
git_ls_remote?(remote_url, target_branch) | ||
end | ||
|
||
def git_commit_opts | ||
' --allow-empty' if allow_empty_commit? | ||
end | ||
|
||
def git_push_opts | ||
' --force' unless keep_history? | ||
end | ||
|
||
def committer_name | ||
committer_from_gh? ? user.name || name : name | ||
end | ||
|
||
def committer_email | ||
committer_from_gh? ? user.email || email : email | ||
end | ||
|
||
def commit_message | ||
interpolate(super) | ||
end | ||
|
||
def project_name | ||
super || fqdn || repo_slug | ||
end | ||
|
||
def name | ||
"#{super} (from Travis CI)" | ||
end | ||
|
||
def sufficient_scopes? | ||
api.scopes.include?('public_repo') || api.scopes.include?('repo') | ||
end | ||
|
||
def remote_url | ||
github_token? ? https_url_with_token : git_url | ||
end | ||
|
||
def https_url_with_token | ||
"https://#{github_token}@#{url}" | ||
end | ||
opt '--strategy NAME', 'GitHub Pages deployment strategy', default: 'git', enum: %w(api git), internal: true | ||
opt '--github_token TOKEN', 'GitHub oauth token with repo permission', required: true, secret: true | ||
opt '--repo SLUG', 'GitHub repo slug', default: :repo_slug | ||
|
||
def git_url | ||
"git@#{github_url}:#{slug}.git" | ||
end | ||
|
||
def url | ||
"#{github_url}/#{slug}.git" | ||
end | ||
|
||
def slug | ||
repo || repo_slug | ||
end | ||
|
||
def user | ||
@user ||= api.user | ||
end | ||
|
||
def src_dir | ||
@src_dir ||= expand(local_dir) | ||
end | ||
|
||
def work_dir | ||
@work_dir ||= tmp_dir | ||
end | ||
|
||
def api | ||
@api ||= Octokit::Client.new(access_token: github_token, api_endpoint: api_endpoint) | ||
end | ||
|
||
def api_endpoint | ||
github_url == 'github.com' ? 'https://api.github.com/' : "https://#{github_url}/api/v3/" | ||
end | ||
|
||
def now | ||
Time.now | ||
end | ||
|
||
def debug(*args) | ||
info *args if verbose? | ||
end | ||
msgs deploy: 'Deploying to repo: %{slug}', | ||
insufficient_scopes: 'Dpl does not have permission to deploy to GitHub Pages. Ensure your token has repo scope.' | ||
end | ||
end | ||
end | ||
|
||
require 'dpl/providers/pages/git' | ||
require 'dpl/providers/pages/api' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
require 'timeout' | ||
|
||
module Dpl | ||
module Providers | ||
class Pages | ||
class Api < Pages | ||
PAGES_PREVIEW_MEDIA_TYPE = 'application/vnd.github.mister-fantastic-preview+json' | ||
|
||
# suppress warnings about preview API | ||
ENV['OCTOKIT_SILENT'] = 'true' | ||
|
||
TIMEOUTS = { | ||
timeout: 180, | ||
open_timeout: 180 | ||
} | ||
|
||
gem 'octokit', '~> 4.14.0' | ||
|
||
status :alpha | ||
|
||
full_name 'GitHub Pages (API)' | ||
|
||
description sq(<<-str) | ||
This provider requests GitHub Pages build for the repository given by | ||
the `--repo` flag, or the current one, if the flag is not given. | ||
Note that `dpl` does not perform any check about the fitness of the request; | ||
it is assumed that the target repository (and the branch that GitHub Pages is | ||
configured to use) is ready for building. | ||
For example, if your GitHub Pages is configured to use `gh-pages` but the | ||
deployment is run on the `master` branch, you would have to ensure that the | ||
`gh-pages` would be updated accordingly during the build. | ||
str | ||
|
||
msgs pages_not_found: 'GitHub Pages not found for %{slug}. ' \ | ||
'Given token has insufficient scope (\'repo\' or \'public_repo\'), ' \ | ||
'or GitHub Pages is not enabled for this repo (see https://github.com/%{slug}/settings)', | ||
build_pages_request_timeout: 'GitHub Pages build request timed out', | ||
deploy_start: 'Requesting GitHub Pages build using API' | ||
|
||
def validate | ||
error :pages_not_found unless pages_enabled? | ||
end | ||
|
||
def deploy | ||
info :deploy_start | ||
|
||
api.request_page_build slug | ||
|
||
response = api.pages slug | ||
logger.debug response | ||
|
||
Timeout::timeout(30) do | ||
until response.status == 'built' | ||
response = api.pages slug | ||
logger.debug response | ||
sleep 1 | ||
end | ||
end | ||
|
||
latest_pages_build = api.latest_pages_build slug | ||
if msg = latest_pages_build.error.message | ||
error "Build failed: #{msg}" | ||
end | ||
|
||
info "Pages deployed to #{response.html_url}, using commit #{latest_pages_build.commit}" | ||
logger.debug latest_pages_build | ||
|
||
rescue Octokit::Forbidden => fb | ||
error fb.message | ||
rescue Timeout::Error => to | ||
error :build_pages_request_timeout | ||
end | ||
|
||
private | ||
|
||
def slug | ||
repo || repo_slug | ||
end | ||
|
||
def api | ||
::Octokit.default_media_type = PAGES_PREVIEW_MEDIA_TYPE unless @api | ||
|
||
@api ||= Octokit::Client.new(**creds, auto_paginate: true, connection_options: { request: TIMEOUTS }) | ||
end | ||
|
||
def creds | ||
{ access_token: github_token } | ||
end | ||
|
||
def pages_enabled? | ||
api.pages slug | ||
rescue Octokit::NotFound => e | ||
false | ||
end | ||
|
||
end | ||
end | ||
end | ||
end |
Oops, something went wrong.