Commits

gastlygem  committed 9c0940d Merge

Merged testing into default

  • Participants
  • Parent commits 1f37929, 417e514

Comments (0)

Files changed (9)

 345545e2fb18253d6194ab14135526fed9191c9f 0.1a
 8242deff0d645b447602f812de69bbec38876cca 0.1b
+54c2fa4d1f030844320ced46b28b70f9edc4a5be 0.3
+54c2fa4d1f030844320ced46b28b70f9edc4a5be 0.3
+f5f816493470f879b927a2912aed5a9731831896 0.3
 # Format is as follows:
 #
 # - Group_NAME:
-#     prjname: Project_Name
-#     syncdirs: 'SyncDir1, SyncDir2, Syncdir3'
+#     Project_Name: 'SyncDir1, SyncDir2, Syncdir3'
 #
 # To avoid Yaml parsing errors on Windows volume names (damn those colons), it 
 # is better to add a single quote to the syncdir group.
 #
-# Group names should be unique, or the latter ones will overwrite earlier ones.
-#
 # ############################################################################ #
 
-- Group0:
-    prjname: 4CX01P
-    syncdirs: U:\ V:\ X:\ Y:\ Z:\
-- BTOA_For_Win7:
-    prjname: BTOA
-    syncdirs: 'U: V: X: Y: Z: P: S: D: A:'
+#- Group0:
+#    4CX01P: 'U:\ V:\ X:\ Y:\ Z:\'
+#- BTOA_For_Win7:
+#    BTOA: 'U: V: X: Y: Z: P: S: D: A:'
+
+- Test Sync Group:
+    4CX01P: 'E:\1 E:\2 E:\3 E:\4 E:\5'

File Config2.yaml

-- Group1:
-	project: SYNC
-	paths: B: E: G: H: I: J: K: L: M: N: O: P: Q: R: S: T: U: V: X: Y: Z: \\192.168.200.224\VOL1\PDLINE
-- Group2:
-	project: 4BK01C
-	paths: K: M: O: R: B: J: Y: I: H: U: E: V:
-- Group3:
-	project: INTEL
-	paths: O:\4X701 R:\4X701 B:\4X701 M:\4X701 K:\4X701 J:\4X701 Z:\4X701 V:\4X701 Y:\4X701 S:\4X701 P:\4X701 U:\4X701 T:\4X701 N:\4X701 I:\4X701 H:\4X701 G:\4X701 E:\4X701 X:\4X701 L:\4X701 \\192.168.200.31\NETWARE\PDLINE\4X701
-- Group4:
-	project: DISCRETE
-	paths: O:\4X701 R:\4X701 B:\4X701 M:\4X701 K:\4X701 J:\4X701 Z:\4X701 V:\4X701 Y:\4X701 S:\4X701 P:\4X701 U:\4X701 T:\4X701 N:\4X701 I:\4X701 H:\4X701 G:\4X701 E:\4X701 X:\4X701 L:\4X701 \\192.168.200.31\NETWARE\PDLINE\4X701
-- Group5:
-	project: 4X701PAI
-	paths: R: B: Y: Z: U: Q: V: S: P: X:
-- Group6:
-	project: 4X701C
-	paths: K: M: O: R: B: J: Y: H: E: U:
-- Group7:
-	project: 4AQ01FG
-	paths: A: D: F: M: K: O: V: Y: J: Z: U: B: S: P: R: T: N: X: H: I: G: E: L:
-- Group8:
-	project: 4AQ01PAI
-	paths: X: Y: V: Q: S: P: Z: A: D: U: T: F: N: \\192.168.200.224\VOL1\PDLINE \\192.168.200.31\NETWARE\PDLINE
-- Group9:
-	project: 4AQ01
-	paths: R: B: U: V: O: M: Z: I: H: S: Y: P: X: G:
-- Group10:
-	project: 4AQ01C
-	paths: M: K: U: V: R: B: H: E: Y: Z:
-- Group11:
-	project: 4R601
-	paths: K: M: O: I: E: H:
-- Group12:
-	project: 4C301
-	paths: K: M: J: O: Y: Z: U: I: H: G: E:
-- Group13:
-	project: 4V101
-	paths: R: B: K: O: M: I: H: E:
-- Group14:
-	project: 4S701
-	paths: O: M: K: I: H: E:
-- Group15:
-	project: 4V601
-	paths: M: O: K: I: H: E:
-- Group16:
-	project: 4Q101F
-	paths: M: O: R: B: I: H:
-- Group17:
-	project: 4Q601
-	paths: K: M: O: J: I: H: G: E:
-- Group18:
-	project: 4Q601FG
-	paths: K: M: O: I: H: E:
-- Group19:
-	project: 4W101
-	paths: O: K: J: U: M: I: H: G: E:
-- Group21:
-	project: 4GN01PAI
-	paths: U: Y: V:  A: F:
-- Group22:
-	project: 4W001FG
-	paths: M: O: V: J: Y: Z: U: S: P: I: H:  G: T: X:
-- Group23:
-	project: 4W001
-	paths: V: Y: Z: Q: U: S: P: X:
-- Group24:
-	project: PAID
-	paths: V: R: B: Z: Y: U: S: P: X: A: D: Q:
-- Group25:
-	project: RUN4W101
-	paths: R: B: M: O: K: J: U: I: H: G: E:
-- Group26:
-	project: 4X201FG
-	paths: M: O: V: I: H:
-- Group27:
-	project: 4Q601V
-	paths: K: M: J: O: I: H: G: E:
-- Group28:
-	project: 4J601FG
-	paths: M: O: V: R: B: J: Z: Y: U: S: P: N: T: I: H: G: X: L:
-- Group29:
-	project: 4J601PAI
-	paths: R: B: Z: Y: U: Q: V: S: P: X:
-- Group30:
-	project: 4AR01
-	paths: R: B: O: K: J: M: U: I: H: G: E:
-- Group31:
-	project: 4AE01
-	paths: O:  U: Y: Z: I: X:
-- Group32:
-	project: 4S701
-	paths: V: R: B:
-- Group33:
-	project: POTOMAC
-	paths: R: Z: Y: Q: U: S: P: X: V:
-- Group34:
-	project: 4W001CA
-	paths: U: Y: Z:
-- Group35:
-	project: RUN4AR01
-	paths: R: B: O: K: J: M: U: I: H: G: E:
-- Group36:
-	project: 4BK01FG
-	paths: M: O: V: J: Z: Y: U: S: P: N: T: R: B: I: H: G: E: X: L: A: F: D: \\192.168.200.31\NETWARE\PDLINE
-- Group37:
-	project: 4BK01PAI
-	paths: R: B: Y: Q: V: S: P: X: Z: U: A: D:
-- Group38:
-	project: 4CX01FG
-	paths: R: B: N: T: U: O: M: I: H: Z: V: P: S: Y: X: L: G: A: D: F: \\192.168.200.224\VOL1\PDLINE
-- Group39:
-	project: 4CN01FG
-	paths: R: B: N: T: U: O: M: I: H: Z: V: P: S: Y: X: L: G: A: D: F: \\192.168.200.224\VOL1\PDLINE
-- Group40:
-	project: 4CN01PAI
-	paths: X: V: U: Z: Y: S: P: Q:  A:  D:  N: T: F: \\192.168.200.224\VOL1\PDLINE
-- Group41:
-	project: 4CN01
-	paths: B: N: Z: H: I: U: Y: X: S: G: T:
-- Group42:
-	project: 4CX01
-	paths: R: B: N: T: U: O: M: I: H: Z: V: P: S: Y: X: L: G:
-- Group43:
-	project: 4CX01PAI
-	paths: Z: Y: U: Q: A: V: S: P: N: T: F: X: D: \\192.168.200.224\VOL1\PDLINE
-- Group44:
-	project: 4CX01P
-	paths: Z: Y: U: Q: V: S: P: X:
-- Group45:
-	project: BTOA
-	paths: Y: Q: V: S: P: X: Z: U: A: D: \\192.168.200.224\VOL1\PDLINE
-- Group46:
-	project: 4EX01C
-	paths: K: M: O: R: B: J: Y: I: H: U: E: V:
-- Group47:
-	project: 4EX01FG
-	paths: M: O: V: J: Z: Y: U: S: P: N: T: R: B: I: H: G: E: X: L: A: F: D:
-- Group48:
-	project: 4EX01PAI
-	paths: R: B: Y: Q: V: S: P: X: Z: U: A: D:
-- Group49:
-	project: 4GN01
-	paths: B: H: I: U: A:  F:  T:
-- Group50:
-	project: 4HH01
-	paths: H: I: U: T: N: Z: F: D: A:
-- Group51:
-	project: 4DA01
-	paths: B: E: H: V:
-- Group52:
-	project: 4HH01PAI
-	paths: U: V: X: Y: Z: P:
-- Group53:
-	project: 4EK01
-	paths: B: T: I: U: H: V: F: D: A:
-- Group54:
-	project: 4EK01FG
-	paths: B: T: I: U: H: V: F: D: A:
-- Group55:
-	project: 4EK01PAI
-	paths: B: T: I: U: H: V: F: D: A:
-- Group56:
-	project: 4EM01
-	paths: B: T: I: U: H: V:
-- Group57:
-	project: 4EM01FG
-	paths: B: T: I: U: H: V:
-- Group58:
-	project: 4EM01PAI
-	paths: B: T: I: U: H: V:
-- Group59:
-	project: 4EN01
-	paths: B: T: I: U: H: V:
-- Group60:
-	project: 4EN01FG
-	paths: B: T: I: U: H: V:
-- Group61:
-	project: 4EN01PAI
-	paths: B: T: I: U: H: V:
 
 The first word in the message is treated as user name, other strings are treated
 as commit messages.
-    
+
 """
 
-import os
-import time
-import yaml
-import logging
-import logging.handlers
+import os, re, shutil, filecmp, yaml
+import logging, logging.handlers
+from time import sleep
 from datetime import datetime
-from subprocess import Popen, PIPE
-# from functools import wraps
-from mercurial import ui, hg, commands
+from hgcmd import init, commit, autosync
 
-def confirm_project(project_dir):
+def make_project_dir(project_dir):
     "Make sure project directory actually exists and create one if it doesn't."
     if os.path.exists(project_dir):
         if not os.path.isdir(project_dir): 
         user, comment = tuple(
             fh.readline().strip().split(' ', 1))
     return user, comment
+    
+def hg_modify_config(project_dir):
+    "Writes config to do auto update after hg push."
+    hgrc_src = 'hgrc.template'
+    hgignore_src = 'hgignore.template'
+    hgrc_dst = os.path.join(project_dir, '.hg', 'hgrc')
+    hgignore_dst = os.path.join(project_dir, '.hgignore')
+    configpairs = [(hgrc_src, hgrc_dst), (hgignore_src, hgignore_dst)]
+    for src, dst in configpairs:
+        if os.path.exists(src) and not os.path.exists(dst):
+            shutil.copy(src, dst)
+        elif os.path.exists(src) and os.path.exists(dst):
+            if not filecmp.cmp(src, dst):
+                os.remove(dst)
+                shutil.copy(src, dst)
+        else: pass
 
-def auto_branch_name():
-    "generates branch name based on datetime.now()"
-    now = datetime.now()
-    d, t = now.date(), now.time()
-    dstr = str(d.year)[2:] + str(d.month).zfill(2) + str(d.day).zfill(2)
-    tstr = str(t.hour).zfill(2) + str(t.minute).zfill(2) + str(t.second).zfill(2)
-    return dstr + '/' + tstr
-    
-def modify_config(project_dir):
-    "Writes config to do auto update after hg push."
-    config_file = os.path.join(project_dir, '.hg', 'hgrc')
-    with open(config_file, 'a') as cfg:
-        cfg.write('[hooks]\n')
-        cfg.write('changegroup = hg update >&2\n')
-
-def loadconfig(configfile):
-    "Project_dir_group_config_file -> dir group list"
+def load_project_config(configfile):
+    "config_file -> dir group list"
     with open(configfile) as f:
         y = yaml.load(f)
     grouplist = []
     for entry in y:
-        for k, v in entry.iteritems():
-            sdirs = v['syncdirs'].split()
-            pname = v['prjname']
-            grouplist.append(set(map(lambda x: os.path.join(x, pname), sdirs)))
+        for group in entry.itervalues():
+            for k, v in group.iteritems():
+                grouplist.append(set(map(lambda x: os.path.join(x, k),
+                                         v.split())))
     return grouplist
 
-def hg_init(dest):
-    return commands.init(ui.ui(), dest=dest)
-
-def hg_commit(u, dest, comment):
-    repo = hg.repository(u, dest)
-    return commands.commit(u, repo, message=comment, addremove=True)
-
-def hg_push(u, src, dest):
-    src_repo = hg.repository(u, src)
-    dest_repo = hg.repository(u, dest)
-    return commands.push(u, src_repo, dest, force=True)
-
-def hg_update_branch(u, dest, br_name=None):
-    repo = hg.repository(u, dest)
-    return commands.update(u, repo, node=br_name)
-
-def hg_update_c(u, dest):
-    repo = hg.repository(u, dest)
-    return commands.update(u, repo, check=True)
-
-def hg_status_is_modified(u, dest):
-    repo = hg.repository(u, dest)
-    return not commands.status(u, repo)
-    # Patched commands.py to get this working.
-    # Appended the two lines in module function commands.status()
-    ## if len(filter(lambda x: len(x)!=0, stat)) == 0: return True
-    ## else: return False
-    # I heard it's always good to return something in your function.
-    
-def hg_branch(u, dest, br_name):
-    repo = hg.repository(u, dest)
-    return commands.branch(u, repo, label=br_name)
-
-def hg_autobranch(u, dest):
-    if hg_status_is_modified(u, dest):
-        hg_branch(u, dest, auto_branch_name())
-        hg_commit(u, dest, 'auto commit')
-        return hg_update_branch(u, dest, br_name='default')
-    else:
-        hg_update_branch(u, dest, br_name='default')
+def main():
+    configfile = 'config.yaml'
+    changefile = 'CHANGED.FLG'
+    lockfile = 'LOCKED.FLG'
+    logging.basicConfig(filename=os.path.join(os.getcwd(), 'hgmon.log'),
+                        level=logging.INFO,
+                        format="[%(asctime)s] - %(message)s",)
+    while True:
+        grouplist = load_project_config(configfile)
+        for group in grouplist:
+            for project_dir in group:
+                make_project_dir(project_dir)
+                chflag = os.path.join(project_dir, changefile)
+                hgflag = os.path.join(project_dir, '.hg')
+                lockflag = os.path.join(project_dir, lockfile)
+                if os.path.exists(lockflag): continue
+                if not os.path.exists(hgflag):
+                    init(project_dir)
+                    hg_modify_config(project_dir)
+                if os.path.exists(chflag):
+                    sleep(0.2)
+                    user, comment = parse_flag(chflag)
+                    os.remove(chflag)
+                    logging.info("%s changed %s" % (user, project_dir))
+                    logging.info("Message: '%s'" % comment)
+                    os.environ['HGUSER'] = user
+                    logging.info("Commiting changes for %s" % project_dir)
+                    commit(project_dir, comment)
+                    syncgroup = group.copy()
+                    autosync(project_dir, syncgroup)
+        sleep(10)
 
 if __name__ == '__main__':
-    configfile = 'config.yaml'
-    flagfile = 'CHANGED.FLG'
-    lockfile = 'LOCK.FLG'
-    while True:
-        grouplist = loadconfig(configfile)
-        for group in grouplist:
-            for project_dir in group:
-                confirm_project(project_dir)
-                chflag = os.path.join(project_dir, flagfile)
-                hgflag = os.path.join(project_dir, '.hg')
-                lockfile = os.path.join(project_dir, lockfile)
-                if os.path.exists(lockfile): continue
-                if not os.path.exists(hgflag):
-                    hg_init(project_dir)
-                if os.path.exists(chflag):
-                    time.sleep(0.2)
-                    user, comment = parse_flag(chflag)
-                    os.environ['HGUSER'] = user
-                    u = ui.ui()
-                    os.remove(chflag)
-                    hg_commit(u, project_dir, comment)
-                    syncgroup = group.copy()
-                    syncgroup.remove(project_dir)
-                    for syncdir in syncgroup:
-                        if os.path.exists(lockfile):
-                            continue
-                        hg_push(u, project_dir, syncdir)
-                        hg_autobranch(u, syncdir)
-        time.sleep(10)
+    main()
-KeePass: Copyright (c) 2010 Joel Wang <gastlygeml@gmail.com>.
+Hgsync: Copyright (c) 2010 Joel Wang <gastlygeml@gmail.com>.
 
 The software is distributed under the terms of the GNU General Public
 License version 2 or later.
 different servers, and has to be synchronized to each other. All the projects 
 are shared with Samba service. There is a Windows server connecting to all the 
 Samba shares. The synchronizing script runs on the Windows Server. Currently we 
-use a simple copy-to-all method to keep the project directories synchronized.
+use a simple copy-to-all method to keep the projects synchronized.
 
 This script is an attempt to integrate version control with directory 
 synchronizing . It provides a simple way to turn the project directories into 
 hg repos, synchronizing projects among different locations, and also tracks 
 project changes. 
 
-Currently only simple auto committing, synchronizing, and auto 
-branching are supported.
+Since there is no way to communicate directly between DOS and Linux, simple flag 
+files are used to exchange infomation. The server side script monitors for flag
+changes, then takes action specified by the flags.
 
+Currently only simple auto committing, synchronizing, and auto branching are 
+supported.
+import os
+import logging, logging.handlers
+from datetime import datetime
+from functools import wraps
+from mercurial import cmdutil, ui, commands, patch, hg
+
+logging.basicConfig(filename=os.path.join(os.getcwd(), 'hgmon.log'),
+                    level=logging.DEBUG,
+                    format="[%(asctime)s] - %(message)s",)
+
+def log_hg(func):
+    "Simple logging decorator."
+    @wraps(func)
+    def wrapper(*args, **kwargs):
+        str_args = filter(lambda x: not isinstance(x, ui.ui), args)
+        logging.debug("%s run with args %s" % (func.__name__, str_args))
+        return func(*args, **kwargs)
+    return wrapper
+
+@log_hg
+def auto_branch_name(dest):
+    "generates branch name based on datetime.now()"
+    for piece in dest.split(os.path.sep):
+        if piece != '': break
+    now = datetime.now()
+    d, t = now.date(), now.time()
+    dstr = '/'.join([str(d.year)[2:],
+                    str(d.month).zfill(2),
+                    str(d.day).zfill(2)])
+    tstr = ':'.join([str(t.hour).zfill(2),
+                    str(t.minute).zfill(2),
+                    str(t.second).zfill(2)])
+    return '-'.join([dstr, tstr, piece])
+
+@log_hg
+def init(dest):
+    "hg init dest"
+    return commands.init(ui.ui(), dest=dest)
+
+@log_hg
+def diff(dest, u=ui.ui()):
+    "prints diff log"
+    repo = hg.repository(ui, dest)
+    m = cmdutil.match(repo)
+    node1, node2 = cmdutil.revpair(repo, 0)
+    it = patch.diff(repo, node1, node2, match=m)
+    if len(list(it)) > 0:
+        for line in it:
+            print line # TODO: output to mail log
+        return 1
+    else: return 0
+
+@log_hg
+def commit(dest, comment, u=ui.ui()):
+    "hg commit"
+    repo = hg.repository(u, dest)
+    return commands.commit(u, repo, message=comment, addremove=True)
+
+@log_hg
+def push(src, dest, u=ui.ui()):
+    "hg push"
+    src_repo = hg.repository(u, src)
+    dest_repo = hg.repository(u, dest)
+    return commands.push(u, src_repo, dest, force=True)
+
+@log_hg
+def update_branch(dest, br_name=None, u=ui.ui()):
+    "hg update br_name"
+    repo = hg.repository(u, dest)
+    return commands.update(u, repo, node=br_name)
+
+@log_hg
+def branch(dest, br_name, u=ui.ui()):
+    "hg branch br_name"
+    repo = hg.repository(u, dest)
+    return commands.branch(u, repo, label=br_name)
+
+@log_hg
+def autobranch(dest, u=ui.ui()):
+    "Create new branch if repo is changed"
+    if repo_is_changed(dest, u):
+        br_name = auto_branch_name(dest)
+        print "branch to %s, name %s" % (dest, br_name)
+        branch(dest, br_name, u=u)
+        commit(dest, 'auto commit', u)
+        update_branch(dest, br_name='default', u=u)
+        return True
+    else:
+        update_branch(dest, br_name='default', u=u)
+        return False
+
+@log_hg
+def repo_is_changed(dest, u=ui.ui()):
+    "'hg status' stripped down. Return true if the repo is changed."
+    repo = hg.repository(u, dest)
+    node1, node2 = cmdutil.revpair(repo, 0)
+    m = cmdutil.match(repo)
+    stat = repo.status(node1, node2, m, False, False, True)
+    print stat
+    return len(filter(lambda x: len(x)!=0, stat)) != 0
+
+@log_hg
+def autosync(project_dir, syncgroup, u=ui.ui()):
+    "Magic"
+    for syncdir in syncgroup:
+        lockflag = os.path.join(syncdir, 'LOCKED.FLG')
+        if os.path.exists(lockflag):
+            logging.info("Skipping locked %s" % syncdir)
+            continue
+        logging.info("Synchronizing %s" % syncdir)
+        push(project_dir, syncdir, u)
+        branched = autobranch(syncdir, u)
+        if branched:
+            logging.info("Auto branch created on %s" % syncdir)
+            logging.info("Synchronizing back..")
+            autosync(syncdir, syncgroup, u)
+            break

File hgignore.template

+syntax: glob
+*.orig
+*.rej
+*~
+*.o
+*.log
+*.err
+*.bak
+CHANGED.FLG
+LOCKED.FLG
+
+syntax: regexp
+# File extension longer than 3 letters.
+.*\.[A-Z0-9]{4,}

File hgrc.template

+[ui]
+verbose = True
+
+[extensions]