Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to set default unit per repo #32363

Draft
wants to merge 20 commits into
base: main
Choose a base branch
from
Draft
62 changes: 62 additions & 0 deletions models/fixtures/repository.yml

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions models/migrations/migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,7 @@ func prepareMigrationTasks() []*migration {
newMigration(304, "Add index for release sha1", v1_23.AddIndexForReleaseSha1),
newMigration(305, "Add Repository Licenses", v1_23.AddRepositoryLicenses),
newMigration(306, "Add BlockAdminMergeOverride to ProtectedBranch", v1_23.AddBlockAdminMergeOverrideBranchProtection),
newMigration(307, "Add DefaultUnit to Repository", v1_23.AddDefaultUnitToRepository),
}
return preparedMigrations
}
Expand Down
13 changes: 13 additions & 0 deletions models/migrations/v1_23/v307.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package v1_23 //nolint

import "xorm.io/xorm"

func AddDefaultUnitToRepository(x *xorm.Engine) error {
type Repository struct {
DefaultUnit int `xorm:"NOT NULL DEFAULT 1"`
}
return x.Sync(new(Repository))
}
1 change: 1 addition & 0 deletions models/repo/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ type Repository struct {
RenderingMetas map[string]string `xorm:"-"`
DocumentRenderingMetas map[string]string `xorm:"-"`
Units []*RepoUnit `xorm:"-"`
DefaultUnit unit.Type `xorm:"NOT NULL DEFAULT 1"`
PrimaryLanguage *LanguageStat `xorm:"-"`

IsFork bool `xorm:"INDEX NOT NULL DEFAULT false"`
Expand Down
2 changes: 1 addition & 1 deletion models/unit/unit.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ var (
UnitCode = Unit{
TypeCode,
"repo.code",
"/",
"/code",
"repo.code.desc",
0,
perm.AccessModeOwner,
Expand Down
13 changes: 11 additions & 2 deletions routers/web/repo/view.go
Original file line number Diff line number Diff line change
Expand Up @@ -771,8 +771,8 @@ func checkCitationFile(ctx *context.Context, entry *git.TreeEntry) {
}
}

// Home render repository home page
func Home(ctx *context.Context) {
// HomeWithFeedCheck redirect to default unit or return feed
func HomeWithFeedCheck(ctx *context.Context) {
if setting.Other.EnableFeed {
isFeed, _, showFeedType := feed.GetFeedType(ctx.PathParam(":reponame"), ctx.Req)
if isFeed {
Expand All @@ -788,6 +788,15 @@ func Home(ctx *context.Context) {
}
}

defaultURI := ctx.Repo.Repository.MustGetUnit(ctx, ctx.Repo.Repository.DefaultUnit).Unit().URI
if defaultURI == "/" { // support legacy code units
defaultURI = "/code"
}
ctx.Redirect(ctx.Repo.RepoLink+defaultURI, http.StatusMovedPermanently)
}

// CodeHome render repository code unit page
func CodeHome(ctx *context.Context) {
checkHomeCodeViewable(ctx)
if ctx.Written() {
return
Expand Down
14 changes: 8 additions & 6 deletions routers/web/web.go
Original file line number Diff line number Diff line change
Expand Up @@ -1139,8 +1139,10 @@ func registerRoutes(m *web.Router) {
)
// end "/{username}/{reponame}/settings"

// user/org home, including rss feeds
m.Get("/{username}/{reponame}", ignSignIn, context.RepoAssignment, context.RepoRef(), repo.SetEditorconfigIfExists, repo.Home)
// user/org home, witch either redirects to default unit or return rss feeds
m.Get("/{username}/{reponame}", ignSignIn, context.RepoAssignment, context.RepoRef(), repo.SetEditorconfigIfExists, repo.HomeWithFeedCheck)
// show the code unit's home view
m.Get("/{username}/{reponame}/code", ignSignIn, context.RepoAssignment, context.RepoRef(), repo.SetEditorconfigIfExists, repo.CodeHome)

// TODO: maybe it should relax the permission to allow "any access"
m.Post("/{username}/{reponame}/markup", ignSignIn, context.RepoAssignment, context.RequireRepoReaderOr(unit.TypeCode, unit.TypeIssues, unit.TypePullRequests, unit.TypeReleases, unit.TypeWiki), web.Bind(structs.MarkupOption{}), misc.Markup)
Expand Down Expand Up @@ -1579,10 +1581,10 @@ func registerRoutes(m *web.Router) {
m.Get("/atom/branch/*", context.RepoRefByType(context.RepoRefBranch), feedEnabled, feed.RenderBranchFeed)

m.Group("/src", func() {
m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.Home)
m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.Home)
m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.Home)
m.Get("/*", context.RepoRefByType(context.RepoRefLegacy), repo.Home) // "/*" route is deprecated, and kept for backward compatibility
m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.CodeHome)
m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.CodeHome)
m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.CodeHome)
m.Get("/*", context.RepoRefByType(context.RepoRefLegacy), repo.CodeHome) // "/*" route is deprecated, and kept for backward compatibility
}, repo.SetEditorconfigIfExists)

m.Get("/forks", context.RepoRef(), repo.Forks)
Expand Down
2 changes: 1 addition & 1 deletion templates/repo/header.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@
{{if not (or .Repository.IsBeingCreated .Repository.IsBroken)}}
<div class="overflow-menu-items">
{{if .Permission.CanRead ctx.Consts.RepoUnitTypeCode}}
<a class="{{if .PageIsViewCode}}active {{end}}item" href="{{.RepoLink}}{{if and (ne .BranchName .Repository.DefaultBranch) (not $.PageIsWiki)}}/src/{{.BranchNameSubURL}}{{end}}">
<a class="{{if .PageIsViewCode}}active {{end}}item" href="{{.RepoLink}}{{if and (ne .BranchName .Repository.DefaultBranch) (not $.PageIsWiki)}}/src/{{.BranchNameSubURL}}{{else}}/code{{end}}">
{{svg "octicon-code"}} {{ctx.Locale.Tr "repo.code"}}
</a>
{{end}}
Expand Down
19 changes: 16 additions & 3 deletions tests/integration/links_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ func TestLinksNoLogin(t *testing.T) {
"/user/login",
"/user/forgot_password",
"/api/swagger",
"/user2/repo1",
"/user2/repo1/",
"/user2/repo1/code",
"/user2/repo1/projects",
"/user2/repo1/projects/1",
"/user2/repo1/releases/tag/delete-tag", // It's the only one existing record on release.yml which has is_tag: true
Expand Down Expand Up @@ -64,6 +63,20 @@ func TestRedirectsNoLogin(t *testing.T) {
}
}

func TestPermanentRedirectsNoLogin(t *testing.T) {
defer tests.PrepareTestEnv(t)()

redirects := map[string]string{
"/user2/repo1": "/user2/repo1/code",
"/user2/repo1/": "/user2/repo1/code",
}
for link, redirectLink := range redirects {
req := NewRequest(t, "GET", link)
resp := MakeRequest(t, req, http.StatusMovedPermanently)
assert.EqualValues(t, path.Join(setting.AppSubURL, redirectLink), test.RedirectURL(resp))
}
}

func TestNoLoginNotExist(t *testing.T) {
defer tests.PrepareTestEnv(t)()

Expand Down Expand Up @@ -139,7 +152,7 @@ func testLinksAsUser(userName string, t *testing.T) {
DecodeJSON(t, respAPI, &apiRepos)

repoLinks := []string{
"",
"/code",
"/issues",
"/pulls",
"/commits/branch/master",
Expand Down
11 changes: 9 additions & 2 deletions tests/integration/migrate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,19 @@ func TestMigrateGiteaForm(t *testing.T) {

// Step 0: verify the repo is available
req := NewRequestf(t, "GET", "/%s/%s", ownerName, repoName)
_ = session.MakeRequest(t, req, http.StatusOK)
resp := session.MakeRequest(t, req, http.StatusMovedPermanently)
req = NewRequest(t, "GET", resp.Result().Header.Get("Location"))
_ = MakeRequest(t, req, http.StatusOK)

// Step 1: get the Gitea migration form
req = NewRequestf(t, "GET", "/repo/migrate/?service_type=%d", structs.GiteaService)
resp := session.MakeRequest(t, req, http.StatusOK)
resp = session.MakeRequest(t, req, http.StatusOK)

// Step 2: load the form
htmlDoc := NewHTMLParser(t, resp.Body)
link, exists := htmlDoc.doc.Find(`form.ui.form[action^="/repo/migrate"]`).Attr("action")
assert.True(t, exists, "The template has changed")

// Step 4: submit the migration to only migrate issues
migratedRepoName := "otherrepo"
req = NewRequestWithValues(t, "POST", link, map[string]string{
Expand All @@ -94,9 +99,11 @@ func TestMigrateGiteaForm(t *testing.T) {
"uid": fmt.Sprintf("%d", repoOwner.ID),
})
resp = session.MakeRequest(t, req, http.StatusSeeOther)

// Step 5: a redirection displays the migrated repository
loc := resp.Header().Get("Location")
assert.EqualValues(t, fmt.Sprintf("/%s/%s", ownerName, migratedRepoName), loc)

// Step 6: check the repo was created
unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{Name: migratedRepoName})
})
Expand Down
14 changes: 7 additions & 7 deletions tests/integration/org_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func TestLimitedOrg(t *testing.T) {
session := loginUser(t, "user2")
req = NewRequest(t, "GET", "/limited_org")
session.MakeRequest(t, req, http.StatusOK)
req = NewRequest(t, "GET", "/limited_org/public_repo_on_limited_org")
req = NewRequest(t, "GET", "/limited_org/public_repo_on_limited_org/code")
session.MakeRequest(t, req, http.StatusOK)
req = NewRequest(t, "GET", "/limited_org/private_repo_on_limited_org")
session.MakeRequest(t, req, http.StatusNotFound)
Expand All @@ -72,9 +72,9 @@ func TestLimitedOrg(t *testing.T) {
session = loginUser(t, "user1")
req = NewRequest(t, "GET", "/limited_org")
session.MakeRequest(t, req, http.StatusOK)
req = NewRequest(t, "GET", "/limited_org/public_repo_on_limited_org")
req = NewRequest(t, "GET", "/limited_org/public_repo_on_limited_org/code")
session.MakeRequest(t, req, http.StatusOK)
req = NewRequest(t, "GET", "/limited_org/private_repo_on_limited_org")
req = NewRequest(t, "GET", "/limited_org/private_repo_on_limited_org/code")
session.MakeRequest(t, req, http.StatusOK)
}

Expand Down Expand Up @@ -102,7 +102,7 @@ func TestPrivateOrg(t *testing.T) {
session = loginUser(t, "user4")
req = NewRequest(t, "GET", "/privated_org")
session.MakeRequest(t, req, http.StatusNotFound)
req = NewRequest(t, "GET", "/privated_org/public_repo_on_private_org") // colab of this repo
req = NewRequest(t, "GET", "/privated_org/public_repo_on_private_org/code") // colab of this repo
session.MakeRequest(t, req, http.StatusOK)
req = NewRequest(t, "GET", "/privated_org/private_repo_on_private_org")
session.MakeRequest(t, req, http.StatusNotFound)
Expand All @@ -111,9 +111,9 @@ func TestPrivateOrg(t *testing.T) {
session = loginUser(t, "user1")
req = NewRequest(t, "GET", "/privated_org")
session.MakeRequest(t, req, http.StatusOK)
req = NewRequest(t, "GET", "/privated_org/public_repo_on_private_org")
req = NewRequest(t, "GET", "/privated_org/public_repo_on_private_org/code")
session.MakeRequest(t, req, http.StatusOK)
req = NewRequest(t, "GET", "/privated_org/private_repo_on_private_org")
req = NewRequest(t, "GET", "/privated_org/private_repo_on_private_org/code")
session.MakeRequest(t, req, http.StatusOK)
}

Expand Down Expand Up @@ -191,7 +191,7 @@ func TestOrgRestrictedUser(t *testing.T) {
req = NewRequest(t, "GET", fmt.Sprintf("/%s", orgName))
restrictedSession.MakeRequest(t, req, http.StatusOK)

req = NewRequest(t, "GET", fmt.Sprintf("/%s/%s", orgName, repoName))
req = NewRequest(t, "GET", fmt.Sprintf("/%s/%s/code", orgName, repoName))
restrictedSession.MakeRequest(t, req, http.StatusOK)
}

Expand Down
2 changes: 1 addition & 1 deletion tests/integration/pull_create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (
)

func testPullCreate(t *testing.T, session *TestSession, user, repo string, toSelf bool, targetBranch, sourceBranch, title string) *httptest.ResponseRecorder {
req := NewRequest(t, "GET", path.Join(user, repo))
req := NewRequest(t, "GET", path.Join(user, repo, "code"))
resp := session.MakeRequest(t, req, http.StatusOK)

// Click the PR button to create a pull
Expand Down
4 changes: 3 additions & 1 deletion tests/integration/repo_branch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,9 @@ func prepareRepoPR(t *testing.T, baseSession, headSession *TestSession, baseRepo
func checkRecentlyPushedNewBranches(t *testing.T, session *TestSession, repoPath string, expected []string) {
branches := make([]string, 0, 2)
req := NewRequest(t, "GET", repoPath)
resp := session.MakeRequest(t, req, http.StatusOK)
resp := session.MakeRequest(t, req, http.StatusMovedPermanently)
req = NewRequest(t, "GET", resp.Result().Header.Get("Location"))
resp = MakeRequest(t, req, http.StatusOK)
doc := NewHTMLParser(t, resp.Body)
doc.doc.Find(".ui.positive.message div a").Each(func(index int, branch *goquery.Selection) {
branches = append(branches, branch.Text())
Expand Down
8 changes: 5 additions & 3 deletions tests/integration/repo_fork_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func testRepoFork(t *testing.T, session *TestSession, ownerName, repoName, forkO
session.MakeRequest(t, req, http.StatusNotFound)

// Step1: go to the main page of repo
req = NewRequestf(t, "GET", "/%s/%s", ownerName, repoName)
req = NewRequestf(t, "GET", "/%s/%s/code", ownerName, repoName)
resp := session.MakeRequest(t, req, http.StatusOK)

// Step2: click the fork button
Expand All @@ -49,7 +49,7 @@ func testRepoFork(t *testing.T, session *TestSession, ownerName, repoName, forkO
session.MakeRequest(t, req, http.StatusSeeOther)

// Step4: check the existence of the forked repo
req = NewRequestf(t, "GET", "/%s/%s", forkOwnerName, forkRepoName)
req = NewRequestf(t, "GET", "/%s/%s/code", forkOwnerName, forkRepoName)
resp = session.MakeRequest(t, req, http.StatusOK)

return resp
Expand All @@ -69,7 +69,9 @@ func TestRepoForkToOrg(t *testing.T) {
// Check that no more forking is allowed as user2 owns repository
// and org3 organization that owner user2 is also now has forked this repository
req := NewRequest(t, "GET", "/user2/repo1")
resp := session.MakeRequest(t, req, http.StatusOK)
resp := session.MakeRequest(t, req, http.StatusMovedPermanently)
req = NewRequest(t, "GET", resp.Result().Header.Get("Location"))
resp = MakeRequest(t, req, http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
_, exists := htmlDoc.doc.Find(`a.ui.button[href*="/fork"]`).Attr("href")
assert.False(t, exists, "Forking should not be allowed anymore")
Expand Down
6 changes: 4 additions & 2 deletions tests/integration/repo_generate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ func testRepoGenerate(t *testing.T, session *TestSession, templateID, templateOw

// Step1: go to the main page of template repo
req = NewRequestf(t, "GET", "/%s/%s", templateOwnerName, templateRepoName)
resp := session.MakeRequest(t, req, http.StatusOK)
resp := session.MakeRequest(t, req, http.StatusMovedPermanently)
req = NewRequest(t, "GET", resp.Result().Header.Get("Location"))
resp = session.MakeRequest(t, req, http.StatusOK)

// Step2: click the "Use this template" button
htmlDoc := NewHTMLParser(t, resp.Body)
Expand All @@ -53,7 +55,7 @@ func testRepoGenerate(t *testing.T, session *TestSession, templateID, templateOw

// Step4: check the existence of the generated repo
req = NewRequestf(t, "GET", "/%s/%s", generateOwnerName, generateRepoName)
session.MakeRequest(t, req, http.StatusOK)
session.MakeRequest(t, req, http.StatusMovedPermanently)

// Step5: check substituted values in Readme
req = NewRequestf(t, "GET", "/%s/%s/raw/branch/master/README.md", generateOwnerName, generateRepoName)
Expand Down
26 changes: 19 additions & 7 deletions tests/integration/repo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@ func TestViewRepo(t *testing.T) {
session := loginUser(t, "user2")

req := NewRequest(t, "GET", "/user2/repo1")
resp := session.MakeRequest(t, req, http.StatusOK)
resp := session.MakeRequest(t, req, http.StatusMovedPermanently)

link := resp.Result().Header.Get("Location")
req = NewRequest(t, "GET", link)
resp = session.MakeRequest(t, req, http.StatusOK)

htmlDoc := NewHTMLParser(t, resp.Body)
repoTopics := htmlDoc.doc.Find("#repo-topics").Children()
Expand All @@ -44,7 +48,7 @@ func TestViewRepo(t *testing.T) {
func testViewRepo(t *testing.T) {
defer tests.PrepareTestEnv(t)()

req := NewRequest(t, "GET", "/org3/repo3")
req := NewRequest(t, "GET", "/org3/repo3/code")
session := loginUser(t, "user2")
resp := session.MakeRequest(t, req, http.StatusOK)

Expand Down Expand Up @@ -115,7 +119,7 @@ func TestViewRepo2(t *testing.T) {
func TestViewRepo3(t *testing.T) {
defer tests.PrepareTestEnv(t)()

req := NewRequest(t, "GET", "/org3/repo3")
req := NewRequest(t, "GET", "/org3/repo3/code")
session := loginUser(t, "user4")
session.MakeRequest(t, req, http.StatusOK)
}
Expand All @@ -124,7 +128,11 @@ func TestViewRepo1CloneLinkAnonymous(t *testing.T) {
defer tests.PrepareTestEnv(t)()

req := NewRequest(t, "GET", "/user2/repo1")
resp := MakeRequest(t, req, http.StatusOK)
resp := MakeRequest(t, req, http.StatusMovedPermanently)

link := resp.Result().Header.Get("Location")
req = NewRequest(t, "GET", link)
resp = MakeRequest(t, req, http.StatusOK)

htmlDoc := NewHTMLParser(t, resp.Body)
link, exists := htmlDoc.doc.Find("#repo-clone-https").Attr("data-link")
Expand All @@ -139,7 +147,7 @@ func TestViewRepo1CloneLinkAuthorized(t *testing.T) {

session := loginUser(t, "user2")

req := NewRequest(t, "GET", "/user2/repo1")
req := NewRequest(t, "GET", "/user2/repo1/code")
resp := session.MakeRequest(t, req, http.StatusOK)

htmlDoc := NewHTMLParser(t, resp.Body)
Expand All @@ -158,7 +166,11 @@ func TestViewRepoWithSymlinks(t *testing.T) {
session := loginUser(t, "user2")

req := NewRequest(t, "GET", "/user2/repo20.git")
resp := session.MakeRequest(t, req, http.StatusOK)
resp := session.MakeRequest(t, req, http.StatusMovedPermanently)

link := resp.Result().Header.Get("Location")
req = NewRequest(t, "GET", link)
resp = session.MakeRequest(t, req, http.StatusOK)

htmlDoc := NewHTMLParser(t, resp.Body)
files := htmlDoc.doc.Find("#repo-files-table > TBODY > TR > TD.name > SPAN.truncate")
Expand Down Expand Up @@ -269,7 +281,7 @@ func TestViewRepoDirectoryReadme(t *testing.T) {
}

// viewing the top level
check("Home", "/user2/readme-test/", "README.md", "markdown", "The cake is a lie.")
check("Home", "/user2/readme-test/code", "README.md", "markdown", "The cake is a lie.")

// viewing different file extensions
check("md", "/user2/readme-test/src/branch/master/", "README.md", "markdown", "The cake is a lie.")
Expand Down
Loading