From a9635adb9c6bae936bd7d374b8ae02412f751c38 Mon Sep 17 00:00:00 2001 From: William Bezuidenhout Date: Thu, 24 Apr 2025 11:13:39 +0200 Subject: [PATCH] feat(docs): implement version-based redirects for docs 5.3+ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added logic to redirect documentation version requests ≥ 5.3 to www.sourcegraph.com/docs/{version}/. Maintains existing behavior for older versions while seamlessly transitioning users to the new docs domain. --- handler.go | 33 +++++++++++++++++++++++++++++ handler_test.go | 56 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) diff --git a/handler.go b/handler.go index 8158803..65b8aa6 100644 --- a/handler.go +++ b/handler.go @@ -6,9 +6,30 @@ import ( "os" "path" "path/filepath" + "regexp" + "strconv" "strings" ) +// versionPattern matches version strings like @5.2, @5.2.0, etc. and captures major and minor version numbers +var versionPattern = regexp.MustCompile(`^@(\d+)\.(\d+)(?:\.(\d+))?$`) + +// shouldRedirectVersion returns true for versions ≥ 5.3 (format: @major.minor[.patch]) +func shouldRedirectVersion(version string) bool { + matches := versionPattern.FindStringSubmatch(version) + if len(matches) < 3 { + return false + } + + major, err1 := strconv.Atoi(matches[1]) + minor, err2 := strconv.Atoi(matches[2]) + if err1 != nil || err2 != nil { + return false + } + + return major > 5 || (major == 5 && minor >= 3) +} + // Handler returns an http.Handler that serves the site. func (s *Site) Handler() http.Handler { m := http.NewServeMux() @@ -124,6 +145,18 @@ func (s *Site) Handler() http.Handler { urlPath = r.URL.Path[1+end+1:] contentVersion = r.URL.Path[1 : 1+end] } + + // Redirect versions ≥ 5.3 to new docs domain with path preservation + version := "@" + contentVersion + if shouldRedirectVersion(version) { + newURL := "https://www.sourcegraph.com/docs/" + contentVersion + "/" + if urlPath != "" { + newURL += urlPath + } + http.Redirect(w, r, newURL, http.StatusPermanentRedirect) + return + } + r = requestShallowCopyWithURLPath(r, urlPath) } diff --git a/handler_test.go b/handler_test.go index ed01a6d..937b56b 100644 --- a/handler_test.go +++ b/handler_test.go @@ -263,4 +263,60 @@ func TestSite_Handler(t *testing.T) { t.Errorf("got body %q, want contains %q", rr.Body.String(), want) } }) + + t.Run("version redirects", func(t *testing.T) { + t.Run("version 5.1 - no redirect", func(t *testing.T) { + rr := httptest.NewRecorder() + req, _ := http.NewRequest("GET", "/@5.1", nil) + handler.ServeHTTP(rr, req) + checkResponseStatus(t, rr, http.StatusNotFound) + }) + + t.Run("version 5.2 - no redirect", func(t *testing.T) { + rr := httptest.NewRecorder() + req, _ := http.NewRequest("GET", "/@5.2", nil) + handler.ServeHTTP(rr, req) + checkResponseStatus(t, rr, http.StatusNotFound) + }) + + t.Run("version 5.3 - should redirect", func(t *testing.T) { + rr := httptest.NewRecorder() + req, _ := http.NewRequest("GET", "/@5.3", nil) + handler.ServeHTTP(rr, req) + checkResponseStatus(t, rr, http.StatusPermanentRedirect) + if got, want := rr.Header().Get("Location"), "https://www.sourcegraph.com/docs/5.3/"; got != want { + t.Errorf("got redirect Location %q, want %q", got, want) + } + }) + + t.Run("version with path - should redirect with path", func(t *testing.T) { + rr := httptest.NewRecorder() + req, _ := http.NewRequest("GET", "/@5.3/some/path", nil) + handler.ServeHTTP(rr, req) + checkResponseStatus(t, rr, http.StatusPermanentRedirect) + if got, want := rr.Header().Get("Location"), "https://www.sourcegraph.com/docs/5.3/some/path"; got != want { + t.Errorf("got redirect Location %q, want %q", got, want) + } + }) + + t.Run("major version > 5 - should redirect", func(t *testing.T) { + rr := httptest.NewRecorder() + req, _ := http.NewRequest("GET", "/@6.0", nil) + handler.ServeHTTP(rr, req) + checkResponseStatus(t, rr, http.StatusPermanentRedirect) + if got, want := rr.Header().Get("Location"), "https://www.sourcegraph.com/docs/6.0/"; got != want { + t.Errorf("got redirect Location %q, want %q", got, want) + } + }) + + t.Run("version with patch - should redirect", func(t *testing.T) { + rr := httptest.NewRecorder() + req, _ := http.NewRequest("GET", "/@5.3.1", nil) + handler.ServeHTTP(rr, req) + checkResponseStatus(t, rr, http.StatusPermanentRedirect) + if got, want := rr.Header().Get("Location"), "https://www.sourcegraph.com/docs/5.3.1/"; got != want { + t.Errorf("got redirect Location %q, want %q", got, want) + } + }) + }) }