Mercurial > piecrust2
comparison piecrust/publishing/publisher.py @ 758:6abb436fea5b
publish: Make publisher more powerful and better exposed on the command line.
* Make the `chef publish` command have one sub-command per publish target.
* Add custom argument parsing per publisher to have strong extra arguments
available per publish target.
* Make publish targets a first class citizen of the `PieCrust` app class.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Sat, 25 Jun 2016 17:03:29 -0700 |
parents | 8f9c0bdb3724 |
children | 58ae026b4c31 |
comparison
equal
deleted
inserted
replaced
757:7147b06670fd | 758:6abb436fea5b |
---|---|
19 | 19 |
20 class Publisher(object): | 20 class Publisher(object): |
21 def __init__(self, app): | 21 def __init__(self, app): |
22 self.app = app | 22 self.app = app |
23 | 23 |
24 def run(self, target, preview=False, log_file=None): | 24 def run(self, target, |
25 force=False, preview=False, extra_args=None, log_file=None, | |
26 applied_config_variant=None, applied_config_values=None): | |
25 start_time = time.perf_counter() | 27 start_time = time.perf_counter() |
26 | 28 |
27 # Get the configuration for this target. | 29 # Get publisher for this target. |
28 target_cfg = self.app.config.get('publish/%s' % target) | 30 pub = self.app.getPublisher(target) |
29 if not target_cfg: | 31 if pub is None: |
30 raise InvalidPublishTargetError( | 32 raise InvalidPublishTargetError( |
31 "No such publish target: %s" % target) | 33 "No such publish target: %s" % target) |
32 | 34 |
33 target_type = None | 35 # Will we need to bake first? |
34 bake_first = True | 36 bake_first = True |
35 parsed_url = None | 37 if not pub.has_url_config: |
36 if isinstance(target_cfg, dict): | 38 bake_first = pub.getConfigValue('bake', True) |
37 target_type = target_cfg.get('type') | |
38 if not target_type: | |
39 raise InvalidPublishTargetError( | |
40 "Publish target '%s' doesn't specify a type." % target) | |
41 bake_first = target_cfg.get('bake', True) | |
42 elif isinstance(target_cfg, str): | |
43 comps = urllib.parse.urlparse(target_cfg) | |
44 if not comps.scheme: | |
45 raise InvalidPublishTargetError( | |
46 "Publish target '%s' has an invalid target URL." % | |
47 target) | |
48 parsed_url = comps | |
49 target_type = find_publisher_name(self.app, comps.scheme) | |
50 if target_type is None: | |
51 raise InvalidPublishTargetError( | |
52 "No such publish target scheme: %s" % comps.scheme) | |
53 | 39 |
54 # Setup logging stuff. | 40 # Setup logging stuff. |
55 hdlr = None | 41 hdlr = None |
56 root_logger = logging.getLogger() | 42 root_logger = logging.getLogger() |
57 if log_file and not preview: | 43 if log_file and not preview: |
62 logger.info("Deploying to %s" % target) | 48 logger.info("Deploying to %s" % target) |
63 else: | 49 else: |
64 logger.info("Previewing deployment to %s" % target) | 50 logger.info("Previewing deployment to %s" % target) |
65 | 51 |
66 # Bake first is necessary. | 52 # Bake first is necessary. |
67 bake_out_dir = None | 53 rec1 = None |
54 rec2 = None | |
55 was_baked = False | |
56 bake_out_dir = os.path.join(self.app.root_dir, '_pub', target) | |
68 if bake_first: | 57 if bake_first: |
69 bake_out_dir = os.path.join(self.app.cache_dir, 'pub', target) | |
70 if not preview: | 58 if not preview: |
71 bake_start_time = time.perf_counter() | 59 bake_start_time = time.perf_counter() |
72 logger.debug("Baking first to: %s" % bake_out_dir) | 60 logger.debug("Baking first to: %s" % bake_out_dir) |
73 | 61 |
74 from piecrust.baking.baker import Baker | 62 from piecrust.baking.baker import Baker |
75 baker = Baker(self.app, bake_out_dir) | 63 baker = Baker( |
64 self.app, bake_out_dir, | |
65 applied_config_variant=applied_config_variant, | |
66 applied_config_values=applied_config_values) | |
76 rec1 = baker.bake() | 67 rec1 = baker.bake() |
77 | 68 |
78 from piecrust.processing.pipeline import ProcessorPipeline | 69 from piecrust.processing.pipeline import ProcessorPipeline |
79 proc = ProcessorPipeline(self.app, bake_out_dir) | 70 proc = ProcessorPipeline( |
71 self.app, bake_out_dir, | |
72 applied_config_variant=applied_config_variant, | |
73 applied_config_values=applied_config_values) | |
80 rec2 = proc.run() | 74 rec2 = proc.run() |
75 | |
76 was_baked = True | |
81 | 77 |
82 if not rec1.success or not rec2.success: | 78 if not rec1.success or not rec2.success: |
83 raise Exception( | 79 raise Exception( |
84 "Error during baking, aborting publishing.") | 80 "Error during baking, aborting publishing.") |
85 logger.info(format_timed(bake_start_time, "Baked website.")) | 81 logger.info(format_timed(bake_start_time, "Baked website.")) |
86 else: | 82 else: |
87 logger.info("Would bake to: %s" % bake_out_dir) | 83 logger.info("Would bake to: %s" % bake_out_dir) |
88 | |
89 # Create the appropriate publisher. | |
90 pub = None | |
91 for pub_cls in self.app.plugin_loader.getPublishers(): | |
92 if pub_cls.PUBLISHER_NAME == target_type: | |
93 pub = pub_cls(self.app, target) | |
94 break | |
95 if pub is None: | |
96 raise InvalidPublishTargetError( | |
97 "Publish target '%s' has invalid type: %s" % | |
98 (target, target_type)) | |
99 pub.parsed_url = parsed_url | |
100 | 84 |
101 # Publish! | 85 # Publish! |
102 logger.debug( | 86 logger.debug( |
103 "Running publish target '%s' with publisher: %s" % | 87 "Running publish target '%s' with publisher: %s" % |
104 (target, pub.PUBLISHER_NAME)) | 88 (target, pub.PUBLISHER_NAME)) |
105 pub_start_time = time.perf_counter() | 89 pub_start_time = time.perf_counter() |
106 | 90 |
107 ctx = PublishingContext() | 91 ctx = PublishingContext() |
108 ctx.bake_out_dir = bake_out_dir | 92 ctx.bake_out_dir = bake_out_dir |
93 ctx.bake_record = rec1 | |
94 ctx.processing_record = rec2 | |
95 ctx.was_baked = was_baked | |
109 ctx.preview = preview | 96 ctx.preview = preview |
97 ctx.args = extra_args | |
110 try: | 98 try: |
111 success = pub.run(ctx) | 99 pub.run(ctx) |
112 except Exception as ex: | 100 except Exception as ex: |
113 raise PublishingError( | 101 raise PublishingError( |
114 "Error publishing to target: %s" % target) from ex | 102 "Error publishing to target: %s" % target) from ex |
115 finally: | 103 finally: |
116 if hdlr: | 104 if hdlr: |
117 root_logger.removeHandler(hdlr) | 105 root_logger.removeHandler(hdlr) |
118 hdlr.close() | 106 hdlr.close() |
119 | 107 |
120 if not success: | |
121 raise PublishingError( | |
122 "Unknown error publishing to target: %s" % target) | |
123 logger.info(format_timed( | 108 logger.info(format_timed( |
124 pub_start_time, "Ran publisher %s" % pub.PUBLISHER_NAME)) | 109 pub_start_time, "Ran publisher %s" % pub.PUBLISHER_NAME)) |
125 | 110 |
126 logger.info(format_timed(start_time, 'Deployed to %s' % target)) | 111 logger.info(format_timed(start_time, 'Deployed to %s' % target)) |
127 | 112 |
128 | 113 |
129 def find_publisher_class(app, scheme): | 114 def find_publisher_class(app, name, is_scheme=False): |
115 attr_name = 'PUBLISHER_SCHEME' if is_scheme else 'PUBLISHER_NAME' | |
130 for pub_cls in app.plugin_loader.getPublishers(): | 116 for pub_cls in app.plugin_loader.getPublishers(): |
131 pub_sch = getattr(pub_cls, 'PUBLISHER_SCHEME', None) | 117 pub_sch = getattr(pub_cls, attr_name, None) |
132 if ('bake+%s' % pub_sch) == scheme: | 118 if pub_sch == name: |
133 return pub_cls | 119 return pub_cls |
134 return None | 120 return None |
135 | 121 |
136 | 122 |
137 def find_publisher_name(app, scheme): | 123 def find_publisher_name(app, scheme): |
138 pub_cls = find_publisher_class(app, scheme) | 124 pub_cls = find_publisher_class(app, scheme, True) |
139 if pub_cls: | 125 if pub_cls: |
140 return pub_cls.PUBLISHER_NAME | 126 return pub_cls.PUBLISHER_NAME |
141 return None | 127 return None |
142 | 128 |