comparison piecrust/publishing/base.py @ 621:8f9c0bdb3724

publish: Polish/refactor the publishing workflows. * Add a `--preview` option. * The `--list` option gives a nicer output, and prints warnings/errors for incorrect configuration. * Moved most of the `shell` code into a base class that's reusable. * Simplified the code to log publishing to a file. * Nicer output overall, with times.
author Ludovic Chabant <ludovic@chabant.com>
date Mon, 08 Feb 2016 20:44:26 -0800
parents e2e955a3bb25
children 6abb436fea5b
comparison
equal deleted inserted replaced
620:c2708f20a87b 621:8f9c0bdb3724
1 import os.path
2 import shlex
3 import logging
4 import threading
5 import subprocess
6
7
8 logger = logging.getLogger(__name__)
1 9
2 10
3 class PublishingContext(object): 11 class PublishingContext(object):
4 def __init__(self): 12 def __init__(self):
5 self.custom_logging_file = None 13 self.bake_out_dir = None
14 self.preview = False
6 15
7 16
8 class Publisher(object): 17 class Publisher(object):
9 def __init__(self, app, target): 18 def __init__(self, app, target):
10 self.app = app 19 self.app = app
11 self.target = target 20 self.target = target
12 self.is_using_custom_logging = False 21 self.parsed_url = None
13 self.log_file_path = None 22 self.log_file_path = None
23
24 @property
25 def has_url_config(self):
26 return self.parsed_url is not None
27
28 @property
29 def url_config(self):
30 if self.parsed_url is not None:
31 return self.getConfig()
32 raise Exception("This publisher has a full configuration.")
14 33
15 def getConfig(self): 34 def getConfig(self):
16 return self.app.config.get('publish/%s' % self.target) 35 return self.app.config.get('publish/%s' % self.target)
17 36
18 def getConfigValue(self, name): 37 def getConfigValue(self, name):
38 if self.has_url_config:
39 raise Exception("This publisher only has a URL configuration.")
19 return self.app.config.get('publish/%s/%s' % (self.target, name)) 40 return self.app.config.get('publish/%s/%s' % (self.target, name))
20 41
21 def run(self, ctx): 42 def run(self, ctx):
22 raise NotImplementedError() 43 raise NotImplementedError()
23 44
45
46 class ShellCommandPublisherBase(Publisher):
47 def __init__(self, app, target):
48 super(ShellCommandPublisherBase, self).__init__(app, target)
49 self.expand_user_args = True
50
51 def run(self, ctx):
52 args = self._getCommandArgs(ctx)
53 if self.expand_user_args:
54 args = [os.path.expanduser(i) for i in args]
55
56 if ctx.preview:
57 preview_args = ' '.join([shlex.quote(i) for i in args])
58 logger.info(
59 "Would run shell command: %s" % preview_args)
60 return True
61
62 logger.debug(
63 "Running shell command: %s" % args)
64
65 proc = subprocess.Popen(
66 args, cwd=self.app.root_dir, bufsize=0,
67 stdout=subprocess.PIPE)
68
69 logger.debug("Running publishing monitor for PID %d" % proc.pid)
70 thread = _PublishThread(proc)
71 thread.start()
72 proc.wait()
73 thread.join()
74
75 if proc.returncode != 0:
76 logger.error(
77 "Publish process returned code %d" % proc.returncode)
78 else:
79 logger.debug("Publish process returned successfully.")
80
81 return proc.returncode == 0
82
83 def _getCommandArgs(self, ctx):
84 raise NotImplementedError()
85
86
87 class _PublishThread(threading.Thread):
88 def __init__(self, proc):
89 super(_PublishThread, self).__init__(
90 name='publish_monitor', daemon=True)
91 self.proc = proc
92 self.root_logger = logging.getLogger()
93
94 def run(self):
95 for line in iter(self.proc.stdout.readline, b''):
96 line_str = line.decode('utf8')
97 logger.info(line_str.rstrip('\r\n'))
98 for h in self.root_logger.handlers:
99 h.flush()
100
101 self.proc.communicate()
102 logger.debug("Publish monitor exiting.")
103