2020-01-12 15:11:17 +03:00
// Copyright 2019 The Gitea Authors. All rights reserved.
2022-11-27 21:20:29 +03:00
// SPDX-License-Identifier: MIT
2020-01-12 15:11:17 +03:00
package repository
import (
2021-09-23 18:45:36 +03:00
"context"
2020-01-12 15:11:17 +03:00
"fmt"
"path/filepath"
2022-03-29 10:23:45 +03:00
"sort"
2020-01-12 15:11:17 +03:00
"strings"
2022-06-13 12:37:59 +03:00
issues_model "code.gitea.io/gitea/models/issues"
2021-12-10 04:27:50 +03:00
repo_model "code.gitea.io/gitea/models/repo"
2020-01-12 15:11:17 +03:00
"code.gitea.io/gitea/modules/git"
2023-03-02 02:44:23 +03:00
"code.gitea.io/gitea/modules/label"
2020-01-12 15:11:17 +03:00
"code.gitea.io/gitea/modules/log"
2022-03-29 10:23:45 +03:00
"code.gitea.io/gitea/modules/options"
2020-06-17 23:53:55 +03:00
"code.gitea.io/gitea/modules/setting"
2020-08-11 23:05:34 +03:00
"code.gitea.io/gitea/modules/util"
2020-01-12 15:11:17 +03:00
)
2023-04-10 11:44:02 +03:00
type OptionFile struct {
DisplayName string
Description string
}
2022-03-29 10:23:45 +03:00
var (
// Gitignores contains the gitiginore files
Gitignores [ ] string
// Licenses contains the license files
Licenses [ ] string
// Readmes contains the readme files
Readmes [ ] string
2023-04-10 11:44:02 +03:00
// LabelTemplateFiles contains the label template files, each item has its DisplayName and Description
LabelTemplateFiles [ ] OptionFile
labelTemplateFileMap = map [ string ] string { } // DisplayName => FileName mapping
2022-03-29 10:23:45 +03:00
)
2023-04-10 11:44:02 +03:00
type optionFileList struct {
all [ ] string // all files provided by bindata & custom-path. Sorted.
custom [ ] string // custom files provided by custom-path. Non-sorted, internal use only.
}
// mergeCustomLabelFiles merges the custom label files. Always use the file's main name (DisplayName) as the key to de-duplicate.
func mergeCustomLabelFiles ( fl optionFileList ) [ ] string {
exts := map [ string ] int { "" : 0 , ".yml" : 1 , ".yaml" : 2 } // "yaml" file has the highest priority to be used.
m := map [ string ] string { }
merge := func ( list [ ] string ) {
sort . Slice ( list , func ( i , j int ) bool { return exts [ filepath . Ext ( list [ i ] ) ] < exts [ filepath . Ext ( list [ j ] ) ] } )
for _ , f := range list {
m [ strings . TrimSuffix ( f , filepath . Ext ( f ) ) ] = f
}
}
merge ( fl . all )
merge ( fl . custom )
files := make ( [ ] string , 0 , len ( m ) )
for _ , f := range m {
files = append ( files , f )
}
sort . Strings ( files )
return files
}
2022-03-29 10:23:45 +03:00
// LoadRepoConfig loads the repository config
2023-04-10 11:44:02 +03:00
func LoadRepoConfig ( ) error {
types := [ ] string { "gitignore" , "license" , "readme" , "label" } // option file directories
typeFiles := make ( [ ] optionFileList , len ( types ) )
2022-03-29 10:23:45 +03:00
for i , t := range types {
2023-04-10 11:44:02 +03:00
var err error
2023-04-12 13:16:45 +03:00
if typeFiles [ i ] . all , err = options . AssetFS ( ) . ListFiles ( t , true ) ; err != nil {
2023-04-10 11:44:02 +03:00
return fmt . Errorf ( "failed to list %s files: %w" , t , err )
2022-03-29 10:23:45 +03:00
}
2023-04-10 11:44:02 +03:00
sort . Strings ( typeFiles [ i ] . all )
customPath := filepath . Join ( setting . CustomPath , "options" , t )
if isDir , err := util . IsDir ( customPath ) ; err != nil {
return fmt . Errorf ( "failed to check custom %s dir: %w" , t , err )
} else if isDir {
if typeFiles [ i ] . custom , err = util . StatDir ( customPath ) ; err != nil {
return fmt . Errorf ( "failed to list custom %s files: %w" , t , err )
2023-03-02 02:44:23 +03:00
}
}
2022-03-29 10:23:45 +03:00
}
2023-04-10 11:44:02 +03:00
Gitignores = typeFiles [ 0 ] . all
Licenses = typeFiles [ 1 ] . all
Readmes = typeFiles [ 2 ] . all
2022-03-29 10:23:45 +03:00
// Load label templates
2023-04-10 11:44:02 +03:00
LabelTemplateFiles = nil
labelTemplateFileMap = map [ string ] string { }
for _ , file := range mergeCustomLabelFiles ( typeFiles [ 3 ] ) {
description , err := label . LoadTemplateDescription ( file )
2022-03-29 10:23:45 +03:00
if err != nil {
2023-04-10 11:44:02 +03:00
return fmt . Errorf ( "failed to load labels: %w" , err )
2022-03-29 10:23:45 +03:00
}
2023-04-10 11:44:02 +03:00
displayName := strings . TrimSuffix ( file , filepath . Ext ( file ) )
labelTemplateFileMap [ displayName ] = file
LabelTemplateFiles = append ( LabelTemplateFiles , OptionFile { DisplayName : displayName , Description : description } )
2022-03-29 10:23:45 +03:00
}
// Filter out invalid names and promote preferred licenses.
sortedLicenses := make ( [ ] string , 0 , len ( Licenses ) )
for _ , name := range setting . Repository . PreferredLicenses {
Improve utils of slices (#22379)
- Move the file `compare.go` and `slice.go` to `slice.go`.
- Fix `ExistsInSlice`, it's buggy
- It uses `sort.Search`, so it assumes that the input slice is sorted.
- It passes `func(i int) bool { return slice[i] == target })` to
`sort.Search`, that's incorrect, check the doc of `sort.Search`.
- Conbine `IsInt64InSlice(int64, []int64)` and `ExistsInSlice(string,
[]string)` to `SliceContains[T]([]T, T)`.
- Conbine `IsSliceInt64Eq([]int64, []int64)` and `IsEqualSlice([]string,
[]string)` to `SliceSortedEqual[T]([]T, T)`.
- Add `SliceEqual[T]([]T, T)` as a distinction from
`SliceSortedEqual[T]([]T, T)`.
- Redesign `RemoveIDFromList([]int64, int64) ([]int64, bool)` to
`SliceRemoveAll[T]([]T, T) []T`.
- Add `SliceContainsFunc[T]([]T, func(T) bool)` and
`SliceRemoveAllFunc[T]([]T, func(T) bool)` for general use.
- Add comments to explain why not `golang.org/x/exp/slices`.
- Add unit tests.
2023-01-11 08:31:16 +03:00
if util . SliceContainsString ( Licenses , name , true ) {
2022-03-29 10:23:45 +03:00
sortedLicenses = append ( sortedLicenses , name )
}
}
for _ , name := range Licenses {
Improve utils of slices (#22379)
- Move the file `compare.go` and `slice.go` to `slice.go`.
- Fix `ExistsInSlice`, it's buggy
- It uses `sort.Search`, so it assumes that the input slice is sorted.
- It passes `func(i int) bool { return slice[i] == target })` to
`sort.Search`, that's incorrect, check the doc of `sort.Search`.
- Conbine `IsInt64InSlice(int64, []int64)` and `ExistsInSlice(string,
[]string)` to `SliceContains[T]([]T, T)`.
- Conbine `IsSliceInt64Eq([]int64, []int64)` and `IsEqualSlice([]string,
[]string)` to `SliceSortedEqual[T]([]T, T)`.
- Add `SliceEqual[T]([]T, T)` as a distinction from
`SliceSortedEqual[T]([]T, T)`.
- Redesign `RemoveIDFromList([]int64, int64) ([]int64, bool)` to
`SliceRemoveAll[T]([]T, T) []T`.
- Add `SliceContainsFunc[T]([]T, func(T) bool)` and
`SliceRemoveAllFunc[T]([]T, func(T) bool)` for general use.
- Add comments to explain why not `golang.org/x/exp/slices`.
- Add unit tests.
2023-01-11 08:31:16 +03:00
if ! util . SliceContainsString ( setting . Repository . PreferredLicenses , name , true ) {
2022-03-29 10:23:45 +03:00
sortedLicenses = append ( sortedLicenses , name )
}
}
Licenses = sortedLicenses
2023-04-10 11:44:02 +03:00
return nil
2022-03-29 10:23:45 +03:00
}
2023-12-17 14:56:08 +03:00
func CheckInitRepository ( ctx context . Context , owner , name , objectFormatName string ) ( err error ) {
2020-01-12 15:11:17 +03:00
// Somehow the directory could exist.
2021-12-10 04:27:50 +03:00
repoPath := repo_model . RepoPath ( owner , name )
2020-11-28 05:42:08 +03:00
isExist , err := util . IsExist ( repoPath )
if err != nil {
log . Error ( "Unable to check if %s exists. Error: %v" , repoPath , err )
return err
}
if isExist {
2021-12-12 18:48:20 +03:00
return repo_model . ErrRepoFilesAlreadyExist {
2020-09-25 07:09:23 +03:00
Uname : owner ,
Name : name ,
}
2020-01-12 15:11:17 +03:00
}
// Init git bare new repository.
2023-12-17 14:56:08 +03:00
if err = git . InitRepository ( ctx , repoPath , true , objectFormatName ) ; err != nil {
2022-10-24 22:29:17 +03:00
return fmt . Errorf ( "git.InitRepository: %w" , err )
2023-09-06 15:08:51 +03:00
} else if err = CreateDelegateHooks ( repoPath ) ; err != nil {
2022-10-24 22:29:17 +03:00
return fmt . Errorf ( "createDelegateHooks: %w" , err )
2020-01-12 15:11:17 +03:00
}
return nil
}
2022-03-29 10:23:45 +03:00
// InitializeLabels adds a label set to a repository using a template
func InitializeLabels ( ctx context . Context , id int64 , labelTemplate string , isOrg bool ) error {
2023-04-10 11:44:02 +03:00
list , err := LoadTemplateLabelsByDisplayName ( labelTemplate )
2022-03-29 10:23:45 +03:00
if err != nil {
return err
}
2022-06-13 12:37:59 +03:00
labels := make ( [ ] * issues_model . Label , len ( list ) )
2022-03-29 10:23:45 +03:00
for i := 0 ; i < len ( list ) ; i ++ {
2022-06-13 12:37:59 +03:00
labels [ i ] = & issues_model . Label {
2023-03-02 02:44:23 +03:00
Name : list [ i ] . Name ,
Exclusive : list [ i ] . Exclusive ,
Description : list [ i ] . Description ,
Color : list [ i ] . Color ,
2022-03-29 10:23:45 +03:00
}
if isOrg {
labels [ i ] . OrgID = id
} else {
labels [ i ] . RepoID = id
}
}
for _ , label := range labels {
2022-06-13 12:37:59 +03:00
if err = issues_model . NewLabel ( ctx , label ) ; err != nil {
2022-03-29 10:23:45 +03:00
return err
}
}
return nil
}
2023-04-10 11:44:02 +03:00
// LoadTemplateLabelsByDisplayName loads a label template by its display name
func LoadTemplateLabelsByDisplayName ( displayName string ) ( [ ] * label . Label , error ) {
if fileName , ok := labelTemplateFileMap [ displayName ] ; ok {
return label . LoadTemplateFile ( fileName )
}
return nil , label . ErrTemplateLoad { TemplateFile : displayName , OriginalError : fmt . Errorf ( "label template %q not found" , displayName ) }
}