changeset 1:6bbebb01f614

Bunch of fixes: * Don't change the current working directory. * Clone the repo as late as possible. * Add `--scan-only` and `--status` options. * Fix `first_tag`. * Correctly pull and update an existing clone. * Use the system's temp directory if no temp directory is specified.
author Ludovic Chabant <ludovic@chabant.com>
date Sat, 28 Mar 2015 15:43:44 -0700
parents ee98303e24b8
children 08dfce548a9f
files september.py
diffstat 1 files changed, 78 insertions(+), 33 deletions(-) [+]
line wrap: on
line diff
--- a/september.py	Wed Mar 04 23:33:07 2015 -0800
+++ b/september.py	Sat Mar 28 15:43:44 2015 -0700
@@ -4,7 +4,9 @@
 import json
 import os.path
 import logging
+import hashlib
 import argparse
+import tempfile
 import subprocess
 import configparser
 from urllib.parse import urlparse
@@ -17,7 +19,7 @@
     def clone(self, repo_url, repo_path):
         raise NotImplementedError()
 
-    def pull(self, repo_path):
+    def pull(self, repo_path, remote):
         raise NotImplementedError()
 
     def getTags(self, repo_path):
@@ -31,11 +33,13 @@
     def clone(self, repo_url, repo_path):
         subprocess.check_call(['git', 'clone', repo_url, repo_path])
 
-    def pull(self, repo_path):
-        subprocess.check_call(['git', 'pull', 'origin', 'master'])
+    def pull(self, repo_path, remote):
+        subprocess.check_call(['git', '-C', repo_path,
+                               'pull', remote, 'master'])
 
     def getTags(self, repo_path):
-        output = subprocess.check_output(['git', 'show-ref', '--tags'])
+        output = subprocess.check_output(['git', '-C', repo_path,
+                                          'show-ref', '--tags'])
         pat = re.compile(r'^(?P<id>[0-9a-f]+) (?P<tag>.+)$')
         for line in output.split('\n'):
             m = pat.match(line)
@@ -44,20 +48,21 @@
 
     def update(self, repo_path, rev_id):
         rev_id = rev_id or 'master'
-        subprocess.check_call(['git', 'checkout', rev_id])
+        subprocess.check_call(['git', '-C', repo_path, 'checkout', rev_id])
 
 
 class MercurialRepo(object):
     def clone(self, repo_url, repo_path):
         subprocess.check_call(['hg', 'clone', repo_url, repo_path])
 
-    def pull(self, repo_path):
-        subprocess.check_call(['hg', 'pull'],
+    def pull(self, repo_path, remote):
+        subprocess.check_call(['hg', '-R', repo_path, 'pull', remote],
                               stderr=subprocess.STDOUT)
 
     def getTags(self, repo_path):
         output = subprocess.check_output(
-                'hg log -r "tag()" --template "{tags} {node}\\n"',
+                ('hg -R "' + repo_path +
+                    '" log -r "tag()" --template "{tags} {node}\\n"'),
                 stderr=subprocess.STDOUT,
                 universal_newlines=True,
                 shell=True)
@@ -69,7 +74,7 @@
 
     def update(self, repo_path, rev_id):
         rev_id = rev_id or 'default'
-        subprocess.check_call(['hg', 'update', rev_id],
+        subprocess.check_call(['hg', '-R', repo_path, 'update', rev_id],
                               stderr=subprocess.STDOUT)
 
 
@@ -105,6 +110,7 @@
                          "something in the background."))
     parser.add_argument(
             'repo',
+            nargs='?',
             help="The repository to observe and process")
     parser.add_argument(
             '-t', '--tmp-dir',
@@ -120,12 +126,24 @@
     parser.add_argument(
             '--command',
             help="The command to run on each tag.")
+    parser.add_argument(
+            '--scan-only',
+            action='store_true',
+            help=("Only scan the repository history. Don't update or run the "
+                  "command"))
+    parser.add_argument(
+            '--status',
+            action='store_true',
+            help="See September's status for the given repository.")
 
-    # Parse arguments, guess repo type.
+    # Parse arguments.
     res = parser.parse_args()
+    repo_dir = res.repo or os.getcwd()
+
+    # Guess the repo type.
     repo_type = res.scm
     if not repo_type or repo_type == 'guess':
-        repo_type = guess_repo_type(res.repo)
+        repo_type = guess_repo_type(repo_dir)
         if not repo_type:
             logger.error("Can't guess the repository type. Please use the "
                          "--scm option to specify it.")
@@ -134,33 +152,21 @@
             logger.error("Unknown repository type: %s" % repo_type)
             sys.exit(1)
 
-    # Create the repo handler.
-    repo = repo_class[repo_type]()
-
-    # Clone or update/checkout the repository in the temp directory.
-    clone_dir = os.path.join(res.tmp_dir, 'clone')
-    if not os.path.exists(clone_dir):
-        logger.info("Cloning '%s' into: %s" % (res.repo, clone_dir))
-        repo.clone(res.repo, clone_dir)
-    else:
-        os.chdir(clone_dir)
-        logger.info("Pulling changes from '%s'." % res.repo)
-        repo.update(res.repo, None)
-
-    os.chdir(clone_dir)
-
-    # Find the configuration file in the repository clone.
-    config_file = res.config or os.path.join(clone_dir, '.september.yml')
+    # Find the configuration file.
+    config_file = res.config or os.path.join(repo_dir, '.september.cfg')
     config = configparser.ConfigParser(interpolation=None)
     if os.path.exists(config_file):
         logger.info("Loading configuration file: %s" % config_file)
         config.read(config_file)
 
+    # Validate the configuration.
     if not config.has_section('september'):
         config.add_section('september')
     config_sec = config['september']
     if res.command:
         config_sec['command'] = res.command
+    if res.tmp_dir:
+        config_sec['tmp_dir'] = res.tmp_dir
 
     if not config.has_option('september', 'command'):
         logger.error("There is no 'command' configuration setting under the "
@@ -168,14 +174,33 @@
                      "option.")
         sys.exit(1)
 
+    # Get the temp dir.
+    tmp_dir = config_sec.get('tmp_dir', None)
+    if not tmp_dir:
+        tmp_name = 'september_%s' % hashlib.md5(
+                repo_dir.encode('utf8')).hexdigest()
+        tmp_dir = os.path.join(tempfile.gettempdir(), tmp_name)
+
     # Find the cache file in the temp directory.
-    cache_file = os.path.join(res.tmp_dir, 'september.json')
+    cache_file = os.path.join(tmp_dir, 'september.json')
     if os.path.exists(cache_file):
         with open(cache_file, 'r') as fp:
             cache = json.load(fp)
     else:
         cache = {'tags': {}}
 
+    # See if we just need to show the status:
+    if res.status:
+        logger.info("Status for '%s':" % repo_dir)
+        for t, v in cache['tags'].items():
+            logger.info("- %s" % t)
+            logger.info("    commit ID  : %s" % v['id'])
+            logger.info("    processed? : %s" % v['processed'])
+        return
+
+    # Create the repo handler.
+    repo = repo_class[repo_type]()
+
     # Update the cache: get any new/moved tags.
     first_tag = config_sec.get('first_tag', None)
     tag_pattern = config_sec.get('tag_pattern', None)
@@ -183,9 +208,19 @@
     if tag_pattern:
         tag_re = re.compile(tag_pattern)
 
+    reached_first_tag = not bool(first_tag)
     previous_tags = cache['tags']
-    tags = repo.getTags(clone_dir)
+    tags = repo.getTags(repo_dir)
     for t, i in tags:
+        if not reached_first_tag and first_tag == t:
+            reached_first_tag = True
+
+        if not reached_first_tag:
+            if t in previous_tags:
+                logger.info("Removing tag '%s'." % t)
+                del previous_tags[t]
+            continue
+
         if not tag_re or tag_re.search(t):
             if t not in previous_tags:
                 logger.info("Adding tag '%s'." % t)
@@ -194,13 +229,23 @@
                 logger.info("Moving tag '%s'." % t)
                 previous_tags[t] = {'id': i, 'processed': False}
 
-        if first_tag and first_tag == t:
-            break
-
     logger.info("Updating cache file '%s'." % cache_file)
     with open(cache_file, 'w') as fp:
         json.dump(cache, fp)
 
+    if res.scan_only:
+        return
+
+    # Clone or update/checkout the repository in the temp directory.
+    clone_dir = os.path.join(tmp_dir, 'clone')
+    if not os.path.exists(clone_dir):
+        logger.info("Cloning '%s' into: %s" % (repo_dir, clone_dir))
+        repo.clone(repo_dir, clone_dir)
+    else:
+        logger.info("Pulling changes from '%s'." % repo_dir)
+        repo.pull(clone_dir, repo_dir)
+        repo.update(clone_dir, None)
+
     # Process tags!
     use_shell = config_sec.get('use_shell') in ['1', 'yes', 'true']
     for tn, ti in cache['tags'].items():