git irkerhook is now called from update.
authorEric S. Raymond <esr@thyrsus.com>
Tue, 2 Oct 2012 16:07:08 +0000 (12:07 -0400)
committerEric S. Raymond <esr@thyrsus.com>
Tue, 2 Oct 2012 16:07:08 +0000 (12:07 -0400)
This is required in order to correctly handle pushes of non-default
branches to a bare repo.

NEWS
install.txt
irkerhook.py
irkerhook.xml

diff --git a/NEWS b/NEWS
index 9f68da3adbf49efe6651751154ef216eb1772025..50feb143c787c08efa8ddf5a4beceed75a291616 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -25,6 +25,7 @@
   Graceful handling of server disconnects and kicks.
   Distribution now inclues an installable irkerd plist for Mac OS/X.
   The color variable is no longer boolean; may be miRC or ANSI.
+  The installation instructions for irkerhook.py have changed!
 
 
 
index 0602ad43b3d52162f0a73a913036093b99658885..4666a6be1c0dd05b5ca735969c9b6890ac714b0b 100644 (file)
@@ -39,34 +39,9 @@ installed to launch irkerd as a boot-time service on that system.
 
 == Installing irkerhook.py ==
 
-irkerhook.py should be called from the post-commit hook of each 
-repository.  See its header comment for detailed installation 
-instructions.
-
-You should set the server and (for Subversion) repository variables
-from the command line in your post-commit hook.  The server variable
-should be set to the inside-the-firewall host running your irker
-instance.
-
-A git invocation line should look something like this:
-
-/usr/local/bin/irkerhook.py server=internal.foobar.net
-
-Each project will be able to set its own channel URLs, provided it
-has access to its git config file.
-
-A Subversion invocation should look something like this:
-
-REPOSITORY=$1
-REV=$2
-irkerhook.py repository=$REPOSITORY commit=$REV server=internal.foobar.net
-
-Note that unless you supply additional overrides of project= and
-channels= in the hook, the basename of the repository will be used as
-the project name and will also be used as the IRC channel name on
-freenode.  This is not a limitation of irker.irkerhook, but a
-result of the absence of a git-config equivalent that the hook can
-mine stuff out of.
+Under git, a call to irkerhook.py should be installed in the update 
+hook script of your repo.  Under Subversion, the call goes in your
+repo's post-commit script. See the irkerhook manual page for details.
 
 == Testing ==
 
index 33da10f2ab7bb06459fcc20a330054367a6fb2f3..eaf9f004b460dd8ce1284ecc1c73dbe1a89d870e 100755 (executable)
@@ -147,8 +147,8 @@ class GenericExtractor:
         "Make command-line overrides possible."
         for tok in self.arguments:
             for key in self.__dict__:
-                if tok.startswith(key + "="):
-                    val = tok[len(key)+1:]
+                if tok.startswith("--" + key + "="):
+                    val = tok[len(key)+3:]
                     setattr(self, key, val)
         for (key, val) in self.__dict__.items():
             if key in GenericExtractor.booleans:
@@ -181,7 +181,8 @@ class GitExtractor(GenericExtractor):
         self.template = '%(bold)s%(project)s:%(reset)s %(green)s%(author)s%(reset)s %(repo)s:%(yellow)s%(branch)s%(reset)s * %(bold)s%(rev)s%(reset)s / %(bold)s%(files)s%(reset)s: %(logmsg)s %(brown)s%(url)s%(reset)s'
         self.color = do("git config --get irker.color")
         self.urlprefix = do("git config --get irker.urlprefix") or "gitweb"
-        # This one is git-specific
+        # These are git-specific
+        self.refname = do("git symbolic-ref HEAD 2>/dev/null")
         self.revformat = do("git config --get irker.revformat")
         # The project variable defaults to the name of the repository toplevel.
         if not self.project:
@@ -201,13 +202,10 @@ class GitExtractor(GenericExtractor):
                 here = os.path.dirname(here)
         # Get overrides
         self.do_overrides()
-    def commit_factory(self, _id):
-        commit = Commit(self, _id)
+    def commit_factory(self, commit_id):
         "Make a Commit object holding data for a specified commit ID."
-        # FIXME: Next two lines will have to change soon
-        refname = do("git symbolic-ref HEAD 2>/dev/null")
-        commit.commit = do("git rev-parse HEAD")
-        commit.branch = os.path.basename(refname)
+        commit = Commit(self, commit_id)
+        commit.branch = os.path.basename(self.refname)
         # Compute a description for the revision
         if self.revformat == 'raw':
             commit.rev = commit.commit
@@ -236,19 +234,19 @@ class SvnExtractor(GenericExtractor):
     "Metadata extraction for the svn version control system."
     def __init__(self, arguments):
         GenericExtractor.__init__(self, arguments)
-        self.id = None
         # Some things we need to have before metadata queries will work
+        self.repository = None
         for tok in arguments:
-            if tok.startswith("repository="):
-                self.repository = tok[11:]
+            if tok.startswith("--repository="):
+                self.repository = tok[13:]
         self.project = os.path.basename(self.repository)
         self.template = '%(bold)s%(project)s%(reset)s: %(green)s%(author)s%(reset)s %(repo)s * %(bold)s%(rev)s%(reset)s / %(bold)s%(files)s%(reset)s: %(logmsg)s %(brown)s%(url)s%(reset)s'
         self.urlprefix = "viewcvs"
         self.load_preferences(os.path.join(self.repository, "irker.conf"))
         self.do_overrides()
-    def commit_factory(self, id):
-        self.id = id
-        commit = Commit(self, id)
+    def commit_factory(self, commit_id):
+        self.id = commit_id
+        commit = Commit(self, commit_id)
         commit.branch = ""
         commit.rev = "r%s" % self.id
         commit.author = self.svnlook("author")
@@ -259,84 +257,79 @@ class SvnExtractor(GenericExtractor):
         return do("svnlook %s %s --revision %s" % (shellquote(info), shellquote(self.repository), shellquote(self.id)))
 
 if __name__ == "__main__":
-    import getopt
-
-    try:
-        (options, arguments) = getopt.getopt(sys.argv[1:], "nV")
-    except getopt.GetoptError, msg:
-        print "irkerhook.py: " + str(msg)
-        raise SystemExit, 1
-
     notify = True
-    channels = ""
-    commit = ""
-    repository = ""
-    for (switch, val) in options:
-        if switch == '-n':
+    repository = None
+    refname = None
+    commits = []
+    for arg in sys.argv[1:]:
+        if arg == '-n':
             notify = False
-        elif switch == '-V':
+        elif arg == '-V':
             print "irkerhook.py: version", version
             sys.exit(0)
-
-    # Gather info for repo type discrimination
-    for tok in arguments:
-        if tok.startswith("repository="):
-            repository = tok[11:]
-        if tok.startswith("commit="):
-            commit = tok[7:]
+        elif arg.startswith("--refname="):
+            refname = arg[10:]
+        elif arg.startswith("--repository="):
+            repository = arg[13:]
+        elif not arg.startswith("--"):
+            commits.append(arg)
 
     # Determine the repository type. Default to git unless user has pointed
     # us at a repo with identifiable internals.
     vcs = "git"
-    if os.path.exists(os.path.join(repository, "format")):
+    if repository and os.path.exists(os.path.join(repository, "format")):
         vcs = "svn"
 
     # Someday we'll have extractors for several version-control systems
     if vcs == "svn":
-        if commit is None or repository is None:
-            sys.stderr.write("irkerhook: svn requires 'repository' and 'commit' variables.")
+        if repository is None or not commits:
+            sys.stderr.write("irkerhook: svn requires a repository and a commit.")
             sys.exit(1)
-        extractor = SvnExtractor(arguments)
+        extractor = SvnExtractor(sys.argv[1:])
     else:
-        extractor = GitExtractor(arguments)
-    metadata = extractor.commit_factory(commit) 
+        extractor = GitExtractor(sys.argv[1:])
+        if not commits:
+            commits = [do("git rev-parse HEAD")]
 
-    # Message reduction.  The assumption here is that IRC can't handle
-    # lines more than 510 characters long. If we exceed that length, we
-    # try knocking out the file list, on the theory that for notification
-    # purposes the commit text is more important.  If it's still too long
-    # there's nothing much can be done other than ship it expecting the IRC
-    # server to truncate.
-    privmsg = str(metadata)
-    if len(privmsg) > 510:
-        metadata.files = ""
+    for commit in commits:
+        metadata = extractor.commit_factory(commit) 
+
+        # Message reduction.  The assumption here is that IRC can't handle
+        # lines more than 510 characters long. If we exceed that length, we
+        # try knocking out the file list, on the theory that for notification
+        # purposes the commit text is more important.  If it's still too long
+        # there's nothing much can be done other than ship it expecting the IRC
+        # server to truncate.
         privmsg = str(metadata)
+        if len(privmsg) > 510:
+            metadata.files = ""
+            privmsg = str(metadata)
 
-    # Anti-spamming guard.
-    channel_list = extractor.channels.split(",")
-    if extractor.maxchannels != 0:
-        channel_list = channel_list[:extractor.maxchannels]
+        # Anti-spamming guard.
+        channel_list = extractor.channels.split(",")
+        if extractor.maxchannels != 0:
+            channel_list = channel_list[:extractor.maxchannels]
 
-    # Ready to ship.
-    message = json.dumps({"to":channel_list, "privmsg":privmsg})
-    if not notify:
-        print message
-    else:
-        try:
-            if extractor.tcp:
-                try:
-                    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-                    sock.connect((extractor.server or default_server, IRKER_PORT))
-                    sock.sendall(message + "\n")
-                finally:
-                    sock.close()
-            else:
-                try:
-                    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
-                    sock.sendto(message + "\n", (extractor.server or default_server, IRKER_PORT))
-                finally:
-                    sock.close()
-        except socket.error, e:
-            sys.stderr.write("%s\n" % e)
+        # Ready to ship.
+        message = json.dumps({"to":channel_list, "privmsg":privmsg})
+        if not notify:
+            print message
+        else:
+            try:
+                if extractor.tcp:
+                    try:
+                        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+                        sock.connect((extractor.server or default_server, IRKER_PORT))
+                        sock.sendall(message + "\n")
+                    finally:
+                        sock.close()
+                else:
+                    try:
+                        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+                        sock.sendto(message + "\n", (extractor.server or default_server, IRKER_PORT))
+                    finally:
+                        sock.close()
+            except socket.error, e:
+                sys.stderr.write("%s\n" % e)
 
 #End
index 67e043c083a830acd669bc349d58162bf2b350b6..914e3a7e27b34845006c38a7dbfac89831249dc8 100644 (file)
@@ -20,7 +20,8 @@
   <command>irkerhook.py</command>
      <arg>-n</arg>
      <arg>-V</arg>
-     <arg rep='repeat'><replaceable>variable=value</replaceable></arg>
+     <group><arg rep='repeat'><replaceable>--variable=value</replaceable></arg></group>
+     <group><arg rep='repeat'><replaceable>commit-id</replaceable></arg></group>
 </cmdsynopsis>
 </refsynopsisdiv>
 
@@ -45,7 +46,7 @@ variables, variables with the prefix "irker.".</para></listitem>
 <listitem><para>In other VCSes, a configuration file, "irker.conf", in the
 repository's internals directory.</para></listitem>
 <listitem><para>Command-line arguments of the form
-variable=value.</para></listitem>
+--variable=value.</para></listitem>
 </orderedlist>
 
 <para>The following variables are general to all supported VCSes:</para>
@@ -145,10 +146,38 @@ a value less than 2, however, would probably be unwise.</para>
 
 <refsect2 id="git"><title>git</title>
 
-<para>Under git, <application>irkerhook.py</application> does not
-require any arguments.  Preferences may be set in the repo
-<filename>config</filename> file in an [irker] section. Here
-is an example of what that can look like:</para>
+<application>irkerhook.py</application>
+
+<para>Under git, the normal way to invoke this hook (from within the
+update hook) passes with a refname followed by a list of commits.  Because 
+<command>git rev-list</command> normally lists from most recent to oldest,
+you'll want to use --reverse to make notifications be omitted in chronological
+order. In a normal update script, the invocation should look like this</para>
+
+<programlisting>
+refname=$1
+old=$2
+new=$3
+irkerhook.py --refname=${refname} $(git rev-list --reverse ${old}..${new})
+</programlisting>
+
+<para>except that you'll need an absolute path for irkerhook.py.</para>
+
+<para>For testing purposes and backward compatibility, if you invoke
+<application>irkerhook.py</application> with no arguments (as in a
+post-commit hook) it will behave as though it had been called like
+this:
+
+<programlisting>
+irkerhook.py --refname=refs/heads/master HEAD
+</programlisting>
+
+<para>However, this will not give the right result when you push to 
+a non-default branch of a bare repo.</para>
+
+<para>Preferences may be set in the repo <filename>config</filename>
+file in an [irker] section. Here is an example of what that can look
+like:</para>
 
 <programlisting>
 [irker]
@@ -157,10 +186,9 @@ is an example of what that can look like:</para>
     channels = {irc://chat.freenode.net/gpsd, irc://chat.freenode.net/commits}
 </programlisting>
 
-<para>Command-line variable=value arguments are accepted but not
-required.  No attempt is made to interpret an
-<filename>irker.conf</filename> file.  You should not set the
-"repository" variable (an equivalent will be computed).</para>
+<para> You should not set the "repository" variable (an equivalent
+will be computed). No attempt is made to interpret an
+<filename>irker.conf</filename> file.</para>
 
 <para>The default value of the "project" variable is the basename
 of the repository directory. The default value of the "urlprefix"
@@ -198,15 +226,17 @@ gives its post-commit hook. Thus, a typical invocation in the post-commit
 script will look like this:</para>
 
 <programlisting>
-irkerhook.py repository=$1 commit=$2
+REPO=$1
+REV=$2
+irkerhook.py --repository=$REPO $REV
 </programlisting>
 
-<para>Other variable=value settings may also be
+<para>Other --variable=value settings may also be
 given on the command line, and will override any settings in an
 <filename>irker.conf</filename> file.</para>
 
 <para>The default for the project variable is the basename of the
-(required) repository= argument.The default value of the "urlprefix"
+(required) --repository argument.The default value of the "urlprefix"
 variable is "viewcvs".</para>
 
 <para>If an <filename>irker.conf</filename> file exists in the repository
@@ -221,7 +251,7 @@ channels  = irc://chat.freenode/irker,irc://chat.freenode/commits
 tcp = false
 </programlisting>
 
-<para>Don't the "repository" or "commit" variables in this file;
+<para>Don't set the "repository" or "commit" variables in this file;
 that would have unhappy results.</para>
 
 <para>There are no Subversion-specific variables.</para>