404d5bb6d8c0418d5f06535ef470e36c35d2a237 chmalee Thu Apr 16 15:57:56 2026 -0700 Add assembly hub support to hubSpace. Users can upload a .2bit to create an assembly hub, optionally alongside their own *.hub.txt (prefix names like araTha1.hub.txt are recognized) and sibling track files. Uploads run in parallel; hub.txt mutations are serialized per-hub via flock so arrival order does not matter. - hubSpace table gains a hubType column ('trackHub' or 'assemblyHub'); ON DUPLICATE KEY UPDATE excludes it so a re-upload cannot revert an upgraded hub. - writeHubText can now emit an assembly stanza derived from the 2bit; upgradeHubTxtForAssembly promotes an existing plain hub.txt in place when a 2bit arrives after tracks. - pre-finish decides synthesize vs upgrade vs leave-alone from server state (existing rows, hub.txt on disk) plus a single client flag (batchHasHubTxt); client-supplied hubType is no longer trusted. - Client UI adds 2bit as a file type, locks the genome field when the hub is authoritative (drilled-in or batch hub.txt), defaults new uploads to an existing assembly hub at top level, and routes hgTracks URLs through 'genome=' vs 'db=' by hubType. - Fix pre-existing nested-path bug in hubPathFromParentDir (*firstSlash = 0). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> diff --git src/hg/lib/trackHub.c src/hg/lib/trackHub.c index 6739172d149..8d6825c0e25 100644 --- src/hg/lib/trackHub.c +++ src/hg/lib/trackHub.c @@ -58,30 +58,49 @@ #include "hgConfig.h" #include "cartTrackDb.h" #ifdef USE_HAL #include "halBlockViz.h" #endif struct grp *trackHubGrps = NULL; // global with grps loaded from track hubs static struct hash *hubCladeHash; // mapping of clade name to hub pointer static struct hash *hubAssemblyHash; // mapping of assembly name to genome struct static struct hash *hubAssemblyUndecoratedHash; // mapping of undecorated assembly name to genome struct static struct hash *hubOrgHash; // mapping from organism name to hub pointer static struct trackHub *globalAssemblyHubList; // list of trackHubs in the user's cart static struct hash *trackHubHash; +static boolean isValidSeqNameChar(char c) +/* Return TRUE if c is a valid character for a sequence name: [A-Za-z0-9._-]. */ +{ +return isalnum((unsigned char)c) || c == '.' || c == '_' || c == '-'; +} + +boolean trackHubIsValidSeqName(char *name) +/* Return TRUE if name is a valid sequence name: non-empty, starts with a + * letter or digit, and contains only [A-Za-z0-9._-]. */ +{ +if (!name || !name[0]) return FALSE; +if (!isalnum((unsigned char)name[0])) return FALSE; +char *p; +for (p = name; *p; p++) + if (!isValidSeqNameChar(*p)) + return FALSE; +return TRUE; +} + static void tdbListAddHubToGroup(char *hubName, struct trackDb *tdbList) /* Prepend hub name to group name for every tdb. */ { struct trackDb *tdb; for (tdb = tdbList; tdb != NULL; tdb = tdb->next) { char buffer[4096]; char *grp = trackDbSetting(tdb, "group"); if (grp == NULL) tdb->grp = cloneString(hubName); else { safef(buffer, sizeof buffer, "%s_%s", hubName, grp); tdb->grp = cloneString(buffer);