Exemplo n.º 1
0
def _check_version(branch):

    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!')
Exemplo n.º 2
0
def gh_deploy(config,
              message=None,
              force=False,
              ignore_version=False,
              shell=False):

    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=force,
                              use_shell=shell,
                              nojekyll=True)
    except ghp_import.GhpError as e:
        log.error("Failed to deploy to GitHub with error: \n{}".format(
            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, 'r')) 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:
        # 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}")
Exemplo n.º 3
0
def _is_cwd_git_repo():
    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
Exemplo n.º 4
0
Arquivo: serve.py Projeto: qqdb/mkdocs
def serve(config_file=None,
          dev_addr=None,
          strict=None,
          theme=None,
          theme_dir=None,
          livereload='livereload',
          watch_theme=False,
          **kwargs):
    """
    Start the MkDocs development server

    By default it will serve the documentation on http://localhost:8000/ and
    it will rebuild the documentation and refresh the page automatically
    whenever a file is edited.
    """

    # Create a temporary build directory, and set some options to serve it
    # PY2 returns a byte string by default. The Unicode prefix ensures a Unicode
    # string is returned. And it makes MkDocs temp dirs easier to identify.
    site_dir = tempfile.mkdtemp(prefix='mkdocs_')

    def builder():
        log.info("Building documentation...")
        config = load_config(config_file=config_file,
                             dev_addr=dev_addr,
                             strict=strict,
                             theme=theme,
                             theme_dir=theme_dir,
                             site_dir=site_dir,
                             **kwargs)
        # Override a few config settings after validation
        config['site_url'] = 'http://{}/'.format(config['dev_addr'])

        live_server = livereload in ['dirty', 'livereload']
        dirty = livereload == 'dirty'
        build(config, live_server=live_server, dirty=dirty)
        return config

    try:
        # Perform the initial build
        config = builder()

        host, port = config['dev_addr']

        if livereload in ['livereload', 'dirty']:
            _livereload(host, port, config, builder, site_dir, watch_theme)
        else:
            _static_server(host, port, site_dir)
    except OSError as e:  # pragma: no cover
        # Avoid ugly, unhelpful traceback
        raise Abort(str(e))
    finally:
        shutil.rmtree(site_dir)
Exemplo n.º 5
0
def build(config, live_server=False, dirty=False):
    """ Perform a full site build. """

    logger = logging.getLogger('mkdocs')

    # Add CountHandler for strict mode
    warning_counter = utils.CountHandler()
    warning_counter.setLevel(logging.WARNING)
    if config['strict']:
        logging.getLogger('mkdocs').addHandler(warning_counter)

    try:
        from time import time
        start = time()

        # Run `config` plugin events.
        config = config['plugins'].run_event('config', config)

        # Run `pre_build` plugin events.
        config['plugins'].run_event('pre_build', config=config)

        if not dirty:
            log.info("Cleaning site directory")
            utils.clean_directory(config['site_dir'])
        else:  # pragma: no cover
            # Warn user about problems that may occur with --dirty option
            log.warning(
                "A 'dirty' build is being performed, this will likely lead to inaccurate navigation and other"
                " links within your site. This option is designed for site development purposes only."
            )

        if not live_server:  # pragma: no cover
            log.info(
                f"Building documentation to directory: {config['site_dir']}")
            if dirty and site_directory_contains_stale_files(
                    config['site_dir']):
                log.info(
                    "The directory contains stale files. Use --clean to remove them."
                )

        # First gather all data from all files/pages to ensure all data is consistent across all pages.

        files = get_files(config)
        env = config['theme'].get_env()
        files.add_files_from_theme(env, config)

        # Run `files` plugin events.
        files = config['plugins'].run_event('files', files, config=config)

        nav = get_navigation(files, config)

        # Run `nav` plugin events.
        nav = config['plugins'].run_event('nav',
                                          nav,
                                          config=config,
                                          files=files)

        log.debug("Reading markdown pages.")
        for file in files.documentation_pages():
            log.debug(f"Reading: {file.src_path}")
            _populate_page(file.page, config, files, dirty)

        # Run `env` plugin events.
        env = config['plugins'].run_event('env',
                                          env,
                                          config=config,
                                          files=files)

        # Start writing files to site_dir now that all data is gathered. Note that order matters. Files
        # with lower precedence get written first so that files with higher precedence can overwrite them.

        log.debug("Copying static assets.")
        files.copy_static_files(dirty=dirty)

        for template in config['theme'].static_templates:
            _build_theme_template(template, env, files, config, nav)

        for template in config['extra_templates']:
            _build_extra_template(template, files, config, nav)

        log.debug("Building markdown pages.")
        doc_files = files.documentation_pages()
        for file in doc_files:
            _build_page(file.page, config, doc_files, nav, env, dirty)

        # Run `post_build` plugin events.
        config['plugins'].run_event('post_build', config=config)

        counts = warning_counter.get_counts()
        if counts:
            msg = ', '.join([f'{v} {k.lower()}s' for k, v in counts])
            raise Abort(f'\nAborted with {msg} in strict mode!')

        log.info('Documentation built in %.2f seconds', time() - start)

    except Exception as e:
        # Run `build_error` plugin events.
        config['plugins'].run_event('build_error', error=e)
        if isinstance(e, BuildError):
            log.error(str(e))
            raise Abort('\nAborted with a BuildError!')
        raise

    finally:
        logger.removeHandler(warning_counter)
Exemplo n.º 6
0
def serve(config_file=None, dev_addr=None, strict=None, theme=None,
          theme_dir=None, livereload='livereload', watch_theme=False, watch=[], **kwargs):
    """
    Start the MkDocs development server

    By default it will serve the documentation on http://localhost:8000/ and
    it will rebuild the documentation and refresh the page automatically
    whenever a file is edited.
    """

    # Create a temporary build directory, and set some options to serve it
    # PY2 returns a byte string by default. The Unicode prefix ensures a Unicode
    # string is returned. And it makes MkDocs temp dirs easier to identify.
    site_dir = tempfile.mkdtemp(prefix='mkdocs_')

    def mount_path(config):
        return urlsplit(config['site_url'] or '/').path

    def builder():
        log.info("Building documentation...")
        config = load_config(
            config_file=config_file,
            dev_addr=dev_addr,
            strict=strict,
            theme=theme,
            theme_dir=theme_dir,
            site_dir=site_dir,
            **kwargs
        )

        # combine CLI watch arguments with config file values
        if config["watch"] is None:
            config["watch"] = watch
        else:
            config["watch"].extend(watch)

        # Override a few config settings after validation
        config['site_url'] = 'http://{}{}'.format(config['dev_addr'], mount_path(config))

        live_server = livereload in ['dirty', 'livereload']
        dirty = livereload == 'dirty'
        build(config, live_server=live_server, dirty=dirty)
        return config

    try:
        # Perform the initial build
        config = builder()

        host, port = config['dev_addr']
        server = LiveReloadServer(builder=builder, host=host, port=port, root=site_dir, mount_path=mount_path(config))

        def error_handler(code):
            if code in (404, 500):
                error_page = join(site_dir, f'{code}.html')
                if isfile(error_page):
                    with open(error_page, 'rb') as f:
                        return f.read()

        server.error_handler = error_handler

        if livereload in ['livereload', 'dirty']:
            # Watch the documentation files, the config file and the theme files.
            server.watch(config['docs_dir'])
            server.watch(config['config_file_path'])

            if watch_theme:
                for d in config['theme'].dirs:
                    server.watch(d)

            # Run `serve` plugin events.
            server = config['plugins'].run_event('serve', server, config=config, builder=builder)

            for item in config['watch']:
                server.watch(item)

        try:
            server.serve()
        except KeyboardInterrupt:
            log.info("Shutting down...")
        finally:
            server.shutdown()
    except jinja2.exceptions.TemplateError:
        # This is a subclass of OSError, but shouldn't be suppressed.
        raise
    except OSError as e:  # pragma: no cover
        # Avoid ugly, unhelpful traceback
        raise Abort(f'{type(e).__name__}: {e}')
    finally:
        if isdir(site_dir):
            shutil.rmtree(site_dir)