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