infocalypse

(djk)
2009-11-15: First pass at implementation of fn-fmsnotify --submit

First pass at implementation of fn-fmsnotify --submit This bundles all locally committed changes that aren't in the repo in Freenet and inserts the bundle on a CHK. Then it sends a machine readable patch notification to the fmsnotify_group set in the .infocalypse / infocalypse.ini file.

diff --git a/infocalypse/__init__.py b/infocalypse/__init__.py
--- a/infocalypse/__init__.py
+++ b/infocalypse/__init__.py
@@ -571,6 +571,7 @@ def infocalypse_fmsnotify(ui_, repo, **o
         return
 
     params['ANNOUNCE'] = opts['announce']
+    params['SUBMIT'] = opts['submit']
     params['DRYRUN'] = opts['dryrun']
     params['INSERT_URI'] = insert_uri
     execute_fmsnotify(ui_, repo, params, stored_cfg)
@@ -724,7 +725,9 @@ cmdtable = {
 
     "fn-fmsnotify": (infocalypse_fmsnotify,
                      [('', 'dryrun', None, "don't send fms message"),
-                     ('', 'announce', None, "include full URI update"), ]
+                     ('', 'announce', None, "include full URI update"),
+                     ('', 'submit', None, "insert patch bundle and send an " +
+                      "fms notification"),]
                      + FCP_OPTS, # Needs to invert the insert uri
                      "[options]"),
 
diff --git a/infocalypse/fmscmds.py b/infocalypse/fmscmds.py
--- a/infocalypse/fmscmds.py
+++ b/infocalypse/fmscmds.py
@@ -29,7 +29,7 @@ from fms import recv_msgs, to_msg_string
      USKNotificationParser, show_table
 
 from config import Config, trust_id_for_repo, untrust_id_for_repo, known_hashes
-from infcmds import do_key_setup, setup, cleanup
+from infcmds import do_key_setup, setup, cleanup, execute_insert_patch
 
 def handled_list(ui_, params, stored_cfg):
     """ INTERNAL: HACKED"""
@@ -198,7 +198,7 @@ def execute_fmsnotify(ui_, repo, params,
 
         usk_hash = get_usk_hash(request_uri)
         index = stored_cfg.get_index(usk_hash)
-        if index is None:
+        if index is None and not params['SUBMIT']:
             ui_.warn("Can't notify because there's no stored index "
                      + "for %s.\n" % usk_hash)
             return
@@ -215,12 +215,16 @@ def execute_fmsnotify(ui_, repo, params,
             ui_.status("Update the fmsnotify_group = line and try again.\n")
             return
 
+        subject = 'Update:' + '/'.join(request_uri.split('/')[1:])
         if params['ANNOUNCE']:
             text = to_msg_string(None, (request_uri, ))
+        elif params['SUBMIT']:
+            params['REQUEST_URI'] = request_uri # REDFLAG: DCI. Think
+            text = execute_insert_patch(ui_, repo, params, stored_cfg)
+            subject = 'Patch:' + '/'.join(request_uri.split('/')[1:])
         else:
             text = to_msg_string(((usk_hash, index), ))
 
-        subject = 'Update:' + '/'.join(request_uri.split('/')[1:])
         msg_tuple = (stored_cfg.defaults['FMS_ID'],
                      stored_cfg.defaults['FMSNOTIFY_GROUP'],
                      subject,
diff --git a/infocalypse/infcmds.py b/infocalypse/infcmds.py
--- a/infocalypse/infcmds.py
+++ b/infocalypse/infcmds.py
@@ -19,14 +19,12 @@
     Author: djk@isFiaD04zgAgnrEC5XJt1i4IE7AkNPqhBG5bONi6Yks
 """
 
-
 # REDFLAG: cleanup exception handling
 #          by converting socket.error to IOError in fcpconnection?
 # REDFLAG: returning vs aborting. set system exit code.
 import os
 import socket
 import time
-
 from binascii import hexlify
 
 from mercurial import util
@@ -633,13 +631,6 @@ def execute_reinsert(ui_, repo, params, 
     finally:
         cleanup(update_sm)
 
-
-# REDFLAG: move into fcpclient?
-#def usks_equal(usk_a, usk_b):
-#    assert is_usk(usk_a) and and is_usk(usk_b)
-#    return (get_usk_for_usk_version(usk_a, 0) ==
-#            get_usk_for_usk_version(usk_b, 0))
-
 def execute_push(ui_, repo, params, stored_cfg):
     """ Run the push command. """
 
@@ -947,23 +938,17 @@ def create_patch_bundle(ui_, repo, freen
     finally:
         ui_.popbuffer()
 
-
-    # explicitly specify heads?
-
-    # insert it into freenet as a CHK
-
-# ':', '|' not in freenet base64
-def patch_msg(usk_hash, bases, heads, chk, kind='B'):
-    """ Return a machine readable patch notification suitable for posting
-        via FMS. """
-    return ':'.join((kind, usk_hash, ':'.join([base[:12] for base in bases]),
-                     '|', ':'.join([head[:12] for head in heads]), chk))
 def execute_insert_patch(ui_, repo, params, stored_cfg):
     """ Create and hg bundle containing all changes not already in the
-        infocalypse repo in Freenet and insert it to a CHK. """
+        infocalypse repo in Freenet and insert it to a CHK.
+
+        Returns a machine readable patch notification message.
+        """
     try:
         update_sm = setup(ui_, repo, params, stored_cfg)
         out_file = make_temp_file(update_sm.ctx.bundle_cache.base_dir)
+
+        ui_.status("Reading repo state from Freenet...\n")
         freenet_heads = read_freenet_heads(params, update_sm,
                                            params['REQUEST_URI'])
 
@@ -980,6 +965,11 @@ def execute_insert_patch(ui_, repo, para
         request.in_params.file_name = out_file
         request.in_params.send_data = True
 
+        # Must do this here because file gets deleted.
+        chk_len = os.path.getsize(out_file)
+
+        ui_.status("Inserting %i byte patch bundle...\n" %
+                   os.path.getsize(out_file))
         update_sm.start_single_request(request)
         run_until_quiescent(update_sm, params['POLL_SECS'])
 
@@ -987,19 +977,22 @@ def execute_insert_patch(ui_, repo, para
         freenet_heads.sort()
         heads = [hexlify(head) for head in repo.heads()]
         heads.sort()
-
         if update_sm.get_state(QUIESCENT).arrived_from(((FINISHING,))):
             chk = update_sm.get_state(RUNNING_SINGLE_REQUEST).\
                   final_msg[1]['URI']
             ui_.status("Patch CHK:\n%s\n" %
                        chk)
+            # ':', '|' not in freenet base64
+            ret = ':'.join(('B', normalize(params['REQUEST_URI']), str(chk_len),
+                            ':'.join([base[:12] for base in freenet_heads]),
+                            '|', ':'.join([head[:12] for head in heads]), chk))
 
-            ui_.status("\nNotification:\n%s\n" %
-                       patch_msg(normalize(params['REQUEST_URI']),
-                                 freenet_heads, heads, chk) + '\n')
+            ui_.status("\nNotification:\n%s\n" % ret
+                        + '\n')
+            return ret
 
-        else:
-            ui_.status("Insert failed.\n")
+        raise util.Abort("Patch CHK insert failed.")
+
     finally:
         # Cleans up out file.
         cleanup(update_sm)