170 lines
5.1 KiB
Python
170 lines
5.1 KiB
Python
|
from __future__ import annotations
|
||
|
|
||
|
import logging
|
||
|
import os
|
||
|
import re
|
||
|
import subprocess
|
||
|
from typing import TYPE_CHECKING
|
||
|
|
||
|
import ghp_import # type: ignore
|
||
|
from packaging import version
|
||
|
|
||
|
import mkdocs
|
||
|
from mkdocs.exceptions import Abort
|
||
|
|
||
|
if TYPE_CHECKING:
|
||
|
from mkdocs.config.defaults import MkDocsConfig
|
||
|
|
||
|
log = logging.getLogger(__name__)
|
||
|
|
||
|
default_message = """Deployed {sha} with MkDocs version: {version}"""
|
||
|
|
||
|
|
||
|
def _is_cwd_git_repo() -> bool:
|
||
|
try:
|
||
|
proc = subprocess.Popen(
|
||
|
['git', 'rev-parse', '--is-inside-work-tree'],
|
||
|
stdout=subprocess.PIPE,
|
||
|
stderr=subprocess.PIPE,
|
||
|
)
|
||
|
except FileNotFoundError:
|
||
|
log.error("Could not find git - is it installed and on your path?")
|
||
|
raise Abort('Deployment Aborted!')
|
||
|
proc.communicate()
|
||
|
return proc.wait() == 0
|
||
|
|
||
|
|
||
|
def _get_current_sha(repo_path) -> str:
|
||
|
proc = subprocess.Popen(
|
||
|
['git', 'rev-parse', '--short', 'HEAD'],
|
||
|
cwd=repo_path or None,
|
||
|
stdout=subprocess.PIPE,
|
||
|
stderr=subprocess.PIPE,
|
||
|
)
|
||
|
|
||
|
stdout, _ = proc.communicate()
|
||
|
sha = stdout.decode('utf-8').strip()
|
||
|
return sha
|
||
|
|
||
|
|
||
|
def _get_remote_url(remote_name: str) -> tuple[str, str] | tuple[None, None]:
|
||
|
# No CNAME found. We will use the origin URL to determine the GitHub
|
||
|
# Pages location.
|
||
|
remote = f"remote.{remote_name}.url"
|
||
|
proc = subprocess.Popen(
|
||
|
["git", "config", "--get", remote],
|
||
|
stdout=subprocess.PIPE,
|
||
|
stderr=subprocess.PIPE,
|
||
|
)
|
||
|
|
||
|
stdout, _ = proc.communicate()
|
||
|
url = stdout.decode('utf-8').strip()
|
||
|
|
||
|
if 'github.com/' in url:
|
||
|
host, path = url.split('github.com/', 1)
|
||
|
elif 'github.com:' in url:
|
||
|
host, path = url.split('github.com:', 1)
|
||
|
else:
|
||
|
return None, None
|
||
|
return host, path
|
||
|
|
||
|
|
||
|
def _check_version(branch: str) -> None:
|
||
|
proc = subprocess.Popen(
|
||
|
['git', 'show', '-s', '--format=%s', f'refs/heads/{branch}'],
|
||
|
stdout=subprocess.PIPE,
|
||
|
stderr=subprocess.PIPE,
|
||
|
)
|
||
|
|
||
|
stdout, _ = proc.communicate()
|
||
|
msg = stdout.decode('utf-8').strip()
|
||
|
m = re.search(r'\d+(\.\d+)+((a|b|rc)\d+)?(\.post\d+)?(\.dev\d+)?', msg, re.X | re.I)
|
||
|
previousv = version.parse(m.group()) if m else None
|
||
|
currentv = version.parse(mkdocs.__version__)
|
||
|
if not previousv:
|
||
|
log.warning('Version check skipped: No version specified in previous deployment.')
|
||
|
elif currentv > previousv:
|
||
|
log.info(
|
||
|
f'Previous deployment was done with MkDocs version {previousv}; '
|
||
|
f'you are deploying with a newer version ({currentv})'
|
||
|
)
|
||
|
elif currentv < previousv:
|
||
|
log.error(
|
||
|
f'Deployment terminated: Previous deployment was made with MkDocs version {previousv}; '
|
||
|
f'you are attempting to deploy with an older version ({currentv}). Use --ignore-version '
|
||
|
'to deploy anyway.'
|
||
|
)
|
||
|
raise Abort('Deployment Aborted!')
|
||
|
|
||
|
|
||
|
def gh_deploy(
|
||
|
config: MkDocsConfig,
|
||
|
message: str | None = None,
|
||
|
force=False,
|
||
|
no_history=False,
|
||
|
ignore_version=False,
|
||
|
shell=False,
|
||
|
) -> None:
|
||
|
if not _is_cwd_git_repo():
|
||
|
log.error('Cannot deploy - this directory does not appear to be a git repository')
|
||
|
|
||
|
remote_branch = config.remote_branch
|
||
|
remote_name = config.remote_name
|
||
|
|
||
|
if not ignore_version:
|
||
|
_check_version(remote_branch)
|
||
|
|
||
|
if message is None:
|
||
|
message = default_message
|
||
|
sha = _get_current_sha(os.path.dirname(config.config_file_path))
|
||
|
message = message.format(version=mkdocs.__version__, sha=sha)
|
||
|
|
||
|
log.info(
|
||
|
"Copying '%s' to '%s' branch and pushing to GitHub.",
|
||
|
config.site_dir,
|
||
|
config.remote_branch,
|
||
|
)
|
||
|
|
||
|
try:
|
||
|
ghp_import.ghp_import(
|
||
|
config.site_dir,
|
||
|
mesg=message,
|
||
|
remote=remote_name,
|
||
|
branch=remote_branch,
|
||
|
push=True,
|
||
|
force=force,
|
||
|
use_shell=shell,
|
||
|
no_history=no_history,
|
||
|
nojekyll=True,
|
||
|
)
|
||
|
except ghp_import.GhpError as e:
|
||
|
log.error(f"Failed to deploy to GitHub with error: \n{e.message}")
|
||
|
raise Abort('Deployment Aborted!')
|
||
|
|
||
|
cname_file = os.path.join(config.site_dir, 'CNAME')
|
||
|
# Does this repository have a CNAME set for GitHub Pages?
|
||
|
if os.path.isfile(cname_file):
|
||
|
# This GitHub Pages repository has a CNAME configured.
|
||
|
with open(cname_file) as f:
|
||
|
cname_host = f.read().strip()
|
||
|
log.info(
|
||
|
f'Based on your CNAME file, your documentation should be '
|
||
|
f'available shortly at: http://{cname_host}'
|
||
|
)
|
||
|
log.info(
|
||
|
'NOTE: Your DNS records must be configured appropriately for your CNAME URL to work.'
|
||
|
)
|
||
|
return
|
||
|
|
||
|
host, path = _get_remote_url(remote_name)
|
||
|
|
||
|
if host is None or path is None:
|
||
|
# This could be a GitHub Enterprise deployment.
|
||
|
log.info('Your documentation should be available shortly.')
|
||
|
else:
|
||
|
username, repo = path.split('/', 1)
|
||
|
if repo.endswith('.git'):
|
||
|
repo = repo[: -len('.git')]
|
||
|
url = f'https://{username}.github.io/{repo}/'
|
||
|
log.info(f"Your documentation should shortly be available at: {url}")
|