2019-10-13 21:23:14 +08:00
// Copyright 2019 Gitea. All rights reserved.
2022-11-27 13:20:29 -05:00
// SPDX-License-Identifier: MIT
2019-10-13 21:23:14 +08:00
package task
import (
2023-09-16 16:39:12 +02:00
"context"
2019-10-13 21:23:14 +08:00
"errors"
"fmt"
"strings"
2023-05-11 16:25:46 +08:00
"time"
2019-10-13 21:23:14 +08:00
2025-03-27 19:40:14 +00:00
admin_model "forgejo.org/models/admin"
"forgejo.org/models/db"
repo_model "forgejo.org/models/repo"
user_model "forgejo.org/models/user"
"forgejo.org/modules/graceful"
"forgejo.org/modules/json"
"forgejo.org/modules/log"
"forgejo.org/modules/migration"
"forgejo.org/modules/process"
"forgejo.org/modules/structs"
"forgejo.org/modules/timeutil"
"forgejo.org/modules/util"
"forgejo.org/services/migrations"
notify_service "forgejo.org/services/notify"
2019-10-13 21:23:14 +08:00
)
2021-11-24 17:49:20 +08:00
func handleCreateError ( owner * user_model . User , err error ) error {
2019-10-13 21:23:14 +08:00
switch {
2021-12-12 23:48:20 +08:00
case repo_model . IsErrReachLimitOfRepo ( err ) :
2023-05-11 16:25:46 +08:00
return fmt . Errorf ( "you have already reached your limit of %d repositories" , owner . MaxCreationLimit ( ) )
2021-12-12 23:48:20 +08:00
case repo_model . IsErrRepoAlreadyExist ( err ) :
2023-05-11 16:25:46 +08:00
return errors . New ( "the repository name is already used" )
2021-11-24 17:49:20 +08:00
case db . IsErrNameReserved ( err ) :
2023-05-11 16:25:46 +08:00
return fmt . Errorf ( "the repository name '%s' is reserved" , err . ( db . ErrNameReserved ) . Name )
2021-11-24 17:49:20 +08:00
case db . IsErrNamePatternNotAllowed ( err ) :
2023-05-11 16:25:46 +08:00
return fmt . Errorf ( "the pattern '%s' is not allowed in a repository name" , err . ( db . ErrNamePatternNotAllowed ) . Pattern )
2019-10-13 21:23:14 +08:00
default :
return err
}
}
2023-09-16 16:39:12 +02:00
func runMigrateTask ( ctx context . Context , t * admin_model . Task ) ( err error ) {
2023-10-22 22:12:27 +08:00
defer func ( ctx context . Context ) {
2019-10-13 21:23:14 +08:00
if e := recover ( ) ; e != nil {
2020-10-24 00:46:35 +01:00
err = fmt . Errorf ( "PANIC whilst trying to do migrate task: %v" , e )
log . Critical ( "PANIC during runMigrateTask[%d] by DoerID[%d] to RepoID[%d] for OwnerID[%d]: %v\nStacktrace: %v" , t . ID , t . DoerID , t . RepoID , t . OwnerID , e , log . Stack ( 2 ) )
2019-10-13 21:23:14 +08:00
}
if err == nil {
2023-10-15 23:46:06 +08:00
err = admin_model . FinishMigrateTask ( ctx , t )
2019-10-13 21:23:14 +08:00
if err == nil {
2023-10-15 23:46:06 +08:00
notify_service . MigrateRepository ( ctx , t . Doer , t . Owner , t . Repo )
2019-10-13 21:23:14 +08:00
return
}
2020-10-11 19:51:13 +01:00
log . Error ( "FinishMigrateTask[%d] by DoerID[%d] to RepoID[%d] for OwnerID[%d] failed: %v" , t . ID , t . DoerID , t . RepoID , t . OwnerID , err )
2019-10-13 21:23:14 +08:00
}
2023-05-11 16:25:46 +08:00
log . Error ( "runMigrateTask[%d] by DoerID[%d] to RepoID[%d] for OwnerID[%d] failed: %v" , t . ID , t . DoerID , t . RepoID , t . OwnerID , err )
2019-10-13 21:23:14 +08:00
t . EndTime = timeutil . TimeStampNow ( )
t . Status = structs . TaskStatusFailed
2021-06-16 23:02:24 +01:00
t . Message = err . Error ( )
2023-10-15 23:46:06 +08:00
if err := t . UpdateCols ( ctx , "status" , "message" , "end_time" ) ; err != nil {
2020-10-11 19:51:13 +01:00
log . Error ( "Task UpdateCols failed: %v" , err )
2019-10-13 21:23:14 +08:00
}
2023-05-11 16:25:46 +08:00
// then, do not delete the repository, otherwise the users won't be able to see the last error
2023-10-22 22:12:27 +08:00
} ( graceful . GetManager ( ) . ShutdownContext ( ) ) // even if the parent ctx is canceled, this defer-function still needs to update the task record in database
2019-10-13 21:23:14 +08:00
2023-09-16 16:39:12 +02:00
if err = t . LoadRepo ( ctx ) ; err != nil {
2023-07-09 13:58:06 +02:00
return err
2019-10-13 21:23:14 +08:00
}
2021-07-08 07:38:13 -04:00
// if repository is ready, then just finish the task
2021-12-10 09:27:50 +08:00
if t . Repo . Status == repo_model . RepositoryReady {
2019-10-13 21:23:14 +08:00
return nil
}
2023-09-16 16:39:12 +02:00
if err = t . LoadDoer ( ctx ) ; err != nil {
2023-07-09 13:58:06 +02:00
return err
2019-10-13 21:23:14 +08:00
}
2023-09-16 16:39:12 +02:00
if err = t . LoadOwner ( ctx ) ; err != nil {
2023-07-09 13:58:06 +02:00
return err
2019-10-13 21:23:14 +08:00
}
2020-09-11 00:29:19 +02:00
var opts * migration . MigrateOptions
2019-10-13 21:23:14 +08:00
opts , err = t . MigrateConfig ( )
if err != nil {
2023-07-09 13:58:06 +02:00
return err
2019-10-13 21:23:14 +08:00
}
opts . MigrateToRepoID = t . RepoID
2020-12-02 19:36:06 +01:00
pm := process . GetManager ( )
2023-05-11 16:25:46 +08:00
ctx , cancel , finished := pm . AddContext ( graceful . GetManager ( ) . ShutdownContext ( ) , fmt . Sprintf ( "MigrateTask: %s/%s" , t . Owner . Name , opts . RepoName ) )
2021-11-30 20:06:32 +00:00
defer finished ( )
2020-12-02 19:36:06 +01:00
t . StartTime = timeutil . TimeStampNow ( )
t . Status = structs . TaskStatusRunning
2023-09-16 16:39:12 +02:00
if err = t . UpdateCols ( ctx , "start_time" , "status" ) ; err != nil {
2023-07-09 13:58:06 +02:00
return err
2020-12-02 19:36:06 +01:00
}
2023-05-11 16:25:46 +08:00
// check whether the task should be canceled, this goroutine is also managed by process manager
go func ( ) {
for {
select {
case <- time . After ( 2 * time . Second ) :
ci: detect and prevent empty `case` statements in Go code (#11593)
One of the security patches released 2026-03-09 [fixed a vulnerability](https://codeberg.org/forgejo/forgejo/pulls/11513/commits/d1c7b04d09f6a13896eaa1322ac690b2021539da) caused by a misapplication of Go `case` statements, where the implementation would have been correct if Go `case` statements automatically fall through to the next case block, but they do not. This PR adds a semgrep rule which detects any empty `case` statement and raises an error, in order to prevent this coding mistake in the future.
For example, code like this will now trigger a build error:
```go
switch setting.Protocol {
case setting.HTTPUnix:
case setting.FCGI:
case setting.FCGIUnix:
default:
defaultLocalURL := string(setting.Protocol) + "://"
}
```
Example error:
```
cmd/web.go
❯❯❱ semgrep.config.forgejo-switch-empty-case
switch has a case block with no content. This is treated as "break" by Go, but developers may
confuse it for "fallthrough". To fix this error, disambiguate by using "break" or
"fallthrough".
279┆ switch setting.Protocol {
280┆ case setting.HTTPUnix:
281┆ case setting.FCGI:
282┆ case setting.FCGIUnix:
283┆ default:
284┆ defaultLocalURL := string(setting.Protocol) + "://"
285┆ if setting.HTTPAddr == "0.0.0.0" {
286┆ defaultLocalURL += "localhost"
287┆ } else {
288┆ defaultLocalURL += setting.HTTPAddr
```
As described in the error output, this error can be fixed by explicitly listing `break` (the real Go behaviour, to do nothing in the block), or by listing `fallthrough` (if the intent was to fall through).
All existing code triggering this detection has been changed to `break` (or, rarely, irrelevant cases have been removed), which should maintain the same code functionality. While performing this fixup, a light analysis was performed on each case and they *appeared* correct, but with ~65 cases I haven't gone into extreme depth.
Tests are present for the semgrep rule in `.semgrep/tests/go.go`.
## Checklist
The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org).
### Documentation
- [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change.
- [x] I did not document these changes and I do not expect someone else to do it.
### Release notes
- [ ] This change will be noticed by a Forgejo user or admin (feature, bug fix, performance, etc.). I suggest to include a release note for this change.
- [x] This change is not visible to a Forgejo user or admin (refactor, dependency upgrade, etc.). I think there is no need to add a release note for this change.
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/11593
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: Mathieu Fenniak <mathieu@fenniak.net>
Co-committed-by: Mathieu Fenniak <mathieu@fenniak.net>
2026-03-10 02:50:28 +01:00
break
2023-05-11 16:25:46 +08:00
case <- ctx . Done ( ) :
return
}
2023-09-16 16:39:12 +02:00
task , _ := admin_model . GetMigratingTask ( ctx , t . RepoID )
2023-05-11 16:25:46 +08:00
if task != nil && task . Status != structs . TaskStatusRunning {
log . Debug ( "MigrateTask[%d] by DoerID[%d] to RepoID[%d] for OwnerID[%d] is canceled due to status is not 'running'" , t . ID , t . DoerID , t . RepoID , t . OwnerID )
cancel ( )
return
}
}
} ( )
2023-07-04 20:36:08 +02:00
t . Repo , err = migrations . MigrateRepository ( ctx , t . Doer , t . Owner . Name , * opts , func ( format string , args ... any ) {
2022-08-25 10:31:57 +08:00
message := admin_model . TranslatableMessage {
2021-06-16 23:02:24 +01:00
Format : format ,
Args : args ,
}
bs , _ := json . Marshal ( message )
t . Message = string ( bs )
2023-09-16 16:39:12 +02:00
_ = t . UpdateCols ( ctx , "message" )
2021-06-16 23:02:24 +01:00
} )
2023-05-11 16:25:46 +08:00
2019-10-13 21:23:14 +08:00
if err == nil {
2021-09-09 01:43:19 +08:00
log . Trace ( "Repository migrated [%d]: %s/%s" , t . Repo . ID , t . Owner . Name , t . Repo . Name )
2023-07-09 13:58:06 +02:00
return nil
2019-10-13 21:23:14 +08:00
}
2021-12-12 23:48:20 +08:00
if repo_model . IsErrRepoAlreadyExist ( err ) {
2023-07-09 13:58:06 +02:00
return errors . New ( "the repository name is already used" )
2019-10-13 21:23:14 +08:00
}
// remoteAddr may contain credentials, so we sanitize it
2022-03-31 10:25:40 +08:00
err = util . SanitizeErrorCredentialURLs ( err )
2019-10-13 21:23:14 +08:00
if strings . Contains ( err . Error ( ) , "Authentication failed" ) ||
strings . Contains ( err . Error ( ) , "could not read Username" ) {
2023-05-11 16:25:46 +08:00
return fmt . Errorf ( "authentication failed: %w" , err )
2019-10-13 21:23:14 +08:00
} else if strings . Contains ( err . Error ( ) , "fatal:" ) {
2023-05-11 16:25:46 +08:00
return fmt . Errorf ( "migration failed: %w" , err )
2019-10-13 21:23:14 +08:00
}
2020-10-24 00:46:35 +01:00
// do not be tempted to coalesce this line with the return
err = handleCreateError ( t . Owner , err )
2022-06-20 12:02:49 +02:00
return err
2019-10-13 21:23:14 +08:00
}