Rev 7: Add a start_commit hook that auto-updates the copyright headers. in http://bzr.arbash-meinel.com/plugins/update_copyright

John Arbash Meinel john at arbash-meinel.com
Fri Jan 8 21:19:19 GMT 2010


At http://bzr.arbash-meinel.com/plugins/update_copyright

------------------------------------------------------------
revno: 7
revision-id: john at arbash-meinel.com-20100108211907-surg3wfcko2f09a5
parent: john at arbash-meinel.com-20100108205546-44sf89j4c0o4s25j
committer: John Arbash Meinel <john at arbash-meinel.com>
branch nick: update_copyright
timestamp: Fri 2010-01-08 15:19:07 -0600
message:
  Add a start_commit hook that auto-updates the copyright headers.
  
  Now when you run 'bzr commit', this can scan your tree and tell it to
  update any Copyright headers for files that appear as modified.
  This way, we only update headers for files that are already modified,
  but they are always kept up to date.
-------------- next part --------------
=== modified file '__init__.py'
--- a/__init__.py	2010-01-08 20:55:46 +0000
+++ b/__init__.py	2010-01-08 21:19:07 +0000
@@ -26,7 +26,9 @@
 from bzrlib import (
     commands,
     errors,
+    mutabletree, # I wish we could make the hook registration fully lazy
     option,
+    trace,
     )
 
 
@@ -54,12 +56,37 @@
         self.outf.write('Checked %(count)d files\n'
                         'Updated %(updated)d\n'
                         'Already correct %(copyright-correct)d\n'
-                        'No copyright %(no-copyright)d\n' % (result))
+                        'No copyright %(no-copyright)d\n' % result)
 
 
 commands.register_command(cmd_update_copyright)
 
 
+def _update_copyright_start_commit_hook(tree):
+    """A hook that will update copyrights at commit time.
+
+    This is configured via "auto_update_copyright=True" in your configuration
+    files.
+    """
+    conf = tree.branch.get_config()
+    auto_update = conf.get_user_option_as_bool('auto_update_copyright')
+    if auto_update is None or not auto_update:
+        return
+    import update_copyright
+    res = update_copyright.update_tree_copyright(tree, None, only_changed=True)
+    # TODO: Consider only muttering if something is actually changed...
+    trace.mutter('Auto-updating copyright:\n'
+                 '  Checked %(count)d files\n'
+                 '  Updated %(updated)d\n'
+                 '  Already correct %(copyright-correct)d\n'
+                 '  No copyright %(no-copyright)d\n'
+                 % res)
+
+
+mutabletree.MutableTree.hooks.install_named_hook('start_commit',
+    _update_copyright_start_commit_hook, 'update-copyright')
+
+
 def load_tests(standard_tests, module, loader):
     standard_tests.addTests(loader.loadTestsFromModuleNames(
         [__name__ + '.' + x for x in [

=== modified file 'test_update_copyright.py'
--- a/test_update_copyright.py	2010-01-08 20:55:46 +0000
+++ b/test_update_copyright.py	2010-01-08 21:19:07 +0000
@@ -19,11 +19,13 @@
 
 from bzrlib import (
     errors,
+    mutabletree,
     tests,
     )
 
 from bzrlib.plugins.update_copyright import (
     update_copyright,
+    _update_copyright_start_commit_hook,
     )
 
 
@@ -110,6 +112,15 @@
             t.commit('mods', timestamp=1231353866, timezone=0)) # 2009
         return t, rev_ids
 
+    def make_tree_with_dirs_modified(self):
+        t, rev_ids = self.make_tree_with_dirs()
+        # Set 'dir/file' as modified, but leave the other files alone.
+        # Passing only_changed should mean that the other files do not get
+        # modified
+        self.build_tree_contents([
+            ('dir/file', 'Copyright 2008 Foo\r\nmodified content\r\n')])
+        return t, rev_ids
+
     def test_get_years(self):
         t, rev_ids = self.make_old_modified_tree()
         self.assertEqual([2008],
@@ -197,24 +208,50 @@
                           'copyright-correct': 0, 'not-versioned': 0},
                          res)
 
+    def check_tree_with_dirs_modified(self):
+        year = datetime.datetime.now().year
+        self.assertFileEqual('Copyright (c) 2008 Foo Bar\n'
+                             'different content\n', 'file')
+        self.assertFileEqual('Copyright 2008, 2009, %d Foo\r\n'
+                             'modified content\r\n' % (year,), 'dir/file')
+        self.assertFileEqual('Copyright 2008 Bar\n'
+                             'content\n', 'dir/subdir/foo')
+
     def test_update_tree_only_changed(self):
-        t, rev_ids = self.make_tree_with_dirs()
-        # Set 'dir/file' as modified, but leave the other files alone.
-        # Passing only_changed should mean that the other files do not get
-        # modified
-        self.build_tree_contents([
-            ('dir/file', 'Copyright 2008 Foo\r\nmodified content\r\n')])
+        t, rev_ids = self.make_tree_with_dirs_modified()
         res = update_copyright.update_tree_copyright(t, None, only_changed=True)
         year = datetime.datetime.now().year
+        self.check_tree_with_dirs_modified()
+        self.assertEqual({'count': 1, 'updated': 1, 'no-copyright': 0,
+                          'copyright-correct': 0, 'not-versioned': 0},
+                         res)
+
+    def test_auto_update_hook_disabled(self):
+        t, rev_ids = self.make_tree_with_dirs_modified()
+        # We have to install it here, because the test suite clears out hooks
+        # as part of its isolation mechanism.
+        mutabletree.MutableTree.hooks.install_named_hook('start_commit',
+            _update_copyright_start_commit_hook, 'update-copyright')
+        # Without any config, nothing should be modified.
+        t.commit('Modified content')
         self.assertFileEqual('Copyright (c) 2008 Foo Bar\n'
                              'different content\n', 'file')
-        self.assertFileEqual('Copyright 2008, 2009, %d Foo\r\n'
-                             'modified content\r\n' % (year,), 'dir/file')
+        self.assertFileEqual('Copyright 2008 Foo\r\n'
+                             'modified content\r\n', 'dir/file')
         self.assertFileEqual('Copyright 2008 Bar\n'
                              'content\n', 'dir/subdir/foo')
-        self.assertEqual({'count': 1, 'updated': 1, 'no-copyright': 0,
-                          'copyright-correct': 0, 'not-versioned': 0},
-                         res)
+
+    def test_auto_update_hook_enabled(self):
+        t, rev_ids = self.make_tree_with_dirs_modified()
+        # We have to install it here, because the test suite clears out hooks
+        # as part of its isolation mechanism.
+        mutabletree.MutableTree.hooks.install_named_hook('start_commit',
+            _update_copyright_start_commit_hook, 'update-copyright')
+        t.branch.get_config().set_user_option('auto_update_copyright', 'True')
+        t.commit('Modified content')
+        # With the hook enabled, commit should update the copyright in the
+        # modified file
+        self.check_tree_with_dirs_modified()
 
     def test_blackbox(self):
         t, rev_ids = self.make_old_modified_tree()
@@ -230,22 +267,24 @@
                              'different content\n' % (year,), 'file')
 
     def test_blackbox_only_changed(self):
-        t, rev_ids = self.make_tree_with_dirs()
-        # Set 'dir/file' as modified, but leave the other files alone.
-        # Passing only_changed should mean that the other files do not get
-        # modified
-        self.build_tree_contents([
-            ('dir/file', 'Copyright 2008 Foo\r\nmodified content\r\n')])
+        t, rev_ids = self.make_tree_with_dirs_modified()
         t.unlock()
         try:
             self.run_bzr('update-copyright --only-changed')
         finally:
             # leave the tree locked, because we have pending cleanup
             t.lock_read()
-        year = datetime.datetime.now().year
-        self.assertFileEqual('Copyright (c) 2008 Foo Bar\n'
-                             'different content\n', 'file')
-        self.assertFileEqual('Copyright 2008, 2009, %d Foo\r\n'
-                             'modified content\r\n' % (year,), 'dir/file')
-        self.assertFileEqual('Copyright 2008 Bar\n'
-                             'content\n', 'dir/subdir/foo')
+        self.check_tree_with_dirs_modified()
+
+    def test_blackbox_with_commit_hook(self):
+        t, rev_ids = self.make_tree_with_dirs_modified()
+        mutabletree.MutableTree.hooks.install_named_hook('start_commit',
+            _update_copyright_start_commit_hook, 'update-copyright')
+        t.branch.get_config().set_user_option('auto_update_copyright', 'True')
+        t.unlock()
+        try:
+            self.run_bzr('commit -m "one changed"')
+        finally:
+            # leave the tree locked, because we have pending cleanup
+            t.lock_read()
+        self.check_tree_with_dirs_modified()

=== modified file 'update_copyright.py'
--- a/update_copyright.py	2010-01-08 20:55:46 +0000
+++ b/update_copyright.py	2010-01-08 21:19:07 +0000
@@ -76,6 +76,10 @@
               'updated': 0,
               'count': 0,
              }
+    # We only *need* lock_tree_write, however the
+    # WorkingTree.put_file_bytes_non_atomic api uses a @needs_lock_write
+    # decorator, which means it tries to write lock the branch...
+    #tree.lock_tree_write()
     tree.lock_write()
     try:
         basis_tree = tree.basis_tree()



More information about the bazaar-commits mailing list