mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2025-01-10 15:42:16 +01:00
[gitea] week 2025-02 cherry pick (gitea/main -> forgejo) (#6471)
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6471 Reviewed-by: Gusted <gusted@noreply.codeberg.org>
This commit is contained in:
commit
4261301dfb
34 changed files with 341 additions and 127 deletions
|
@ -69,6 +69,10 @@ var microcmdUserCreate = &cli.Command{
|
|||
}
|
||||
|
||||
func runCreateUser(c *cli.Context) error {
|
||||
// this command highly depends on the many setting options (create org, visibility, etc.), so it must have a full setting load first
|
||||
// duplicate setting loading should be safe at the moment, but it should be refactored & improved in the future.
|
||||
setting.LoadSettings()
|
||||
|
||||
if err := argsSet(c, "email"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -206,6 +206,7 @@ func innerNewMainApp(version, versionExtra string, subCmdsStandaloneArgs, subCmd
|
|||
app.Commands = append(app.Commands, subCmdWithConfig...)
|
||||
app.Commands = append(app.Commands, subCmdStandalone...)
|
||||
|
||||
setting.InitGiteaEnvVars()
|
||||
return app
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@ package cmd
|
|||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
@ -114,37 +113,17 @@ func TestCliCmd(t *testing.T) {
|
|||
_, _ = fmt.Fprint(ctx.App.Writer, makePathOutput(setting.AppWorkPath, setting.CustomPath, setting.CustomConf))
|
||||
return nil
|
||||
})
|
||||
var envBackup []string
|
||||
for _, s := range os.Environ() {
|
||||
if strings.HasPrefix(s, "GITEA_") && strings.Contains(s, "=") {
|
||||
envBackup = append(envBackup, s)
|
||||
}
|
||||
}
|
||||
clearGiteaEnv := func() {
|
||||
for _, s := range os.Environ() {
|
||||
if strings.HasPrefix(s, "GITEA_") {
|
||||
_ = os.Unsetenv(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
defer func() {
|
||||
clearGiteaEnv()
|
||||
for _, s := range envBackup {
|
||||
k, v, _ := strings.Cut(s, "=")
|
||||
_ = os.Setenv(k, v)
|
||||
}
|
||||
}()
|
||||
|
||||
for _, c := range cases {
|
||||
clearGiteaEnv()
|
||||
t.Run(c.cmd, func(t *testing.T) {
|
||||
for k, v := range c.env {
|
||||
_ = os.Setenv(k, v)
|
||||
t.Setenv(k, v)
|
||||
}
|
||||
args := strings.Split(c.cmd, " ") // for test only, "split" is good enough
|
||||
r, err := runTestApp(app, args...)
|
||||
require.NoError(t, err, c.cmd)
|
||||
assert.NotEmpty(t, c.exp, c.cmd)
|
||||
assert.Contains(t, r.Stdout, c.exp, c.cmd)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
11
cmd/web.go
11
cmd/web.go
|
@ -12,6 +12,7 @@ import (
|
|||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
_ "net/http/pprof" // Used for debugging if enabled and a web server is running
|
||||
|
||||
|
@ -115,6 +116,16 @@ func showWebStartupMessage(msg string) {
|
|||
log.Info("* CustomPath: %s", setting.CustomPath)
|
||||
log.Info("* ConfigFile: %s", setting.CustomConf)
|
||||
log.Info("%s", msg) // show startup message
|
||||
|
||||
if setting.CORSConfig.Enabled {
|
||||
log.Info("CORS Service Enabled")
|
||||
}
|
||||
if setting.DefaultUILocation != time.Local {
|
||||
log.Info("Default UI Location is %v", setting.DefaultUILocation.String())
|
||||
}
|
||||
if setting.MailService != nil {
|
||||
log.Info("Mail Service Enabled: RegisterEmailConfirm=%v, Service.EnableNotifyMail=%v", setting.Service.RegisterEmailConfirm, setting.Service.EnableNotifyMail)
|
||||
}
|
||||
}
|
||||
|
||||
func serveInstall(ctx *cli.Context) error {
|
||||
|
|
|
@ -194,6 +194,20 @@ func (t CommentType) HasMailReplySupport() bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (t CommentType) CountedAsConversation() bool {
|
||||
for _, ct := range ConversationCountedCommentType() {
|
||||
if t == ct {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ConversationCountedCommentType returns the comment types that are counted as a conversation
|
||||
func ConversationCountedCommentType() []CommentType {
|
||||
return []CommentType{CommentTypeComment, CommentTypeReview}
|
||||
}
|
||||
|
||||
// RoleInRepo presents the user's participation in the repo
|
||||
type RoleInRepo string
|
||||
|
||||
|
@ -887,7 +901,7 @@ func updateCommentInfos(ctx context.Context, opts *CreateCommentOptions, comment
|
|||
}
|
||||
fallthrough
|
||||
case CommentTypeComment:
|
||||
if _, err = db.Exec(ctx, "UPDATE `issue` SET num_comments=num_comments+1 WHERE id=?", opts.Issue.ID); err != nil {
|
||||
if err := UpdateIssueNumComments(ctx, opts.Issue.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
fallthrough
|
||||
|
@ -1182,8 +1196,8 @@ func DeleteComment(ctx context.Context, comment *Comment) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if comment.Type == CommentTypeComment {
|
||||
if _, err := e.ID(comment.IssueID).Decr("num_comments").Update(new(Issue)); err != nil {
|
||||
if comment.Type.CountedAsConversation() {
|
||||
if err := UpdateIssueNumComments(ctx, comment.IssueID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -1300,6 +1314,21 @@ func (c *Comment) HasOriginalAuthor() bool {
|
|||
return c.OriginalAuthor != "" && c.OriginalAuthorID != 0
|
||||
}
|
||||
|
||||
func UpdateIssueNumCommentsBuilder(issueID int64) *builder.Builder {
|
||||
subQuery := builder.Select("COUNT(*)").From("`comment`").Where(
|
||||
builder.Eq{"issue_id": issueID}.And(
|
||||
builder.In("`type`", ConversationCountedCommentType()),
|
||||
))
|
||||
|
||||
return builder.Update(builder.Eq{"num_comments": subQuery}).
|
||||
From("`issue`").Where(builder.Eq{"id": issueID})
|
||||
}
|
||||
|
||||
func UpdateIssueNumComments(ctx context.Context, issueID int64) error {
|
||||
_, err := db.GetEngine(ctx).Exec(UpdateIssueNumCommentsBuilder(issueID))
|
||||
return err
|
||||
}
|
||||
|
||||
// InsertIssueComments inserts many comments of issues.
|
||||
func InsertIssueComments(ctx context.Context, comments []*Comment) error {
|
||||
if len(comments) == 0 {
|
||||
|
@ -1332,8 +1361,7 @@ func InsertIssueComments(ctx context.Context, comments []*Comment) error {
|
|||
}
|
||||
|
||||
for _, issueID := range issueIDs {
|
||||
if _, err := db.Exec(ctx, "UPDATE issue set num_comments = (SELECT count(*) FROM comment WHERE issue_id = ? AND `type`=?) WHERE id = ?",
|
||||
issueID, CommentTypeComment, issueID); err != nil {
|
||||
if err := UpdateIssueNumComments(ctx, issueID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -125,3 +125,12 @@ func TestUpdateCommentsMigrationsByType(t *testing.T) {
|
|||
assert.Empty(t, comment.OriginalAuthorID)
|
||||
assert.EqualValues(t, 513, comment.PosterID)
|
||||
}
|
||||
|
||||
func Test_UpdateIssueNumComments(t *testing.T) {
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
issue2 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2})
|
||||
|
||||
require.NoError(t, issues_model.UpdateIssueNumComments(db.DefaultContext, issue2.ID))
|
||||
issue2 = unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2})
|
||||
assert.EqualValues(t, 1, issue2.NumComments)
|
||||
}
|
||||
|
|
|
@ -614,6 +614,10 @@ func InsertReviews(ctx context.Context, reviews []*Review) error {
|
|||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := UpdateIssueNumComments(ctx, review.IssueID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return committer.Commit()
|
||||
|
|
|
@ -126,6 +126,14 @@ func (p *Project) LoadRepo(ctx context.Context) (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
func ProjectLinkForOrg(org *user_model.User, projectID int64) string { //nolint
|
||||
return fmt.Sprintf("%s/-/projects/%d", org.HomeLink(), projectID)
|
||||
}
|
||||
|
||||
func ProjectLinkForRepo(repo *repo_model.Repository, projectID int64) string { //nolint
|
||||
return fmt.Sprintf("%s/projects/%d", repo.Link(), projectID)
|
||||
}
|
||||
|
||||
// Link returns the project's relative URL.
|
||||
func (p *Project) Link(ctx context.Context) string {
|
||||
if p.OwnerID > 0 {
|
||||
|
@ -134,7 +142,7 @@ func (p *Project) Link(ctx context.Context) string {
|
|||
log.Error("LoadOwner: %v", err)
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%s/-/projects/%d", p.Owner.HomeLink(), p.ID)
|
||||
return ProjectLinkForOrg(p.Owner, p.ID)
|
||||
}
|
||||
if p.RepoID > 0 {
|
||||
err := p.LoadRepo(ctx)
|
||||
|
@ -142,7 +150,7 @@ func (p *Project) Link(ctx context.Context) string {
|
|||
log.Error("LoadRepo: %v", err)
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%s/projects/%d", p.Repo.Link(), p.ID)
|
||||
return ProjectLinkForRepo(p.Repo, p.ID)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@ import (
|
|||
"code.gitea.io/gitea/models/unit"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
// Init initialize model
|
||||
|
@ -27,7 +29,7 @@ func Init(ctx context.Context) error {
|
|||
}
|
||||
|
||||
type repoChecker struct {
|
||||
querySQL func(ctx context.Context) ([]map[string][]byte, error)
|
||||
querySQL func(ctx context.Context) ([]int64, error)
|
||||
correctSQL func(ctx context.Context, id int64) error
|
||||
desc string
|
||||
}
|
||||
|
@ -38,8 +40,7 @@ func repoStatsCheck(ctx context.Context, checker *repoChecker) {
|
|||
log.Error("Select %s: %v", checker.desc, err)
|
||||
return
|
||||
}
|
||||
for _, result := range results {
|
||||
id, _ := strconv.ParseInt(string(result["id"]), 10, 64)
|
||||
for _, id := range results {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
log.Warn("CheckRepoStats: Cancelled before checking %s for with id=%d", checker.desc, id)
|
||||
|
@ -54,21 +55,23 @@ func repoStatsCheck(ctx context.Context, checker *repoChecker) {
|
|||
}
|
||||
}
|
||||
|
||||
func StatsCorrectSQL(ctx context.Context, sql string, id int64) error {
|
||||
_, err := db.GetEngine(ctx).Exec(sql, id, id)
|
||||
func StatsCorrectSQL(ctx context.Context, sql any, ids ...any) error {
|
||||
args := []any{sql}
|
||||
args = append(args, ids...)
|
||||
_, err := db.GetEngine(ctx).Exec(args...)
|
||||
return err
|
||||
}
|
||||
|
||||
func repoStatsCorrectNumWatches(ctx context.Context, id int64) error {
|
||||
return StatsCorrectSQL(ctx, "UPDATE `repository` SET num_watches=(SELECT COUNT(*) FROM `watch` WHERE repo_id=? AND mode<>2) WHERE id=?", id)
|
||||
return StatsCorrectSQL(ctx, "UPDATE `repository` SET num_watches=(SELECT COUNT(*) FROM `watch` WHERE repo_id=? AND mode<>2) WHERE id=?", id, id)
|
||||
}
|
||||
|
||||
func repoStatsCorrectNumStars(ctx context.Context, id int64) error {
|
||||
return StatsCorrectSQL(ctx, "UPDATE `repository` SET num_stars=(SELECT COUNT(*) FROM `star` WHERE repo_id=?) WHERE id=?", id)
|
||||
return StatsCorrectSQL(ctx, "UPDATE `repository` SET num_stars=(SELECT COUNT(*) FROM `star` WHERE repo_id=?) WHERE id=?", id, id)
|
||||
}
|
||||
|
||||
func labelStatsCorrectNumIssues(ctx context.Context, id int64) error {
|
||||
return StatsCorrectSQL(ctx, "UPDATE `label` SET num_issues=(SELECT COUNT(*) FROM `issue_label` WHERE label_id=?) WHERE id=?", id)
|
||||
return StatsCorrectSQL(ctx, "UPDATE `label` SET num_issues=(SELECT COUNT(*) FROM `issue_label` WHERE label_id=?) WHERE id=?", id, id)
|
||||
}
|
||||
|
||||
func labelStatsCorrectNumIssuesRepo(ctx context.Context, id int64) error {
|
||||
|
@ -105,11 +108,11 @@ func milestoneStatsCorrectNumIssuesRepo(ctx context.Context, id int64) error {
|
|||
}
|
||||
|
||||
func userStatsCorrectNumRepos(ctx context.Context, id int64) error {
|
||||
return StatsCorrectSQL(ctx, "UPDATE `user` SET num_repos=(SELECT COUNT(*) FROM `repository` WHERE owner_id=?) WHERE id=?", id)
|
||||
return StatsCorrectSQL(ctx, "UPDATE `user` SET num_repos=(SELECT COUNT(*) FROM `repository` WHERE owner_id=?) WHERE id=?", id, id)
|
||||
}
|
||||
|
||||
func repoStatsCorrectIssueNumComments(ctx context.Context, id int64) error {
|
||||
return StatsCorrectSQL(ctx, "UPDATE `issue` SET num_comments=(SELECT COUNT(*) FROM `comment` WHERE issue_id=? AND type=0) WHERE id=?", id)
|
||||
return StatsCorrectSQL(ctx, issues_model.UpdateIssueNumCommentsBuilder(id))
|
||||
}
|
||||
|
||||
func repoStatsCorrectNumIssues(ctx context.Context, id int64) error {
|
||||
|
@ -128,9 +131,12 @@ func repoStatsCorrectNumClosedPulls(ctx context.Context, id int64) error {
|
|||
return repo_model.UpdateRepoIssueNumbers(ctx, id, true, true)
|
||||
}
|
||||
|
||||
func statsQuery(args ...any) func(context.Context) ([]map[string][]byte, error) {
|
||||
return func(ctx context.Context) ([]map[string][]byte, error) {
|
||||
return db.GetEngine(ctx).Query(args...)
|
||||
// statsQuery returns a function that queries the database for a list of IDs
|
||||
// sql could be a string or a *builder.Builder
|
||||
func statsQuery(sql any, args ...any) func(context.Context) ([]int64, error) {
|
||||
return func(ctx context.Context) ([]int64, error) {
|
||||
var ids []int64
|
||||
return ids, db.GetEngine(ctx).SQL(sql, args...).Find(&ids)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -201,7 +207,16 @@ func CheckRepoStats(ctx context.Context) error {
|
|||
},
|
||||
// Issue.NumComments
|
||||
{
|
||||
statsQuery("SELECT `issue`.id FROM `issue` WHERE `issue`.num_comments!=(SELECT COUNT(*) FROM `comment` WHERE issue_id=`issue`.id AND type=0)"),
|
||||
statsQuery(builder.Select("`issue`.id").From("`issue`").Where(
|
||||
builder.Neq{
|
||||
"`issue`.num_comments": builder.Select("COUNT(*)").From("`comment`").Where(
|
||||
builder.Expr("issue_id = `issue`.id").And(
|
||||
builder.In("type", issues_model.ConversationCountedCommentType()),
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
repoStatsCorrectIssueNumComments,
|
||||
"issue count 'num_comments'",
|
||||
},
|
||||
|
|
|
@ -7,8 +7,10 @@ import (
|
|||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
@ -22,3 +24,16 @@ func TestDoctorUserStarNum(t *testing.T) {
|
|||
|
||||
require.NoError(t, DoctorUserStarNum(db.DefaultContext))
|
||||
}
|
||||
|
||||
func Test_repoStatsCorrectIssueNumComments(t *testing.T) {
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
issue2 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2})
|
||||
assert.NotNil(t, issue2)
|
||||
assert.EqualValues(t, 0, issue2.NumComments) // the fixture data is wrong, but we don't fix it here
|
||||
|
||||
require.NoError(t, repoStatsCorrectIssueNumComments(db.DefaultContext, 2))
|
||||
// reload the issue
|
||||
issue2 = unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2})
|
||||
assert.EqualValues(t, 1, issue2.NumComments)
|
||||
}
|
||||
|
|
|
@ -59,6 +59,13 @@ func InitSettings() {
|
|||
_ = hash.Register("dummy", hash.NewDummyHasher)
|
||||
|
||||
setting.PasswordHashAlgo, _ = hash.SetDefaultPasswordHashAlgorithm("dummy")
|
||||
setting.InitGiteaEnvVars()
|
||||
|
||||
// Avoid loading the git's system config.
|
||||
// On macOS, system config sets the osxkeychain credential helper, which will cause tests to freeze with a dialog.
|
||||
// But we do not set it in production at the moment, because it might be a "breaking" change,
|
||||
// more details are in "modules/git.commonBaseEnvs".
|
||||
_ = os.Setenv("GIT_CONFIG_NOSYSTEM", "true")
|
||||
}
|
||||
|
||||
// TestOptions represents test options
|
||||
|
|
|
@ -176,3 +176,14 @@ func (repo *Repository) LsTree(ref string, filenames ...string) ([]string, error
|
|||
|
||||
return filelist, err
|
||||
}
|
||||
|
||||
// GetTreePathLatestCommitID returns the latest commit of a tree path
|
||||
func (repo *Repository) GetTreePathLatestCommit(refName, treePath string) (*Commit, error) {
|
||||
stdout, _, err := NewCommand(repo.Ctx, "rev-list", "-1").
|
||||
AddDynamicArguments(refName).AddDashesAndList(treePath).
|
||||
RunStdString(&RunOpts{Dir: repo.Path})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return repo.GetCommit(strings.TrimSpace(stdout))
|
||||
}
|
||||
|
|
|
@ -26,3 +26,18 @@ func TestSubTree_Issue29101(t *testing.T) {
|
|||
assert.True(t, IsErrNotExist(err))
|
||||
}
|
||||
}
|
||||
|
||||
func Test_GetTreePathLatestCommit(t *testing.T) {
|
||||
repo, err := openRepositoryWithDefaultContext(filepath.Join(testReposDir, "repo6_blame"))
|
||||
require.NoError(t, err)
|
||||
defer repo.Close()
|
||||
|
||||
commitID, err := repo.GetBranchCommitID("master")
|
||||
require.NoError(t, err)
|
||||
assert.EqualValues(t, "544d8f7a3b15927cddf2299b4b562d6ebd71b6a7", commitID)
|
||||
|
||||
commit, err := repo.GetTreePathLatestCommit("master", "blame.txt")
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, commit)
|
||||
assert.EqualValues(t, "45fb6cbc12f970b04eacd5cd4165edd11c8d7376", commit.ID.String())
|
||||
}
|
||||
|
|
|
@ -168,3 +168,22 @@ func EnvironmentToConfig(cfg ConfigProvider, envs []string) (changed bool) {
|
|||
}
|
||||
return changed
|
||||
}
|
||||
|
||||
// InitGiteaEnvVars initializes the environment variables for gitea
|
||||
func InitGiteaEnvVars() {
|
||||
// Ideally Gitea should only accept the environment variables which it clearly knows instead of unsetting the ones it doesn't want,
|
||||
// but the ideal behavior would be a breaking change, and it seems not bringing enough benefits to end users,
|
||||
// so at the moment we could still keep "unsetting the unnecessary environments"
|
||||
|
||||
// HOME is managed by Gitea, Gitea's git should use "HOME/.gitconfig".
|
||||
// But git would try "XDG_CONFIG_HOME/git/config" first if "HOME/.gitconfig" does not exist,
|
||||
// then our git.InitFull would still write to "XDG_CONFIG_HOME/git/config" if XDG_CONFIG_HOME is set.
|
||||
_ = os.Unsetenv("XDG_CONFIG_HOME")
|
||||
|
||||
_ = os.Unsetenv("GIT_AUTHOR_NAME")
|
||||
_ = os.Unsetenv("GIT_AUTHOR_EMAIL")
|
||||
_ = os.Unsetenv("GIT_AUTHOR_DATE")
|
||||
_ = os.Unsetenv("GIT_COMMITTER_NAME")
|
||||
_ = os.Unsetenv("GIT_COMMITTER_EMAIL")
|
||||
_ = os.Unsetenv("GIT_COMMITTER_DATE")
|
||||
}
|
||||
|
|
|
@ -5,8 +5,6 @@ package setting
|
|||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
)
|
||||
|
||||
// CORSConfig defines CORS settings
|
||||
|
@ -28,7 +26,4 @@ var CORSConfig = struct {
|
|||
|
||||
func loadCorsFrom(rootCfg ConfigProvider) {
|
||||
mustMapSetting(rootCfg, "cors", &CORSConfig)
|
||||
if CORSConfig.Enabled {
|
||||
log.Info("CORS Service Enabled")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -109,7 +109,7 @@ func IndexerGlobFromString(globstr string) []Glob {
|
|||
expr = strings.TrimSpace(expr)
|
||||
if expr != "" {
|
||||
if g, err := glob.Compile(expr, '.', '/'); err != nil {
|
||||
log.Info("Invalid glob expression '%s' (skipped): %v", expr, err)
|
||||
log.Warn("Invalid glob expression '%s' (skipped): %v", expr, err)
|
||||
} else {
|
||||
extarr = append(extarr, Glob{glob: g, pattern: expr})
|
||||
}
|
||||
|
|
|
@ -263,8 +263,6 @@ func loadMailerFrom(rootCfg ConfigProvider) {
|
|||
MailService.OverrideEnvelopeFrom = true
|
||||
MailService.EnvelopeFrom = parsed.Address
|
||||
}
|
||||
|
||||
log.Info("Mail Service Enabled")
|
||||
}
|
||||
|
||||
func loadRegisterMailFrom(rootCfg ConfigProvider) {
|
||||
|
@ -275,7 +273,6 @@ func loadRegisterMailFrom(rootCfg ConfigProvider) {
|
|||
return
|
||||
}
|
||||
Service.RegisterEmailConfirm = true
|
||||
log.Info("Register Mail Service Enabled")
|
||||
}
|
||||
|
||||
func loadNotifyMailFrom(rootCfg ConfigProvider) {
|
||||
|
@ -286,7 +283,6 @@ func loadNotifyMailFrom(rootCfg ConfigProvider) {
|
|||
return
|
||||
}
|
||||
Service.EnableNotifyMail = true
|
||||
log.Info("Notify Mail Service Enabled")
|
||||
}
|
||||
|
||||
func tryResolveAddr(addr string) []net.IPAddr {
|
||||
|
|
|
@ -73,6 +73,4 @@ func loadSessionFrom(rootCfg ConfigProvider) {
|
|||
SessionConfig.ProviderConfig = string(shadowConfig)
|
||||
SessionConfig.OriginalProvider = SessionConfig.Provider
|
||||
SessionConfig.Provider = "VirtualSession"
|
||||
|
||||
log.Info("Session Service Enabled")
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ func loadTimeFrom(rootCfg ConfigProvider) {
|
|||
if err != nil {
|
||||
log.Fatal("Load time zone failed: %v", err)
|
||||
}
|
||||
log.Info("Default UI Location is %v", zone)
|
||||
}
|
||||
if DefaultUILocation == nil {
|
||||
DefaultUILocation = time.Local
|
||||
|
|
|
@ -3643,6 +3643,7 @@ versions = Versions
|
|||
versions.view_all = View all
|
||||
dependency.id = ID
|
||||
dependency.version = Version
|
||||
search_in_external_registry = Search in %s
|
||||
alpine.registry = Setup this registry by adding the url in your <code>/etc/apk/repositories</code> file:
|
||||
alpine.registry.key = Download the registry public RSA key into the <code>/etc/apk/keys/</code> folder to verify the index signature:
|
||||
alpine.registry.info = Choose $branch and $repository from the list below.
|
||||
|
|
1
release-notes/6471.md
Normal file
1
release-notes/6471.md
Normal file
|
@ -0,0 +1 @@
|
|||
feat: [commit](https://codeberg.org/forgejo/forgejo/commit/76a85d26c8576fc410dc6494f2907ffc2b353c39) Use `Project-URL` metadata field to get a PyPI package's homepage URL
|
|
@ -10,6 +10,7 @@ import (
|
|||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
packages_model "code.gitea.io/gitea/models/packages"
|
||||
packages_module "code.gitea.io/gitea/modules/packages"
|
||||
|
@ -139,9 +140,30 @@ func UploadPackageFile(ctx *context.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
projectURL := ctx.Req.FormValue("home_page")
|
||||
if !validation.IsValidURL(projectURL) {
|
||||
projectURL = ""
|
||||
// Ensure ctx.Req.Form exists.
|
||||
_ = ctx.Req.ParseForm()
|
||||
|
||||
var homepageURL string
|
||||
projectURLs := ctx.Req.Form["project_urls"]
|
||||
for _, purl := range projectURLs {
|
||||
label, url, found := strings.Cut(purl, ",")
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
if normalizeLabel(label) != "homepage" {
|
||||
continue
|
||||
}
|
||||
homepageURL = strings.TrimSpace(url)
|
||||
break
|
||||
}
|
||||
|
||||
if len(homepageURL) == 0 {
|
||||
// TODO: Home-page is a deprecated metadata field. Remove this branch once it's no longer apart of the spec.
|
||||
homepageURL = ctx.Req.FormValue("home_page")
|
||||
}
|
||||
|
||||
if !validation.IsValidURL(homepageURL) {
|
||||
homepageURL = ""
|
||||
}
|
||||
|
||||
_, _, err = packages_service.CreatePackageOrAddFileToExisting(
|
||||
|
@ -160,7 +182,7 @@ func UploadPackageFile(ctx *context.Context) {
|
|||
Description: ctx.Req.FormValue("description"),
|
||||
LongDescription: ctx.Req.FormValue("long_description"),
|
||||
Summary: ctx.Req.FormValue("summary"),
|
||||
ProjectURL: projectURL,
|
||||
ProjectURL: homepageURL,
|
||||
License: ctx.Req.FormValue("license"),
|
||||
RequiresPython: ctx.Req.FormValue("requires_python"),
|
||||
},
|
||||
|
@ -189,6 +211,23 @@ func UploadPackageFile(ctx *context.Context) {
|
|||
ctx.Status(http.StatusCreated)
|
||||
}
|
||||
|
||||
// Normalizes a Project-URL label.
|
||||
// See https://packaging.python.org/en/latest/specifications/well-known-project-urls/#label-normalization.
|
||||
func normalizeLabel(label string) string {
|
||||
var builder strings.Builder
|
||||
|
||||
// "A label is normalized by deleting all ASCII punctuation and whitespace, and then converting the result
|
||||
// to lowercase."
|
||||
for _, r := range label {
|
||||
if unicode.IsPunct(r) || unicode.IsSpace(r) {
|
||||
continue
|
||||
}
|
||||
builder.WriteRune(unicode.ToLower(r))
|
||||
}
|
||||
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
func isValidNameAndVersion(packageName, packageVersion string) bool {
|
||||
return nameMatcher.MatchString(packageName) && versionMatcher.MatchString(packageVersion)
|
||||
}
|
||||
|
|
|
@ -36,3 +36,13 @@ func TestIsValidNameAndVersion(t *testing.T) {
|
|||
assert.False(t, isValidNameAndVersion("test-name", "1.0.1aa"))
|
||||
assert.False(t, isValidNameAndVersion("test-name", "1.0.0-alpha.beta"))
|
||||
}
|
||||
|
||||
func TestNormalizeLabel(t *testing.T) {
|
||||
// Cases fetched from https://packaging.python.org/en/latest/specifications/well-known-project-urls/#label-normalization.
|
||||
assert.Equal(t, "homepage", normalizeLabel("Homepage"))
|
||||
assert.Equal(t, "homepage", normalizeLabel("Home-page"))
|
||||
assert.Equal(t, "homepage", normalizeLabel("Home page"))
|
||||
assert.Equal(t, "changelog", normalizeLabel("Change_Log"))
|
||||
assert.Equal(t, "whatsnew", normalizeLabel("What's New?"))
|
||||
assert.Equal(t, "github", normalizeLabel("github"))
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -247,19 +246,14 @@ func getBlobForEntry(ctx *context.APIContext) (blob *git.Blob, entry *git.TreeEn
|
|||
return nil, nil, nil
|
||||
}
|
||||
|
||||
info, _, err := git.Entries([]*git.TreeEntry{entry}).GetCommitsInfo(ctx, ctx.Repo.Commit, path.Dir("/" + ctx.Repo.TreePath)[1:])
|
||||
latestCommit, err := ctx.Repo.GitRepo.GetTreePathLatestCommit(ctx.Repo.Commit.ID.String(), ctx.Repo.TreePath)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetCommitsInfo", err)
|
||||
ctx.Error(http.StatusInternalServerError, "GetTreePathLatestCommit", err)
|
||||
return nil, nil, nil
|
||||
}
|
||||
when := &latestCommit.Committer.When
|
||||
|
||||
if len(info) == 1 {
|
||||
// Not Modified
|
||||
lastModified = &info[0].Commit.Committer.When
|
||||
}
|
||||
blob = entry.Blob()
|
||||
|
||||
return blob, entry, lastModified
|
||||
return entry.Blob(), entry, when
|
||||
}
|
||||
|
||||
// GetArchive get archive of a repository
|
||||
|
|
|
@ -209,7 +209,7 @@ func ChangeProjectStatus(ctx *context.Context) {
|
|||
ctx.NotFoundOrServerError("ChangeProjectStatusByRepoIDAndID", project_model.IsErrProjectNotExist, err)
|
||||
return
|
||||
}
|
||||
ctx.JSONRedirect(fmt.Sprintf("%s/-/projects/%d", ctx.ContextUser.HomeLink(), id))
|
||||
ctx.JSONRedirect(project_model.ProjectLinkForOrg(ctx.ContextUser, id))
|
||||
}
|
||||
|
||||
// DeleteProject delete a project
|
||||
|
@ -259,7 +259,7 @@ func RenderEditProject(ctx *context.Context) {
|
|||
ctx.Data["redirect"] = ctx.FormString("redirect")
|
||||
ctx.Data["HomeLink"] = ctx.ContextUser.HomeLink()
|
||||
ctx.Data["card_type"] = p.CardType
|
||||
ctx.Data["CancelLink"] = fmt.Sprintf("%s/-/projects/%d", ctx.ContextUser.HomeLink(), p.ID)
|
||||
ctx.Data["CancelLink"] = project_model.ProjectLinkForOrg(ctx.ContextUser, p.ID)
|
||||
|
||||
ctx.HTML(http.StatusOK, tplProjectsNew)
|
||||
}
|
||||
|
@ -273,7 +273,7 @@ func EditProjectPost(ctx *context.Context) {
|
|||
ctx.Data["PageIsViewProjects"] = true
|
||||
ctx.Data["CanWriteProjects"] = canWriteProjects(ctx)
|
||||
ctx.Data["CardTypes"] = project_model.GetCardConfig()
|
||||
ctx.Data["CancelLink"] = fmt.Sprintf("%s/-/projects/%d", ctx.ContextUser.HomeLink(), projectID)
|
||||
ctx.Data["CancelLink"] = project_model.ProjectLinkForOrg(ctx.ContextUser, projectID)
|
||||
|
||||
shared_user.RenderUserHeader(ctx)
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
package repo
|
||||
|
||||
import (
|
||||
"path"
|
||||
"time"
|
||||
|
||||
git_model "code.gitea.io/gitea/models/git"
|
||||
|
@ -82,7 +81,7 @@ func ServeBlobOrLFS(ctx *context.Context, blob *git.Blob, lastModified *time.Tim
|
|||
return common.ServeBlob(ctx.Base, ctx.Repo.TreePath, blob, lastModified)
|
||||
}
|
||||
|
||||
func getBlobForEntry(ctx *context.Context) (blob *git.Blob, lastModified *time.Time) {
|
||||
func getBlobForEntry(ctx *context.Context) (*git.Blob, *time.Time) {
|
||||
entry, err := ctx.Repo.Commit.GetTreeEntryByPath(ctx.Repo.TreePath)
|
||||
if err != nil {
|
||||
if git.IsErrNotExist(err) {
|
||||
|
@ -98,19 +97,14 @@ func getBlobForEntry(ctx *context.Context) (blob *git.Blob, lastModified *time.T
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
info, _, err := git.Entries([]*git.TreeEntry{entry}).GetCommitsInfo(ctx, ctx.Repo.Commit, path.Dir("/" + ctx.Repo.TreePath)[1:])
|
||||
latestCommit, err := ctx.Repo.GitRepo.GetTreePathLatestCommit(ctx.Repo.Commit.ID.String(), ctx.Repo.TreePath)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetCommitsInfo", err)
|
||||
ctx.ServerError("GetTreePathLatestCommit", err)
|
||||
return nil, nil
|
||||
}
|
||||
lastModified := &latestCommit.Committer.When
|
||||
|
||||
if len(info) == 1 {
|
||||
// Not Modified
|
||||
lastModified = &info[0].Commit.Committer.When
|
||||
}
|
||||
blob = entry.Blob()
|
||||
|
||||
return blob, lastModified
|
||||
return entry.Blob(), lastModified
|
||||
}
|
||||
|
||||
// SingleDownload download a file by repos path
|
||||
|
|
|
@ -1291,10 +1291,17 @@ func NewIssuePost(ctx *context.Context) {
|
|||
|
||||
log.Trace("Issue created: %d/%d", repo.ID, issue.ID)
|
||||
if ctx.FormString("redirect_after_creation") == "project" && projectID > 0 {
|
||||
ctx.JSONRedirect(ctx.Repo.RepoLink + "/projects/" + strconv.FormatInt(projectID, 10))
|
||||
project, err := project_model.GetProjectByID(ctx, projectID)
|
||||
if err == nil {
|
||||
if project.Type == project_model.TypeOrganization {
|
||||
ctx.JSONRedirect(project_model.ProjectLinkForOrg(ctx.Repo.Owner, project.ID))
|
||||
} else {
|
||||
ctx.JSONRedirect(issue.Link())
|
||||
ctx.JSONRedirect(project_model.ProjectLinkForRepo(repo, project.ID))
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
ctx.JSONRedirect(issue.Link())
|
||||
}
|
||||
|
||||
// roleDescriptor returns the role descriptor for a comment in/with the given repo, poster and issue
|
||||
|
|
|
@ -183,7 +183,7 @@ func ChangeProjectStatus(ctx *context.Context) {
|
|||
ctx.NotFoundOrServerError("ChangeProjectStatusByRepoIDAndID", project_model.IsErrProjectNotExist, err)
|
||||
return
|
||||
}
|
||||
ctx.JSONRedirect(fmt.Sprintf("%s/projects/%d", ctx.Repo.RepoLink, id))
|
||||
ctx.JSONRedirect(project_model.ProjectLinkForRepo(ctx.Repo.Repository, id))
|
||||
}
|
||||
|
||||
// DeleteProject delete a project
|
||||
|
@ -237,7 +237,7 @@ func RenderEditProject(ctx *context.Context) {
|
|||
ctx.Data["content"] = p.Description
|
||||
ctx.Data["card_type"] = p.CardType
|
||||
ctx.Data["redirect"] = ctx.FormString("redirect")
|
||||
ctx.Data["CancelLink"] = fmt.Sprintf("%s/projects/%d", ctx.Repo.Repository.Link(), p.ID)
|
||||
ctx.Data["CancelLink"] = project_model.ProjectLinkForRepo(ctx.Repo.Repository, p.ID)
|
||||
|
||||
ctx.HTML(http.StatusOK, tplProjectsNew)
|
||||
}
|
||||
|
@ -251,7 +251,7 @@ func EditProjectPost(ctx *context.Context) {
|
|||
ctx.Data["PageIsEditProjects"] = true
|
||||
ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects)
|
||||
ctx.Data["CardTypes"] = project_model.GetCardConfig()
|
||||
ctx.Data["CancelLink"] = fmt.Sprintf("%s/projects/%d", ctx.Repo.Repository.Link(), projectID)
|
||||
ctx.Data["CancelLink"] = project_model.ProjectLinkForRepo(ctx.Repo.Repository, projectID)
|
||||
|
||||
if ctx.HasError() {
|
||||
ctx.HTML(http.StatusOK, tplProjectsNew)
|
||||
|
|
|
@ -5,6 +5,7 @@ package pull
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
|
@ -66,7 +67,10 @@ func doMergeStyleSquash(ctx *mergeContext, message string) error {
|
|||
|
||||
if setting.Repository.PullRequest.AddCoCommitterTrailers && ctx.committer.String() != sig.String() {
|
||||
// add trailer
|
||||
message += fmt.Sprintf("\nCo-authored-by: %s\nCo-committed-by: %s\n", sig.String(), sig.String())
|
||||
if !strings.Contains(message, fmt.Sprintf("Co-authored-by: %s", sig.String())) {
|
||||
message += fmt.Sprintf("\nCo-authored-by: %s", sig.String())
|
||||
}
|
||||
message += fmt.Sprintf("\nCo-committed-by: %s\n", sig.String())
|
||||
}
|
||||
cmdCommit := git.NewCommand(ctx, "commit").
|
||||
AddOptionFormat("--author='%s <%s>'", sig.Name, sig.Email).
|
||||
|
|
|
@ -10,9 +10,12 @@ import (
|
|||
"code.gitea.io/gitea/models/db"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
webhook_model "code.gitea.io/gitea/models/webhook"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
webhook_module "code.gitea.io/gitea/modules/webhook"
|
||||
"code.gitea.io/gitea/services/convert"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
@ -98,3 +101,11 @@ func TestPrepareWebhooksBranchFilterNoMatch(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWebhookUserMail(t *testing.T) {
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
setting.Service.NoReplyAddress = "no-reply.com"
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
|
||||
assert.Equal(t, user.GetPlaceholderEmail(), convert.ToUser(db.DefaultContext, user, nil).Email)
|
||||
assert.Equal(t, user.Email, convert.ToUser(db.DefaultContext, user, user).Email)
|
||||
}
|
||||
|
|
|
@ -35,11 +35,12 @@
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{$tooltipSearchInNuget := ctx.Locale.Tr "packages.search_in_external_registry" "nuget.org"}}
|
||||
{{range $framework, $dependencies := .PackageDescriptor.Metadata.Dependencies}}
|
||||
{{range $dependencies}}
|
||||
<tr>
|
||||
<td>{{.ID}}</td>
|
||||
<td>{{.Version}}</td>
|
||||
<td>{{.ID}} <a target="_blank" rel="noreferrer" href="https://www.nuget.org/packages/{{.ID}}" data-tooltip-content="{{$tooltipSearchInNuget}}">{{svg "octicon-link-external"}}</a></td>
|
||||
<td>{{.Version}} <a target="_blank" rel="noreferrer" href="https://www.nuget.org/packages/{{.ID}}/{{.Version}}" data-tooltip-content="{{$tooltipSearchInNuget}}">{{svg "octicon-link-external"}}</a></td>
|
||||
<td>{{$framework}}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
|
|
|
@ -40,13 +40,6 @@ func TestMain(m *testing.M) {
|
|||
initChangedFiles()
|
||||
testE2eWebRoutes = routers.NormalRoutes()
|
||||
|
||||
os.Unsetenv("GIT_AUTHOR_NAME")
|
||||
os.Unsetenv("GIT_AUTHOR_EMAIL")
|
||||
os.Unsetenv("GIT_AUTHOR_DATE")
|
||||
os.Unsetenv("GIT_COMMITTER_NAME")
|
||||
os.Unsetenv("GIT_COMMITTER_EMAIL")
|
||||
os.Unsetenv("GIT_COMMITTER_DATE")
|
||||
|
||||
err := unittest.InitFixtures(
|
||||
unittest.FixturesOptions{
|
||||
Dir: filepath.Join(setting.AppWorkPath, "models/fixtures/"),
|
||||
|
|
|
@ -33,15 +33,16 @@ func TestPackagePyPI(t *testing.T) {
|
|||
packageVersion := "1!1.0.1+r1234"
|
||||
packageAuthor := "KN4CK3R"
|
||||
packageDescription := "Test Description"
|
||||
projectURL := "https://example.com"
|
||||
|
||||
content := "test"
|
||||
hashSHA256 := "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"
|
||||
|
||||
root := fmt.Sprintf("/api/packages/%s/pypi", user.Name)
|
||||
|
||||
uploadFile := func(t *testing.T, filename, content string, expectedStatus int) {
|
||||
body := &bytes.Buffer{}
|
||||
writer := multipart.NewWriter(body)
|
||||
createBasicMultipartFile := func(filename, packageName, content string) (body *bytes.Buffer, writer *multipart.Writer, closer func() error) {
|
||||
body = &bytes.Buffer{}
|
||||
writer = multipart.NewWriter(body)
|
||||
part, _ := writer.CreateFormFile("content", filename)
|
||||
_, _ = io.Copy(part, strings.NewReader(content))
|
||||
|
||||
|
@ -53,14 +54,27 @@ func TestPackagePyPI(t *testing.T) {
|
|||
writer.WriteField("sha256_digest", hashSHA256)
|
||||
writer.WriteField("requires_python", "3.6")
|
||||
|
||||
_ = writer.Close()
|
||||
return body, writer, writer.Close
|
||||
}
|
||||
|
||||
uploadHelper := func(t *testing.T, body *bytes.Buffer, contentType string, expectedStatus int) {
|
||||
req := NewRequestWithBody(t, "POST", root, body).
|
||||
SetHeader("Content-Type", writer.FormDataContentType()).
|
||||
SetHeader("Content-Type", contentType).
|
||||
AddBasicAuth(user.Name)
|
||||
MakeRequest(t, req, expectedStatus)
|
||||
}
|
||||
|
||||
uploadFile := func(t *testing.T, filename, content string, expectedStatus int) {
|
||||
body, writer, closeFunc := createBasicMultipartFile(filename, packageName, content)
|
||||
|
||||
writer.WriteField("project_urls", "DOCUMENTATION , https://readthedocs.org")
|
||||
writer.WriteField("project_urls", fmt.Sprintf("Home-page, %s", projectURL))
|
||||
|
||||
_ = closeFunc()
|
||||
|
||||
uploadHelper(t, body, writer.FormDataContentType(), expectedStatus)
|
||||
}
|
||||
|
||||
t.Run("Upload", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
|
||||
|
@ -75,6 +89,7 @@ func TestPackagePyPI(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
assert.Nil(t, pd.SemVer)
|
||||
assert.IsType(t, &pypi.Metadata{}, pd.Metadata)
|
||||
assert.Equal(t, projectURL, pd.Metadata.(*pypi.Metadata).ProjectURL)
|
||||
assert.Equal(t, packageName, pd.Package.Name)
|
||||
assert.Equal(t, packageVersion, pd.Version.Version)
|
||||
|
||||
|
@ -134,6 +149,48 @@ func TestPackagePyPI(t *testing.T) {
|
|||
uploadFile(t, "test.tar.gz", content, http.StatusConflict)
|
||||
})
|
||||
|
||||
t.Run("UploadUsingDeprecatedHomepageMetadata", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
|
||||
pkgName := "homepage-package"
|
||||
body, writer, closeFunc := createBasicMultipartFile("test.whl", pkgName, content)
|
||||
|
||||
writer.WriteField("home_page", projectURL)
|
||||
|
||||
_ = closeFunc()
|
||||
|
||||
uploadHelper(t, body, writer.FormDataContentType(), http.StatusCreated)
|
||||
|
||||
pvs, err := packages.GetVersionsByPackageName(db.DefaultContext, user.ID, packages.TypePyPI, pkgName)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, pvs, 1)
|
||||
|
||||
pd, err := packages.GetPackageDescriptor(db.DefaultContext, pvs[0])
|
||||
require.NoError(t, err)
|
||||
assert.IsType(t, &pypi.Metadata{}, pd.Metadata)
|
||||
assert.Equal(t, projectURL, pd.Metadata.(*pypi.Metadata).ProjectURL)
|
||||
})
|
||||
|
||||
t.Run("UploadWithoutAnyHomepageURLMetadata", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
|
||||
pkgName := "no-project-url-or-homepage-package"
|
||||
body, writer, closeFunc := createBasicMultipartFile("test.whl", pkgName, content)
|
||||
|
||||
_ = closeFunc()
|
||||
|
||||
uploadHelper(t, body, writer.FormDataContentType(), http.StatusCreated)
|
||||
|
||||
pvs, err := packages.GetVersionsByPackageName(db.DefaultContext, user.ID, packages.TypePyPI, pkgName)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, pvs, 1)
|
||||
|
||||
pd, err := packages.GetPackageDescriptor(db.DefaultContext, pvs[0])
|
||||
require.NoError(t, err)
|
||||
assert.IsType(t, &pypi.Metadata{}, pd.Metadata)
|
||||
assert.Empty(t, pd.Metadata.(*pypi.Metadata).ProjectURL)
|
||||
})
|
||||
|
||||
t.Run("Download", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
|
||||
|
@ -148,7 +205,7 @@ func TestPackagePyPI(t *testing.T) {
|
|||
downloadFile("test.whl")
|
||||
downloadFile("test.tar.gz")
|
||||
|
||||
pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypePyPI)
|
||||
pvs, err := packages.GetVersionsByPackageName(db.DefaultContext, user.ID, packages.TypePyPI, packageName)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, pvs, 1)
|
||||
assert.Equal(t, int64(2), pvs[0].DownloadCount)
|
||||
|
|
|
@ -159,18 +159,6 @@ func TestMain(m *testing.M) {
|
|||
}
|
||||
}
|
||||
|
||||
os.Unsetenv("GIT_AUTHOR_NAME")
|
||||
os.Unsetenv("GIT_AUTHOR_EMAIL")
|
||||
os.Unsetenv("GIT_AUTHOR_DATE")
|
||||
os.Unsetenv("GIT_COMMITTER_NAME")
|
||||
os.Unsetenv("GIT_COMMITTER_EMAIL")
|
||||
os.Unsetenv("GIT_COMMITTER_DATE")
|
||||
|
||||
// Avoid loading the default system config. On MacOS, this config
|
||||
// sets the osxkeychain credential helper, which will cause tests
|
||||
// to freeze with a dialog.
|
||||
os.Setenv("GIT_CONFIG_NOSYSTEM", "true")
|
||||
|
||||
err := unittest.InitFixtures(
|
||||
unittest.FixturesOptions{
|
||||
Dir: filepath.Join(filepath.Dir(setting.AppPath), "models/fixtures/"),
|
||||
|
|
Loading…
Reference in a new issue