diff --git a/models/repo/repo.go b/models/repo/repo.go index 2403b3b40bafe..fbb42b5e2db1e 100644 --- a/models/repo/repo.go +++ b/models/repo/repo.go @@ -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{ diff --git a/models/repo/repo_unit.go b/models/repo/repo_unit.go index a5207bc22a805..4250560e5dac3 100644 --- a/models/repo/repo_unit.go +++ b/models/repo/repo_unit.go @@ -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) @@ -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 diff --git a/modules/structs/repo.go b/modules/structs/repo.go index 404718def0f81..67e7da926f74e 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -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 diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index d7e73a0cfbb08..b020c1be7c3ac 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -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 %[2]s for more detail) + diff.browse_source = Browse Source diff.parent = parent diff.commit = commit diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index e69b7729a0fb0..8a11d37a8108a 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -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) diff --git a/routers/web/goget.go b/routers/web/goget.go index 67e0bee866c12..c5ed7c871b32f 100644 --- a/routers/web/goget.go +++ b/routers/web/goget.go @@ -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) @@ -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) diff --git a/routers/web/repo/setting/setting.go b/routers/web/repo/setting/setting.go index 0865d9d7c0f59..8c69078ab06ff 100644 --- a/routers/web/repo/setting/setting.go +++ b/routers/web/repo/setting/setting.go @@ -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) } diff --git a/services/context/repo.go b/services/context/repo.go index afc6de9b1666d..71f2e168f8b5f 100644 --- a/services/context/repo.go +++ b/services/context/repo.go @@ -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") diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index cb267f891ccb7..89639a6d5d38e 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -150,6 +150,8 @@ type RepoSettingForm struct { EnableActions bool + GoModuleSubDir string + IsArchived bool // Signing Settings diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl index fc42056e0a2bb..c048d0f01f2a7 100644 --- a/templates/repo/settings/options.tmpl +++ b/templates/repo/settings/options.tmpl @@ -304,14 +304,26 @@ {{$isCodeEnabled := .Repository.UnitEnabled ctx ctx.Consts.RepoUnitTypeCode}} {{$isCodeGlobalDisabled := ctx.Consts.RepoUnitTypeCode.UnitGlobalDisabled}} + {{$codeUnit := .Repository.MustGetUnit ctx ctx.Consts.RepoUnitTypeCode}} +
{{ctx.Locale.Tr "repo.settings.go_module_sub_dir_desc" "https://github.com/golang/go/commit/835e36fc7f631f74233edfd4ab43b6b56833db86" "github.com/golang/go@835e36fc7f"}}
+