Repository: getnikola/nikola

Issue:
Allow multiple sets of deployment commands
Whatever your use may be, I find myself in need of two or three different deployment presets of commands for the same site.

The `deploy` command should support named presets of commands.

Example use-cases: `nikola deploy main`, `nikola deploy server2`, `nikola deploy force`, `nikola deploy delayed`.

Currently, I open up the conf.py file and comment out blocks of commands as needed. Not the best work-flow.



Discussion:
Turn it into a dict, handle list input properly for backwards compatibility (with a warning, handle in nikola.py as everything else).  Have a `default` preset.  Shouldn’t be too hard to implement.  Want to go for it?

I’ve got too many projects going on at once now. Can’t commit to it at this time. Will return to it at some later time as time permits.

I’ll do it then.  Possibly even right now.


Resolving patch:
diff --git a/nikola/nikola.py b/nikola/nikola.py
--- a/nikola/nikola.py
+++ b/nikola/nikola.py
@@ -255,7 +255,7 @@ def __init__(self, **config):
             'CREATE_SINGLE_ARCHIVE': False,
             'DATE_FORMAT': '%Y-%m-%d %H:%M',
             'DEFAULT_LANG': "en",
-            'DEPLOY_COMMANDS': [],
+            'DEPLOY_COMMANDS': {'default': []},
             'DISABLED_PLUGINS': [],
             'EXTRA_PLUGINS_DIRS': [],
             'COMMENT_SYSTEM_ID': 'nikolademo',
@@ -501,6 +501,14 @@ def __init__(self, **config):
         if self.config['BASE_URL'] and self.config['BASE_URL'][-1] != '/':
             utils.LOGGER.warn("Your BASE_URL doesn't end in / -- adding it, but please fix it in your config file!")
 
+        # todo: remove in v8
+        if not isinstance(self.config['DEPLOY_COMMANDS'], dict):
+            utils.LOGGER.warn("A single list as DEPLOY_COMMANDS is deprecated.  DEPLOY_COMMANDS should be a dict, with deploy preset names as keys and lists of commands as values.")
+            utils.LOGGER.warn("The key `default` is used by `nikola deploy`:")
+            self.config['DEPLOY_COMMANDS'] = {'default': self.config['DEPLOY_COMMANDS']}
+            utils.LOGGER.warn("DEPLOY_COMMANDS = {0}".format(self.config['DEPLOY_COMMANDS']))
+            utils.LOGGER.info("(The above can be used with `nikola deploy` or `nikola deploy default`.  Multiple presets are accepted.)")
+
         # We use one global tzinfo object all over Nikola.
         self.tzinfo = dateutil.tz.gettz(self.config['TIMEZONE'])
         self.config['__tzinfo__'] = self.tzinfo
diff --git a/nikola/plugins/command/deploy.py b/nikola/plugins/command/deploy.py
--- a/nikola/plugins/command/deploy.py
+++ b/nikola/plugins/command/deploy.py
@@ -43,9 +43,9 @@ class CommandDeploy(Command):
     """Deploy site."""
     name = "deploy"
 
-    doc_usage = ""
+    doc_usage = "[[preset [preset...]]"
     doc_purpose = "deploy the site"
-
+    doc_description = "Deploy the site by executing deploy commands from the presets listed on the command line.  If no presets are specified, `default` is executed."
     logger = None
 
     def _execute(self, command, args):
@@ -74,14 +74,29 @@ def _execute(self, command, args):
                     remove_file(os.path.join(out_dir, post.source_path))
                     undeployed_posts.append(post)
 
-        for command in self.site.config['DEPLOY_COMMANDS']:
-            self.logger.info("==> {0}".format(command))
+        if args:
+            presets = args
+        else:
+            presets = ['default']
+
+        # test for preset existence
+        for preset in presets:
             try:
-                subprocess.check_call(command, shell=True)
-            except subprocess.CalledProcessError as e:
-                self.logger.error('Failed deployment — command {0} '
-                                  'returned {1}'.format(e.cmd, e.returncode))
-                sys.exit(e.returncode)
+                self.site.config['DEPLOY_COMMANDS'][preset]
+            except:
+                self.logger.error('No such preset: {0}'.format(preset))
+                sys.exit(255)
+
+        for preset in presets:
+            self.logger.info("=> preset '{0}'".format(preset))
+            for command in self.site.config['DEPLOY_COMMANDS'][preset]:
+                self.logger.info("==> {0}".format(command))
+                try:
+                    subprocess.check_call(command, shell=True)
+                except subprocess.CalledProcessError as e:
+                    self.logger.error('Failed deployment — command {0} '
+                                      'returned {1}'.format(e.cmd, e.returncode))
+                    sys.exit(e.returncode)
 
         self.logger.info("Successful deployment")
         try:


Test patch: