view piecrust/admin/siteinfo.py @ 1188:a7c43131d871

bake: Fix file write flushing problem with Python 3.8+ Writing the cache files fails in Python 3.8 because it looks like flushing behaviour has changed. We need to explicitly flush. And even then, in very rare occurrences, it looks like it can still run into racing conditions, so we do a very hacky and ugly "retry" loop when fetching cached data :(
author Ludovic Chabant <ludovic@chabant.com>
date Tue, 15 Jun 2021 22:36:23 -0700
parents 62900c42d6dd
children
line wrap: on
line source

import os
import os.path
import sys
import copy
import logging
import subprocess
from flask import flash
from piecrust import CACHE_DIR
from piecrust.app import PieCrustFactory


logger = logging.getLogger(__name__)


class UnauthorizedSiteAccessError(Exception):
    pass


class InvalidSiteError(Exception):
    pass


class SiteInfo:
    def __init__(self, root_dir, *, url_prefix='', debug=False):
        self.root_dir = root_dir
        self.url_prefix = url_prefix
        self.debug = debug
        self._piecrust_factory = None
        self._piecrust_app = None
        self._scm = None

    def make_url(self, rel_url):
        prefix = self.url_prefix
        if not prefix:
            return rel_url
        return prefix + rel_url

    @property
    def piecrust_factory(self):
        if self._piecrust_factory is None:
            self._piecrust_factory = PieCrustFactory(
                self.root_dir,
                cache_key='admin',
                debug=self.debug,
                config_values=[
                    ('site/root', self.make_url('/preview/')),
                    ('site/asset_url_format', self.make_url(
                        '/preview/_asset/%path%'))]
            )
        return self._piecrust_factory

    @property
    def piecrust_app(self):
        if self._piecrust_app is None:
            logger.debug("Creating PieCrust admin app: %s" % self.root_dir)
            self._piecrust_app = self.piecrust_factory.create()
        return self._piecrust_app

    @property
    def scm(self):
        if self._scm is None:
            cfg = copy.deepcopy(self.piecrust_app.config.get('scm', {}))

            if os.path.isdir(os.path.join(self.root_dir, '.hg')):
                from .scm.mercurial import MercurialSourceControl
                self._scm = MercurialSourceControl(self.root_dir, cfg)
            elif os.path.isdir(os.path.join(self.root_dir, '.git')):
                from .scm.git import GitSourceControl
                self._scm = GitSourceControl(self.root_dir, cfg)
            else:
                self._scm = False

        return self._scm

    @property
    def publish_pid_file(self):
        return os.path.join(self.piecrust_app.cache_dir, 'publish.pid')

    @property
    def publish_log_file(self):
        return os.path.join(self.piecrust_app.cache_dir, 'publish.log')

    def rebakeAssets(self):
        out_dir = os.path.join(
            self.root_dir,
            CACHE_DIR,
            self.piecrust_factory.cache_key,
            'server')
        args = [
            '--no-color',
            'bake',
            '-o', out_dir,
            '--assets-only']
        proc = self._runChef(args)
        try:
            proc.wait(timeout=2)
            if proc.returncode == 0:
                flash("Assets baked successfully!")
            else:
                flash("Asset baking process returned '%s'... check the log." %
                      proc.returncode)
        except subprocess.TimeoutExpired:
            flash("Asset baking process is still running... "
                  "check the log later.")

    def getPublishTargetLogFile(self, target):
        target = target.replace(' ', '_').lower()
        return os.path.join(self.piecrust_app.cache_dir,
                            'publish.%s.log' % target)

    def publish(self, target):
        args = [
            '--no-color',
            '--pid-file', self.publish_pid_file,
            '--log', self.publish_log_file,
            'publish',
            '--log-publisher', self.getPublishTargetLogFile(target),
            '--log-debug-info',
            target]
        proc = self._runChef(args)
        try:
            proc.wait(timeout=2)
            if proc.returncode == 0:
                flash("Publish process ran successfully!")
            else:
                flash("Publish process returned '%s'... check the log." %
                      proc.returncode)
        except subprocess.TimeoutExpired:
            flash("Publish process is still running... check the log later.")

    def runTask(self, task_id):
        args = [
            '--no-color',
            'tasks', 'run',
            '-t', task_id]
        self._runChef(args)

    def _runChef(self, args):
        chef_path = os.path.realpath(os.path.join(
            os.path.dirname(__file__),
            '../../chef.py'))
        args = [sys.executable, chef_path] + args

        logger.info("Running chef command: %s" % args)
        proc = subprocess.Popen(args, cwd=self.root_dir)
        logger.info("Chef process ID: %s" % proc.pid)
        return proc