Răsfoiți Sursa

Replace github.com/unknwon/com with stdlib and internal helpers (#8148)

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Joe Chen <jc@unknwon.io>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Copilot 3 zile în urmă
părinte
comite
bf17cc6c69

+ 9 - 7
cmd/gogs/hook.go

@@ -9,9 +9,9 @@ import (
 	"os"
 	"os"
 	"os/exec"
 	"os/exec"
 	"path/filepath"
 	"path/filepath"
+	"strconv"
 	"strings"
 	"strings"
 
 
-	"github.com/unknwon/com"
 	"github.com/urfave/cli"
 	"github.com/urfave/cli"
 	log "unknwon.dev/clog/v2"
 	log "unknwon.dev/clog/v2"
 
 
@@ -21,6 +21,7 @@ import (
 	"gogs.io/gogs/internal/database"
 	"gogs.io/gogs/internal/database"
 	"gogs.io/gogs/internal/email"
 	"gogs.io/gogs/internal/email"
 	"gogs.io/gogs/internal/httplib"
 	"gogs.io/gogs/internal/httplib"
+	"gogs.io/gogs/internal/osutil"
 )
 )
 
 
 var (
 var (
@@ -85,7 +86,7 @@ func runHookPreReceive(c *cli.Context) error {
 		branchName := git.RefShortName(string(fields[2]))
 		branchName := git.RefShortName(string(fields[2]))
 
 
 		// Branch protection
 		// Branch protection
-		repoID := com.StrTo(os.Getenv(database.EnvRepoID)).MustInt64()
+		repoID, _ := strconv.ParseInt(os.Getenv(database.EnvRepoID), 10, 64)
 		protectBranch, err := database.GetProtectBranchOfRepoByName(repoID, branchName)
 		protectBranch, err := database.GetProtectBranchOfRepoByName(repoID, branchName)
 		if err != nil {
 		if err != nil {
 			if database.IsErrBranchNotExist(err) {
 			if database.IsErrBranchNotExist(err) {
@@ -101,7 +102,7 @@ func runHookPreReceive(c *cli.Context) error {
 		bypassRequirePullRequest := false
 		bypassRequirePullRequest := false
 
 
 		// Check if user is in whitelist when enabled
 		// Check if user is in whitelist when enabled
-		userID := com.StrTo(os.Getenv(database.EnvAuthUserID)).MustInt64()
+		userID, _ := strconv.ParseInt(os.Getenv(database.EnvAuthUserID), 10, 64)
 		if protectBranch.EnableWhitelist {
 		if protectBranch.EnableWhitelist {
 			if !database.IsUserInProtectBranchWhitelist(repoID, userID, branchName) {
 			if !database.IsUserInProtectBranchWhitelist(repoID, userID, branchName) {
 				fail(fmt.Sprintf("Branch '%s' is protected and you are not in the push whitelist", branchName), "")
 				fail(fmt.Sprintf("Branch '%s' is protected and you are not in the push whitelist", branchName), "")
@@ -131,7 +132,7 @@ func runHookPreReceive(c *cli.Context) error {
 	}
 	}
 
 
 	customHooksPath := filepath.Join(os.Getenv(database.EnvRepoCustomHooksPath), "pre-receive")
 	customHooksPath := filepath.Join(os.Getenv(database.EnvRepoCustomHooksPath), "pre-receive")
-	if !com.IsFile(customHooksPath) {
+	if !osutil.IsFile(customHooksPath) {
 		return nil
 		return nil
 	}
 	}
 
 
@@ -165,7 +166,7 @@ func runHookUpdate(c *cli.Context) error {
 	}
 	}
 
 
 	customHooksPath := filepath.Join(os.Getenv(database.EnvRepoCustomHooksPath), "update")
 	customHooksPath := filepath.Join(os.Getenv(database.EnvRepoCustomHooksPath), "update")
-	if !com.IsFile(customHooksPath) {
+	if !osutil.IsFile(customHooksPath) {
 		return nil
 		return nil
 	}
 	}
 
 
@@ -213,11 +214,12 @@ func runHookPostReceive(c *cli.Context) error {
 			continue
 			continue
 		}
 		}
 
 
+		pusherID, _ := strconv.ParseInt(os.Getenv(database.EnvAuthUserID), 10, 64)
 		options := database.PushUpdateOptions{
 		options := database.PushUpdateOptions{
 			OldCommitID:  string(fields[0]),
 			OldCommitID:  string(fields[0]),
 			NewCommitID:  string(fields[1]),
 			NewCommitID:  string(fields[1]),
 			FullRefspec:  string(fields[2]),
 			FullRefspec:  string(fields[2]),
-			PusherID:     com.StrTo(os.Getenv(database.EnvAuthUserID)).MustInt64(),
+			PusherID:     pusherID,
 			PusherName:   os.Getenv(database.EnvAuthUserName),
 			PusherName:   os.Getenv(database.EnvAuthUserName),
 			RepoUserName: os.Getenv(database.EnvRepoOwnerName),
 			RepoUserName: os.Getenv(database.EnvRepoOwnerName),
 			RepoName:     os.Getenv(database.EnvRepoName),
 			RepoName:     os.Getenv(database.EnvRepoName),
@@ -249,7 +251,7 @@ func runHookPostReceive(c *cli.Context) error {
 	}
 	}
 
 
 	customHooksPath := filepath.Join(os.Getenv(database.EnvRepoCustomHooksPath), "post-receive")
 	customHooksPath := filepath.Join(os.Getenv(database.EnvRepoCustomHooksPath), "post-receive")
-	if !com.IsFile(customHooksPath) {
+	if !osutil.IsFile(customHooksPath) {
 		return nil
 		return nil
 	}
 	}
 
 

+ 4 - 4
cmd/gogs/import.go

@@ -9,10 +9,10 @@ import (
 	"time"
 	"time"
 
 
 	"github.com/cockroachdb/errors"
 	"github.com/cockroachdb/errors"
-	"github.com/unknwon/com"
 	"github.com/urfave/cli"
 	"github.com/urfave/cli"
 
 
 	"gogs.io/gogs/internal/conf"
 	"gogs.io/gogs/internal/conf"
+	"gogs.io/gogs/internal/osutil"
 )
 )
 
 
 var (
 var (
@@ -44,9 +44,9 @@ func runImportLocale(c *cli.Context) error {
 	} else if !c.IsSet("target") {
 	} else if !c.IsSet("target") {
 		return errors.New("target directory is not specified")
 		return errors.New("target directory is not specified")
 	}
 	}
-	if !com.IsDir(c.String("source")) {
+	if !osutil.IsDir(c.String("source")) {
 		return errors.Newf("source directory %q does not exist or is not a directory", c.String("source"))
 		return errors.Newf("source directory %q does not exist or is not a directory", c.String("source"))
-	} else if !com.IsDir(c.String("target")) {
+	} else if !osutil.IsDir(c.String("target")) {
 		return errors.Newf("target directory %q does not exist or is not a directory", c.String("target"))
 		return errors.Newf("target directory %q does not exist or is not a directory", c.String("target"))
 	}
 	}
 
 
@@ -66,7 +66,7 @@ func runImportLocale(c *cli.Context) error {
 		name := fmt.Sprintf("locale_%s.ini", lang)
 		name := fmt.Sprintf("locale_%s.ini", lang)
 		source := filepath.Join(c.String("source"), name)
 		source := filepath.Join(c.String("source"), name)
 		target := filepath.Join(c.String("target"), name)
 		target := filepath.Join(c.String("target"), name)
-		if !com.IsFile(source) {
+		if !osutil.IsFile(source) {
 			continue
 			continue
 		}
 		}
 
 

+ 3 - 2
cmd/gogs/serv.go

@@ -6,10 +6,10 @@ import (
 	"os"
 	"os"
 	"os/exec"
 	"os/exec"
 	"path/filepath"
 	"path/filepath"
+	"strconv"
 	"strings"
 	"strings"
 	"time"
 	"time"
 
 
-	"github.com/unknwon/com"
 	"github.com/urfave/cli"
 	"github.com/urfave/cli"
 	log "unknwon.dev/clog/v2"
 	log "unknwon.dev/clog/v2"
 
 
@@ -188,7 +188,8 @@ func runServ(c *cli.Context) error {
 	// Allow anonymous (user is nil) clone for public repositories.
 	// Allow anonymous (user is nil) clone for public repositories.
 	var user *database.User
 	var user *database.User
 
 
-	key, err := database.GetPublicKeyByID(com.StrTo(strings.TrimPrefix(c.Args()[0], "key-")).MustInt64())
+	keyID, _ := strconv.ParseInt(strings.TrimPrefix(c.Args()[0], "key-"), 10, 64)
+	key, err := database.GetPublicKeyByID(keyID)
 	if err != nil {
 	if err != nil {
 		fail("Invalid key ID", "Invalid key ID '%s': %v", c.Args()[0], err)
 		fail("Invalid key ID", "Invalid key ID '%s': %v", c.Args()[0], err)
 	}
 	}

+ 1 - 2
cmd/gogs/web.go

@@ -20,7 +20,6 @@ import (
 	"github.com/go-macaron/session"
 	"github.com/go-macaron/session"
 	"github.com/go-macaron/toolbox"
 	"github.com/go-macaron/toolbox"
 	"github.com/prometheus/client_golang/prometheus/promhttp"
 	"github.com/prometheus/client_golang/prometheus/promhttp"
-	"github.com/unknwon/com"
 	"github.com/urfave/cli"
 	"github.com/urfave/cli"
 	"gopkg.in/macaron.v1"
 	"gopkg.in/macaron.v1"
 	log "unknwon.dev/clog/v2"
 	log "unknwon.dev/clog/v2"
@@ -308,7 +307,7 @@ func runWeb(c *cli.Context) error {
 				if err != nil {
 				if err != nil {
 					c.NotFoundOrError(err, "get attachment by UUID")
 					c.NotFoundOrError(err, "get attachment by UUID")
 					return
 					return
-				} else if !com.IsFile(attach.LocalPath()) {
+				} else if !osutil.IsFile(attach.LocalPath()) {
 					c.NotFound()
 					c.NotFound()
 					return
 					return
 				}
 				}

+ 2 - 2
internal/context/notice.go

@@ -4,11 +4,11 @@ import (
 	"os"
 	"os"
 	"path/filepath"
 	"path/filepath"
 
 
-	"github.com/unknwon/com"
 	log "unknwon.dev/clog/v2"
 	log "unknwon.dev/clog/v2"
 
 
 	"gogs.io/gogs/internal/conf"
 	"gogs.io/gogs/internal/conf"
 	"gogs.io/gogs/internal/markup"
 	"gogs.io/gogs/internal/markup"
+	"gogs.io/gogs/internal/osutil"
 	"gogs.io/gogs/internal/tool"
 	"gogs.io/gogs/internal/tool"
 )
 )
 
 
@@ -16,7 +16,7 @@ import (
 // on all pages.
 // on all pages.
 func (c *Context) renderNoticeBanner() {
 func (c *Context) renderNoticeBanner() {
 	fpath := filepath.Join(conf.CustomDir(), "notice", "banner.md")
 	fpath := filepath.Join(conf.CustomDir(), "notice", "banner.md")
-	if !com.IsExist(fpath) {
+	if !osutil.Exist(fpath) {
 		return
 		return
 	}
 	}
 
 

+ 3 - 3
internal/database/comment.go

@@ -3,11 +3,11 @@ package database
 import (
 import (
 	"context"
 	"context"
 	"fmt"
 	"fmt"
+	"strconv"
 	"strings"
 	"strings"
 	"time"
 	"time"
 
 
 	"github.com/cockroachdb/errors"
 	"github.com/cockroachdb/errors"
-	"github.com/unknwon/com"
 	log "unknwon.dev/clog/v2"
 	log "unknwon.dev/clog/v2"
 	"xorm.io/xorm"
 	"xorm.io/xorm"
 
 
@@ -148,7 +148,7 @@ func (c *Comment) APIFormat() *api.Comment {
 }
 }
 
 
 func CommentHashTag(id int64) string {
 func CommentHashTag(id int64) string {
-	return "issuecomment-" + com.ToStr(id)
+	return "issuecomment-" + strconv.FormatInt(id, 10)
 }
 }
 
 
 // HashTag returns unique hash tag for comment.
 // HashTag returns unique hash tag for comment.
@@ -158,7 +158,7 @@ func (c *Comment) HashTag() string {
 
 
 // EventTag returns unique event hash tag for comment.
 // EventTag returns unique event hash tag for comment.
 func (c *Comment) EventTag() string {
 func (c *Comment) EventTag() string {
-	return "event-" + com.ToStr(c.ID)
+	return "event-" + strconv.FormatInt(c.ID, 10)
 }
 }
 
 
 // mailParticipants sends new comment emails to repository watchers
 // mailParticipants sends new comment emails to repository watchers

+ 5 - 4
internal/database/issue.go

@@ -3,11 +3,11 @@ package database
 import (
 import (
 	"context"
 	"context"
 	"fmt"
 	"fmt"
+	"strconv"
 	"strings"
 	"strings"
 	"time"
 	"time"
 
 
 	"github.com/cockroachdb/errors"
 	"github.com/cockroachdb/errors"
-	"github.com/unknwon/com"
 	log "unknwon.dev/clog/v2"
 	log "unknwon.dev/clog/v2"
 	"xorm.io/xorm"
 	"xorm.io/xorm"
 
 
@@ -221,7 +221,7 @@ func (issue *Issue) APIFormat() *api.Issue {
 
 
 // HashTag returns unique hash tag for issue.
 // HashTag returns unique hash tag for issue.
 func (issue *Issue) HashTag() string {
 func (issue *Issue) HashTag() string {
-	return "issue-" + com.ToStr(issue.ID)
+	return "issue-" + strconv.FormatInt(issue.ID, 10)
 }
 }
 
 
 // IsPoster returns true if given user by ID is the poster.
 // IsPoster returns true if given user by ID is the poster.
@@ -828,7 +828,7 @@ func GetIssueByRef(ref string) (*Issue, error) {
 		return nil, ErrIssueNotExist{args: map[string]any{"ref": ref}}
 		return nil, ErrIssueNotExist{args: map[string]any{"ref": ref}}
 	}
 	}
 
 
-	index := com.StrTo(after).MustInt64()
+	index, _ := strconv.ParseInt(after, 10, 64)
 	if index == 0 {
 	if index == 0 {
 		return nil, ErrIssueNotExist{args: map[string]any{"ref": ref}}
 		return nil, ErrIssueNotExist{args: map[string]any{"ref": ref}}
 	}
 	}
@@ -1219,7 +1219,8 @@ func parseCountResult(results []map[string][]byte) int64 {
 		return 0
 		return 0
 	}
 	}
 	for _, result := range results[0] {
 	for _, result := range results[0] {
-		return com.StrTo(string(result)).MustInt64()
+		count, _ := strconv.ParseInt(string(result), 10, 64)
+		return count
 	}
 	}
 	return 0
 	return 0
 }
 }

+ 4 - 4
internal/database/issue_mail.go

@@ -5,12 +5,12 @@ import (
 	"fmt"
 	"fmt"
 
 
 	"github.com/cockroachdb/errors"
 	"github.com/cockroachdb/errors"
-	"github.com/unknwon/com"
 	log "unknwon.dev/clog/v2"
 	log "unknwon.dev/clog/v2"
 
 
 	"gogs.io/gogs/internal/conf"
 	"gogs.io/gogs/internal/conf"
 	"gogs.io/gogs/internal/email"
 	"gogs.io/gogs/internal/email"
 	"gogs.io/gogs/internal/markup"
 	"gogs.io/gogs/internal/markup"
+	"gogs.io/gogs/internal/strutil"
 	"gogs.io/gogs/internal/userutil"
 	"gogs.io/gogs/internal/userutil"
 )
 )
 
 
@@ -138,7 +138,7 @@ func mailIssueCommentToParticipants(issue *Issue, doer *User, mentions []string)
 	for i := range participants {
 	for i := range participants {
 		if participants[i].ID == doer.ID {
 		if participants[i].ID == doer.ID {
 			continue
 			continue
-		} else if com.IsSliceContainsStr(names, participants[i].Name) {
+		} else if strutil.ContainsFold(names, participants[i].Name) {
 			continue
 			continue
 		}
 		}
 
 
@@ -146,7 +146,7 @@ func mailIssueCommentToParticipants(issue *Issue, doer *User, mentions []string)
 		names = append(names, participants[i].Name)
 		names = append(names, participants[i].Name)
 	}
 	}
 	if issue.Assignee != nil && issue.Assignee.ID != doer.ID {
 	if issue.Assignee != nil && issue.Assignee.ID != doer.ID {
-		if !com.IsSliceContainsStr(names, issue.Assignee.Name) {
+		if !strutil.ContainsFold(names, issue.Assignee.Name) {
 			tos = append(tos, issue.Assignee.Email)
 			tos = append(tos, issue.Assignee.Email)
 			names = append(names, issue.Assignee.Name)
 			names = append(names, issue.Assignee.Name)
 		}
 		}
@@ -157,7 +157,7 @@ func mailIssueCommentToParticipants(issue *Issue, doer *User, mentions []string)
 	names = append(names, doer.Name)
 	names = append(names, doer.Name)
 	toUsernames := make([]string, 0, len(mentions)) // list of user names.
 	toUsernames := make([]string, 0, len(mentions)) // list of user names.
 	for i := range mentions {
 	for i := range mentions {
-		if com.IsSliceContainsStr(names, mentions[i]) {
+		if strutil.ContainsFold(names, mentions[i]) {
 			continue
 			continue
 		}
 		}
 
 

+ 3 - 2
internal/database/mirror.go

@@ -4,11 +4,11 @@ import (
 	"context"
 	"context"
 	"fmt"
 	"fmt"
 	"net/url"
 	"net/url"
+	"strconv"
 	"strings"
 	"strings"
 	"time"
 	"time"
 
 
 	"github.com/cockroachdb/errors"
 	"github.com/cockroachdb/errors"
-	"github.com/unknwon/com"
 	"gopkg.in/ini.v1"
 	"gopkg.in/ini.v1"
 	log "unknwon.dev/clog/v2"
 	log "unknwon.dev/clog/v2"
 	"xorm.io/xorm"
 	"xorm.io/xorm"
@@ -341,7 +341,8 @@ func SyncMirrors() {
 		log.Trace("SyncMirrors [repo_id: %s]", repoID)
 		log.Trace("SyncMirrors [repo_id: %s]", repoID)
 		MirrorQueue.Remove(repoID)
 		MirrorQueue.Remove(repoID)
 
 
-		m, err := GetMirrorByRepoID(com.StrTo(repoID).MustInt64())
+		id, _ := strconv.ParseInt(repoID, 10, 64)
+		m, err := GetMirrorByRepoID(id)
 		if err != nil {
 		if err != nil {
 			log.Error("GetMirrorByRepoID [%v]: %v", repoID, err)
 			log.Error("GetMirrorByRepoID [%v]: %v", repoID, err)
 			continue
 			continue

+ 8 - 7
internal/database/pull.go

@@ -5,10 +5,10 @@ import (
 	"fmt"
 	"fmt"
 	"os"
 	"os"
 	"path/filepath"
 	"path/filepath"
+	"strconv"
 	"time"
 	"time"
 
 
 	"github.com/cockroachdb/errors"
 	"github.com/cockroachdb/errors"
-	"github.com/unknwon/com"
 	log "unknwon.dev/clog/v2"
 	log "unknwon.dev/clog/v2"
 	"xorm.io/xorm"
 	"xorm.io/xorm"
 
 
@@ -217,7 +217,7 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle
 
 
 	// Create temporary directory to store temporary copy of the base repository,
 	// Create temporary directory to store temporary copy of the base repository,
 	// and clean it up when operation finished regardless of succeed or not.
 	// and clean it up when operation finished regardless of succeed or not.
-	tmpBasePath := filepath.Join(conf.Server.AppDataPath, "tmp", "repos", com.ToStr(time.Now().Nanosecond())+".git")
+	tmpBasePath := filepath.Join(conf.Server.AppDataPath, "tmp", "repos", strconv.Itoa(time.Now().Nanosecond())+".git")
 	if err = os.MkdirAll(filepath.Dir(tmpBasePath), os.ModePerm); err != nil {
 	if err = os.MkdirAll(filepath.Dir(tmpBasePath), os.ModePerm); err != nil {
 		return err
 		return err
 	}
 	}
@@ -284,7 +284,7 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle
 		}
 		}
 
 
 		// Name non-branch commit state to a new temporary branch in order to save changes.
 		// Name non-branch commit state to a new temporary branch in order to save changes.
-		tmpBranch := com.ToStr(time.Now().UnixNano(), 10)
+		tmpBranch := strconv.FormatInt(time.Now().UnixNano(), 10)
 		if _, stderr, err = process.ExecDir(-1, tmpBasePath,
 		if _, stderr, err = process.ExecDir(-1, tmpBasePath,
 			fmt.Sprintf("PullRequest.Merge (git checkout): %s", tmpBasePath),
 			fmt.Sprintf("PullRequest.Merge (git checkout): %s", tmpBasePath),
 			"git", "checkout", "-b", tmpBranch); err != nil {
 			"git", "checkout", "-b", tmpBranch); err != nil {
@@ -414,8 +414,8 @@ func (pr *PullRequest) testPatch() (err error) {
 		return nil
 		return nil
 	}
 	}
 
 
-	repoWorkingPool.CheckIn(com.ToStr(pr.BaseRepoID))
-	defer repoWorkingPool.CheckOut(com.ToStr(pr.BaseRepoID))
+	repoWorkingPool.CheckIn(strconv.FormatInt(pr.BaseRepoID, 10))
+	defer repoWorkingPool.CheckOut(strconv.FormatInt(pr.BaseRepoID, 10))
 
 
 	log.Trace("PullRequest[%d].testPatch (patchPath): %s", pr.ID, patchPath)
 	log.Trace("PullRequest[%d].testPatch (patchPath): %s", pr.ID, patchPath)
 
 
@@ -625,7 +625,7 @@ func (pr *PullRequest) UpdatePatch() (err error) {
 	}
 	}
 
 
 	// Add a temporary remote.
 	// Add a temporary remote.
-	tmpRemote := com.ToStr(time.Now().UnixNano())
+	tmpRemote := strconv.FormatInt(time.Now().UnixNano(), 10)
 	baseRepoPath := RepoPath(pr.BaseRepo.MustOwner().Name, pr.BaseRepo.Name)
 	baseRepoPath := RepoPath(pr.BaseRepo.MustOwner().Name, pr.BaseRepo.Name)
 	err = headGitRepo.RemoteAdd(tmpRemote, baseRepoPath, git.RemoteAddOptions{Fetch: true})
 	err = headGitRepo.RemoteAdd(tmpRemote, baseRepoPath, git.RemoteAddOptions{Fetch: true})
 	if err != nil {
 	if err != nil {
@@ -868,7 +868,8 @@ func TestPullRequests() {
 		log.Trace("TestPullRequests[%v]: processing test task", prID)
 		log.Trace("TestPullRequests[%v]: processing test task", prID)
 		PullRequestQueue.Remove(prID)
 		PullRequestQueue.Remove(prID)
 
 
-		pr, err := GetPullRequestByID(com.StrTo(prID).MustInt64())
+		id, _ := strconv.ParseInt(prID, 10, 64)
+		pr, err := GetPullRequestByID(id)
 		if err != nil {
 		if err != nil {
 			log.Error("GetPullRequestByID[%s]: %v", prID, err)
 			log.Error("GetPullRequestByID[%s]: %v", prID, err)
 			continue
 			continue

+ 34 - 28
internal/database/repo.go

@@ -11,7 +11,9 @@ import (
 	"os/exec"
 	"os/exec"
 	"path"
 	"path"
 	"path/filepath"
 	"path/filepath"
+	"slices"
 	"sort"
 	"sort"
+	"strconv"
 	"strings"
 	"strings"
 	"time"
 	"time"
 
 
@@ -36,6 +38,7 @@ import (
 	"gogs.io/gogs/internal/process"
 	"gogs.io/gogs/internal/process"
 	"gogs.io/gogs/internal/repoutil"
 	"gogs.io/gogs/internal/repoutil"
 	"gogs.io/gogs/internal/semverutil"
 	"gogs.io/gogs/internal/semverutil"
+	"gogs.io/gogs/internal/strutil"
 	"gogs.io/gogs/internal/sync"
 	"gogs.io/gogs/internal/sync"
 )
 )
 
 
@@ -77,14 +80,15 @@ func LoadRepoConfig() {
 		}
 		}
 
 
 		customPath := filepath.Join(conf.CustomDir(), "conf", t)
 		customPath := filepath.Join(conf.CustomDir(), "conf", t)
-		if com.IsDir(customPath) {
-			customFiles, err := com.StatDir(customPath)
+		if osutil.IsDir(customPath) {
+			entries, err := os.ReadDir(customPath)
 			if err != nil {
 			if err != nil {
 				log.Fatal("Failed to get custom %s files: %v", t, err)
 				log.Fatal("Failed to get custom %s files: %v", t, err)
 			}
 			}
 
 
-			for _, f := range customFiles {
-				if !com.IsSliceContainsStr(files, f) {
+			for _, entry := range entries {
+				f := entry.Name()
+				if !strutil.ContainsFold(files, f) {
 					files = append(files, f)
 					files = append(files, f)
 				}
 				}
 			}
 			}
@@ -104,12 +108,12 @@ func LoadRepoConfig() {
 	// Filter out invalid names and promote preferred licenses.
 	// Filter out invalid names and promote preferred licenses.
 	sortedLicenses := make([]string, 0, len(Licenses))
 	sortedLicenses := make([]string, 0, len(Licenses))
 	for _, name := range conf.Repository.PreferredLicenses {
 	for _, name := range conf.Repository.PreferredLicenses {
-		if com.IsSliceContainsStr(Licenses, name) {
+		if slices.Contains(Licenses, name) {
 			sortedLicenses = append(sortedLicenses, name)
 			sortedLicenses = append(sortedLicenses, name)
 		}
 		}
 	}
 	}
 	for _, name := range Licenses {
 	for _, name := range Licenses {
-		if !com.IsSliceContainsStr(conf.Repository.PreferredLicenses, name) {
+		if !slices.Contains(conf.Repository.PreferredLicenses, name) {
 			sortedLicenses = append(sortedLicenses, name)
 			sortedLicenses = append(sortedLicenses, name)
 		}
 		}
 	}
 	}
@@ -309,7 +313,7 @@ func (r *Repository) HTMLURL() string {
 
 
 // CustomAvatarPath returns repository custom avatar file path.
 // CustomAvatarPath returns repository custom avatar file path.
 func (r *Repository) CustomAvatarPath() string {
 func (r *Repository) CustomAvatarPath() string {
-	return filepath.Join(conf.Picture.RepositoryAvatarUploadPath, com.ToStr(r.ID))
+	return filepath.Join(conf.Picture.RepositoryAvatarUploadPath, strconv.FormatInt(r.ID, 10))
 }
 }
 
 
 // RelAvatarLink returns relative avatar link to the site domain,
 // RelAvatarLink returns relative avatar link to the site domain,
@@ -317,7 +321,7 @@ func (r *Repository) CustomAvatarPath() string {
 // Since Gravatar support not needed here - just check for image path.
 // Since Gravatar support not needed here - just check for image path.
 func (r *Repository) RelAvatarLink() string {
 func (r *Repository) RelAvatarLink() string {
 	defaultImgURL := ""
 	defaultImgURL := ""
-	if !com.IsExist(r.CustomAvatarPath()) {
+	if !osutil.Exist(r.CustomAvatarPath()) {
 		return defaultImgURL
 		return defaultImgURL
 	}
 	}
 	return fmt.Sprintf("%s/%s/%d", conf.Server.Subpath, RepoAvatarURLPrefix, r.ID)
 	return fmt.Sprintf("%s/%s/%d", conf.Server.Subpath, RepoAvatarURLPrefix, r.ID)
@@ -645,7 +649,7 @@ func (r *Repository) NextIssueIndex() int64 {
 }
 }
 
 
 func (r *Repository) LocalCopyPath() string {
 func (r *Repository) LocalCopyPath() string {
-	return filepath.Join(conf.Server.AppDataPath, "tmp", "local-r", com.ToStr(r.ID))
+	return filepath.Join(conf.Server.AppDataPath, "tmp", "local-r", strconv.FormatInt(r.ID, 10))
 }
 }
 
 
 // UpdateLocalCopy fetches latest changes of given branch from repoPath to localPath.
 // UpdateLocalCopy fetches latest changes of given branch from repoPath to localPath.
@@ -705,7 +709,7 @@ func (r *Repository) PatchPath(index int64) (string, error) {
 		return "", err
 		return "", err
 	}
 	}
 
 
-	return filepath.Join(RepoPath(r.Owner.Name, r.Name), "pulls", com.ToStr(index)+".patch"), nil
+	return filepath.Join(RepoPath(r.Owner.Name, r.Name), "pulls", strconv.FormatInt(index, 10)+".patch"), nil
 }
 }
 
 
 // SavePatch saves patch data to corresponding location by given issue ID.
 // SavePatch saves patch data to corresponding location by given issue ID.
@@ -730,7 +734,7 @@ func isRepositoryExist(e Engine, u *User, repoName string) (bool, error) {
 		OwnerID:   u.ID,
 		OwnerID:   u.ID,
 		LowerName: strings.ToLower(repoName),
 		LowerName: strings.ToLower(repoName),
 	})
 	})
-	return has && com.IsDir(RepoPath(u.Name, repoName)), err
+	return has && osutil.IsDir(RepoPath(u.Name, repoName)), err
 }
 }
 
 
 // IsRepositoryExist returns true if the repository with given name under user has already existed.
 // IsRepositoryExist returns true if the repository with given name under user has already existed.
@@ -842,12 +846,14 @@ func MigrateRepository(doer, owner *User, opts MigrateRepoOptions) (*Repository,
 	}
 	}
 
 
 	// Check if repository is empty.
 	// Check if repository is empty.
-	_, stderr, err := com.ExecCmdDir(repoPath, "git", "log", "-1")
+	cmd := exec.Command("git", "log", "-1")
+	cmd.Dir = repoPath
+	output, err := cmd.CombinedOutput()
 	if err != nil {
 	if err != nil {
-		if strings.Contains(stderr, "fatal: bad default revision 'HEAD'") {
+		if strings.Contains(string(output), "fatal: bad default revision 'HEAD'") {
 			repo.IsBare = true
 			repo.IsBare = true
 		} else {
 		} else {
-			return repo, errors.Newf("check bare: %v - %s", err, stderr)
+			return repo, errors.Newf("check bare: %v - %s", err, output)
 		}
 		}
 	}
 	}
 
 
@@ -1070,7 +1076,7 @@ func initRepository(e Engine, repoPath string, doer *User, repo *Repository, opt
 		return errors.Wrap(err, "set default branch")
 		return errors.Wrap(err, "set default branch")
 	}
 	}
 
 
-	tmpDir := filepath.Join(os.TempDir(), "gogs-"+repo.Name+"-"+com.ToStr(time.Now().Nanosecond()))
+	tmpDir := filepath.Join(os.TempDir(), "gogs-"+repo.Name+"-"+strconv.Itoa(time.Now().Nanosecond()))
 
 
 	// Initialize repository according to user's choice.
 	// Initialize repository according to user's choice.
 	if opts.AutoInit {
 	if opts.AutoInit {
@@ -1493,7 +1499,7 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error
 
 
 	// Rename remote wiki repository to new path and delete local copy.
 	// Rename remote wiki repository to new path and delete local copy.
 	wikiPath := WikiPath(owner.Name, repo.Name)
 	wikiPath := WikiPath(owner.Name, repo.Name)
-	if com.IsExist(wikiPath) {
+	if osutil.Exist(wikiPath) {
 		RemoveAllWithNotice("Delete repository wiki local copy", repo.LocalWikiPath())
 		RemoveAllWithNotice("Delete repository wiki local copy", repo.LocalWikiPath())
 		if err = os.Rename(wikiPath, WikiPath(newOwner.Name, repo.Name)); err != nil {
 		if err = os.Rename(wikiPath, WikiPath(newOwner.Name, repo.Name)); err != nil {
 			return errors.Newf("rename repository wiki: %v", err)
 			return errors.Newf("rename repository wiki: %v", err)
@@ -1504,8 +1510,8 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error
 }
 }
 
 
 func deleteRepoLocalCopy(repoID int64) {
 func deleteRepoLocalCopy(repoID int64) {
-	repoWorkingPool.CheckIn(com.ToStr(repoID))
-	defer repoWorkingPool.CheckOut(com.ToStr(repoID))
+	repoWorkingPool.CheckIn(strconv.FormatInt(repoID, 10))
+	defer repoWorkingPool.CheckOut(strconv.FormatInt(repoID, 10))
 	RemoveAllWithNotice(fmt.Sprintf("Delete repository %d local copy", repoID), repoutil.RepositoryLocalPath(repoID))
 	RemoveAllWithNotice(fmt.Sprintf("Delete repository %d local copy", repoID), repoutil.RepositoryLocalPath(repoID))
 }
 }
 
 
@@ -1535,7 +1541,7 @@ func ChangeRepositoryName(u *User, oldRepoName, newRepoName string) (err error)
 	}
 	}
 
 
 	wikiPath := repo.WikiPath()
 	wikiPath := repo.WikiPath()
-	if com.IsExist(wikiPath) {
+	if osutil.Exist(wikiPath) {
 		if err = os.Rename(wikiPath, WikiPath(u.Name, newRepoName)); err != nil {
 		if err = os.Rename(wikiPath, WikiPath(u.Name, newRepoName)); err != nil {
 			return errors.Newf("rename repository wiki: %v", err)
 			return errors.Newf("rename repository wiki: %v", err)
 		}
 		}
@@ -1593,11 +1599,11 @@ func updateRepository(e Engine, repo *Repository, visibilityChanged bool) (err e
 
 
 		// Create/Remove git-daemon-export-ok for git-daemon
 		// Create/Remove git-daemon-export-ok for git-daemon
 		daemonExportFile := path.Join(repo.RepoPath(), "git-daemon-export-ok")
 		daemonExportFile := path.Join(repo.RepoPath(), "git-daemon-export-ok")
-		if repo.IsPrivate && com.IsExist(daemonExportFile) {
+		if repo.IsPrivate && osutil.Exist(daemonExportFile) {
 			if err = os.Remove(daemonExportFile); err != nil {
 			if err = os.Remove(daemonExportFile); err != nil {
 				log.Error("Failed to remove %s: %v", daemonExportFile, err)
 				log.Error("Failed to remove %s: %v", daemonExportFile, err)
 			}
 			}
-		} else if !repo.IsPrivate && !com.IsExist(daemonExportFile) {
+		} else if !repo.IsPrivate && !osutil.Exist(daemonExportFile) {
 			if f, err := os.Create(daemonExportFile); err != nil {
 			if f, err := os.Create(daemonExportFile); err != nil {
 				log.Error("Failed to create %s: %v", daemonExportFile, err)
 				log.Error("Failed to create %s: %v", daemonExportFile, err)
 			} else {
 			} else {
@@ -1929,7 +1935,7 @@ func DeleteOldRepositoryArchives() {
 			basePath := filepath.Join(repo.RepoPath(), "archives")
 			basePath := filepath.Join(repo.RepoPath(), "archives")
 			for _, format := range formats {
 			for _, format := range formats {
 				dirPath := filepath.Join(basePath, format)
 				dirPath := filepath.Join(basePath, format)
-				if !com.IsDir(dirPath) {
+				if !osutil.IsDir(dirPath) {
 					continue
 					continue
 				}
 				}
 
 
@@ -1992,7 +1998,7 @@ func gatherMissingRepoRecords() ([]*Repository, error) {
 	if err := x.Where("id > 0").Iterate(new(Repository),
 	if err := x.Where("id > 0").Iterate(new(Repository),
 		func(idx int, bean any) error {
 		func(idx int, bean any) error {
 			repo := bean.(*Repository)
 			repo := bean.(*Repository)
-			if !com.IsDir(repo.RepoPath()) {
+			if !osutil.IsDir(repo.RepoPath()) {
 				repos = append(repos, repo)
 				repos = append(repos, repo)
 			}
 			}
 			return nil
 			return nil
@@ -2143,7 +2149,7 @@ func repoStatsCheck(checker *repoChecker) {
 		return
 		return
 	}
 	}
 	for _, result := range results {
 	for _, result := range results {
-		id := com.StrTo(result["id"]).MustInt64()
+		id, _ := strconv.ParseInt(string(result["id"]), 10, 64)
 		log.Trace("Updating %s: %d", checker.desc, id)
 		log.Trace("Updating %s: %d", checker.desc, id)
 		_, err = x.Exec(checker.correctSQL, id, id)
 		_, err = x.Exec(checker.correctSQL, id, id)
 		if err != nil {
 		if err != nil {
@@ -2204,7 +2210,7 @@ func CheckRepoStats() {
 		log.Error("Select %s: %v", desc, err)
 		log.Error("Select %s: %v", desc, err)
 	} else {
 	} else {
 		for _, result := range results {
 		for _, result := range results {
-			id := com.StrTo(result["id"]).MustInt64()
+			id, _ := strconv.ParseInt(string(result["id"]), 10, 64)
 			log.Trace("Updating %s: %d", desc, id)
 			log.Trace("Updating %s: %d", desc, id)
 			_, err = x.Exec("UPDATE `repository` SET num_closed_issues=(SELECT COUNT(*) FROM `issue` WHERE repo_id=? AND is_closed=? AND is_pull=?) WHERE id=?", id, true, false, id)
 			_, err = x.Exec("UPDATE `repository` SET num_closed_issues=(SELECT COUNT(*) FROM `issue` WHERE repo_id=? AND is_closed=? AND is_pull=?) WHERE id=?", id, true, false, id)
 			if err != nil {
 			if err != nil {
@@ -2221,7 +2227,7 @@ func CheckRepoStats() {
 		log.Error("Select repository count 'num_forks': %v", err)
 		log.Error("Select repository count 'num_forks': %v", err)
 	} else {
 	} else {
 		for _, result := range results {
 		for _, result := range results {
-			id := com.StrTo(result["id"]).MustInt64()
+			id, _ := strconv.ParseInt(string(result["id"]), 10, 64)
 			log.Trace("Updating repository count 'num_forks': %d", id)
 			log.Trace("Updating repository count 'num_forks': %d", id)
 
 
 			repo, err := GetRepositoryByID(id)
 			repo, err := GetRepositoryByID(id)
@@ -2632,8 +2638,8 @@ func (r *Repository) GetForks() ([]*Repository, error) {
 //
 //
 
 
 func (r *Repository) CreateNewBranch(oldBranch, newBranch string) (err error) {
 func (r *Repository) CreateNewBranch(oldBranch, newBranch string) (err error) {
-	repoWorkingPool.CheckIn(com.ToStr(r.ID))
-	defer repoWorkingPool.CheckOut(com.ToStr(r.ID))
+	repoWorkingPool.CheckIn(strconv.FormatInt(r.ID, 10))
+	defer repoWorkingPool.CheckOut(strconv.FormatInt(r.ID, 10))
 
 
 	localPath := r.LocalCopyPath()
 	localPath := r.LocalCopyPath()
 
 

+ 2 - 2
internal/database/repo_branch.go

@@ -3,11 +3,11 @@ package database
 import (
 import (
 	"context"
 	"context"
 	"fmt"
 	"fmt"
+	"slices"
 	"strings"
 	"strings"
 
 
 	"github.com/cockroachdb/errors"
 	"github.com/cockroachdb/errors"
 	"github.com/gogs/git-module"
 	"github.com/gogs/git-module"
-	"github.com/unknwon/com"
 
 
 	"gogs.io/gogs/internal/errutil"
 	"gogs.io/gogs/internal/errutil"
 	"gogs.io/gogs/internal/tool"
 	"gogs.io/gogs/internal/tool"
@@ -199,7 +199,7 @@ func UpdateOrgProtectBranch(repo *Repository, protectBranch *ProtectBranch, whit
 		}
 		}
 		validTeamIDs = make([]int64, 0, len(teams))
 		validTeamIDs = make([]int64, 0, len(teams))
 		for i := range teams {
 		for i := range teams {
-			if teams[i].HasWriteAccess() && com.IsSliceContainsInt64(teamIDs, teams[i].ID) {
+			if teams[i].HasWriteAccess() && slices.Contains(teamIDs, teams[i].ID) {
 				validTeamIDs = append(validTeamIDs, teams[i].ID)
 				validTeamIDs = append(validTeamIDs, teams[i].ID)
 			}
 			}
 		}
 		}

+ 14 - 13
internal/database/repo_editor.go

@@ -8,18 +8,19 @@ import (
 	"os/exec"
 	"os/exec"
 	"path"
 	"path"
 	"path/filepath"
 	"path/filepath"
+	"strconv"
 	"strings"
 	"strings"
 	"time"
 	"time"
 
 
 	"github.com/cockroachdb/errors"
 	"github.com/cockroachdb/errors"
 	gouuid "github.com/satori/go.uuid"
 	gouuid "github.com/satori/go.uuid"
-	"github.com/unknwon/com"
 
 
 	"github.com/gogs/git-module"
 	"github.com/gogs/git-module"
 
 
 	"gogs.io/gogs/internal/conf"
 	"gogs.io/gogs/internal/conf"
 	"gogs.io/gogs/internal/cryptoutil"
 	"gogs.io/gogs/internal/cryptoutil"
 	"gogs.io/gogs/internal/gitutil"
 	"gogs.io/gogs/internal/gitutil"
+	"gogs.io/gogs/internal/ioutil"
 	"gogs.io/gogs/internal/osutil"
 	"gogs.io/gogs/internal/osutil"
 	"gogs.io/gogs/internal/pathutil"
 	"gogs.io/gogs/internal/pathutil"
 	"gogs.io/gogs/internal/process"
 	"gogs.io/gogs/internal/process"
@@ -63,12 +64,12 @@ type ComposeHookEnvsOptions struct {
 func ComposeHookEnvs(opts ComposeHookEnvsOptions) []string {
 func ComposeHookEnvs(opts ComposeHookEnvsOptions) []string {
 	envs := []string{
 	envs := []string{
 		"SSH_ORIGINAL_COMMAND=1",
 		"SSH_ORIGINAL_COMMAND=1",
-		EnvAuthUserID + "=" + com.ToStr(opts.AuthUser.ID),
+		EnvAuthUserID + "=" + strconv.FormatInt(opts.AuthUser.ID, 10),
 		EnvAuthUserName + "=" + opts.AuthUser.Name,
 		EnvAuthUserName + "=" + opts.AuthUser.Name,
 		EnvAuthUserEmail + "=" + opts.AuthUser.Email,
 		EnvAuthUserEmail + "=" + opts.AuthUser.Email,
 		EnvRepoOwnerName + "=" + opts.OwnerName,
 		EnvRepoOwnerName + "=" + opts.OwnerName,
 		EnvRepoOwnerSaltMd5 + "=" + cryptoutil.MD5(opts.OwnerSalt),
 		EnvRepoOwnerSaltMd5 + "=" + cryptoutil.MD5(opts.OwnerSalt),
-		EnvRepoID + "=" + com.ToStr(opts.RepoID),
+		EnvRepoID + "=" + strconv.FormatInt(opts.RepoID, 10),
 		EnvRepoName + "=" + opts.RepoName,
 		EnvRepoName + "=" + opts.RepoName,
 		EnvRepoCustomHooksPath + "=" + filepath.Join(opts.RepoPath, "custom_hooks"),
 		EnvRepoCustomHooksPath + "=" + filepath.Join(opts.RepoPath, "custom_hooks"),
 	}
 	}
@@ -85,7 +86,7 @@ func ComposeHookEnvs(opts ComposeHookEnvsOptions) []string {
 // discardLocalRepoBranchChanges discards local commits/changes of
 // discardLocalRepoBranchChanges discards local commits/changes of
 // given branch to make sure it is even to remote branch.
 // given branch to make sure it is even to remote branch.
 func discardLocalRepoBranchChanges(localPath, branch string) error {
 func discardLocalRepoBranchChanges(localPath, branch string) error {
-	if !com.IsExist(localPath) {
+	if !osutil.Exist(localPath) {
 		return nil
 		return nil
 	}
 	}
 
 
@@ -146,8 +147,8 @@ func (r *Repository) UpdateRepoFile(doer *User, opts UpdateRepoFileOptions) erro
 		return errors.Errorf("bad tree path %q", opts.NewTreeName)
 		return errors.Errorf("bad tree path %q", opts.NewTreeName)
 	}
 	}
 
 
-	repoWorkingPool.CheckIn(com.ToStr(r.ID))
-	defer repoWorkingPool.CheckOut(com.ToStr(r.ID))
+	repoWorkingPool.CheckIn(strconv.FormatInt(r.ID, 10))
+	defer repoWorkingPool.CheckOut(strconv.FormatInt(r.ID, 10))
 
 
 	if err := r.DiscardLocalRepoBranchChanges(opts.OldBranch); err != nil {
 	if err := r.DiscardLocalRepoBranchChanges(opts.OldBranch); err != nil {
 		return errors.Newf("discard local repo branch[%s] changes: %v", opts.OldBranch, err)
 		return errors.Newf("discard local repo branch[%s] changes: %v", opts.OldBranch, err)
@@ -249,8 +250,8 @@ func (r *Repository) GetDiffPreview(branch, treePath, content string) (*gitutil.
 		return nil, errors.Errorf("bad tree path %q", treePath)
 		return nil, errors.Errorf("bad tree path %q", treePath)
 	}
 	}
 
 
-	repoWorkingPool.CheckIn(com.ToStr(r.ID))
-	defer repoWorkingPool.CheckOut(com.ToStr(r.ID))
+	repoWorkingPool.CheckIn(strconv.FormatInt(r.ID, 10))
+	defer repoWorkingPool.CheckOut(strconv.FormatInt(r.ID, 10))
 
 
 	if err := r.DiscardLocalRepoBranchChanges(branch); err != nil {
 	if err := r.DiscardLocalRepoBranchChanges(branch); err != nil {
 		return nil, errors.Newf("discard local repo branch[%s] changes: %v", branch, err)
 		return nil, errors.Newf("discard local repo branch[%s] changes: %v", branch, err)
@@ -322,8 +323,8 @@ func (r *Repository) DeleteRepoFile(doer *User, opts DeleteRepoFileOptions) (err
 		return errors.Errorf("bad tree path %q", opts.TreePath)
 		return errors.Errorf("bad tree path %q", opts.TreePath)
 	}
 	}
 
 
-	repoWorkingPool.CheckIn(com.ToStr(r.ID))
-	defer repoWorkingPool.CheckOut(com.ToStr(r.ID))
+	repoWorkingPool.CheckIn(strconv.FormatInt(r.ID, 10))
+	defer repoWorkingPool.CheckOut(strconv.FormatInt(r.ID, 10))
 
 
 	if err = r.DiscardLocalRepoBranchChanges(opts.OldBranch); err != nil {
 	if err = r.DiscardLocalRepoBranchChanges(opts.OldBranch); err != nil {
 		return errors.Newf("discard local r branch[%s] changes: %v", opts.OldBranch, err)
 		return errors.Newf("discard local r branch[%s] changes: %v", opts.OldBranch, err)
@@ -562,8 +563,8 @@ func (r *Repository) UploadRepoFiles(doer *User, opts UploadRepoFileOptions) err
 		return errors.Newf("get uploads by UUIDs[%v]: %v", opts.Files, err)
 		return errors.Newf("get uploads by UUIDs[%v]: %v", opts.Files, err)
 	}
 	}
 
 
-	repoWorkingPool.CheckIn(com.ToStr(r.ID))
-	defer repoWorkingPool.CheckOut(com.ToStr(r.ID))
+	repoWorkingPool.CheckIn(strconv.FormatInt(r.ID, 10))
+	defer repoWorkingPool.CheckOut(strconv.FormatInt(r.ID, 10))
 
 
 	if err = r.DiscardLocalRepoBranchChanges(opts.OldBranch); err != nil {
 	if err = r.DiscardLocalRepoBranchChanges(opts.OldBranch); err != nil {
 		return errors.Newf("discard local r branch[%s] changes: %v", opts.OldBranch, err)
 		return errors.Newf("discard local r branch[%s] changes: %v", opts.OldBranch, err)
@@ -606,7 +607,7 @@ func (r *Repository) UploadRepoFiles(doer *User, opts UploadRepoFileOptions) err
 			return errors.Newf("cannot overwrite symbolic link: %s", upload.Name)
 			return errors.Newf("cannot overwrite symbolic link: %s", upload.Name)
 		}
 		}
 
 
-		if err = com.Copy(tmpPath, targetPath); err != nil {
+		if err = ioutil.CopyFile(tmpPath, targetPath); err != nil {
 			return errors.Newf("copy: %v", err)
 			return errors.Newf("copy: %v", err)
 		}
 		}
 	}
 	}

+ 5 - 3
internal/database/ssh_key.go

@@ -9,18 +9,19 @@ import (
 	"os"
 	"os"
 	"path"
 	"path"
 	"path/filepath"
 	"path/filepath"
+	"strconv"
 	"strings"
 	"strings"
 	"sync"
 	"sync"
 	"time"
 	"time"
 
 
 	"github.com/cockroachdb/errors"
 	"github.com/cockroachdb/errors"
-	"github.com/unknwon/com"
 	"golang.org/x/crypto/ssh"
 	"golang.org/x/crypto/ssh"
 	log "unknwon.dev/clog/v2"
 	log "unknwon.dev/clog/v2"
 	"xorm.io/xorm"
 	"xorm.io/xorm"
 
 
 	"gogs.io/gogs/internal/conf"
 	"gogs.io/gogs/internal/conf"
 	"gogs.io/gogs/internal/errutil"
 	"gogs.io/gogs/internal/errutil"
+	"gogs.io/gogs/internal/osutil"
 	"gogs.io/gogs/internal/process"
 	"gogs.io/gogs/internal/process"
 )
 )
 
 
@@ -210,7 +211,8 @@ func SSHKeygenParsePublicKey(key, keyTestPath, keygenPath string) (string, int,
 	}
 	}
 
 
 	keyType := strings.Trim(fields[len(fields)-1], "()\r\n")
 	keyType := strings.Trim(fields[len(fields)-1], "()\r\n")
-	return strings.ToLower(keyType), com.StrTo(fields[0]).MustInt(), nil
+	length, _ := strconv.Atoi(fields[0])
+	return strings.ToLower(keyType), length, nil
 }
 }
 
 
 // SSHNativeParsePublicKey extracts the key type and length using the golang SSH library.
 // SSHNativeParsePublicKey extracts the key type and length using the golang SSH library.
@@ -540,7 +542,7 @@ func RewriteAuthorizedKeys() error {
 		return err
 		return err
 	}
 	}
 
 
-	if com.IsExist(fpath) {
+	if osutil.Exist(fpath) {
 		if err = os.Remove(fpath); err != nil {
 		if err = os.Remove(fpath); err != nil {
 			return err
 			return err
 		}
 		}

+ 1 - 2
internal/database/two_factor.go

@@ -7,7 +7,6 @@ import (
 
 
 	"github.com/cockroachdb/errors"
 	"github.com/cockroachdb/errors"
 	"github.com/pquerna/otp/totp"
 	"github.com/pquerna/otp/totp"
-	"github.com/unknwon/com"
 
 
 	"gogs.io/gogs/internal/conf"
 	"gogs.io/gogs/internal/conf"
 	"gogs.io/gogs/internal/cryptoutil"
 	"gogs.io/gogs/internal/cryptoutil"
@@ -29,7 +28,7 @@ func (t *TwoFactor) ValidateTOTP(passcode string) (bool, error) {
 	if err != nil {
 	if err != nil {
 		return false, errors.Newf("DecodeString: %v", err)
 		return false, errors.Newf("DecodeString: %v", err)
 	}
 	}
-	decryptSecret, err := com.AESGCMDecrypt(cryptoutil.MD5Bytes(conf.Security.SecretKey), secret)
+	decryptSecret, err := cryptoutil.AESGCMDecrypt(cryptoutil.MD5Bytes(conf.Security.SecretKey), secret)
 	if err != nil {
 	if err != nil {
 		return false, errors.Newf("AESGCMDecrypt: %v", err)
 		return false, errors.Newf("AESGCMDecrypt: %v", err)
 	}
 	}

+ 9 - 8
internal/database/wiki.go

@@ -5,15 +5,16 @@ import (
 	"os"
 	"os"
 	"path"
 	"path"
 	"path/filepath"
 	"path/filepath"
+	"strconv"
 	"strings"
 	"strings"
 	"time"
 	"time"
 
 
 	"github.com/cockroachdb/errors"
 	"github.com/cockroachdb/errors"
-	"github.com/unknwon/com"
 
 
 	"github.com/gogs/git-module"
 	"github.com/gogs/git-module"
 
 
 	"gogs.io/gogs/internal/conf"
 	"gogs.io/gogs/internal/conf"
+	"gogs.io/gogs/internal/osutil"
 	"gogs.io/gogs/internal/pathutil"
 	"gogs.io/gogs/internal/pathutil"
 	"gogs.io/gogs/internal/repoutil"
 	"gogs.io/gogs/internal/repoutil"
 	"gogs.io/gogs/internal/sync"
 	"gogs.io/gogs/internal/sync"
@@ -61,7 +62,7 @@ func (r *Repository) WikiPath() string {
 
 
 // HasWiki returns true if repository has wiki.
 // HasWiki returns true if repository has wiki.
 func (r *Repository) HasWiki() bool {
 func (r *Repository) HasWiki() bool {
-	return com.IsDir(r.WikiPath())
+	return osutil.IsDir(r.WikiPath())
 }
 }
 
 
 // InitWiki initializes a wiki for repository,
 // InitWiki initializes a wiki for repository,
@@ -80,7 +81,7 @@ func (r *Repository) InitWiki() error {
 }
 }
 
 
 func (r *Repository) LocalWikiPath() string {
 func (r *Repository) LocalWikiPath() string {
-	return filepath.Join(conf.Server.AppDataPath, "tmp", "local-wiki", com.ToStr(r.ID))
+	return filepath.Join(conf.Server.AppDataPath, "tmp", "local-wiki", strconv.FormatInt(r.ID, 10))
 }
 }
 
 
 // UpdateLocalWiki makes sure the local copy of repository wiki is up-to-date.
 // UpdateLocalWiki makes sure the local copy of repository wiki is up-to-date.
@@ -95,8 +96,8 @@ func discardLocalWikiChanges(localPath string) error {
 
 
 // updateWikiPage adds new page to repository wiki.
 // updateWikiPage adds new page to repository wiki.
 func (r *Repository) updateWikiPage(doer *User, oldTitle, title, content, message string, isNew bool) error {
 func (r *Repository) updateWikiPage(doer *User, oldTitle, title, content, message string, isNew bool) error {
-	wikiWorkingPool.CheckIn(com.ToStr(r.ID))
-	defer wikiWorkingPool.CheckOut(com.ToStr(r.ID))
+	wikiWorkingPool.CheckIn(strconv.FormatInt(r.ID, 10))
+	defer wikiWorkingPool.CheckOut(strconv.FormatInt(r.ID, 10))
 
 
 	if err := r.InitWiki(); err != nil {
 	if err := r.InitWiki(); err != nil {
 		return errors.Newf("InitWiki: %v", err)
 		return errors.Newf("InitWiki: %v", err)
@@ -114,7 +115,7 @@ func (r *Repository) updateWikiPage(doer *User, oldTitle, title, content, messag
 
 
 	// If not a new file, show perform update not create.
 	// If not a new file, show perform update not create.
 	if isNew {
 	if isNew {
-		if com.IsExist(filename) {
+		if osutil.Exist(filename) {
 			return ErrWikiAlreadyExist{filename}
 			return ErrWikiAlreadyExist{filename}
 		}
 		}
 	} else {
 	} else {
@@ -167,8 +168,8 @@ func (r *Repository) EditWikiPage(doer *User, oldTitle, title, content, message
 }
 }
 
 
 func (r *Repository) DeleteWikiPage(doer *User, title string) (err error) {
 func (r *Repository) DeleteWikiPage(doer *User, title string) (err error) {
-	wikiWorkingPool.CheckIn(com.ToStr(r.ID))
-	defer wikiWorkingPool.CheckOut(com.ToStr(r.ID))
+	wikiWorkingPool.CheckIn(strconv.FormatInt(r.ID, 10))
+	defer wikiWorkingPool.CheckOut(strconv.FormatInt(r.ID, 10))
 
 
 	localPath := r.LocalWikiPath()
 	localPath := r.LocalWikiPath()
 	if err = discardLocalWikiChanges(localPath); err != nil {
 	if err = discardLocalWikiChanges(localPath); err != nil {

+ 2 - 2
internal/form/repo.go

@@ -5,12 +5,12 @@ import (
 	"strings"
 	"strings"
 
 
 	"github.com/go-macaron/binding"
 	"github.com/go-macaron/binding"
-	"github.com/unknwon/com"
 	"gopkg.in/macaron.v1"
 	"gopkg.in/macaron.v1"
 
 
 	"gogs.io/gogs/internal/conf"
 	"gogs.io/gogs/internal/conf"
 	"gogs.io/gogs/internal/database"
 	"gogs.io/gogs/internal/database"
 	"gogs.io/gogs/internal/netutil"
 	"gogs.io/gogs/internal/netutil"
+	"gogs.io/gogs/internal/osutil"
 )
 )
 
 
 // _______________________________________    _________.______________________ _______________.___.
 // _______________________________________    _________.______________________ _______________.___.
@@ -82,7 +82,7 @@ func (f MigrateRepo) ParseRemoteAddr(user *database.User) (string, error) {
 		remoteAddr = u.String()
 		remoteAddr = u.String()
 	} else if !user.CanImportLocal() {
 	} else if !user.CanImportLocal() {
 		return "", database.ErrInvalidCloneAddr{IsPermissionDenied: true}
 		return "", database.ErrInvalidCloneAddr{IsPermissionDenied: true}
-	} else if !com.IsDir(remoteAddr) {
+	} else if !osutil.IsDir(remoteAddr) {
 		return "", database.ErrInvalidCloneAddr{IsInvalidPath: true}
 		return "", database.ErrInvalidCloneAddr{IsInvalidPath: true}
 	}
 	}
 
 

+ 47 - 0
internal/ioutil/ioutil.go

@@ -0,0 +1,47 @@
+package ioutil
+
+import (
+	"io"
+	"os"
+
+	"github.com/cockroachdb/errors"
+)
+
+// CopyFile copies the file at src to dst, preserving file mode and
+// modification time.
+func CopyFile(src, dst string) error {
+	si, err := os.Stat(src)
+	if err != nil {
+		return errors.Wrap(err, "stat source")
+	}
+
+	in, err := os.Open(src)
+	if err != nil {
+		return errors.Wrap(err, "open source")
+	}
+	defer in.Close()
+
+	out, err := os.Create(dst)
+	if err != nil {
+		return errors.Wrap(err, "create target")
+	}
+	defer out.Close()
+
+	if _, err = io.Copy(out, in); err != nil {
+		return errors.Wrap(err, "copy")
+	}
+
+	if err = out.Sync(); err != nil {
+		return errors.Wrap(err, "sync target")
+	}
+
+	if err = os.Chmod(dst, si.Mode()); err != nil {
+		return errors.Wrap(err, "chmod target")
+	}
+
+	if err = os.Chtimes(dst, si.ModTime(), si.ModTime()); err != nil {
+		return errors.Wrap(err, "chtimes target")
+	}
+
+	return nil
+}

+ 5 - 3
internal/markup/markup.go

@@ -4,6 +4,8 @@ import (
 	"bytes"
 	"bytes"
 	"fmt"
 	"fmt"
 	"io"
 	"io"
+	"slices"
+	"strconv"
 	"strings"
 	"strings"
 
 
 	"github.com/unknwon/com"
 	"github.com/unknwon/com"
@@ -138,7 +140,7 @@ func RenderCrossReferenceIssueIndexPattern(rawBytes []byte, _ string, _ map[stri
 // RenderSha1CurrentPattern renders SHA1 strings to corresponding links that assumes in the same repository.
 // RenderSha1CurrentPattern renders SHA1 strings to corresponding links that assumes in the same repository.
 func RenderSha1CurrentPattern(rawBytes []byte, urlPrefix string) []byte {
 func RenderSha1CurrentPattern(rawBytes []byte, urlPrefix string) []byte {
 	return []byte(Sha1CurrentPattern.ReplaceAllStringFunc(string(rawBytes), func(m string) string {
 	return []byte(Sha1CurrentPattern.ReplaceAllStringFunc(string(rawBytes), func(m string) string {
-		if com.StrTo(m).MustInt() > 0 {
+		if v, _ := strconv.Atoi(m); v > 0 {
 			return m
 			return m
 		}
 		}
 
 
@@ -264,7 +266,7 @@ outerLoop:
 					buf.WriteString(token.String())
 					buf.WriteString(token.String())
 
 
 					// Stack number doesn't increase for tags without end tags.
 					// Stack number doesn't increase for tags without end tags.
-					if token.Type == html.StartTagToken && !com.IsSliceContainsStr(noEndTags, token.Data) {
+					if token.Type == html.StartTagToken && !slices.Contains(noEndTags, token.Data) {
 						stackNum++
 						stackNum++
 					}
 					}
 
 
@@ -279,7 +281,7 @@ outerLoop:
 				continue outerLoop
 				continue outerLoop
 			}
 			}
 
 
-			if !com.IsSliceContainsStr(noEndTags, tagName) {
+			if !slices.Contains(noEndTags, tagName) {
 				startTags = append(startTags, tagName)
 				startTags = append(startTags, tagName)
 			}
 			}
 
 

+ 2 - 2
internal/route/admin/auths.go

@@ -3,9 +3,9 @@ package admin
 import (
 import (
 	"fmt"
 	"fmt"
 	"net/http"
 	"net/http"
+	"strconv"
 	"strings"
 	"strings"
 
 
-	"github.com/unknwon/com"
 	log "unknwon.dev/clog/v2"
 	log "unknwon.dev/clog/v2"
 
 
 	"gogs.io/gogs/internal/auth"
 	"gogs.io/gogs/internal/auth"
@@ -269,7 +269,7 @@ func EditAuthSourcePost(c *context.Context, f form.Authentication) {
 	log.Trace("Authentication changed by admin '%s': %d", c.User.Name, source.ID)
 	log.Trace("Authentication changed by admin '%s': %d", c.User.Name, source.ID)
 
 
 	c.Flash.Success(c.Tr("admin.auths.update_success"))
 	c.Flash.Success(c.Tr("admin.auths.update_success"))
-	c.Redirect(conf.Server.Subpath + "/admin/auths/" + com.ToStr(f.ID))
+	c.Redirect(conf.Server.Subpath + "/admin/auths/" + strconv.FormatInt(f.ID, 10))
 }
 }
 
 
 func DeleteAuthSource(c *context.Context) {
 func DeleteAuthSource(c *context.Context) {

+ 2 - 2
internal/route/admin/notice.go

@@ -2,8 +2,8 @@ package admin
 
 
 import (
 import (
 	"net/http"
 	"net/http"
+	"strconv"
 
 
-	"github.com/unknwon/com"
 	"github.com/unknwon/paginater"
 	"github.com/unknwon/paginater"
 	log "unknwon.dev/clog/v2"
 	log "unknwon.dev/clog/v2"
 
 
@@ -40,7 +40,7 @@ func DeleteNotices(c *context.Context) {
 	strs := c.QueryStrings("ids[]")
 	strs := c.QueryStrings("ids[]")
 	ids := make([]int64, 0, len(strs))
 	ids := make([]int64, 0, len(strs))
 	for i := range strs {
 	for i := range strs {
-		id := com.StrTo(strs[i]).MustInt64()
+		id, _ := strconv.ParseInt(strs[i], 10, 64)
 		if id > 0 {
 		if id > 0 {
 			ids = append(ids, id)
 			ids = append(ids, id)
 		}
 		}

+ 3 - 4
internal/route/api/v1/convert/convert.go

@@ -3,8 +3,7 @@ package convert
 import (
 import (
 	"context"
 	"context"
 	"fmt"
 	"fmt"
-
-	"github.com/unknwon/com"
+	"strconv"
 
 
 	"github.com/gogs/git-module"
 	"github.com/gogs/git-module"
 	api "github.com/gogs/go-gogs-client"
 	api "github.com/gogs/go-gogs-client"
@@ -72,7 +71,7 @@ func ToPublicKey(apiLink string, key *database.PublicKey) *api.PublicKey {
 	return &api.PublicKey{
 	return &api.PublicKey{
 		ID:      key.ID,
 		ID:      key.ID,
 		Key:     key.Content,
 		Key:     key.Content,
-		URL:     apiLink + com.ToStr(key.ID),
+		URL:     apiLink + strconv.FormatInt(key.ID, 10),
 		Title:   key.Name,
 		Title:   key.Name,
 		Created: key.Created,
 		Created: key.Created,
 	}
 	}
@@ -107,7 +106,7 @@ func ToDeployKey(apiLink string, key *database.DeployKey) *api.DeployKey {
 	return &api.DeployKey{
 	return &api.DeployKey{
 		ID:       key.ID,
 		ID:       key.ID,
 		Key:      key.Content,
 		Key:      key.Content,
-		URL:      apiLink + com.ToStr(key.ID),
+		URL:      apiLink + strconv.FormatInt(key.ID, 10),
 		Title:    key.Name,
 		Title:    key.Name,
 		Created:  key.Created,
 		Created:  key.Created,
 		ReadOnly: true, // All deploy keys are read-only.
 		ReadOnly: true, // All deploy keys are read-only.

+ 17 - 17
internal/route/api/v1/repo/hook.go

@@ -2,10 +2,10 @@ package repo
 
 
 import (
 import (
 	"net/http"
 	"net/http"
+	"slices"
 
 
 	"github.com/cockroachdb/errors"
 	"github.com/cockroachdb/errors"
 	jsoniter "github.com/json-iterator/go"
 	jsoniter "github.com/json-iterator/go"
-	"github.com/unknwon/com"
 
 
 	api "github.com/gogs/go-gogs-client"
 	api "github.com/gogs/go-gogs-client"
 
 
@@ -57,14 +57,14 @@ func CreateHook(c *context.APIContext, form api.CreateHookOption) {
 		HookEvent: &database.HookEvent{
 		HookEvent: &database.HookEvent{
 			ChooseEvents: true,
 			ChooseEvents: true,
 			HookEvents: database.HookEvents{
 			HookEvents: database.HookEvents{
-				Create:       com.IsSliceContainsStr(form.Events, string(database.HookEventTypeCreate)),
-				Delete:       com.IsSliceContainsStr(form.Events, string(database.HookEventTypeDelete)),
-				Fork:         com.IsSliceContainsStr(form.Events, string(database.HookEventTypeFork)),
-				Push:         com.IsSliceContainsStr(form.Events, string(database.HookEventTypePush)),
-				Issues:       com.IsSliceContainsStr(form.Events, string(database.HookEventTypeIssues)),
-				IssueComment: com.IsSliceContainsStr(form.Events, string(database.HookEventTypeIssueComment)),
-				PullRequest:  com.IsSliceContainsStr(form.Events, string(database.HookEventTypePullRequest)),
-				Release:      com.IsSliceContainsStr(form.Events, string(database.HookEventTypeRelease)),
+				Create:       slices.Contains(form.Events, string(database.HookEventTypeCreate)),
+				Delete:       slices.Contains(form.Events, string(database.HookEventTypeDelete)),
+				Fork:         slices.Contains(form.Events, string(database.HookEventTypeFork)),
+				Push:         slices.Contains(form.Events, string(database.HookEventTypePush)),
+				Issues:       slices.Contains(form.Events, string(database.HookEventTypeIssues)),
+				IssueComment: slices.Contains(form.Events, string(database.HookEventTypeIssueComment)),
+				PullRequest:  slices.Contains(form.Events, string(database.HookEventTypePullRequest)),
+				Release:      slices.Contains(form.Events, string(database.HookEventTypeRelease)),
 			},
 			},
 		},
 		},
 		IsActive:     form.Active,
 		IsActive:     form.Active,
@@ -144,14 +144,14 @@ func EditHook(c *context.APIContext, form api.EditHookOption) {
 	w.PushOnly = false
 	w.PushOnly = false
 	w.SendEverything = false
 	w.SendEverything = false
 	w.ChooseEvents = true
 	w.ChooseEvents = true
-	w.Create = com.IsSliceContainsStr(form.Events, string(database.HookEventTypeCreate))
-	w.Delete = com.IsSliceContainsStr(form.Events, string(database.HookEventTypeDelete))
-	w.Fork = com.IsSliceContainsStr(form.Events, string(database.HookEventTypeFork))
-	w.Push = com.IsSliceContainsStr(form.Events, string(database.HookEventTypePush))
-	w.Issues = com.IsSliceContainsStr(form.Events, string(database.HookEventTypeIssues))
-	w.IssueComment = com.IsSliceContainsStr(form.Events, string(database.HookEventTypeIssueComment))
-	w.PullRequest = com.IsSliceContainsStr(form.Events, string(database.HookEventTypePullRequest))
-	w.Release = com.IsSliceContainsStr(form.Events, string(database.HookEventTypeRelease))
+	w.Create = slices.Contains(form.Events, string(database.HookEventTypeCreate))
+	w.Delete = slices.Contains(form.Events, string(database.HookEventTypeDelete))
+	w.Fork = slices.Contains(form.Events, string(database.HookEventTypeFork))
+	w.Push = slices.Contains(form.Events, string(database.HookEventTypePush))
+	w.Issues = slices.Contains(form.Events, string(database.HookEventTypeIssues))
+	w.IssueComment = slices.Contains(form.Events, string(database.HookEventTypeIssueComment))
+	w.PullRequest = slices.Contains(form.Events, string(database.HookEventTypePullRequest))
+	w.Release = slices.Contains(form.Events, string(database.HookEventTypeRelease))
 	if err = w.UpdateEvent(); err != nil {
 	if err = w.UpdateEvent(); err != nil {
 		c.Errorf(err, "update event")
 		c.Errorf(err, "update event")
 		return
 		return

+ 2 - 2
internal/route/api/v1/repo/label.go

@@ -2,9 +2,9 @@ package repo
 
 
 import (
 import (
 	"net/http"
 	"net/http"
+	"strconv"
 
 
 	api "github.com/gogs/go-gogs-client"
 	api "github.com/gogs/go-gogs-client"
-	"github.com/unknwon/com"
 
 
 	"gogs.io/gogs/internal/context"
 	"gogs.io/gogs/internal/context"
 	"gogs.io/gogs/internal/database"
 	"gogs.io/gogs/internal/database"
@@ -28,7 +28,7 @@ func GetLabel(c *context.APIContext) {
 	var label *database.Label
 	var label *database.Label
 	var err error
 	var err error
 	idStr := c.Params(":id")
 	idStr := c.Params(":id")
-	if id := com.StrTo(idStr).MustInt64(); id > 0 {
+	if id, _ := strconv.ParseInt(idStr, 10, 64); id > 0 {
 		label, err = database.GetLabelOfRepoByID(c.Repo.Repository.ID, id)
 		label, err = database.GetLabelOfRepoByID(c.Repo.Repository.ID, id)
 	} else {
 	} else {
 		label, err = database.GetLabelOfRepoByName(c.Repo.Repository.ID, idStr)
 		label, err = database.GetLabelOfRepoByName(c.Repo.Repository.ID, idStr)

+ 11 - 11
internal/route/install.go

@@ -6,11 +6,11 @@ import (
 	"os"
 	"os"
 	"os/exec"
 	"os/exec"
 	"path/filepath"
 	"path/filepath"
+	"strconv"
 	"strings"
 	"strings"
 
 
 	"github.com/cockroachdb/errors"
 	"github.com/cockroachdb/errors"
 	"github.com/gogs/git-module"
 	"github.com/gogs/git-module"
-	"github.com/unknwon/com"
 	"gopkg.in/ini.v1"
 	"gopkg.in/ini.v1"
 	"gopkg.in/macaron.v1"
 	"gopkg.in/macaron.v1"
 	log "unknwon.dev/clog/v2"
 	log "unknwon.dev/clog/v2"
@@ -330,8 +330,8 @@ func InstallPost(c *context.Context, f form.Install) {
 		cfg.Section("server").Key("DISABLE_SSH").SetValue("true")
 		cfg.Section("server").Key("DISABLE_SSH").SetValue("true")
 	} else {
 	} else {
 		cfg.Section("server").Key("DISABLE_SSH").SetValue("false")
 		cfg.Section("server").Key("DISABLE_SSH").SetValue("false")
-		cfg.Section("server").Key("SSH_PORT").SetValue(com.ToStr(f.SSHPort))
-		cfg.Section("server").Key("START_SSH_SERVER").SetValue(com.ToStr(f.UseBuiltinSSHServer))
+		cfg.Section("server").Key("SSH_PORT").SetValue(strconv.Itoa(f.SSHPort))
+		cfg.Section("server").Key("START_SSH_SERVER").SetValue(strconv.FormatBool(f.UseBuiltinSSHServer))
 	}
 	}
 
 
 	if len(strings.TrimSpace(f.SMTPHost)) > 0 {
 	if len(strings.TrimSpace(f.SMTPHost)) > 0 {
@@ -343,14 +343,14 @@ func InstallPost(c *context.Context, f form.Install) {
 	} else {
 	} else {
 		cfg.Section("email").Key("ENABLED").SetValue("false")
 		cfg.Section("email").Key("ENABLED").SetValue("false")
 	}
 	}
-	cfg.Section("server").Key("OFFLINE_MODE").SetValue(com.ToStr(f.OfflineMode))
-	cfg.Section("auth").Key("REQUIRE_EMAIL_CONFIRMATION").SetValue(com.ToStr(f.RegisterConfirm))
-	cfg.Section("auth").Key("DISABLE_REGISTRATION").SetValue(com.ToStr(f.DisableRegistration))
-	cfg.Section("auth").Key("ENABLE_REGISTRATION_CAPTCHA").SetValue(com.ToStr(f.EnableCaptcha))
-	cfg.Section("auth").Key("REQUIRE_SIGNIN_VIEW").SetValue(com.ToStr(f.RequireSignInView))
-	cfg.Section("user").Key("ENABLE_EMAIL_NOTIFICATION").SetValue(com.ToStr(f.MailNotify))
-	cfg.Section("picture").Key("DISABLE_GRAVATAR").SetValue(com.ToStr(f.DisableGravatar))
-	cfg.Section("picture").Key("ENABLE_FEDERATED_AVATAR").SetValue(com.ToStr(f.EnableFederatedAvatar))
+	cfg.Section("server").Key("OFFLINE_MODE").SetValue(strconv.FormatBool(f.OfflineMode))
+	cfg.Section("auth").Key("REQUIRE_EMAIL_CONFIRMATION").SetValue(strconv.FormatBool(f.RegisterConfirm))
+	cfg.Section("auth").Key("DISABLE_REGISTRATION").SetValue(strconv.FormatBool(f.DisableRegistration))
+	cfg.Section("auth").Key("ENABLE_REGISTRATION_CAPTCHA").SetValue(strconv.FormatBool(f.EnableCaptcha))
+	cfg.Section("auth").Key("REQUIRE_SIGNIN_VIEW").SetValue(strconv.FormatBool(f.RequireSignInView))
+	cfg.Section("user").Key("ENABLE_EMAIL_NOTIFICATION").SetValue(strconv.FormatBool(f.MailNotify))
+	cfg.Section("picture").Key("DISABLE_GRAVATAR").SetValue(strconv.FormatBool(f.DisableGravatar))
+	cfg.Section("picture").Key("ENABLE_FEDERATED_AVATAR").SetValue(strconv.FormatBool(f.EnableFederatedAvatar))
 
 
 	cfg.Section("").Key("RUN_MODE").SetValue("prod")
 	cfg.Section("").Key("RUN_MODE").SetValue("prod")
 
 

+ 3 - 2
internal/route/org/members.go

@@ -1,7 +1,8 @@
 package org
 package org
 
 
 import (
 import (
-	"github.com/unknwon/com"
+	"strconv"
+
 	log "unknwon.dev/clog/v2"
 	log "unknwon.dev/clog/v2"
 
 
 	"gogs.io/gogs/internal/conf"
 	"gogs.io/gogs/internal/conf"
@@ -29,7 +30,7 @@ func Members(c *context.Context) {
 }
 }
 
 
 func MembersAction(c *context.Context) {
 func MembersAction(c *context.Context) {
-	uid := com.StrTo(c.Query("uid")).MustInt64()
+	uid, _ := strconv.ParseInt(c.Query("uid"), 10, 64)
 	if uid == 0 {
 	if uid == 0 {
 		c.Redirect(c.Org.OrgLink + "/members")
 		c.Redirect(c.Org.OrgLink + "/members")
 		return
 		return

+ 4 - 3
internal/route/org/teams.go

@@ -3,8 +3,8 @@ package org
 import (
 import (
 	"net/http"
 	"net/http"
 	"path"
 	"path"
+	"strconv"
 
 
-	"github.com/unknwon/com"
 	log "unknwon.dev/clog/v2"
 	log "unknwon.dev/clog/v2"
 
 
 	"gogs.io/gogs/internal/context"
 	"gogs.io/gogs/internal/context"
@@ -36,7 +36,7 @@ func Teams(c *context.Context) {
 }
 }
 
 
 func TeamsAction(c *context.Context) {
 func TeamsAction(c *context.Context) {
-	uid := com.StrTo(c.Query("uid")).MustInt64()
+	uid, _ := strconv.ParseInt(c.Query("uid"), 10, 64)
 	if uid == 0 {
 	if uid == 0 {
 		c.Redirect(c.Org.OrgLink + "/teams")
 		c.Redirect(c.Org.OrgLink + "/teams")
 		return
 		return
@@ -127,7 +127,8 @@ func TeamsRepoAction(c *context.Context) {
 		}
 		}
 		err = c.Org.Team.AddRepository(repo)
 		err = c.Org.Team.AddRepository(repo)
 	case "remove":
 	case "remove":
-		err = c.Org.Team.RemoveRepository(com.StrTo(c.Query("repoid")).MustInt64())
+		repoID, _ := strconv.ParseInt(c.Query("repoid"), 10, 64)
+		err = c.Org.Team.RemoveRepository(repoID)
 	}
 	}
 
 
 	if err != nil {
 	if err != nil {

+ 4 - 3
internal/route/repo/issue.go

@@ -5,11 +5,11 @@ import (
 	"net/http"
 	"net/http"
 	"net/url"
 	"net/url"
 	"slices"
 	"slices"
+	"strconv"
 	"strings"
 	"strings"
 	"time"
 	"time"
 
 
 	"github.com/cockroachdb/errors"
 	"github.com/cockroachdb/errors"
-	"github.com/unknwon/com"
 	"github.com/unknwon/paginater"
 	"github.com/unknwon/paginater"
 	log "unknwon.dev/clog/v2"
 	log "unknwon.dev/clog/v2"
 
 
@@ -103,7 +103,7 @@ func issues(c *context.Context, isPullList bool) {
 	viewType := c.Query("type")
 	viewType := c.Query("type")
 	sortType := c.Query("sort")
 	sortType := c.Query("sort")
 	types := []string{"assigned", "created_by", "mentioned"}
 	types := []string{"assigned", "created_by", "mentioned"}
-	if !com.IsSliceContainsStr(types, viewType) {
+	if !slices.Contains(types, viewType) {
 		viewType = "all"
 		viewType = "all"
 	}
 	}
 
 
@@ -221,7 +221,8 @@ func issues(c *context.Context, isPullList bool) {
 	}
 	}
 
 
 	c.Data["IssueStats"] = issueStats
 	c.Data["IssueStats"] = issueStats
-	c.Data["SelectLabels"] = com.StrTo(selectLabels).MustInt64()
+	selectLabelsInt, _ := strconv.ParseInt(selectLabels, 10, 64)
+	c.Data["SelectLabels"] = selectLabelsInt
 	c.Data["ViewType"] = viewType
 	c.Data["ViewType"] = viewType
 	c.Data["SortType"] = sortType
 	c.Data["SortType"] = sortType
 	c.Data["MilestoneID"] = milestoneID
 	c.Data["MilestoneID"] = milestoneID

+ 3 - 3
internal/route/repo/pull.go

@@ -3,10 +3,10 @@ package repo
 import (
 import (
 	"net/http"
 	"net/http"
 	"path"
 	"path"
+	"strconv"
 	"strings"
 	"strings"
 	"time"
 	"time"
 
 
-	"github.com/unknwon/com"
 	log "unknwon.dev/clog/v2"
 	log "unknwon.dev/clog/v2"
 
 
 	"github.com/gogs/git-module"
 	"github.com/gogs/git-module"
@@ -427,7 +427,7 @@ func MergePullRequest(c *context.Context) {
 	}
 	}
 
 
 	log.Trace("Pull request merged: %d", pr.ID)
 	log.Trace("Pull request merged: %d", pr.ID)
-	c.Redirect(c.Repo.RepoLink + "/pulls/" + com.ToStr(pr.Index))
+	c.Redirect(c.Repo.RepoLink + "/pulls/" + strconv.FormatInt(pr.Index, 10))
 }
 }
 
 
 func ParseCompareInfo(c *context.Context) (*database.User, *database.Repository, *git.Repository, *gitutil.PullRequestMeta, string, string) {
 func ParseCompareInfo(c *context.Context) (*database.User, *database.Repository, *git.Repository, *gitutil.PullRequestMeta, string, string) {
@@ -752,5 +752,5 @@ func CompareAndPullRequestPost(c *context.Context, f form.NewIssue) {
 	}
 	}
 
 
 	log.Trace("Pull request created: %d/%d", repo.ID, pullIssue.ID)
 	log.Trace("Pull request created: %d/%d", repo.ID, pullIssue.ID)
-	c.Redirect(c.Repo.RepoLink + "/pulls/" + com.ToStr(pullIssue.Index))
+	c.Redirect(c.Repo.RepoLink + "/pulls/" + strconv.FormatInt(pullIssue.Index, 10))
 }
 }

+ 3 - 3
internal/route/repo/repo.go

@@ -7,7 +7,6 @@ import (
 	"path/filepath"
 	"path/filepath"
 	"strings"
 	"strings"
 
 
-	"github.com/unknwon/com"
 	log "unknwon.dev/clog/v2"
 	log "unknwon.dev/clog/v2"
 
 
 	"github.com/gogs/git-module"
 	"github.com/gogs/git-module"
@@ -16,6 +15,7 @@ import (
 	"gogs.io/gogs/internal/context"
 	"gogs.io/gogs/internal/context"
 	"gogs.io/gogs/internal/database"
 	"gogs.io/gogs/internal/database"
 	"gogs.io/gogs/internal/form"
 	"gogs.io/gogs/internal/form"
+	"gogs.io/gogs/internal/osutil"
 	"gogs.io/gogs/internal/tool"
 	"gogs.io/gogs/internal/tool"
 	"gogs.io/gogs/internal/urlutil"
 	"gogs.io/gogs/internal/urlutil"
 )
 )
@@ -292,7 +292,7 @@ func Download(c *context.Context) {
 	}
 	}
 	refName = strings.TrimSuffix(uri, ext)
 	refName = strings.TrimSuffix(uri, ext)
 
 
-	if !com.IsDir(archivePath) {
+	if !osutil.IsDir(archivePath) {
 		if err := os.MkdirAll(archivePath, os.ModePerm); err != nil {
 		if err := os.MkdirAll(archivePath, os.ModePerm); err != nil {
 			c.Error(err, "create archive directory")
 			c.Error(err, "create archive directory")
 			return
 			return
@@ -329,7 +329,7 @@ func Download(c *context.Context) {
 	}
 	}
 
 
 	archivePath = path.Join(archivePath, tool.ShortSHA1(commit.ID.String())+ext)
 	archivePath = path.Join(archivePath, tool.ShortSHA1(commit.ID.String())+ext)
-	if !com.IsFile(archivePath) {
+	if !osutil.IsFile(archivePath) {
 		if err := commit.CreateArchive(archiveFormat, archivePath); err != nil {
 		if err := commit.CreateArchive(archiveFormat, archivePath); err != nil {
 			c.Error(err, "creates archive")
 			c.Error(err, "creates archive")
 			return
 			return

+ 1 - 2
internal/route/repo/setting.go

@@ -10,7 +10,6 @@ import (
 
 
 	"github.com/cockroachdb/errors"
 	"github.com/cockroachdb/errors"
 	"github.com/gogs/git-module"
 	"github.com/gogs/git-module"
-	"github.com/unknwon/com"
 	log "unknwon.dev/clog/v2"
 	log "unknwon.dev/clog/v2"
 
 
 	"gogs.io/gogs/internal/conf"
 	"gogs.io/gogs/internal/conf"
@@ -345,7 +344,7 @@ func UpdateAvatarSetting(c *context.Context, f form.Avatar, ctxRepo *database.Re
 		}
 		}
 	} else {
 	} else {
 		// No avatar is uploaded and reset setting back.
 		// No avatar is uploaded and reset setting back.
-		if !com.IsFile(ctxRepo.CustomAvatarPath()) {
+		if !osutil.IsFile(ctxRepo.CustomAvatarPath()) {
 			ctxRepo.UseCustomAvatar = false
 			ctxRepo.UseCustomAvatar = false
 		}
 		}
 	}
 	}

+ 3 - 3
internal/route/user/auth.go

@@ -5,10 +5,10 @@ import (
 	"encoding/hex"
 	"encoding/hex"
 	"net/http"
 	"net/http"
 	"net/url"
 	"net/url"
+	"strconv"
 
 
 	"github.com/cockroachdb/errors"
 	"github.com/cockroachdb/errors"
 	"github.com/go-macaron/captcha"
 	"github.com/go-macaron/captcha"
-	"github.com/unknwon/com"
 	log "unknwon.dev/clog/v2"
 	log "unknwon.dev/clog/v2"
 
 
 	"gogs.io/gogs/internal/auth"
 	"gogs.io/gogs/internal/auth"
@@ -427,7 +427,7 @@ func verifyUserActiveCode(code string) (user *database.User) {
 	if user = parseUserFromCode(code); user != nil {
 	if user = parseUserFromCode(code); user != nil {
 		// time limit code
 		// time limit code
 		prefix := code[:tool.TimeLimitCodeLength]
 		prefix := code[:tool.TimeLimitCodeLength]
-		data := com.ToStr(user.ID) + user.Email + user.LowerName + user.Password + user.Rands
+		data := strconv.FormatInt(user.ID, 10) + user.Email + user.LowerName + user.Password + user.Rands
 
 
 		if tool.VerifyTimeLimitCode(data, minutes, prefix) {
 		if tool.VerifyTimeLimitCode(data, minutes, prefix) {
 			return user
 			return user
@@ -443,7 +443,7 @@ func verifyActiveEmailCode(code, email string) *database.EmailAddress {
 	if user := parseUserFromCode(code); user != nil {
 	if user := parseUserFromCode(code); user != nil {
 		// time limit code
 		// time limit code
 		prefix := code[:tool.TimeLimitCodeLength]
 		prefix := code[:tool.TimeLimitCodeLength]
-		data := com.ToStr(user.ID) + email + user.LowerName + user.Password + user.Rands
+		data := strconv.FormatInt(user.ID, 10) + email + user.LowerName + user.Password + user.Rands
 
 
 		if tool.VerifyTimeLimitCode(data, minutes, prefix) {
 		if tool.VerifyTimeLimitCode(data, minutes, prefix) {
 			emailAddress, err := database.Handle.Users().GetEmail(gocontext.TODO(), user.ID, email, false)
 			emailAddress, err := database.Handle.Users().GetEmail(gocontext.TODO(), user.ID, email, false)

+ 2 - 2
internal/route/user/home.go

@@ -4,8 +4,8 @@ import (
 	"bytes"
 	"bytes"
 	"fmt"
 	"fmt"
 	"net/http"
 	"net/http"
+	"slices"
 
 
-	"github.com/unknwon/com"
 	"github.com/unknwon/paginater"
 	"github.com/unknwon/paginater"
 
 
 	"gogs.io/gogs/internal/conf"
 	"gogs.io/gogs/internal/conf"
@@ -210,7 +210,7 @@ func Issues(c *context.Context) {
 			string(database.FilterModeAssign),
 			string(database.FilterModeAssign),
 			string(database.FilterModeCreate),
 			string(database.FilterModeCreate),
 		}
 		}
-		if !com.IsSliceContainsStr(types, viewType) {
+		if !slices.Contains(types, viewType) {
 			viewType = string(database.FilterModeYourRepos)
 			viewType = string(database.FilterModeYourRepos)
 		}
 		}
 		filterMode = database.FilterMode(viewType)
 		filterMode = database.FilterMode(viewType)

+ 3 - 3
internal/ssh/ssh.go

@@ -7,12 +7,12 @@ import (
 	"os"
 	"os"
 	"os/exec"
 	"os/exec"
 	"path/filepath"
 	"path/filepath"
+	"strconv"
 	"strings"
 	"strings"
 	"syscall"
 	"syscall"
 
 
 	"github.com/cockroachdb/errors"
 	"github.com/cockroachdb/errors"
 	"github.com/sourcegraph/run"
 	"github.com/sourcegraph/run"
-	"github.com/unknwon/com"
 	"golang.org/x/crypto/ssh"
 	"golang.org/x/crypto/ssh"
 	log "unknwon.dev/clog/v2"
 	log "unknwon.dev/clog/v2"
 
 
@@ -107,7 +107,7 @@ func handleServerConn(keyID string, chans <-chan ssh.NewChannel) {
 }
 }
 
 
 func listen(config *ssh.ServerConfig, host string, port int) {
 func listen(config *ssh.ServerConfig, host string, port int) {
-	listener, err := net.Listen("tcp", host+":"+com.ToStr(port))
+	listener, err := net.Listen("tcp", host+":"+strconv.Itoa(port))
 	if err != nil {
 	if err != nil {
 		log.Fatal("Failed to start SSH server: %v", err)
 		log.Fatal("Failed to start SSH server: %v", err)
 	}
 	}
@@ -158,7 +158,7 @@ func Listen(opts conf.SSHOpts, appDataPath string) {
 				}
 				}
 				return nil, err
 				return nil, err
 			}
 			}
-			return &ssh.Permissions{Extensions: map[string]string{"key-id": com.ToStr(pkey.ID)}}, nil
+			return &ssh.Permissions{Extensions: map[string]string{"key-id": strconv.FormatInt(pkey.ID, 10)}}, nil
 		},
 		},
 	}
 	}
 
 

+ 11 - 0
internal/strutil/strutil.go

@@ -3,6 +3,7 @@ package strutil
 import (
 import (
 	"crypto/rand"
 	"crypto/rand"
 	"math/big"
 	"math/big"
+	"strings"
 	"unicode"
 	"unicode"
 )
 )
 
 
@@ -59,3 +60,13 @@ func Truncate(str string, limit int) string {
 	}
 	}
 	return str[:limit]
 	return str[:limit]
 }
 }
+
+// ContainsFold reports whether s is within the slice, ignoring case.
+func ContainsFold(ss []string, s string) bool {
+	for _, v := range ss {
+		if strings.EqualFold(v, s) {
+			return true
+		}
+	}
+	return false
+}

+ 12 - 0
internal/strutil/strutil_test.go

@@ -131,3 +131,15 @@ func TestTruncate(t *testing.T) {
 		})
 		})
 	}
 	}
 }
 }
+
+func TestContainsFold(t *testing.T) {
+	ss := []string{"Alice", "Bob", "Charlie"}
+
+	assert.True(t, ContainsFold(ss, "alice"))
+	assert.True(t, ContainsFold(ss, "Alice"))
+	assert.True(t, ContainsFold(ss, "ALICE"))
+	assert.True(t, ContainsFold(ss, "bob"))
+	assert.False(t, ContainsFold(ss, "dave"))
+	assert.False(t, ContainsFold(nil, "alice"))
+	assert.False(t, ContainsFold([]string{}, "alice"))
+}

+ 4 - 4
internal/sync/unique_queue.go

@@ -1,7 +1,7 @@
 package sync
 package sync
 
 
 import (
 import (
-	"github.com/unknwon/com"
+	"fmt"
 )
 )
 
 
 // UniqueQueue is a queue which guarantees only one instance of same
 // UniqueQueue is a queue which guarantees only one instance of same
@@ -35,7 +35,7 @@ func (q *UniqueQueue) Queue() <-chan string {
 // Exist returns true if there is an instance with given identity
 // Exist returns true if there is an instance with given identity
 // exists in the queue.
 // exists in the queue.
 func (q *UniqueQueue) Exist(id any) bool {
 func (q *UniqueQueue) Exist(id any) bool {
-	return q.table.IsRunning(com.ToStr(id))
+	return q.table.IsRunning(fmt.Sprintf("%v", id))
 }
 }
 
 
 // AddFunc adds new instance to the queue with a custom runnable function,
 // AddFunc adds new instance to the queue with a custom runnable function,
@@ -45,7 +45,7 @@ func (q *UniqueQueue) AddFunc(id any, fn func()) {
 		return
 		return
 	}
 	}
 
 
-	idStr := com.ToStr(id)
+	idStr := fmt.Sprintf("%v", id)
 	q.table.Lock()
 	q.table.Lock()
 	q.table.pool[idStr] = true
 	q.table.pool[idStr] = true
 	if fn != nil {
 	if fn != nil {
@@ -62,5 +62,5 @@ func (q *UniqueQueue) Add(id any) {
 
 
 // Remove removes instance from the queue.
 // Remove removes instance from the queue.
 func (q *UniqueQueue) Remove(id any) {
 func (q *UniqueQueue) Remove(id any) {
-	q.table.Stop(com.ToStr(id))
+	q.table.Stop(fmt.Sprintf("%v", id))
 }
 }

+ 8 - 7
internal/tool/tool.go

@@ -6,12 +6,12 @@ import (
 	"encoding/hex"
 	"encoding/hex"
 	"fmt"
 	"fmt"
 	"html/template"
 	"html/template"
+	"strconv"
 	"strings"
 	"strings"
 	"time"
 	"time"
 	"unicode"
 	"unicode"
 	"unicode/utf8"
 	"unicode/utf8"
 
 
-	"github.com/unknwon/com"
 	"github.com/unknwon/i18n"
 	"github.com/unknwon/i18n"
 	log "unknwon.dev/clog/v2"
 	log "unknwon.dev/clog/v2"
 
 
@@ -67,7 +67,7 @@ func VerifyTimeLimitCode(data string, minutes int, code string) bool {
 	// split code
 	// split code
 	start := code[:12]
 	start := code[:12]
 	lives := code[12:18]
 	lives := code[12:18]
-	if d, err := com.StrTo(lives).Int(); err == nil {
+	if d, err := strconv.Atoi(lives); err == nil {
 		minutes = d
 		minutes = d
 	}
 	}
 
 
@@ -111,7 +111,7 @@ func CreateTimeLimitCode(data string, minutes int, startInf any) string {
 
 
 	// create sha1 encode string
 	// create sha1 encode string
 	sh := sha1.New()
 	sh := sha1.New()
-	_, _ = sh.Write([]byte(data + conf.Security.SecretKey + startStr + endStr + com.ToStr(minutes)))
+	_, _ = sh.Write([]byte(data + conf.Security.SecretKey + startStr + endStr + strconv.Itoa(minutes)))
 	encoded := hex.EncodeToString(sh.Sum(nil))
 	encoded := hex.EncodeToString(sh.Sum(nil))
 
 
 	code := fmt.Sprintf("%s%06d%s", startStr, minutes, encoded)
 	code := fmt.Sprintf("%s%06d%s", startStr, minutes, encoded)
@@ -148,9 +148,9 @@ func AvatarLink(email string) (url string) {
 // AppendAvatarSize appends avatar size query parameter to the URL in the correct format.
 // AppendAvatarSize appends avatar size query parameter to the URL in the correct format.
 func AppendAvatarSize(url string, size int) string {
 func AppendAvatarSize(url string, size int) string {
 	if strings.Contains(url, "?") {
 	if strings.Contains(url, "?") {
-		return url + "&s=" + com.ToStr(size)
+		return url + "&s=" + strconv.Itoa(size)
 	}
 	}
-	return url + "?s=" + com.ToStr(size)
+	return url + "?s=" + strconv.Itoa(size)
 }
 }
 
 
 // Seconds-based time units
 // Seconds-based time units
@@ -348,10 +348,11 @@ func Subtract(left, right any) any {
 }
 }
 
 
 // StringsToInt64s converts a slice of string to a slice of int64.
 // StringsToInt64s converts a slice of string to a slice of int64.
+// Invalid strings are converted to 0 (parse errors are silently ignored).
 func StringsToInt64s(strs []string) []int64 {
 func StringsToInt64s(strs []string) []int64 {
 	ints := make([]int64, len(strs))
 	ints := make([]int64, len(strs))
 	for i := range strs {
 	for i := range strs {
-		ints[i] = com.StrTo(strs[i]).MustInt64()
+		ints[i], _ = strconv.ParseInt(strs[i], 10, 64)
 	}
 	}
 	return ints
 	return ints
 }
 }
@@ -360,7 +361,7 @@ func StringsToInt64s(strs []string) []int64 {
 func Int64sToStrings(ints []int64) []string {
 func Int64sToStrings(ints []int64) []string {
 	strs := make([]string, len(ints))
 	strs := make([]string, len(ints))
 	for i := range ints {
 	for i := range ints {
-		strs[i] = com.ToStr(ints[i])
+		strs[i] = strconv.FormatInt(ints[i], 10)
 	}
 	}
 	return strs
 	return strs
 }
 }