f518e03d8f56af784ec14536a7bdc0daed9a38fd
chmalee
  Wed Sep 16 11:11:32 2026 -0700
Send each file's genome with a hubtools upload so hubSpace rows get a db, and stop the server rewriting a user-uploaded hub.txt, no redmine

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

diff --git src/hg/hgHubConnect/hooks/pre-finish.c src/hg/hgHubConnect/hooks/pre-finish.c
index d41b1aebe6f..a79d7988cf7 100644
--- src/hg/hgHubConnect/hooks/pre-finish.c
+++ src/hg/hgHubConnect/hooks/pre-finish.c
@@ -56,31 +56,30 @@
 else
     {
     char *tusFile = NULL, *tusInfo = NULL;
     struct errCatch *errCatch = errCatchNew(0);
     if (errCatchStart(errCatch))
         {
         // the variables for the row entry for this file, some can be NULL
         char *userName = NULL;
         char *dataDir = NULL, *userDataDir = NULL;
         char *fileName = NULL;
         long long fileSize = 0;
         char *fileType = NULL;
         char *db = NULL;
         char *reqLm = NULL;
         time_t lastModified = 0;
-        boolean isHubToolsUpload = FALSE;
         char *parentDir = NULL, *encodedParentDir = NULL;
 
         struct lineFile *lf = lineFileStdin(FALSE);
         char *request = lineFileReadAll(lf);
         struct jsonElement *req = jsonParse(request);
         fprintf(stderr, "Hook request:\n");
         jsonPrintToFile(req, NULL, stderr, 0);
         char *reqCookie= jsonQueryString(req, "", "Event.HTTPRequest.Header.Cookie[0]", NULL);
         if (reqCookie)
             {
             setenv("HTTP_COOKIE", reqCookie, 0);
             }
         fprintf(stderr, "reqCookie='%s'\n", reqCookie);
         userName = getUserName();
         if (!userName)
@@ -205,64 +204,66 @@
             // Derive hubType server-side; never trust the client's hubType.
             // A 2bit always promotes its hub to assembly. Otherwise inherit
             // the existing hub's type, defaulting to trackHub.
             // both lookups below are about the hub as a whole, whose row and hub.txt
             // live at the top level, so use the hub component of parentDir
             char *parentDirForCheck = encodedParentDir ? hubRootFromParentDir(encodedParentDir) : "";
             char *hubDir = encodedParentDir ?
                 hubPathFromParentDir(encodedParentDir, userDataDir) : NULL;
             if (sameOk(fileType, "2bit"))
                 row->hubType = "assemblyHub";
             else
                 {
                 char *existingType = existingHubTypeForDir(userName, parentDirForCheck);
                 row->hubType = existingType ? existingType : "trackHub";
                 }
+            // both the browser and hubtools set batchHasHubTxt when the upload brings
+            // its own hub.txt, which makes that hub.txt the user's to write, not ours
             char *batchHasHubTxtStr = jsonQueryString(req, "", "Event.Upload.MetaData.batchHasHubTxt", NULL);
             boolean batchHasHubTxt = sameOk(batchHasHubTxtStr, "true");
             boolean userOwnNamedHubTxt = userHasOwnNamedHubTxtInDir(userName, parentDirForCheck, hubDir);
             boolean userAuth = batchHasHubTxt || userOwnNamedHubTxt;
             boolean isHubTxt = sameOk(fileType, "hub.txt");
             boolean isTwoBit = sameOk(fileType, "2bit");
 
             // Serialize hub.txt read-modify-write across parallel pre-finish
             // processes for the same hub. flock is held for the entire
             // decision + action so writeHubText's fileExists check and the
             // upgrade's read-rewrite are atomic with respect to siblings.
             // Lock the directory the hub.txt is in, so uploads into different
             // subdirectories of one hub still serialize against each other.
             int hubLockFd = -1;
             if (hubDir)
                 hubLockFd = lockHubDir(hubDir);
-            if (!isHubToolsUpload && !isHubTxt)
+            if (!isHubTxt)
                 {
                 if (!userAuth)
                     {
                     if (isTwoBit)
                         {
                         // createNewTempHubForUpload is a no-op when the hub.txt and its
                         // row are already there, and it backfills the row when they are not
                         createNewTempHubForUpload(reqId, row, userDataDir);
-                        upgradeExistingHubToAssembly(row, userDataDir);
+                        upgradeExistingHubToAssembly(row, userDataDir, TRUE);
                         }
                     else
                         createNewTempHubForUpload(reqId, row, userDataDir);
                     }
                 else if (isTwoBit)
                     {
                     // user's hub.txt is authoritative; just flip rows to assemblyHub.
-                    upgradeExistingHubToAssembly(row, userDataDir);
+                    upgradeExistingHubToAssembly(row, userDataDir, FALSE);
                     }
                 }
             // still under the hub lock: makeParentDirRows checks for a row and then
             // inserts it, so two uploads to one hub would otherwise both insert the
             // same directory row
             // first make the parentDir rows
             // the directory rows carry the upload's own timestamp. row->lastModified
             // holds it as a GMT clock string, which sqlDateToUnixTime would read as
             // local time, so pass the seconds directly
             makeParentDirRows(row->userName, lastModified, row->db, row->parentDir, userDataDir, row->hubType);
             row->parentDir = encodedParentDir ? hubLeafFromPath(encodedParentDir) : "";
             addHubSpaceRowForFile(row);
             unlockHubDir(hubLockFd);
             fprintf(stderr, "added hubSpace row for file '%s'\n", fileName);
             fflush(stderr);