code.gitea.io/gitea@v1.22.3/modules/repository/init.go (about) 1 // Copyright 2019 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package repository 5 6 import ( 7 "context" 8 "fmt" 9 "path/filepath" 10 "sort" 11 "strings" 12 13 issues_model "code.gitea.io/gitea/models/issues" 14 repo_model "code.gitea.io/gitea/models/repo" 15 "code.gitea.io/gitea/modules/git" 16 "code.gitea.io/gitea/modules/label" 17 "code.gitea.io/gitea/modules/log" 18 "code.gitea.io/gitea/modules/options" 19 "code.gitea.io/gitea/modules/setting" 20 "code.gitea.io/gitea/modules/util" 21 ) 22 23 type OptionFile struct { 24 DisplayName string 25 Description string 26 } 27 28 var ( 29 // Gitignores contains the gitiginore files 30 Gitignores []string 31 32 // Licenses contains the license files 33 Licenses []string 34 35 // Readmes contains the readme files 36 Readmes []string 37 38 // LabelTemplateFiles contains the label template files, each item has its DisplayName and Description 39 LabelTemplateFiles []OptionFile 40 labelTemplateFileMap = map[string]string{} // DisplayName => FileName mapping 41 ) 42 43 type optionFileList struct { 44 all []string // all files provided by bindata & custom-path. Sorted. 45 custom []string // custom files provided by custom-path. Non-sorted, internal use only. 46 } 47 48 // mergeCustomLabelFiles merges the custom label files. Always use the file's main name (DisplayName) as the key to de-duplicate. 49 func mergeCustomLabelFiles(fl optionFileList) []string { 50 exts := map[string]int{"": 0, ".yml": 1, ".yaml": 2} // "yaml" file has the highest priority to be used. 51 52 m := map[string]string{} 53 merge := func(list []string) { 54 sort.Slice(list, func(i, j int) bool { return exts[filepath.Ext(list[i])] < exts[filepath.Ext(list[j])] }) 55 for _, f := range list { 56 m[strings.TrimSuffix(f, filepath.Ext(f))] = f 57 } 58 } 59 merge(fl.all) 60 merge(fl.custom) 61 62 files := make([]string, 0, len(m)) 63 for _, f := range m { 64 files = append(files, f) 65 } 66 sort.Strings(files) 67 return files 68 } 69 70 // LoadRepoConfig loads the repository config 71 func LoadRepoConfig() error { 72 types := []string{"gitignore", "license", "readme", "label"} // option file directories 73 typeFiles := make([]optionFileList, len(types)) 74 for i, t := range types { 75 var err error 76 if typeFiles[i].all, err = options.AssetFS().ListFiles(t, true); err != nil { 77 return fmt.Errorf("failed to list %s files: %w", t, err) 78 } 79 sort.Strings(typeFiles[i].all) 80 customPath := filepath.Join(setting.CustomPath, "options", t) 81 if isDir, err := util.IsDir(customPath); err != nil { 82 return fmt.Errorf("failed to check custom %s dir: %w", t, err) 83 } else if isDir { 84 if typeFiles[i].custom, err = util.StatDir(customPath); err != nil { 85 return fmt.Errorf("failed to list custom %s files: %w", t, err) 86 } 87 } 88 } 89 90 Gitignores = typeFiles[0].all 91 Licenses = typeFiles[1].all 92 Readmes = typeFiles[2].all 93 94 // Load label templates 95 LabelTemplateFiles = nil 96 labelTemplateFileMap = map[string]string{} 97 for _, file := range mergeCustomLabelFiles(typeFiles[3]) { 98 description, err := label.LoadTemplateDescription(file) 99 if err != nil { 100 return fmt.Errorf("failed to load labels: %w", err) 101 } 102 displayName := strings.TrimSuffix(file, filepath.Ext(file)) 103 labelTemplateFileMap[displayName] = file 104 LabelTemplateFiles = append(LabelTemplateFiles, OptionFile{DisplayName: displayName, Description: description}) 105 } 106 107 // Filter out invalid names and promote preferred licenses. 108 sortedLicenses := make([]string, 0, len(Licenses)) 109 for _, name := range setting.Repository.PreferredLicenses { 110 if util.SliceContainsString(Licenses, name, true) { 111 sortedLicenses = append(sortedLicenses, name) 112 } 113 } 114 for _, name := range Licenses { 115 if !util.SliceContainsString(setting.Repository.PreferredLicenses, name, true) { 116 sortedLicenses = append(sortedLicenses, name) 117 } 118 } 119 Licenses = sortedLicenses 120 return nil 121 } 122 123 func CheckInitRepository(ctx context.Context, owner, name, objectFormatName string) (err error) { 124 // Somehow the directory could exist. 125 repoPath := repo_model.RepoPath(owner, name) 126 isExist, err := util.IsExist(repoPath) 127 if err != nil { 128 log.Error("Unable to check if %s exists. Error: %v", repoPath, err) 129 return err 130 } 131 if isExist { 132 return repo_model.ErrRepoFilesAlreadyExist{ 133 Uname: owner, 134 Name: name, 135 } 136 } 137 138 // Init git bare new repository. 139 if err = git.InitRepository(ctx, repoPath, true, objectFormatName); err != nil { 140 return fmt.Errorf("git.InitRepository: %w", err) 141 } else if err = CreateDelegateHooks(repoPath); err != nil { 142 return fmt.Errorf("createDelegateHooks: %w", err) 143 } 144 return nil 145 } 146 147 // InitializeLabels adds a label set to a repository using a template 148 func InitializeLabels(ctx context.Context, id int64, labelTemplate string, isOrg bool) error { 149 list, err := LoadTemplateLabelsByDisplayName(labelTemplate) 150 if err != nil { 151 return err 152 } 153 154 labels := make([]*issues_model.Label, len(list)) 155 for i := 0; i < len(list); i++ { 156 labels[i] = &issues_model.Label{ 157 Name: list[i].Name, 158 Exclusive: list[i].Exclusive, 159 Description: list[i].Description, 160 Color: list[i].Color, 161 } 162 if isOrg { 163 labels[i].OrgID = id 164 } else { 165 labels[i].RepoID = id 166 } 167 } 168 for _, label := range labels { 169 if err = issues_model.NewLabel(ctx, label); err != nil { 170 return err 171 } 172 } 173 return nil 174 } 175 176 // LoadTemplateLabelsByDisplayName loads a label template by its display name 177 func LoadTemplateLabelsByDisplayName(displayName string) ([]*label.Label, error) { 178 if fileName, ok := labelTemplateFileMap[displayName]; ok { 179 return label.LoadTemplateFile(fileName) 180 } 181 return nil, label.ErrTemplateLoad{TemplateFile: displayName, OriginalError: fmt.Errorf("label template %q not found", displayName)} 182 }