Backfill relative GitHub submodules
Authored by
mfwolffe <wolffemf@dukes.jmu.edu>
- SHA
6a4cb28f46b240e1d5cf4690603c3bbcff93cd19- Parents
-
42a4b83 - Tree
6ee993c
6a4cb28
6a4cb28f46b240e1d5cf4690603c3bbcff93cd1942a4b83
6ee993c| Status | File | + | - |
|---|---|---|---|
| M |
docs/internal/code-tab.md
|
11 | 11 |
| M |
internal/web/handlers/repo/code_tree_rows_test.go
|
20 | 3 |
| M |
internal/web/handlers/repo/submodule_links.go
|
13 | 5 |
docs/internal/code-tab.mdmodified@@ -73,17 +73,17 @@ on the rendered ref, the Code tab parses it once, matches entries by | ||
| 73 | 73 | submodule path, and links GitHub or configured shithub clone remotes to |
| 74 | 74 | the local `/{owner}/{repo}/tree/{gitlink-oid}` route when the target |
| 75 | 75 | repo has that commit. If the target repo exists locally but does not |
| 76 | -have the pinned commit object, and `.gitmodules` points at GitHub, the | |
| 77 | -handler performs a bounded, non-forced fetch of heads/tags from that | |
| 78 | -GitHub remote, re-checks the object, and then links to the exact | |
| 79 | -detached-commit tree when it arrived. Successful backfills update the | |
| 80 | -target repo's default-branch OID when that ref moved, then enqueue the | |
| 81 | -same code-index and size-recalc maintenance used after pushes. Diverged | |
| 82 | -local refs are never force-updated; on fetch failure or still-missing | |
| 83 | -objects, the row links to the target repo's default Code tab so | |
| 84 | -independently-created mirrors don't produce dead links. Unknown, | |
| 85 | -external, absent, or malformed remotes stay as plain `name @ shortsha` | |
| 86 | -rows. | |
| 76 | +have the pinned commit object, and `.gitmodules` points at GitHub or a | |
| 77 | +relative sibling repo, the handler performs a bounded, non-forced fetch | |
| 78 | +of heads/tags from the corresponding GitHub remote, re-checks the | |
| 79 | +object, and then links to the exact detached-commit tree when it | |
| 80 | +arrived. Successful backfills update the target repo's default-branch | |
| 81 | +OID when that ref moved, then enqueue the same code-index and | |
| 82 | +size-recalc maintenance used after pushes. Diverged local refs are | |
| 83 | +never force-updated; on fetch failure or still-missing objects, the row | |
| 84 | +links to the target repo's default Code tab so independently-created | |
| 85 | +mirrors don't produce dead links. Unknown, external, absent, or | |
| 86 | +malformed remotes stay as plain `name @ shortsha` rows. | |
| 87 | 87 | |
| 88 | 88 | The S17 ship excludes the htmx-driven "last commit per entry" column |
| 89 | 89 | that the spec describes — an extra round-trip we can add later without |
internal/web/handlers/repo/code_tree_rows_test.gomodified@@ -127,6 +127,12 @@ func TestSubmoduleRouteURL_UnsupportedRemotesStayPlain(t *testing.T) { | ||
| 127 | 127 | |
| 128 | 128 | func TestGitHubSubmoduleFetchURL_CanonicalizesSupportedRemotes(t *testing.T) { |
| 129 | 129 | t.Parallel() |
| 130 | + cfg := submoduleRouteConfig{ | |
| 131 | + owner: "FortranGoingOnForty", | |
| 132 | + repoName: "lib-modules", | |
| 133 | + baseURL: "https://shithub.sh", | |
| 134 | + sshHost: "git@shithub.sh", | |
| 135 | + } | |
| 130 | 136 | |
| 131 | 137 | for _, tt := range []struct { |
| 132 | 138 | name string |
@@ -148,11 +154,21 @@ func TestGitHubSubmoduleFetchURL_CanonicalizesSupportedRemotes(t *testing.T) { | ||
| 148 | 154 | remote: "ssh://git@github.com/FortranGoingOnForty/afs-ld.git", |
| 149 | 155 | want: "https://github.com/FortranGoingOnForty/afs-ld.git", |
| 150 | 156 | }, |
| 157 | + { | |
| 158 | + name: "relative sibling without suffix", | |
| 159 | + remote: "../fgof-process", | |
| 160 | + want: "https://github.com/FortranGoingOnForty/fgof-process.git", | |
| 161 | + }, | |
| 162 | + { | |
| 163 | + name: "relative sibling with suffix", | |
| 164 | + remote: "../fgof-fs.git", | |
| 165 | + want: "https://github.com/FortranGoingOnForty/fgof-fs.git", | |
| 166 | + }, | |
| 151 | 167 | } { |
| 152 | 168 | tt := tt |
| 153 | 169 | t.Run(tt.name, func(t *testing.T) { |
| 154 | 170 | t.Parallel() |
| 155 | - got, ok := githubSubmoduleFetchURL(tt.remote) | |
| 171 | + got, ok := githubSubmoduleFetchURL(cfg, tt.remote) | |
| 156 | 172 | if !ok { |
| 157 | 173 | t.Fatalf("githubSubmoduleFetchURL(%q) ok = false", tt.remote) |
| 158 | 174 | } |
@@ -165,10 +181,11 @@ func TestGitHubSubmoduleFetchURL_CanonicalizesSupportedRemotes(t *testing.T) { | ||
| 165 | 181 | |
| 166 | 182 | func TestGitHubSubmoduleFetchURL_RejectsUnsupportedRemotes(t *testing.T) { |
| 167 | 183 | t.Parallel() |
| 184 | + cfg := submoduleRouteConfig{owner: "octo", repoName: "super", baseURL: "https://shithub.sh", sshHost: "git@shithub.sh"} | |
| 168 | 185 | |
| 169 | 186 | for _, remote := range []string{ |
| 170 | 187 | "https://shithub.sh/tenseleyFlow/bencch.git", |
| 171 | - "../afs-ld.git", | |
| 188 | + "../../../afs-ld.git", | |
| 172 | 189 | "https://example.com/octo/lib.git", |
| 173 | 190 | "https://github.com/octo/nested/lib.git", |
| 174 | 191 | "https://github.com/%2F/lib.git", |
@@ -178,7 +195,7 @@ func TestGitHubSubmoduleFetchURL_RejectsUnsupportedRemotes(t *testing.T) { | ||
| 178 | 195 | remote := remote |
| 179 | 196 | t.Run(remote, func(t *testing.T) { |
| 180 | 197 | t.Parallel() |
| 181 | - if got, ok := githubSubmoduleFetchURL(remote); ok || got != "" { | |
| 198 | + if got, ok := githubSubmoduleFetchURL(cfg, remote); ok || got != "" { | |
| 182 | 199 | t.Fatalf("githubSubmoduleFetchURL(%q) = %q, %v; want empty, false", remote, got, ok) |
| 183 | 200 | } |
| 184 | 201 | }) |
internal/web/handlers/repo/submodule_links.gomodified@@ -34,12 +34,13 @@ type submoduleRoute struct { | ||
| 34 | 34 | } |
| 35 | 35 | |
| 36 | 36 | func (h *Handlers) submoduleTreeURL(ctx context.Context, cc *codeContext, remoteURL, oid string) string { |
| 37 | - route, ok := submoduleRouteForRemote(submoduleRouteConfig{ | |
| 37 | + cfg := submoduleRouteConfig{ | |
| 38 | 38 | owner: cc.owner, |
| 39 | 39 | repoName: cc.row.Name, |
| 40 | 40 | baseURL: h.d.CloneURLs.BaseURL, |
| 41 | 41 | sshHost: h.d.CloneURLs.SSHHost, |
| 42 | - }, remoteURL, oid) | |
| 42 | + } | |
| 43 | + route, ok := submoduleRouteForRemote(cfg, remoteURL, oid) | |
| 43 | 44 | if !ok { |
| 44 | 45 | return "" |
| 45 | 46 | } |
@@ -61,7 +62,7 @@ func (h *Handlers) submoduleTreeURL(ctx context.Context, cc *codeContext, remote | ||
| 61 | 62 | return route.RepoURL |
| 62 | 63 | } |
| 63 | 64 | if !existsAtCommit { |
| 64 | - if fetchURL, ok := githubSubmoduleFetchURL(remoteURL); ok { | |
| 65 | + if fetchURL, ok := githubSubmoduleFetchURL(cfg, remoteURL); ok { | |
| 65 | 66 | backfilled, backfillErr := h.backfillSubmoduleCommit(ctx, gitDir, fetchURL, oid) |
| 66 | 67 | if backfillErr != nil { |
| 67 | 68 | if h.d.Logger != nil { |
@@ -225,11 +226,14 @@ func submoduleRepoTarget(cfg submoduleRouteConfig, remoteURL string) (owner, rep | ||
| 225 | 226 | return ownerRepoFromRemotePath(strings.TrimPrefix(u.EscapedPath(), "/")) |
| 226 | 227 | } |
| 227 | 228 | |
| 228 | -func githubSubmoduleFetchURL(remoteURL string) (string, bool) { | |
| 229 | +func githubSubmoduleFetchURL(cfg submoduleRouteConfig, remoteURL string) (string, bool) { | |
| 229 | 230 | remoteURL = strings.TrimSpace(remoteURL) |
| 230 | 231 | if remoteURL == "" { |
| 231 | 232 | return "", false |
| 232 | 233 | } |
| 234 | + if owner, repoName, ok := submoduleRepoFromRelativeURL(cfg, remoteURL); ok { | |
| 235 | + return githubRepoFetchURL(owner, repoName), true | |
| 236 | + } | |
| 233 | 237 | var repoPath string |
| 234 | 238 | if host, path, ok := scpLikeRemote(remoteURL); ok { |
| 235 | 239 | if normalizeRemoteHost(host) != "github.com" { |
@@ -255,7 +259,11 @@ func githubSubmoduleFetchURL(remoteURL string) (string, bool) { | ||
| 255 | 259 | if !ok { |
| 256 | 260 | return "", false |
| 257 | 261 | } |
| 258 | - return "https://github.com/" + url.PathEscape(owner) + "/" + url.PathEscape(repoName) + ".git", true | |
| 262 | + return githubRepoFetchURL(owner, repoName), true | |
| 263 | +} | |
| 264 | + | |
| 265 | +func githubRepoFetchURL(owner, repoName string) string { | |
| 266 | + return "https://github.com/" + url.PathEscape(owner) + "/" + url.PathEscape(repoName) + ".git" | |
| 259 | 267 | } |
| 260 | 268 | |
| 261 | 269 | func submoduleRepoFromRelativeURL(cfg submoduleRouteConfig, remoteURL string) (owner, repoName string, ok bool) { |