Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions models/repo/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,12 @@ func (repo *Repository) MustGetUnit(ctx context.Context, tp unit.Type) *RepoUnit
Type: tp,
Config: cfg,
}
case unit.TypeCode:
cfg := new(CodeConfig)
return &RepoUnit{
Type: tp,
Config: cfg,
}
}

return &RepoUnit{
Expand Down
23 changes: 20 additions & 3 deletions models/repo/repo_unit.go
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,9 @@ func (r *RepoUnit) BeforeSet(colName string, val xorm.Cell) {
r.Config = new(ActionsConfig)
case unit.TypeProjects:
r.Config = new(ProjectsConfig)
case unit.TypeCode, unit.TypeReleases, unit.TypeWiki, unit.TypePackages:
case unit.TypeCode:
r.Config = new(CodeConfig)
case unit.TypeReleases, unit.TypeWiki, unit.TypePackages:
fallthrough
default:
r.Config = new(UnitConfig)
Expand All @@ -279,9 +281,24 @@ func (r *RepoUnit) Unit() unit.Unit {
return unit.Units[r.Type]
}

// CodeConfig describes code config
type CodeConfig struct {
GoModuleSubDir string
}

// FromDB fills up a CodeConfig from serialized format.
func (cfg *CodeConfig) FromDB(bs []byte) error {
return json.UnmarshalHandleDoubleEncode(bs, &cfg)
}

// ToDB exports a CodeConfig to a serialized format.
func (cfg *CodeConfig) ToDB() ([]byte, error) {
return json.Marshal(cfg)
}

// CodeConfig returns config for unit.TypeCode
func (r *RepoUnit) CodeConfig() *UnitConfig {
return r.Config.(*UnitConfig)
func (r *RepoUnit) CodeConfig() *CodeConfig {
return r.Config.(*CodeConfig)
}

// PullRequestsConfig returns config for unit.TypePullRequests
Expand Down
2 changes: 2 additions & 0 deletions modules/structs/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,8 @@ type EditRepoOption struct {
MirrorInterval *string `json:"mirror_interval,omitempty"`
// enable prune - remove obsolete remote-tracking references when mirroring
EnablePrune *bool `json:"enable_prune,omitempty"`
// set the subdirectory for Go modules
GoModuleSubDir *string `json:"go_module_sub_dir,omitempty"`
}

// GenerateRepoOption options when creating a repository using a template
Expand Down
3 changes: 3 additions & 0 deletions options/locale/locale_en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -2629,6 +2629,9 @@ settings.rename_branch_from=old branch name
settings.rename_branch_to=new branch name
settings.rename_branch=Rename branch

settings.go_module_sub_dir = Golang module subdirectory
settings.go_module_sub_dir_desc = The subdirectory in repository redirect for go module (which is supported since go 1.25, see <a target="_blank" rel="noopener noreferrer" href="%[1]s">%[2]s</a> for more detail)

diff.browse_source = Browse Source
diff.parent = parent
diff.commit = commit
Expand Down
8 changes: 7 additions & 1 deletion routers/api/v1/repo/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -871,10 +871,16 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {

if opts.HasCode != nil && !unit_model.TypeCode.UnitGlobalDisabled() {
if *opts.HasCode {
cfg := &repo_model.CodeConfig{}

if opts.GoModuleSubDir != nil {
cfg.GoModuleSubDir = *opts.GoModuleSubDir
}

units = append(units, repo_model.RepoUnit{
RepoID: repo.ID,
Type: unit_model.TypeCode,
Config: &repo_model.UnitConfig{},
Config: cfg,
})
} else {
deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeCode)
Expand Down
18 changes: 17 additions & 1 deletion routers/web/goget.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,18 @@ func goGet(ctx *context.Context) {
if err == nil && len(repo.DefaultBranch) > 0 {
branchName = repo.DefaultBranch
}
prefix := setting.AppURL + path.Join(url.PathEscape(ownerName), url.PathEscape(repoName), "src", "branch", util.PathEscapeSegments(branchName))

subDir := ""
if repo != nil {
subDir = context.GetGoModuleSubDirConfig(ctx, repo)
}

var prefix string
if len(subDir) > 0 {
prefix = setting.AppURL + path.Join(url.PathEscape(ownerName), url.PathEscape(repoName), "src", "branch", util.PathEscapeSegments(branchName), subDir)
} else {
prefix = setting.AppURL + path.Join(url.PathEscape(ownerName), url.PathEscape(repoName), "src", "branch", util.PathEscapeSegments(branchName))
}

appURL, _ := url.Parse(setting.AppURL)

Expand All @@ -73,6 +84,11 @@ func goGet(ctx *context.Context) {
} else {
cloneURL = repo_model.ComposeHTTPSCloneURL(ctx, ownerName, repoName)
}

if len(subDir) > 0 {
cloneURL += " " + util.PathEscapeSegments(subDir)
}

goImportContent := fmt.Sprintf("%s git %s", goGetImport, cloneURL /*CloneLink*/)
goSourceContent := fmt.Sprintf("%s _ %s %s", goGetImport, prefix+"{/dir}" /*GoDocDirectory*/, prefix+"{/dir}/{file}#L{line}" /*GoDocFile*/)
goGetCli := fmt.Sprintf("go get %s%s", insecure, goGetImport)
Expand Down
4 changes: 3 additions & 1 deletion routers/web/repo/setting/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,9 @@ func handleSettingsPostAdvanced(ctx *context.Context) {
}

if form.EnableCode && !unit_model.TypeCode.UnitGlobalDisabled() {
units = append(units, newRepoUnit(repo, unit_model.TypeCode, nil))
units = append(units, newRepoUnit(repo, unit_model.TypeCode, &repo_model.CodeConfig{
GoModuleSubDir: form.GoModuleSubDir,
}))
} else if !unit_model.TypeCode.UnitGlobalDisabled() {
deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeCode)
}
Expand Down
7 changes: 7 additions & 0 deletions services/context/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,13 @@ func EarlyResponseForGoGetMeta(ctx *Context) {
ctx.PlainText(http.StatusOK, htmlMeta)
}

// GetGoModuleSubDirConfig retrieves the subdirectory configuration for a Go module.
func GetGoModuleSubDirConfig(ctx *Context, repo *repo_model.Repository) string {
codeCfg := repo.MustGetUnit(ctx, unit_model.TypeCode).CodeConfig()

return strings.TrimSpace(codeCfg.GoModuleSubDir)
}

// RedirectToRepo redirect to a differently-named repository
func RedirectToRepo(ctx *Base, redirectRepoID int64) {
ownerName := ctx.PathParam("username")
Expand Down
2 changes: 2 additions & 0 deletions services/forms/repo_form.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ type RepoSettingForm struct {

EnableActions bool

GoModuleSubDir string

IsArchived bool

// Signing Settings
Expand Down
14 changes: 13 additions & 1 deletion templates/repo/settings/options.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -304,14 +304,26 @@

{{$isCodeEnabled := .Repository.UnitEnabled ctx ctx.Consts.RepoUnitTypeCode}}
{{$isCodeGlobalDisabled := ctx.Consts.RepoUnitTypeCode.UnitGlobalDisabled}}
{{$codeUnit := .Repository.MustGetUnit ctx ctx.Consts.RepoUnitTypeCode}}

<div class="inline field">
<label>{{ctx.Locale.Tr "repo.code"}}</label>
<div class="ui checkbox{{if $isCodeGlobalDisabled}} disabled{{end}}"{{if $isCodeGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
<input class="enable-system" name="enable_code" type="checkbox"{{if $isCodeEnabled}} checked{{end}}>
<input class="enable-system" name="enable_code" type="checkbox" data-target="#code_box" {{if $isCodeEnabled}} checked{{end}}>
<label>{{ctx.Locale.Tr "repo.code.desc"}}</label>
</div>
</div>

<div class="field{{if not $isCodeEnabled}} disabled{{end}}" id="code_box">
<div class="field tw-pl-4 {{if $isCodeGlobalDisabled}}disabled{{end}}">
<label for="go_module_sub_dir">{{ctx.Locale.Tr "repo.settings.go_module_sub_dir"}}</label>
<input id="go_module_sub_dir" name="go_module_sub_dir" value="{{$codeUnit.CodeConfig.GoModuleSubDir}}">
<p class="help">{{ctx.Locale.Tr "repo.settings.go_module_sub_dir_desc" "https://github.com/golang/go/commit/835e36fc7f631f74233edfd4ab43b6b56833db86" "github.com/golang/go@835e36fc7f"}}</p>
</div>
</div>

<div class="divider"></div>

{{$isInternalWikiEnabled := .Repository.UnitEnabled ctx ctx.Consts.RepoUnitTypeWiki}}
{{$isExternalWikiEnabled := .Repository.UnitEnabled ctx ctx.Consts.RepoUnitTypeExternalWiki}}
{{$isWikiEnabled := or $isInternalWikiEnabled $isExternalWikiEnabled}}
Expand Down
5 changes: 5 additions & 0 deletions templates/swagger/v1_json.tmpl

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

37 changes: 37 additions & 0 deletions tests/integration/goget_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ import (
"net/http"
"testing"

auth_model "code.gitea.io/gitea/models/auth"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/tests"

Expand All @@ -34,6 +38,39 @@ func TestGoGet(t *testing.T) {
assert.Equal(t, expected, resp.Body.String())
}

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

user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})

// Get user2's token
session := loginUser(t, user2.Name)
token2 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
repoEditOption := getRepoEditOptionFromRepo(repo1)
goModuleSubDir := "package"
repoEditOption.GoModuleSubDir = &goModuleSubDir
req := NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, repo1.Name), &repoEditOption).
AddTokenAuth(token2)
MakeRequest(t, req, http.StatusOK)

req = NewRequest(t, "GET", fmt.Sprintf("/%s/%s/plah?go-get=1", user2.Name, repo1.Name))
resp := MakeRequest(t, req, http.StatusOK)

expected := fmt.Sprintf(`<!doctype html>
<html>
<head>
<meta name="go-import" content="%[1]s:%[2]s/%[4]s/%[5]s git %[3]s%[4]s/%[5]s.git package">
<meta name="go-source" content="%[1]s:%[2]s/%[4]s/%[5]s _ %[3]s%[4]s/%[5]s/src/branch/master/package{/dir} %[3]s%[4]s/%[5]s/src/branch/master/package{/dir}/{file}#L{line}">
</head>
<body>
go get --insecure %[1]s:%[2]s/%[4]s/%[5]s
</body>
</html>`, setting.Domain, setting.HTTPPort, setting.AppURL, user2.Name, repo1.Name)

assert.Equal(t, expected, resp.Body.String())
}

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

Expand Down