Mercurial > piecrust2
comparison foodtruck/sites.py @ 587:d4a01a023998
admin: Add "FoodTruck" admin panel from the side experiment project.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Sat, 16 Jan 2016 14:24:35 -0800 |
parents | |
children | e2c91ba44d6c |
comparison
equal
deleted
inserted
replaced
586:59268b4d8c71 | 587:d4a01a023998 |
---|---|
1 import os | |
2 import os.path | |
3 import shlex | |
4 import logging | |
5 import threading | |
6 import subprocess | |
7 from piecrust.app import PieCrust | |
8 | |
9 | |
10 logger = logging.getLogger(__name__) | |
11 | |
12 | |
13 class UnauthorizedSiteAccessError(Exception): | |
14 pass | |
15 | |
16 | |
17 class Site(object): | |
18 def __init__(self, name, root_dir, config): | |
19 self.name = name | |
20 self.root_dir = root_dir | |
21 self.config = config | |
22 self._piecrust_app = None | |
23 self._scm = None | |
24 self._bake_thread = None | |
25 logger.debug("Creating site object for %s" % self.name) | |
26 | |
27 @property | |
28 def piecrust_app(self): | |
29 if self._piecrust_app is None: | |
30 s = PieCrust(self.root_dir) | |
31 s.config.set('site/root', '/site/%s/' % self.name) | |
32 self._piecrust_app = s | |
33 return self._piecrust_app | |
34 | |
35 @property | |
36 def scm(self): | |
37 if self._scm is None: | |
38 scm_type = self.config.get('scm/type') | |
39 if scm_type == 'hg': | |
40 from .scm.mercurial import MercurialSourceControl | |
41 self._scm = MercurialSourceControl(self.root_dir) | |
42 else: | |
43 raise NotImplementedError() | |
44 return self._scm | |
45 | |
46 @property | |
47 def is_bake_running(self): | |
48 return self._bake_thread is not None and self._bake_thread.is_alive() | |
49 | |
50 @property | |
51 def bake_thread(self): | |
52 return self._bake_thread | |
53 | |
54 def bake(self): | |
55 bake_cmd = self.config.get('triggers/bake') | |
56 bake_args = shlex.split(bake_cmd) | |
57 | |
58 logger.debug("Running bake: %s" % bake_args) | |
59 proc = subprocess.Popen(bake_args, cwd=self.root_dir, | |
60 stdout=subprocess.PIPE, | |
61 stderr=subprocess.PIPE) | |
62 | |
63 pid_file_path = os.path.join(self.root_dir, 'foodtruck_bake.pid') | |
64 with open(pid_file_path, 'w') as fp: | |
65 fp.write(str(proc.pid)) | |
66 | |
67 logger.debug("Running bake monitor for PID %d" % proc.pid) | |
68 self._bake_thread = _BakeThread(self.name, self.root_dir, proc, | |
69 self._onBakeEnd) | |
70 self._bake_thread.start() | |
71 | |
72 def _onBakeEnd(self): | |
73 os.unlink(os.path.join(self.root_dir, 'foodtruck_bake.pid')) | |
74 self._bake_thread = None | |
75 | |
76 | |
77 class _BakeThread(threading.Thread): | |
78 def __init__(self, sitename, siteroot, proc, callback): | |
79 super(_BakeThread, self).__init__( | |
80 name='%s_bake' % sitename, daemon=True) | |
81 self.sitename = sitename | |
82 self.siteroot = siteroot | |
83 self.proc = proc | |
84 self.callback = callback | |
85 | |
86 log_file_path = os.path.join(self.siteroot, 'foodtruck_bake.log') | |
87 self.log_fp = open(log_file_path, 'w', encoding='utf8') | |
88 | |
89 def run(self): | |
90 for line in self.proc.stdout: | |
91 self.log_fp.write(line.decode('utf8')) | |
92 for line in self.proc.stderr: | |
93 self.log_fp.write(line.decode('utf8')) | |
94 self.proc.communicate() | |
95 if self.proc.returncode != 0: | |
96 self.log_fp.write("Error, bake process returned code %d" % | |
97 self.proc.returncode) | |
98 self.log_fp.close() | |
99 | |
100 logger.debug("Bake ended for %s." % self.sitename) | |
101 self.callback() | |
102 | |
103 | |
104 class FoodTruckSites(): | |
105 def __init__(self, config, current_site=None): | |
106 self._sites = {} | |
107 self._site_dirs = {} | |
108 self.config = config | |
109 self.current_site = current_site | |
110 | |
111 def get_root_dir(self, name=None): | |
112 name = name or self.current_site | |
113 s = self._site_dirs.get(name) | |
114 if s: | |
115 return s | |
116 | |
117 scfg = self.config.get('sites/%s' % name) | |
118 root_dir = scfg.get('path') | |
119 if root_dir is None: | |
120 raise Exception("Site '%s' has no path defined." % name) | |
121 if not os.path.isdir(root_dir): | |
122 raise Exception("Site '%s' has an invalid path." % name) | |
123 self._site_dirs[name] = root_dir | |
124 return root_dir | |
125 | |
126 def get(self, name=None): | |
127 name = name or self.current_site | |
128 s = self._sites.get(name) | |
129 if s: | |
130 return s | |
131 | |
132 root_dir = self.get_root_dir(name) | |
133 s = Site(name, root_dir, self.config) | |
134 self._sites[name] = s | |
135 return s | |
136 |