diff --git a/contrib/ide/vscode/launch.json b/contrib/ide/vscode/launch.json index 10df14ef3788a..b80b826fc0b5e 100644 --- a/contrib/ide/vscode/launch.json +++ b/contrib/ide/vscode/launch.json @@ -7,10 +7,10 @@ "request": "launch", "mode": "debug", "buildFlags": "", - "port": 2345, - "host": "127.0.0.1", "program": "${workspaceRoot}/main.go", - "env": {}, + "env": { + "GITEA_WORK_DIR": "${workspaceRoot}", + }, "args": ["web"], "showLog": true }, @@ -20,10 +20,10 @@ "request": "launch", "mode": "debug", "buildFlags": "-tags='sqlite sqlite_unlock_notify'", - "port": 2345, - "host": "127.0.0.1", "program": "${workspaceRoot}/main.go", - "env": {}, + "env": { + "GITEA_WORK_DIR": "${workspaceRoot}", + }, "args": ["web"], "showLog": true } diff --git a/models/repo/repo.go b/models/repo/repo.go index a78d287315413..353d707e60957 100644 --- a/models/repo/repo.go +++ b/models/repo/repo.go @@ -222,6 +222,30 @@ func (repo *Repository) MustOwner() *user_model.User { return repo.mustOwner(db.DefaultContext) } +// LoadAttributes loads attributes of the repository. +func (repo *Repository) LoadAttributes(ctx context.Context) error { + // Load owner + if err := repo.GetOwner(ctx); err != nil { + return fmt.Errorf("load owner: %w", err) + } + + // Load primary language + stats := make(LanguageStatList, 0, 1) + if err := db.GetEngine(ctx). + Where("`repo_id` = ? AND `is_primary` = ? AND `language` != ?", repo.ID, true, "other"). + Find(&stats); err != nil { + return fmt.Errorf("find primary languages: %w", err) + } + stats.LoadAttributes() + for _, st := range stats { + if st.RepoID == repo.ID { + repo.PrimaryLanguage = st + break + } + } + return nil +} + // FullName returns the repository full name func (repo *Repository) FullName() string { return repo.OwnerName + "/" + repo.Name diff --git a/models/repo_list.go b/models/repo_list.go index 9cb7a163fc861..290919bb6daa4 100644 --- a/models/repo_list.go +++ b/models/repo_list.go @@ -623,7 +623,7 @@ func FindUserAccessibleRepoIDs(user *user_model.User) ([]int64, error) { } // GetUserRepositories returns a list of repositories of given user. -func GetUserRepositories(opts *SearchRepoOptions) ([]*repo_model.Repository, int64, error) { +func GetUserRepositories(opts *SearchRepoOptions) (RepositoryList, int64, error) { if len(opts.OrderBy) == 0 { opts.OrderBy = "updated_unix DESC" } @@ -646,6 +646,6 @@ func GetUserRepositories(opts *SearchRepoOptions) ([]*repo_model.Repository, int } sess = sess.Where(cond).OrderBy(opts.OrderBy.String()) - repos := make([]*repo_model.Repository, 0, opts.PageSize) + repos := make(RepositoryList, 0, opts.PageSize) return repos, count, db.SetSessionPagination(sess, opts).Find(&repos) } diff --git a/modules/convert/repository.go b/modules/convert/repository.go index a356925539605..1f11fda7ac2cf 100644 --- a/modules/convert/repository.go +++ b/modules/convert/repository.go @@ -125,6 +125,13 @@ func innerToRepo(repo *repo_model.Repository, mode perm.AccessMode, isParent boo } } + var language string + if repo.PrimaryLanguage != nil { + language = repo.PrimaryLanguage.Language + } + + repoAPIURL := repo.APIURL() + return &api.Repository{ ID: repo.ID, Owner: ToUserWithAccessMode(repo.Owner, mode), @@ -144,6 +151,8 @@ func innerToRepo(repo *repo_model.Repository, mode perm.AccessMode, isParent boo CloneURL: cloneLink.HTTPS, OriginalURL: repo.SanitizedOriginalURL(), Website: repo.Website, + Language: language, + LanguagesURL: repoAPIURL + "/languages", Stars: repo.NumStars, Forks: repo.NumForks, Watchers: repo.NumWatches, diff --git a/modules/queue/queue.go b/modules/queue/queue.go index 3a519651430e1..a166a935a6777 100644 --- a/modules/queue/queue.go +++ b/modules/queue/queue.go @@ -196,7 +196,7 @@ func RegisteredTypesAsString() []string { func NewQueue(queueType Type, handlerFunc HandlerFunc, opts, exemplar interface{}) (Queue, error) { newFn, ok := queuesMap[queueType] if !ok { - return nil, fmt.Errorf("Unsupported queue type: %v", queueType) + return nil, fmt.Errorf("unsupported queue type: %v", queueType) } return newFn(handlerFunc, opts, exemplar) } diff --git a/modules/queue/queue_bytefifo.go b/modules/queue/queue_bytefifo.go index 0380497ea675f..7f2acf3debbd7 100644 --- a/modules/queue/queue_bytefifo.go +++ b/modules/queue/queue_bytefifo.go @@ -92,7 +92,7 @@ func (q *ByteFIFOQueue) Push(data Data) error { // PushBack pushes data to the fifo func (q *ByteFIFOQueue) PushBack(data Data) error { if !assignableTo(data, q.exemplar) { - return fmt.Errorf("Unable to assign data: %v to same type as exemplar: %v in %s", data, q.exemplar, q.name) + return fmt.Errorf("unable to assign data: %v to same type as exemplar: %v in %s", data, q.exemplar, q.name) } bs, err := json.Marshal(data) if err != nil { @@ -110,7 +110,7 @@ func (q *ByteFIFOQueue) PushBack(data Data) error { // PushFunc pushes data to the fifo func (q *ByteFIFOQueue) PushFunc(data Data, fn func() error) error { if !assignableTo(data, q.exemplar) { - return fmt.Errorf("Unable to assign data: %v to same type as exemplar: %v in %s", data, q.exemplar, q.name) + return fmt.Errorf("unable to assign data: %v to same type as exemplar: %v in %s", data, q.exemplar, q.name) } bs, err := json.Marshal(data) if err != nil { @@ -398,7 +398,7 @@ func NewByteFIFOUniqueQueue(typ Type, byteFIFO UniqueByteFIFO, handle HandlerFun // Has checks if the provided data is in the queue func (q *ByteFIFOUniqueQueue) Has(data Data) (bool, error) { if !assignableTo(data, q.exemplar) { - return false, fmt.Errorf("Unable to assign data: %v to same type as exemplar: %v in %s", data, q.exemplar, q.name) + return false, fmt.Errorf("unable to assign data: %v to same type as exemplar: %v in %s", data, q.exemplar, q.name) } bs, err := json.Marshal(data) if err != nil { diff --git a/modules/queue/queue_channel.go b/modules/queue/queue_channel.go index 7de9c17c86243..105388f4214e9 100644 --- a/modules/queue/queue_channel.go +++ b/modules/queue/queue_channel.go @@ -93,7 +93,7 @@ func (q *ChannelQueue) Run(atShutdown, atTerminate func(func())) { // Push will push data into the queue func (q *ChannelQueue) Push(data Data) error { if !assignableTo(data, q.exemplar) { - return fmt.Errorf("Unable to assign data: %v to same type as exemplar: %v in queue: %s", data, q.exemplar, q.name) + return fmt.Errorf("unable to assign data: %v to same type as exemplar: %v in queue: %s", data, q.exemplar, q.name) } q.WorkerPool.Push(data) return nil diff --git a/modules/queue/queue_wrapped.go b/modules/queue/queue_wrapped.go index edb589338ae45..02f7818aa8912 100644 --- a/modules/queue/queue_wrapped.go +++ b/modules/queue/queue_wrapped.go @@ -59,7 +59,7 @@ func (q *delayedStarter) setInternal(atShutdown func(func()), handle HandlerFunc if s, ok := cfg.([]byte); ok { cfg = string(s) } - return fmt.Errorf("Timedout creating queue %v with cfg %#v in %s", q.underlying, cfg, q.name) + return fmt.Errorf("timedout creating queue %v with cfg %#v in %s", q.underlying, cfg, q.name) default: queue, err := NewQueue(q.underlying, handle, q.cfg, exemplar) if err == nil { @@ -76,9 +76,9 @@ func (q *delayedStarter) setInternal(atShutdown func(func()), handle HandlerFunc i++ if q.maxAttempts > 0 && i > q.maxAttempts { if bs, ok := q.cfg.([]byte); ok { - return fmt.Errorf("Unable to create queue %v for %s with cfg %s by max attempts: error: %v", q.underlying, q.name, string(bs), err) + return fmt.Errorf("unable to create queue %v for %s with cfg %s by max attempts: error: %v", q.underlying, q.name, string(bs), err) } - return fmt.Errorf("Unable to create queue %v for %s with cfg %#v by max attempts: error: %v", q.underlying, q.name, q.cfg, err) + return fmt.Errorf("unable to create queue %v for %s with cfg %#v by max attempts: error: %v", q.underlying, q.name, q.cfg, err) } sleepTime := 100 * time.Millisecond if q.timeout > 0 && q.maxAttempts > 0 { @@ -271,6 +271,46 @@ func (q *WrappedQueue) Terminate() { log.Debug("WrappedQueue: %s Terminated", q.name) } +// IsPaused will return if the pool or queue is paused +func (q *WrappedQueue) IsPaused() bool { + q.lock.Lock() + defer q.lock.Unlock() + pausable, ok := q.internal.(Pausable) + return ok && pausable.IsPaused() +} + +// Pause will pause the pool or queue +func (q *WrappedQueue) Pause() { + q.lock.Lock() + defer q.lock.Unlock() + if pausable, ok := q.internal.(Pausable); ok { + pausable.Pause() + } +} + +// Resume will resume the pool or queue +func (q *WrappedQueue) Resume() { + q.lock.Lock() + defer q.lock.Unlock() + if pausable, ok := q.internal.(Pausable); ok { + pausable.Resume() + } +} + +// IsPausedIsResumed will return a bool indicating if the pool or queue is paused and a channel that will be closed when it is resumed +func (q *WrappedQueue) IsPausedIsResumed() (paused, resumed <-chan struct{}) { + q.lock.Lock() + defer q.lock.Unlock() + if pausable, ok := q.internal.(Pausable); ok { + return pausable.IsPausedIsResumed() + } + return context.Background().Done(), closedChan +} + +var closedChan chan struct{} + func init() { queuesMap[WrappedQueueType] = NewWrappedQueue + closedChan = make(chan struct{}) + close(closedChan) } diff --git a/modules/queue/setting.go b/modules/queue/setting.go index 61f156c377e26..880770f073957 100644 --- a/modules/queue/setting.go +++ b/modules/queue/setting.go @@ -22,7 +22,7 @@ func validType(t string) (Type, error) { return typ, nil } } - return PersistableChannelQueueType, fmt.Errorf("Unknown queue type: %s defaulting to %s", t, string(PersistableChannelQueueType)) + return PersistableChannelQueueType, fmt.Errorf("unknown queue type: %s defaulting to %s", t, string(PersistableChannelQueueType)) } func getQueueSettings(name string) (setting.QueueSettings, []byte) { diff --git a/modules/queue/unique_queue_channel.go b/modules/queue/unique_queue_channel.go index b6d2e770fce2d..59210855a1893 100644 --- a/modules/queue/unique_queue_channel.go +++ b/modules/queue/unique_queue_channel.go @@ -111,7 +111,7 @@ func (q *ChannelUniqueQueue) Push(data Data) error { // PushFunc will push data into the queue func (q *ChannelUniqueQueue) PushFunc(data Data, fn func() error) error { if !assignableTo(data, q.exemplar) { - return fmt.Errorf("Unable to assign data: %v to same type as exemplar: %v in queue: %s", data, q.exemplar, q.name) + return fmt.Errorf("unable to assign data: %v to same type as exemplar: %v in queue: %s", data, q.exemplar, q.name) } bs, err := json.Marshal(data) diff --git a/modules/queue/unique_queue_disk_channel.go b/modules/queue/unique_queue_disk_channel.go index 7fc304b17e9bd..ac7919926f22e 100644 --- a/modules/queue/unique_queue_disk_channel.go +++ b/modules/queue/unique_queue_disk_channel.go @@ -239,6 +239,26 @@ func (q *PersistableChannelUniqueQueue) IsEmpty() bool { return q.channelQueue.IsEmpty() } +// IsPaused will return if the pool or queue is paused +func (q *PersistableChannelUniqueQueue) IsPaused() bool { + return q.channelQueue.IsPaused() +} + +// Pause will pause the pool or queue +func (q *PersistableChannelUniqueQueue) Pause() { + q.channelQueue.Pause() +} + +// Resume will resume the pool or queue +func (q *PersistableChannelUniqueQueue) Resume() { + q.channelQueue.Resume() +} + +// IsPausedIsResumed will return a bool indicating if the pool or queue is paused and a channel that will be closed when it is resumed +func (q *PersistableChannelUniqueQueue) IsPausedIsResumed() (paused, resumed <-chan struct{}) { + return q.channelQueue.IsPausedIsResumed() +} + // Shutdown processing this queue func (q *PersistableChannelUniqueQueue) Shutdown() { log.Trace("PersistableChannelUniqueQueue: %s Shutting down", q.delayedStarter.name) diff --git a/modules/queue/unique_queue_wrapped.go b/modules/queue/unique_queue_wrapped.go index 32fa9ed970dbc..5245a35f77175 100644 --- a/modules/queue/unique_queue_wrapped.go +++ b/modules/queue/unique_queue_wrapped.go @@ -105,7 +105,7 @@ func (q *WrappedUniqueQueue) Push(data Data) error { // PushFunc will push the data to the internal channel checking it against the exemplar func (q *WrappedUniqueQueue) PushFunc(data Data, fn func() error) error { if !assignableTo(data, q.exemplar) { - return fmt.Errorf("Unable to assign data: %v to same type as exemplar: %v in %s", data, q.exemplar, q.name) + return fmt.Errorf("unable to assign data: %v to same type as exemplar: %v in %s", data, q.exemplar, q.name) } q.tlock.Lock() diff --git a/modules/queue/workerpool.go b/modules/queue/workerpool.go index da56216dcb124..30dc8073c90b7 100644 --- a/modules/queue/workerpool.go +++ b/modules/queue/workerpool.go @@ -57,14 +57,12 @@ func NewWorkerPool(handle HandlerFunc, config WorkerPoolConfiguration) *WorkerPo ctx, cancel := context.WithCancel(context.Background()) dataChan := make(chan Data, config.QueueLength) - resumed := make(chan struct{}) - close(resumed) pool := &WorkerPool{ baseCtx: ctx, baseCtxCancel: cancel, batchLength: config.BatchLength, dataChan: dataChan, - resumed: resumed, + resumed: closedChan, paused: make(chan struct{}), handle: handle, blockTimeout: config.BlockTimeout, diff --git a/modules/structs/repo.go b/modules/structs/repo.go index 671885f20a612..5a1e99e36bea8 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -59,6 +59,8 @@ type Repository struct { Parent *Repository `json:"parent"` Mirror bool `json:"mirror"` Size int `json:"size"` + Language string `json:"language"` + LanguagesURL string `json:"languages_url"` HTMLURL string `json:"html_url"` SSHURL string `json:"ssh_url"` CloneURL string `json:"clone_url"` diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini index 6688d99c773d3..9b07fb850d7b5 100644 --- a/options/locale/locale_pt-PT.ini +++ b/options/locale/locale_pt-PT.ini @@ -1931,6 +1931,10 @@ settings.add_matrix_hook_desc=Integrar Matrix no seu repositór settings.add_msteams_hook_desc=Integrar Microsoft Teams no seu repositório. settings.add_feishu_hook_desc=Integrar Feishu no seu repositório. settings.add_Wechat_hook_desc=Integrar Wechatwork no seu repositório. +settings.add_packagist_hook_desc=Integrar Packagist no seu repositório. +settings.packagist_username=Nome de utilizador no Packagist +settings.packagist_api_token=Código da API +settings.packagist_package_url=URL do pacote Packagist settings.deploy_keys=Chaves de instalação settings.add_deploy_key=Adicionar chave de instalação settings.deploy_key_desc=Chaves de instalação têm acesso para puxar do repositório apenas em modo de leitura. diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index 9fc12f2293b6a..d3259a03b3807 100644 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -1947,6 +1947,10 @@ settings.add_matrix_hook_desc=将 Matrix 集成到您的仓库 settings.add_msteams_hook_desc=将 Microsoft Teams 集成到您的仓库中。 settings.add_feishu_hook_desc=将 Feishu 集成到您的仓库中。 settings.add_Wechat_hook_desc=将 企业微信 集成到您的仓库中。 +settings.add_packagist_hook_desc=将 Packagist 集成到您的仓库中。 +settings.packagist_username=Packagist 用户名 +settings.packagist_api_token=API 令牌 +settings.packagist_package_url=Packagist 软件包 URL settings.deploy_keys=部署密钥 settings.add_deploy_key=添加部署密钥 settings.deploy_key_desc=部署密钥具有对仓库的只读拉取权限。 diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index c2dfc4f193e81..7a7fe218e820f 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -533,6 +533,11 @@ func Get(ctx *context.APIContext) { // "200": // "$ref": "#/responses/Repository" + if err := ctx.Repo.Repository.LoadAttributes(ctx); err != nil { + ctx.Error(http.StatusInternalServerError, "Repository.LoadAttributes", err) + return + } + ctx.JSON(http.StatusOK, convert.ToRepo(ctx.Repo.Repository, ctx.Repo.AccessMode)) } diff --git a/routers/api/v1/user/repo.go b/routers/api/v1/user/repo.go index 3d5c841856604..109548ec768cb 100644 --- a/routers/api/v1/user/repo.go +++ b/routers/api/v1/user/repo.go @@ -32,6 +32,11 @@ func listUserRepos(ctx *context.APIContext, u *user_model.User, private bool) { return } + if err := repos.LoadAttributes(); err != nil { + ctx.Error(http.StatusInternalServerError, "RepositoryList.LoadAttributes", err) + return + } + apiRepos := make([]*api.Repository, 0, len(repos)) for i := range repos { access, err := models.AccessLevel(ctx.User, repos[i]) diff --git a/services/migrations/restore.go b/services/migrations/restore.go index 4499f78701a92..c927de0b2bfba 100644 --- a/services/migrations/restore.go +++ b/services/migrations/restore.go @@ -97,6 +97,9 @@ func (r *RepositoryRestorer) GetTopics() ([]string, error) { bs, err := os.ReadFile(p) if err != nil { + if os.IsNotExist(err) { + return nil, nil + } return nil, err } diff --git a/templates/repo/commits_list.tmpl b/templates/repo/commits_list.tmpl index 7bc37175c2b88..e8ac4020f44e0 100644 --- a/templates/repo/commits_list.tmpl +++ b/templates/repo/commits_list.tmpl @@ -75,7 +75,11 @@
{{end}} -