You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

init.go 13KB

Add configurable Trust Models (#11712) * Add configurable Trust Models Gitea's default signature verification model differs from GitHub. GitHub uses signatures to verify that the committer is who they say they are - meaning that when GitHub makes a signed commit it must be the committer. The GitHub model prevents re-publishing of commits after revocation of a key and prevents re-signing of other people's commits to create a completely trusted repository signed by one key or a set of trusted keys. The default behaviour of Gitea in contrast is to always display the avatar and information related to a signature. This allows signatures to be decoupled from the committer. That being said, allowing arbitary users to present other peoples commits as theirs is not necessarily desired therefore we have a trust model whereby signatures from collaborators are marked trusted, signatures matching the commit line are marked untrusted and signatures that match a user in the db but not the committer line are marked unmatched. The problem with this model is that this conflicts with Github therefore we need to provide an option to allow users to choose the Github model should they wish to. Signed-off-by: Andrew Thornton <art27@cantab.net> * Adjust locale strings Signed-off-by: Andrew Thornton <art27@cantab.net> * as per @6543 Co-authored-by: 6543 <6543@obermui.de> * Update models/gpg_key.go * Add migration for repository Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
3 years ago
Refactor git command package to improve security and maintainability (#22678) This PR follows #21535 (and replace #22592) ## Review without space diff https://github.com/go-gitea/gitea/pull/22678/files?diff=split&w=1 ## Purpose of this PR 1. Make git module command completely safe (risky user inputs won't be passed as argument option anymore) 2. Avoid low-level mistakes like https://github.com/go-gitea/gitea/pull/22098#discussion_r1045234918 3. Remove deprecated and dirty `CmdArgCheck` function, hide the `CmdArg` type 4. Simplify code when using git command ## The main idea of this PR * Move the `git.CmdArg` to the `internal` package, then no other package except `git` could use it. Then developers could never do `AddArguments(git.CmdArg(userInput))` any more. * Introduce `git.ToTrustedCmdArgs`, it's for user-provided and already trusted arguments. It's only used in a few cases, for example: use git arguments from config file, help unit test with some arguments. * Introduce `AddOptionValues` and `AddOptionFormat`, they make code more clear and simple: * Before: `AddArguments("-m").AddDynamicArguments(message)` * After: `AddOptionValues("-m", message)` * - * Before: `AddArguments(git.CmdArg(fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email)))` * After: `AddOptionFormat("--author='%s <%s>'", sig.Name, sig.Email)` ## FAQ ### Why these changes were not done in #21535 ? #21535 is mainly a search&replace, it did its best to not change too much logic. Making the framework better needs a lot of changes, so this separate PR is needed as the second step. ### The naming of `AddOptionXxx` According to git's manual, the `--xxx` part is called `option`. ### How can it guarantee that `internal.CmdArg` won't be not misused? Go's specification guarantees that. Trying to access other package's internal package causes compilation error. And, `golangci-lint` also denies the git/internal package. Only the `git/command.go` can use it carefully. ### There is still a `ToTrustedCmdArgs`, will it still allow developers to make mistakes and pass untrusted arguments? Generally speaking, no. Because when using `ToTrustedCmdArgs`, the code will be very complex (see the changes for examples). Then developers and reviewers can know that something might be unreasonable. ### Why there was a `CmdArgCheck` and why it's removed? At the moment of #21535, to reduce unnecessary changes, `CmdArgCheck` was introduced as a hacky patch. Now, almost all code could be written as `cmd := NewCommand(); cmd.AddXxx(...)`, then there is no need for `CmdArgCheck` anymore. ### Why many codes for `signArg == ""` is deleted? Because in the old code, `signArg` could never be empty string, it's either `-S[key-id]` or `--no-gpg-sign`. So the `signArg == ""` is just dead code. --------- Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
1 year ago
Add configurable Trust Models (#11712) * Add configurable Trust Models Gitea's default signature verification model differs from GitHub. GitHub uses signatures to verify that the committer is who they say they are - meaning that when GitHub makes a signed commit it must be the committer. The GitHub model prevents re-publishing of commits after revocation of a key and prevents re-signing of other people's commits to create a completely trusted repository signed by one key or a set of trusted keys. The default behaviour of Gitea in contrast is to always display the avatar and information related to a signature. This allows signatures to be decoupled from the committer. That being said, allowing arbitary users to present other peoples commits as theirs is not necessarily desired therefore we have a trust model whereby signatures from collaborators are marked trusted, signatures matching the commit line are marked untrusted and signatures that match a user in the db but not the committer line are marked unmatched. The problem with this model is that this conflicts with Github therefore we need to provide an option to allow users to choose the Github model should they wish to. Signed-off-by: Andrew Thornton <art27@cantab.net> * Adjust locale strings Signed-off-by: Andrew Thornton <art27@cantab.net> * as per @6543 Co-authored-by: 6543 <6543@obermui.de> * Update models/gpg_key.go * Add migration for repository Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
3 years ago
Adopt repositories (#12920) * Don't automatically delete repository files if they are present Prior to this PR Gitea would delete any repository files if they are present during creation or migration. This can in certain circumstances lead to data-loss and is slightly unpleasant. This PR provides a mechanism for Gitea to adopt repositories on creation and otherwise requires an explicit flag for deletion. PushCreate is slightly different - the create will cause adoption if that is allowed otherwise it will delete the data if that is allowed. Signed-off-by: Andrew Thornton <art27@cantab.net> * Update swagger Signed-off-by: Andrew Thornton <art27@cantab.net> * Fix tests and migrate overwrite Signed-off-by: Andrew Thornton <art27@cantab.net> * as per @lunny Only offer to adopt or overwrite if the user can do that. Allow the site administrator to adopt or overwrite in all circumstances Signed-off-by: Andrew Thornton <art27@cantab.net> * Use setting.Repository.DefaultBranch for the default branch Signed-off-by: Andrew Thornton <art27@cantab.net> * Always set setting.Repository.DefaultBranch Signed-off-by: Andrew Thornton <art27@cantab.net> * update swagger Signed-off-by: Andrew Thornton <art27@cantab.net> * update templates Signed-off-by: Andrew Thornton <art27@cantab.net> * ensure repo closed Signed-off-by: Andrew Thornton <art27@cantab.net> * Rewrite of adoption as per @6543 and @lunny Signed-off-by: Andrew Thornton <art27@cantab.net> * Apply suggestions from code review * update swagger Signed-off-by: Andrew Thornton <art27@cantab.net> * missing not Signed-off-by: Andrew Thornton <art27@cantab.net> * add modals and flash reporting Signed-off-by: Andrew Thornton <art27@cantab.net> * Make the unadopted page searchable Signed-off-by: Andrew Thornton <art27@cantab.net> * Add API Signed-off-by: Andrew Thornton <art27@cantab.net> * Fix swagger Signed-off-by: Andrew Thornton <art27@cantab.net> * fix swagger Signed-off-by: Andrew Thornton <art27@cantab.net> * Handle empty and non-master branched repositories Signed-off-by: Andrew Thornton <art27@cantab.net> * placate lint Signed-off-by: Andrew Thornton <art27@cantab.net> * remove commented out code Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: techknowlogick <techknowlogick@gitea.io>
3 years ago
Adopt repositories (#12920) * Don't automatically delete repository files if they are present Prior to this PR Gitea would delete any repository files if they are present during creation or migration. This can in certain circumstances lead to data-loss and is slightly unpleasant. This PR provides a mechanism for Gitea to adopt repositories on creation and otherwise requires an explicit flag for deletion. PushCreate is slightly different - the create will cause adoption if that is allowed otherwise it will delete the data if that is allowed. Signed-off-by: Andrew Thornton <art27@cantab.net> * Update swagger Signed-off-by: Andrew Thornton <art27@cantab.net> * Fix tests and migrate overwrite Signed-off-by: Andrew Thornton <art27@cantab.net> * as per @lunny Only offer to adopt or overwrite if the user can do that. Allow the site administrator to adopt or overwrite in all circumstances Signed-off-by: Andrew Thornton <art27@cantab.net> * Use setting.Repository.DefaultBranch for the default branch Signed-off-by: Andrew Thornton <art27@cantab.net> * Always set setting.Repository.DefaultBranch Signed-off-by: Andrew Thornton <art27@cantab.net> * update swagger Signed-off-by: Andrew Thornton <art27@cantab.net> * update templates Signed-off-by: Andrew Thornton <art27@cantab.net> * ensure repo closed Signed-off-by: Andrew Thornton <art27@cantab.net> * Rewrite of adoption as per @6543 and @lunny Signed-off-by: Andrew Thornton <art27@cantab.net> * Apply suggestions from code review * update swagger Signed-off-by: Andrew Thornton <art27@cantab.net> * missing not Signed-off-by: Andrew Thornton <art27@cantab.net> * add modals and flash reporting Signed-off-by: Andrew Thornton <art27@cantab.net> * Make the unadopted page searchable Signed-off-by: Andrew Thornton <art27@cantab.net> * Add API Signed-off-by: Andrew Thornton <art27@cantab.net> * Fix swagger Signed-off-by: Andrew Thornton <art27@cantab.net> * fix swagger Signed-off-by: Andrew Thornton <art27@cantab.net> * Handle empty and non-master branched repositories Signed-off-by: Andrew Thornton <art27@cantab.net> * placate lint Signed-off-by: Andrew Thornton <art27@cantab.net> * remove commented out code Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: techknowlogick <techknowlogick@gitea.io>
3 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. // Copyright 2019 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package repository
  4. import (
  5. "bytes"
  6. "context"
  7. "fmt"
  8. "os"
  9. "path/filepath"
  10. "sort"
  11. "strings"
  12. "time"
  13. issues_model "code.gitea.io/gitea/models/issues"
  14. repo_model "code.gitea.io/gitea/models/repo"
  15. user_model "code.gitea.io/gitea/models/user"
  16. "code.gitea.io/gitea/modules/git"
  17. "code.gitea.io/gitea/modules/label"
  18. "code.gitea.io/gitea/modules/log"
  19. "code.gitea.io/gitea/modules/options"
  20. "code.gitea.io/gitea/modules/setting"
  21. "code.gitea.io/gitea/modules/templates/vars"
  22. "code.gitea.io/gitea/modules/util"
  23. asymkey_service "code.gitea.io/gitea/services/asymkey"
  24. )
  25. type OptionFile struct {
  26. DisplayName string
  27. Description string
  28. }
  29. var (
  30. // Gitignores contains the gitiginore files
  31. Gitignores []string
  32. // Licenses contains the license files
  33. Licenses []string
  34. // Readmes contains the readme files
  35. Readmes []string
  36. // LabelTemplateFiles contains the label template files, each item has its DisplayName and Description
  37. LabelTemplateFiles []OptionFile
  38. labelTemplateFileMap = map[string]string{} // DisplayName => FileName mapping
  39. )
  40. type optionFileList struct {
  41. all []string // all files provided by bindata & custom-path. Sorted.
  42. custom []string // custom files provided by custom-path. Non-sorted, internal use only.
  43. }
  44. // mergeCustomLabelFiles merges the custom label files. Always use the file's main name (DisplayName) as the key to de-duplicate.
  45. func mergeCustomLabelFiles(fl optionFileList) []string {
  46. exts := map[string]int{"": 0, ".yml": 1, ".yaml": 2} // "yaml" file has the highest priority to be used.
  47. m := map[string]string{}
  48. merge := func(list []string) {
  49. sort.Slice(list, func(i, j int) bool { return exts[filepath.Ext(list[i])] < exts[filepath.Ext(list[j])] })
  50. for _, f := range list {
  51. m[strings.TrimSuffix(f, filepath.Ext(f))] = f
  52. }
  53. }
  54. merge(fl.all)
  55. merge(fl.custom)
  56. files := make([]string, 0, len(m))
  57. for _, f := range m {
  58. files = append(files, f)
  59. }
  60. sort.Strings(files)
  61. return files
  62. }
  63. // LoadRepoConfig loads the repository config
  64. func LoadRepoConfig() error {
  65. types := []string{"gitignore", "license", "readme", "label"} // option file directories
  66. typeFiles := make([]optionFileList, len(types))
  67. for i, t := range types {
  68. var err error
  69. if typeFiles[i].all, err = options.AssetFS().ListFiles(t, true); err != nil {
  70. return fmt.Errorf("failed to list %s files: %w", t, err)
  71. }
  72. sort.Strings(typeFiles[i].all)
  73. customPath := filepath.Join(setting.CustomPath, "options", t)
  74. if isDir, err := util.IsDir(customPath); err != nil {
  75. return fmt.Errorf("failed to check custom %s dir: %w", t, err)
  76. } else if isDir {
  77. if typeFiles[i].custom, err = util.StatDir(customPath); err != nil {
  78. return fmt.Errorf("failed to list custom %s files: %w", t, err)
  79. }
  80. }
  81. }
  82. Gitignores = typeFiles[0].all
  83. Licenses = typeFiles[1].all
  84. Readmes = typeFiles[2].all
  85. // Load label templates
  86. LabelTemplateFiles = nil
  87. labelTemplateFileMap = map[string]string{}
  88. for _, file := range mergeCustomLabelFiles(typeFiles[3]) {
  89. description, err := label.LoadTemplateDescription(file)
  90. if err != nil {
  91. return fmt.Errorf("failed to load labels: %w", err)
  92. }
  93. displayName := strings.TrimSuffix(file, filepath.Ext(file))
  94. labelTemplateFileMap[displayName] = file
  95. LabelTemplateFiles = append(LabelTemplateFiles, OptionFile{DisplayName: displayName, Description: description})
  96. }
  97. // Filter out invalid names and promote preferred licenses.
  98. sortedLicenses := make([]string, 0, len(Licenses))
  99. for _, name := range setting.Repository.PreferredLicenses {
  100. if util.SliceContainsString(Licenses, name, true) {
  101. sortedLicenses = append(sortedLicenses, name)
  102. }
  103. }
  104. for _, name := range Licenses {
  105. if !util.SliceContainsString(setting.Repository.PreferredLicenses, name, true) {
  106. sortedLicenses = append(sortedLicenses, name)
  107. }
  108. }
  109. Licenses = sortedLicenses
  110. return nil
  111. }
  112. func prepareRepoCommit(ctx context.Context, repo *repo_model.Repository, tmpDir, repoPath string, opts CreateRepoOptions) error {
  113. commitTimeStr := time.Now().Format(time.RFC3339)
  114. authorSig := repo.Owner.NewGitSig()
  115. // Because this may call hooks we should pass in the environment
  116. env := append(os.Environ(),
  117. "GIT_AUTHOR_NAME="+authorSig.Name,
  118. "GIT_AUTHOR_EMAIL="+authorSig.Email,
  119. "GIT_AUTHOR_DATE="+commitTimeStr,
  120. "GIT_COMMITTER_NAME="+authorSig.Name,
  121. "GIT_COMMITTER_EMAIL="+authorSig.Email,
  122. "GIT_COMMITTER_DATE="+commitTimeStr,
  123. )
  124. // Clone to temporary path and do the init commit.
  125. if stdout, _, err := git.NewCommand(ctx, "clone").AddDynamicArguments(repoPath, tmpDir).
  126. SetDescription(fmt.Sprintf("prepareRepoCommit (git clone): %s to %s", repoPath, tmpDir)).
  127. RunStdString(&git.RunOpts{Dir: "", Env: env}); err != nil {
  128. log.Error("Failed to clone from %v into %s: stdout: %s\nError: %v", repo, tmpDir, stdout, err)
  129. return fmt.Errorf("git clone: %w", err)
  130. }
  131. // README
  132. data, err := options.Readme(opts.Readme)
  133. if err != nil {
  134. return fmt.Errorf("GetRepoInitFile[%s]: %w", opts.Readme, err)
  135. }
  136. cloneLink := repo.CloneLink()
  137. match := map[string]string{
  138. "Name": repo.Name,
  139. "Description": repo.Description,
  140. "CloneURL.SSH": cloneLink.SSH,
  141. "CloneURL.HTTPS": cloneLink.HTTPS,
  142. "OwnerName": repo.OwnerName,
  143. }
  144. res, err := vars.Expand(string(data), match)
  145. if err != nil {
  146. // here we could just log the error and continue the rendering
  147. log.Error("unable to expand template vars for repo README: %s, err: %v", opts.Readme, err)
  148. }
  149. if err = os.WriteFile(filepath.Join(tmpDir, "README.md"),
  150. []byte(res), 0o644); err != nil {
  151. return fmt.Errorf("write README.md: %w", err)
  152. }
  153. // .gitignore
  154. if len(opts.Gitignores) > 0 {
  155. var buf bytes.Buffer
  156. names := strings.Split(opts.Gitignores, ",")
  157. for _, name := range names {
  158. data, err = options.Gitignore(name)
  159. if err != nil {
  160. return fmt.Errorf("GetRepoInitFile[%s]: %w", name, err)
  161. }
  162. buf.WriteString("# ---> " + name + "\n")
  163. buf.Write(data)
  164. buf.WriteString("\n")
  165. }
  166. if buf.Len() > 0 {
  167. if err = os.WriteFile(filepath.Join(tmpDir, ".gitignore"), buf.Bytes(), 0o644); err != nil {
  168. return fmt.Errorf("write .gitignore: %w", err)
  169. }
  170. }
  171. }
  172. // LICENSE
  173. if len(opts.License) > 0 {
  174. data, err = getLicense(opts.License, &licenseValues{
  175. Owner: repo.OwnerName,
  176. Email: authorSig.Email,
  177. Repo: repo.Name,
  178. Year: time.Now().Format("2006"),
  179. })
  180. if err != nil {
  181. return fmt.Errorf("getLicense[%s]: %w", opts.License, err)
  182. }
  183. if err = os.WriteFile(filepath.Join(tmpDir, "LICENSE"), data, 0o644); err != nil {
  184. return fmt.Errorf("write LICENSE: %w", err)
  185. }
  186. }
  187. return nil
  188. }
  189. // initRepoCommit temporarily changes with work directory.
  190. func initRepoCommit(ctx context.Context, tmpPath string, repo *repo_model.Repository, u *user_model.User, defaultBranch string) (err error) {
  191. commitTimeStr := time.Now().Format(time.RFC3339)
  192. sig := u.NewGitSig()
  193. // Because this may call hooks we should pass in the environment
  194. env := append(os.Environ(),
  195. "GIT_AUTHOR_NAME="+sig.Name,
  196. "GIT_AUTHOR_EMAIL="+sig.Email,
  197. "GIT_AUTHOR_DATE="+commitTimeStr,
  198. "GIT_COMMITTER_DATE="+commitTimeStr,
  199. )
  200. committerName := sig.Name
  201. committerEmail := sig.Email
  202. if stdout, _, err := git.NewCommand(ctx, "add", "--all").
  203. SetDescription(fmt.Sprintf("initRepoCommit (git add): %s", tmpPath)).
  204. RunStdString(&git.RunOpts{Dir: tmpPath}); err != nil {
  205. log.Error("git add --all failed: Stdout: %s\nError: %v", stdout, err)
  206. return fmt.Errorf("git add --all: %w", err)
  207. }
  208. cmd := git.NewCommand(ctx, "commit", "--message=Initial commit").
  209. AddOptionFormat("--author='%s <%s>'", sig.Name, sig.Email)
  210. sign, keyID, signer, _ := asymkey_service.SignInitialCommit(ctx, tmpPath, u)
  211. if sign {
  212. cmd.AddOptionFormat("-S%s", keyID)
  213. if repo.GetTrustModel() == repo_model.CommitterTrustModel || repo.GetTrustModel() == repo_model.CollaboratorCommitterTrustModel {
  214. // need to set the committer to the KeyID owner
  215. committerName = signer.Name
  216. committerEmail = signer.Email
  217. }
  218. } else {
  219. cmd.AddArguments("--no-gpg-sign")
  220. }
  221. env = append(env,
  222. "GIT_COMMITTER_NAME="+committerName,
  223. "GIT_COMMITTER_EMAIL="+committerEmail,
  224. )
  225. if stdout, _, err := cmd.
  226. SetDescription(fmt.Sprintf("initRepoCommit (git commit): %s", tmpPath)).
  227. RunStdString(&git.RunOpts{Dir: tmpPath, Env: env}); err != nil {
  228. log.Error("Failed to commit: %v: Stdout: %s\nError: %v", cmd.String(), stdout, err)
  229. return fmt.Errorf("git commit: %w", err)
  230. }
  231. if len(defaultBranch) == 0 {
  232. defaultBranch = setting.Repository.DefaultBranch
  233. }
  234. if stdout, _, err := git.NewCommand(ctx, "push", "origin").AddDynamicArguments("HEAD:" + defaultBranch).
  235. SetDescription(fmt.Sprintf("initRepoCommit (git push): %s", tmpPath)).
  236. RunStdString(&git.RunOpts{Dir: tmpPath, Env: InternalPushingEnvironment(u, repo)}); err != nil {
  237. log.Error("Failed to push back to HEAD: Stdout: %s\nError: %v", stdout, err)
  238. return fmt.Errorf("git push: %w", err)
  239. }
  240. return nil
  241. }
  242. func checkInitRepository(ctx context.Context, owner, name string) (err error) {
  243. // Somehow the directory could exist.
  244. repoPath := repo_model.RepoPath(owner, name)
  245. isExist, err := util.IsExist(repoPath)
  246. if err != nil {
  247. log.Error("Unable to check if %s exists. Error: %v", repoPath, err)
  248. return err
  249. }
  250. if isExist {
  251. return repo_model.ErrRepoFilesAlreadyExist{
  252. Uname: owner,
  253. Name: name,
  254. }
  255. }
  256. // Init git bare new repository.
  257. if err = git.InitRepository(ctx, repoPath, true); err != nil {
  258. return fmt.Errorf("git.InitRepository: %w", err)
  259. } else if err = createDelegateHooks(repoPath); err != nil {
  260. return fmt.Errorf("createDelegateHooks: %w", err)
  261. }
  262. return nil
  263. }
  264. // InitRepository initializes README and .gitignore if needed.
  265. func initRepository(ctx context.Context, repoPath string, u *user_model.User, repo *repo_model.Repository, opts CreateRepoOptions) (err error) {
  266. if err = checkInitRepository(ctx, repo.OwnerName, repo.Name); err != nil {
  267. return err
  268. }
  269. // Initialize repository according to user's choice.
  270. if opts.AutoInit {
  271. tmpDir, err := os.MkdirTemp(os.TempDir(), "gitea-"+repo.Name)
  272. if err != nil {
  273. return fmt.Errorf("Failed to create temp dir for repository %s: %w", repo.RepoPath(), err)
  274. }
  275. defer func() {
  276. if err := util.RemoveAll(tmpDir); err != nil {
  277. log.Warn("Unable to remove temporary directory: %s: Error: %v", tmpDir, err)
  278. }
  279. }()
  280. if err = prepareRepoCommit(ctx, repo, tmpDir, repoPath, opts); err != nil {
  281. return fmt.Errorf("prepareRepoCommit: %w", err)
  282. }
  283. // Apply changes and commit.
  284. if err = initRepoCommit(ctx, tmpDir, repo, u, opts.DefaultBranch); err != nil {
  285. return fmt.Errorf("initRepoCommit: %w", err)
  286. }
  287. }
  288. // Re-fetch the repository from database before updating it (else it would
  289. // override changes that were done earlier with sql)
  290. if repo, err = repo_model.GetRepositoryByID(ctx, repo.ID); err != nil {
  291. return fmt.Errorf("getRepositoryByID: %w", err)
  292. }
  293. if !opts.AutoInit {
  294. repo.IsEmpty = true
  295. }
  296. repo.DefaultBranch = setting.Repository.DefaultBranch
  297. if len(opts.DefaultBranch) > 0 {
  298. repo.DefaultBranch = opts.DefaultBranch
  299. gitRepo, err := git.OpenRepository(ctx, repo.RepoPath())
  300. if err != nil {
  301. return fmt.Errorf("openRepository: %w", err)
  302. }
  303. defer gitRepo.Close()
  304. if err = gitRepo.SetDefaultBranch(repo.DefaultBranch); err != nil {
  305. return fmt.Errorf("setDefaultBranch: %w", err)
  306. }
  307. if !repo.IsEmpty {
  308. if _, err := SyncRepoBranches(ctx, repo.ID, u.ID); err != nil {
  309. return fmt.Errorf("SyncRepoBranches: %w", err)
  310. }
  311. }
  312. }
  313. if err = UpdateRepository(ctx, repo, false); err != nil {
  314. return fmt.Errorf("updateRepository: %w", err)
  315. }
  316. return nil
  317. }
  318. // InitializeLabels adds a label set to a repository using a template
  319. func InitializeLabels(ctx context.Context, id int64, labelTemplate string, isOrg bool) error {
  320. list, err := LoadTemplateLabelsByDisplayName(labelTemplate)
  321. if err != nil {
  322. return err
  323. }
  324. labels := make([]*issues_model.Label, len(list))
  325. for i := 0; i < len(list); i++ {
  326. labels[i] = &issues_model.Label{
  327. Name: list[i].Name,
  328. Exclusive: list[i].Exclusive,
  329. Description: list[i].Description,
  330. Color: list[i].Color,
  331. }
  332. if isOrg {
  333. labels[i].OrgID = id
  334. } else {
  335. labels[i].RepoID = id
  336. }
  337. }
  338. for _, label := range labels {
  339. if err = issues_model.NewLabel(ctx, label); err != nil {
  340. return err
  341. }
  342. }
  343. return nil
  344. }
  345. // LoadTemplateLabelsByDisplayName loads a label template by its display name
  346. func LoadTemplateLabelsByDisplayName(displayName string) ([]*label.Label, error) {
  347. if fileName, ok := labelTemplateFileMap[displayName]; ok {
  348. return label.LoadTemplateFile(fileName)
  349. }
  350. return nil, label.ErrTemplateLoad{TemplateFile: displayName, OriginalError: fmt.Errorf("label template %q not found", displayName)}
  351. }