diff options
37 files changed, 549 insertions, 43 deletions
diff --git a/.forgejo/workflows/build-release.yml b/.forgejo/workflows/build-release.yml index c6cae8909d..433b085969 100644 --- a/.forgejo/workflows/build-release.yml +++ b/.forgejo/workflows/build-release.yml @@ -14,6 +14,12 @@ # secrets.CASCADE_DESTINATION_TOKEN: <generated from code.forgejo.org/forgejo-ci> scope read:user, write:repository, write:issue # vars.CASCADE_DESTINATION_DOER: forgejo-ci # +# vars.SKIP_END_TO_END: `true` or `false` +# It must be `false` (or absent) so https://code.forgejo.org/forgejo/end-to-end is run +# with the newly built release. +# It must be set to `true` when a release is missing, for instance because it was +# removed and failed to upload. +# on: push: tags: 'v[0-9]+.[0-9]+.*' @@ -109,7 +109,7 @@ require ( golang.org/x/sys v0.28.0 golang.org/x/text v0.21.0 google.golang.org/grpc v1.69.2 - google.golang.org/protobuf v1.36.0 + google.golang.org/protobuf v1.36.1 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df gopkg.in/ini.v1 v1.67.0 gopkg.in/yaml.v3 v3.0.1 @@ -2157,8 +2157,8 @@ google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.36.0 h1:mjIs9gYtt56AzC4ZaffQuh88TZurBGhIJMBZGSxNerQ= -google.golang.org/protobuf v1.36.0/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= +google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/models/actions/run_job.go b/models/actions/run_job.go index 2319af8e08..de4b6aab66 100644 --- a/models/actions/run_job.go +++ b/models/actions/run_job.go @@ -153,28 +153,34 @@ func UpdateRunJob(ctx context.Context, job *ActionRunJob, cond builder.Cond, col } func AggregateJobStatus(jobs []*ActionRunJob) Status { - allDone := true - allWaiting := true - hasFailure := false + allSuccessOrSkipped := len(jobs) != 0 + allSkipped := len(jobs) != 0 + var hasFailure, hasCancelled, hasWaiting, hasRunning, hasBlocked bool for _, job := range jobs { - if !job.Status.IsDone() { - allDone = false - } - if job.Status != StatusWaiting && !job.Status.IsDone() { - allWaiting = false - } - if job.Status == StatusFailure || job.Status == StatusCancelled { - hasFailure = true - } + allSuccessOrSkipped = allSuccessOrSkipped && (job.Status == StatusSuccess || job.Status == StatusSkipped) + allSkipped = allSkipped && job.Status == StatusSkipped + hasFailure = hasFailure || job.Status == StatusFailure + hasCancelled = hasCancelled || job.Status == StatusCancelled + hasWaiting = hasWaiting || job.Status == StatusWaiting + hasRunning = hasRunning || job.Status == StatusRunning + hasBlocked = hasBlocked || job.Status == StatusBlocked } - if allDone { - if hasFailure { - return StatusFailure - } + switch { + case allSkipped: + return StatusSkipped + case allSuccessOrSkipped: return StatusSuccess - } - if allWaiting { + case hasCancelled: + return StatusCancelled + case hasFailure: + return StatusFailure + case hasRunning: + return StatusRunning + case hasWaiting: return StatusWaiting + case hasBlocked: + return StatusBlocked + default: + return StatusUnknown // it shouldn't happen } - return StatusRunning } diff --git a/models/actions/run_job_status_test.go b/models/actions/run_job_status_test.go new file mode 100644 index 0000000000..04fd9ceba7 --- /dev/null +++ b/models/actions/run_job_status_test.go @@ -0,0 +1,85 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package actions + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestAggregateJobStatus(t *testing.T) { + testStatuses := func(expected Status, statuses []Status) { + t.Helper() + var jobs []*ActionRunJob + for _, v := range statuses { + jobs = append(jobs, &ActionRunJob{Status: v}) + } + actual := AggregateJobStatus(jobs) + if !assert.Equal(t, expected, actual) { + var statusStrings []string + for _, s := range statuses { + statusStrings = append(statusStrings, s.String()) + } + t.Errorf("AggregateJobStatus(%v) = %v, want %v", statusStrings, statusNames[actual], statusNames[expected]) + } + } + + cases := []struct { + statuses []Status + expected Status + }{ + // unknown cases, maybe it shouldn't happen in real world + {[]Status{}, StatusUnknown}, + {[]Status{StatusUnknown, StatusSuccess}, StatusUnknown}, + {[]Status{StatusUnknown, StatusSkipped}, StatusUnknown}, + {[]Status{StatusUnknown, StatusFailure}, StatusFailure}, + {[]Status{StatusUnknown, StatusCancelled}, StatusCancelled}, + {[]Status{StatusUnknown, StatusWaiting}, StatusWaiting}, + {[]Status{StatusUnknown, StatusRunning}, StatusRunning}, + {[]Status{StatusUnknown, StatusBlocked}, StatusBlocked}, + + // success with other status + {[]Status{StatusSuccess}, StatusSuccess}, + {[]Status{StatusSuccess, StatusSkipped}, StatusSuccess}, // skipped doesn't affect success + {[]Status{StatusSuccess, StatusFailure}, StatusFailure}, + {[]Status{StatusSuccess, StatusCancelled}, StatusCancelled}, + {[]Status{StatusSuccess, StatusWaiting}, StatusWaiting}, + {[]Status{StatusSuccess, StatusRunning}, StatusRunning}, + {[]Status{StatusSuccess, StatusBlocked}, StatusBlocked}, + + // any cancelled, then cancelled + {[]Status{StatusCancelled}, StatusCancelled}, + {[]Status{StatusCancelled, StatusSuccess}, StatusCancelled}, + {[]Status{StatusCancelled, StatusSkipped}, StatusCancelled}, + {[]Status{StatusCancelled, StatusFailure}, StatusCancelled}, + {[]Status{StatusCancelled, StatusWaiting}, StatusCancelled}, + {[]Status{StatusCancelled, StatusRunning}, StatusCancelled}, + {[]Status{StatusCancelled, StatusBlocked}, StatusCancelled}, + + // failure with other status, fail fast + // Should "running" win? Maybe no: old code does make "running" win, but GitHub does fail fast. + {[]Status{StatusFailure}, StatusFailure}, + {[]Status{StatusFailure, StatusSuccess}, StatusFailure}, + {[]Status{StatusFailure, StatusSkipped}, StatusFailure}, + {[]Status{StatusFailure, StatusCancelled}, StatusCancelled}, + {[]Status{StatusFailure, StatusWaiting}, StatusFailure}, + {[]Status{StatusFailure, StatusRunning}, StatusFailure}, + {[]Status{StatusFailure, StatusBlocked}, StatusFailure}, + + // skipped with other status + // TODO: need to clarify whether a PR with "skipped" job status is considered as "mergeable" or not. + {[]Status{StatusSkipped}, StatusSkipped}, + {[]Status{StatusSkipped, StatusSuccess}, StatusSuccess}, + {[]Status{StatusSkipped, StatusFailure}, StatusFailure}, + {[]Status{StatusSkipped, StatusCancelled}, StatusCancelled}, + {[]Status{StatusSkipped, StatusWaiting}, StatusWaiting}, + {[]Status{StatusSkipped, StatusRunning}, StatusRunning}, + {[]Status{StatusSkipped, StatusBlocked}, StatusBlocked}, + } + + for _, c := range cases { + testStatuses(c.expected, c.statuses) + } +} diff --git a/models/fixtures/action_run.yml b/models/fixtures/action_run.yml index 2fe9094d13..7a7bf34197 100644 --- a/models/fixtures/action_run.yml +++ b/models/fixtures/action_run.yml @@ -433,6 +433,25 @@ need_approval: 0 approved_by: 0 - + id: 794 + title: "job output" + repo_id: 4 + owner_id: 1 + workflow_id: "test.yaml" + index: 190 + trigger_user_id: 1 + ref: "refs/heads/test" + commit_sha: "c2d72f548424103f01ee1dc02889c1e2bff816b0" + event: "push" + is_fork_pull_request: 0 + status: 1 + started: 1683636528 + stopped: 1683636626 + created: 1683636108 + updated: 1683636626 + need_approval: 0 + approved_by: 0 +- id: 891 title: "update actions" repo_id: 1 diff --git a/models/fixtures/branch.yml b/models/fixtures/branch.yml index 93003049c6..b75d3706cc 100644 --- a/models/fixtures/branch.yml +++ b/models/fixtures/branch.yml @@ -45,3 +45,15 @@ is_deleted: false deleted_by_id: 0 deleted_unix: 0 + +- + id: 15 + repo_id: 4 + name: 'master' + commit_id: 'c7cd3cd144e6d23c9d6f3d07e52b2c1a956e0338' + commit_message: 'add Readme' + commit_time: 1588147171 + pusher_id: 13 + is_deleted: false + deleted_by_id: 0 + deleted_unix: 0 diff --git a/models/forgejo_migrations/v25.go b/models/forgejo_migrations/v25.go index 2e9641929c..e2316007cf 100644 --- a/models/forgejo_migrations/v25.go +++ b/models/forgejo_migrations/v25.go @@ -20,9 +20,34 @@ import ( func MigrateTwoFactorToKeying(x *xorm.Engine) error { var err error + // When upgrading from Forgejo v9 to v10, this migration will already be + // called from models/migrations/migrations.go migration 304 and must not + // be run twice. + var version int + _, err = x.Table("version").Where("`id` = 1").Select("version").Get(&version) + if err != nil { + // the version table does not exist when a test environment only applies Forgejo migrations + } else if version > 304 { + return nil + } + switch x.Dialect().URI().DBType { case schemas.MYSQL: _, err = x.Exec("ALTER TABLE `two_factor` MODIFY `secret` BLOB") + case schemas.SQLITE: + _, err = x.Exec("ALTER TABLE `two_factor` RENAME COLUMN `secret` TO `secret_backup`") + if err != nil { + return err + } + _, err = x.Exec("ALTER TABLE `two_factor` ADD COLUMN `secret` BLOB") + if err != nil { + return err + } + _, err = x.Exec("UPDATE `two_factor` SET `secret` = `secret_backup`") + if err != nil { + return err + } + _, err = x.Exec("ALTER TABLE `two_factor` DROP COLUMN `secret_backup`") case schemas.POSTGRES: _, err = x.Exec("ALTER TABLE `two_factor` ALTER COLUMN `secret` SET DATA TYPE bytea USING secret::text::bytea") } diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 77a97fb3f6..1674af08cd 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -362,6 +362,10 @@ func prepareMigrationTasks() []*migration { newMigration(300, "Add force-push branch protection support", v1_23.AddForcePushBranchProtection), newMigration(301, "Add skip_secondary_authorization option to oauth2 application table", v1_23.AddSkipSecondaryAuthColumnToOAuth2ApplicationTable), newMigration(302, "Add index to action_task stopped log_expired", v1_23.AddIndexToActionTaskStoppedLogExpired), + + // Migration to Forgejo v10 + newMigration(303, "Gitea last drop", v1_23.GiteaLastDrop), + newMigration(304, "Migrate `secret` column to store keying material", forgejo_migrations.MigrateTwoFactorToKeying), } return preparedMigrations } diff --git a/models/migrations/v1_23/v303.go b/models/migrations/v1_23/v303.go new file mode 100644 index 0000000000..c1e74c596a --- /dev/null +++ b/models/migrations/v1_23/v303.go @@ -0,0 +1,33 @@ +// Copyright 2024 The Forgejo Authors. +// SPDX-License-Identifier: MIT + +package v1_23 //nolint + +import ( + "code.gitea.io/gitea/models/migrations/base" + + "xorm.io/xorm" +) + +func GiteaLastDrop(x *xorm.Engine) error { + sess := x.NewSession() + defer sess.Close() + + if err := base.DropTableColumns(sess, "badge", "slug"); err != nil { + return err + } + if err := base.DropTableColumns(sess, "oauth2_application", "skip_secondary_authorization"); err != nil { + return err + } + if err := base.DropTableColumns(sess, "repository", "default_wiki_branch"); err != nil { + return err + } + // the migration v297.go that adds everyone_access_mode exists in Gitea >= v1.22 and the column must be dropped + // but it does not exist in Forgejo and a failure to drop the column can be ignored + base.DropTableColumns(sess, "repo_unit", "everyone_access_mode") + if err := base.DropTableColumns(sess, "protected_branch", "can_force_push", "enable_force_push_allowlist", "force_push_allowlist_user_i_ds", "force_push_allowlist_team_i_ds", "force_push_allowlist_deploy_keys"); err != nil { + return err + } + + return sess.Commit() +} diff --git a/models/repo/git.go b/models/repo/git.go index 388bf86522..d39915e869 100644 --- a/models/repo/git.go +++ b/models/repo/git.go @@ -29,6 +29,15 @@ const ( MergeStyleRebaseUpdate MergeStyle = "rebase-update-only" ) +type UpdateStyle string + +const ( + // UpdateStyleMerge create merge commit to update + UpdateStyleMerge UpdateStyle = "merge" + // UpdateStyleRebase rebase to update + UpdateStyleRebase UpdateStyle = "rebase" +) + // UpdateDefaultBranch updates the default branch func UpdateDefaultBranch(ctx context.Context, repo *Repository) error { _, err := db.GetEngine(ctx).ID(repo.ID).Cols("default_branch").Update(repo) diff --git a/models/repo/repo_unit.go b/models/repo/repo_unit.go index ed553844fc..5b4066d994 100644 --- a/models/repo/repo_unit.go +++ b/models/repo/repo_unit.go @@ -159,6 +159,7 @@ type PullRequestsConfig struct { AllowRebaseUpdate bool DefaultDeleteBranchAfterMerge bool DefaultMergeStyle MergeStyle + DefaultUpdateStyle UpdateStyle DefaultAllowMaintainerEdit bool } @@ -197,6 +198,25 @@ func (cfg *PullRequestsConfig) GetDefaultMergeStyle() MergeStyle { return MergeStyleMerge } +// IsUpdateStyleAllowed returns if update style is allowed +func (cfg *PullRequestsConfig) IsUpdateStyleAllowed(updateStyle UpdateStyle) bool { + return updateStyle == UpdateStyleMerge || + updateStyle == UpdateStyleRebase && cfg.AllowRebaseUpdate +} + +// GetDefaultUpdateStyle returns the default update style for this pull request +func (cfg *PullRequestsConfig) GetDefaultUpdateStyle() UpdateStyle { + if len(cfg.DefaultUpdateStyle) != 0 { + return cfg.DefaultUpdateStyle + } + + if setting.Repository.PullRequest.DefaultUpdateStyle != "" { + return UpdateStyle(setting.Repository.PullRequest.DefaultUpdateStyle) + } + + return UpdateStyleMerge +} + type ActionsConfig struct { DisabledWorkflows []string } diff --git a/models/repo/repo_unit_test.go b/models/repo/repo_unit_test.go index deee1a7472..129d913cfb 100644 --- a/models/repo/repo_unit_test.go +++ b/models/repo/repo_unit_test.go @@ -7,6 +7,8 @@ import ( "testing" "code.gitea.io/gitea/models/perm" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/test" "github.com/stretchr/testify/assert" ) @@ -37,3 +39,50 @@ func TestRepoUnitAccessMode(t *testing.T) { assert.Equal(t, perm.AccessModeWrite, UnitAccessModeWrite.ToAccessMode(perm.AccessModeAdmin)) assert.Equal(t, perm.AccessModeRead, UnitAccessModeUnset.ToAccessMode(perm.AccessModeRead)) } + +func TestRepoPRIsUpdateStyleAllowed(t *testing.T) { + var cfg PullRequestsConfig + cfg = PullRequestsConfig{ + AllowRebaseUpdate: true, + } + assert.True(t, cfg.IsUpdateStyleAllowed(UpdateStyleMerge)) + assert.True(t, cfg.IsUpdateStyleAllowed(UpdateStyleRebase)) + + cfg = PullRequestsConfig{ + AllowRebaseUpdate: false, + } + assert.True(t, cfg.IsUpdateStyleAllowed(UpdateStyleMerge)) + assert.False(t, cfg.IsUpdateStyleAllowed(UpdateStyleRebase)) +} + +func TestRepoPRGetDefaultUpdateStyle(t *testing.T) { + defer test.MockVariableValue(&setting.Repository.PullRequest.DefaultUpdateStyle, "merge")() + + var cfg PullRequestsConfig + cfg = PullRequestsConfig{ + DefaultUpdateStyle: "", + } + assert.Equal(t, UpdateStyleMerge, cfg.GetDefaultUpdateStyle()) + cfg = PullRequestsConfig{ + DefaultUpdateStyle: "rebase", + } + assert.Equal(t, UpdateStyleRebase, cfg.GetDefaultUpdateStyle()) + cfg = PullRequestsConfig{ + DefaultUpdateStyle: "merge", + } + assert.Equal(t, UpdateStyleMerge, cfg.GetDefaultUpdateStyle()) + + setting.Repository.PullRequest.DefaultUpdateStyle = "rebase" + cfg = PullRequestsConfig{ + DefaultUpdateStyle: "", + } + assert.Equal(t, UpdateStyleRebase, cfg.GetDefaultUpdateStyle()) + cfg = PullRequestsConfig{ + DefaultUpdateStyle: "rebase", + } + assert.Equal(t, UpdateStyleRebase, cfg.GetDefaultUpdateStyle()) + cfg = PullRequestsConfig{ + DefaultUpdateStyle: "merge", + } + assert.Equal(t, UpdateStyleMerge, cfg.GetDefaultUpdateStyle()) +} diff --git a/models/system/setting.go b/models/system/setting.go index 4472b4c228..cda60d1758 100644 --- a/models/system/setting.go +++ b/models/system/setting.go @@ -18,10 +18,10 @@ import ( ) type Setting struct { - ID int64 `xorm:"pk autoincr"` - SettingKey string `xorm:"varchar(255) unique"` // key should be lowercase - SettingValue string `xorm:"text"` - Version int `xorm:"version"` + ID int64 `xorm:"pk autoincr"` + SettingKey string `xorm:"varchar(255) unique"` // key should be lowercase + SettingValue string `xorm:"text"` + Version int Created timeutil.TimeStamp `xorm:"created"` Updated timeutil.TimeStamp `xorm:"updated"` } diff --git a/modules/repository/create.go b/modules/repository/create.go index ca2150b972..32c6235544 100644 --- a/modules/repository/create.go +++ b/modules/repository/create.go @@ -89,8 +89,9 @@ func CreateRepositoryByExample(ctx context.Context, doer, u *user_model.User, re Type: tp, Config: &repo_model.PullRequestsConfig{ AllowMerge: true, AllowRebase: true, AllowRebaseMerge: true, AllowSquash: true, AllowFastForwardOnly: true, - DefaultMergeStyle: repo_model.MergeStyle(setting.Repository.PullRequest.DefaultMergeStyle), - AllowRebaseUpdate: true, + DefaultMergeStyle: repo_model.MergeStyle(setting.Repository.PullRequest.DefaultMergeStyle), + DefaultUpdateStyle: repo_model.UpdateStyle(setting.Repository.PullRequest.DefaultUpdateStyle), + AllowRebaseUpdate: true, }, }) } else { diff --git a/modules/setting/repository.go b/modules/setting/repository.go index 6086dd1d57..d13f25f554 100644 --- a/modules/setting/repository.go +++ b/modules/setting/repository.go @@ -87,6 +87,7 @@ var ( DefaultMergeMessageAllAuthors bool DefaultMergeMessageMaxApprovers int DefaultMergeMessageOfficialApproversOnly bool + DefaultUpdateStyle string PopulateSquashCommentWithCommitMessages bool AddCoCommitterTrailers bool TestConflictingPatchesWithGitApply bool @@ -216,6 +217,7 @@ var ( DefaultMergeMessageAllAuthors bool DefaultMergeMessageMaxApprovers int DefaultMergeMessageOfficialApproversOnly bool + DefaultUpdateStyle string PopulateSquashCommentWithCommitMessages bool AddCoCommitterTrailers bool TestConflictingPatchesWithGitApply bool @@ -232,6 +234,7 @@ var ( DefaultMergeMessageAllAuthors: false, DefaultMergeMessageMaxApprovers: 10, DefaultMergeMessageOfficialApproversOnly: true, + DefaultUpdateStyle: "merge", PopulateSquashCommentWithCommitMessages: false, AddCoCommitterTrailers: true, RetargetChildrenOnMerge: true, diff --git a/modules/structs/repo.go b/modules/structs/repo.go index 36190fe36b..b5f54a2a7a 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -105,6 +105,7 @@ type Repository struct { DefaultDeleteBranchAfterMerge bool `json:"default_delete_branch_after_merge"` DefaultMergeStyle string `json:"default_merge_style"` DefaultAllowMaintainerEdit bool `json:"default_allow_maintainer_edit"` + DefaultUpdateStyle string `json:"default_update_style"` AvatarURL string `json:"avatar_url"` Internal bool `json:"internal"` MirrorInterval string `json:"mirror_interval"` @@ -225,6 +226,8 @@ type EditRepoOption struct { DefaultDeleteBranchAfterMerge *bool `json:"default_delete_branch_after_merge,omitempty"` // set to a merge style to be used by this repository: "merge", "rebase", "rebase-merge", "squash", or "fast-forward-only". DefaultMergeStyle *string `json:"default_merge_style,omitempty"` + // set to a update style to be used by this repository: "rebase" or "merge" + DefaultUpdateStyle *string `json:"default_update_style,omitempty"` // set to `true` to allow edits from maintainers by default DefaultAllowMaintainerEdit *bool `json:"default_allow_maintainer_edit,omitempty"` // set to `true` to archive this repository. diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 6e1524f583..3b413b8f92 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -2246,6 +2246,7 @@ settings.pulls_desc = Enable repository pull requests settings.pulls.ignore_whitespace = Ignore whitespace for conflicts settings.pulls.enable_autodetect_manual_merge = Enable autodetect manual merge (Note: In some special cases, misjudgments can occur) settings.pulls.allow_rebase_update = Enable updating pull request branch by rebase +settings.default_update_style_desc=Default update style used for updating pull requests that are behind the base branch. settings.pulls.default_delete_branch_after_merge = Delete pull request branch after merge by default settings.pulls.default_allow_edits_from_maintainers = Allow edits from maintainers by default settings.releases_desc = Enable repository releases diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index 93d40d2dbf..04c6fde453 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -937,6 +937,7 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { AllowRebaseUpdate: true, DefaultDeleteBranchAfterMerge: false, DefaultMergeStyle: repo_model.MergeStyleMerge, + DefaultUpdateStyle: repo_model.UpdateStyleMerge, DefaultAllowMaintainerEdit: false, } } else { @@ -976,6 +977,9 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { if opts.DefaultMergeStyle != nil { config.DefaultMergeStyle = repo_model.MergeStyle(*opts.DefaultMergeStyle) } + if opts.DefaultUpdateStyle != nil { + config.DefaultUpdateStyle = repo_model.UpdateStyle(*opts.DefaultUpdateStyle) + } if opts.DefaultAllowMaintainerEdit != nil { config.DefaultAllowMaintainerEdit = *opts.DefaultAllowMaintainerEdit } diff --git a/routers/web/repo/actions/actions.go b/routers/web/repo/actions/actions.go index 283d476df1..e5134c1f62 100644 --- a/routers/web/repo/actions/actions.go +++ b/routers/web/repo/actions/actions.go @@ -5,6 +5,7 @@ package actions import ( "bytes" + stdCtx "context" "fmt" "net/http" "slices" @@ -224,7 +225,7 @@ func List(ctx *context.Context) { return } - if err := loadIsRefDeleted(ctx, runs); err != nil { + if err := loadIsRefDeleted(ctx, ctx.Repo.Repository.ID, runs); err != nil { log.Error("LoadIsRefDeleted", err) } @@ -254,7 +255,7 @@ func List(ctx *context.Context) { // loadIsRefDeleted loads the IsRefDeleted field for each run in the list. // TODO: move this function to models/actions/run_list.go but now it will result in a circular import. -func loadIsRefDeleted(ctx *context.Context, runs actions_model.RunList) error { +func loadIsRefDeleted(ctx stdCtx.Context, repoID int64, runs actions_model.RunList) error { branches := make(container.Set[string], len(runs)) for _, run := range runs { refName := git.RefName(run.Ref) @@ -266,14 +267,14 @@ func loadIsRefDeleted(ctx *context.Context, runs actions_model.RunList) error { return nil } - branchInfos, err := git_model.GetBranches(ctx, ctx.Repo.Repository.ID, branches.Values(), false) + branchInfos, err := git_model.GetBranches(ctx, repoID, branches.Values(), false) if err != nil { return err } branchSet := git_model.BranchesToNamesSet(branchInfos) for _, run := range runs { refName := git.RefName(run.Ref) - if refName.IsBranch() && !branchSet.Contains(run.Ref) { + if refName.IsBranch() && !branchSet.Contains(refName.ShortName()) { run.IsRefDeleted = true } } diff --git a/routers/web/repo/actions/actions_test.go b/routers/web/repo/actions/actions_test.go new file mode 100644 index 0000000000..939c4aaf57 --- /dev/null +++ b/routers/web/repo/actions/actions_test.go @@ -0,0 +1,33 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package actions + +import ( + "testing" + + actions_model "code.gitea.io/gitea/models/actions" + "code.gitea.io/gitea/models/db" + unittest "code.gitea.io/gitea/models/unittest" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_loadIsRefDeleted(t *testing.T) { + unittest.PrepareTestEnv(t) + + runs, total, err := db.FindAndCount[actions_model.ActionRun](db.DefaultContext, + actions_model.FindRunOptions{RepoID: 4, Ref: "refs/heads/test"}) + require.NoError(t, err) + assert.Len(t, runs, 1) + assert.EqualValues(t, 1, total) + for _, run := range runs { + assert.False(t, run.IsRefDeleted) + } + + require.NoError(t, loadIsRefDeleted(db.DefaultContext, 4, runs)) + for _, run := range runs { + assert.True(t, run.IsRefDeleted) + } +} diff --git a/routers/web/repo/actions/main_test.go b/routers/web/repo/actions/main_test.go new file mode 100644 index 0000000000..a82f9c6672 --- /dev/null +++ b/routers/web/repo/actions/main_test.go @@ -0,0 +1,14 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package actions + +import ( + "testing" + + "code.gitea.io/gitea/models/unittest" +) + +func TestMain(m *testing.M) { + unittest.MainTest(m) +} diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 78fb5e6c01..c154a9b665 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -1918,6 +1918,21 @@ func ViewIssue(ctx *context.Context) { ctx.Data["MergeStyle"] = mergeStyle + var updateStyle repo_model.UpdateStyle + // Check correct values and select default + if ms, ok := ctx.Data["UpdateStyle"].(repo_model.UpdateStyle); !ok || + !prConfig.IsUpdateStyleAllowed(ms) { + defaultUpdateStyle := prConfig.GetDefaultUpdateStyle() + if prConfig.IsUpdateStyleAllowed(defaultUpdateStyle) && !ok { + updateStyle = defaultUpdateStyle + } else if prConfig.AllowMerge { + updateStyle = repo_model.UpdateStyleMerge + } else if prConfig.AllowRebase { + updateStyle = repo_model.UpdateStyleRebase + } + } + ctx.Data["UpdateStyle"] = updateStyle + defaultMergeMessage, defaultMergeBody, err := pull_service.GetDefaultMergeMessage(ctx, ctx.Repo.GitRepo, pull, mergeStyle) if err != nil { ctx.ServerError("GetDefaultMergeMessage", err) diff --git a/routers/web/repo/setting/setting.go b/routers/web/repo/setting/setting.go index ce506eafb1..2c6ca77a6a 100644 --- a/routers/web/repo/setting/setting.go +++ b/routers/web/repo/setting/setting.go @@ -262,6 +262,7 @@ func UnitsPost(ctx *context.Context) { AllowRebaseUpdate: form.PullsAllowRebaseUpdate, DefaultDeleteBranchAfterMerge: form.DefaultDeleteBranchAfterMerge, DefaultMergeStyle: repo_model.MergeStyle(form.PullsDefaultMergeStyle), + DefaultUpdateStyle: repo_model.UpdateStyle(form.PullsDefaultUpdateStyle), DefaultAllowMaintainerEdit: form.DefaultAllowMaintainerEdit, }, }) diff --git a/services/convert/pull.go b/services/convert/pull.go index 4ec24a8276..70dc22445a 100644 --- a/services/convert/pull.go +++ b/services/convert/pull.go @@ -29,6 +29,11 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u err error ) + if err = pr.LoadIssue(ctx); err != nil { + log.Error("pr.LoadIssue[%d]: %v", pr.ID, err) + return nil + } + if err = pr.Issue.LoadRepo(ctx); err != nil { log.Error("pr.Issue.LoadRepo[%d]: %v", pr.ID, err) return nil diff --git a/services/convert/repository.go b/services/convert/repository.go index 2fb6f6d7c0..e4b2c7b8bc 100644 --- a/services/convert/repository.go +++ b/services/convert/repository.go @@ -101,6 +101,7 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR allowRebaseUpdate := false defaultDeleteBranchAfterMerge := false defaultMergeStyle := repo_model.MergeStyleMerge + defaultUpdateStyle := repo_model.UpdateStyleMerge defaultAllowMaintainerEdit := false if unit, err := repo.GetUnit(ctx, unit_model.TypePullRequests); err == nil { config := unit.PullRequestsConfig() @@ -114,6 +115,7 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR allowRebaseUpdate = config.AllowRebaseUpdate defaultDeleteBranchAfterMerge = config.DefaultDeleteBranchAfterMerge defaultMergeStyle = config.GetDefaultMergeStyle() + defaultUpdateStyle = config.GetDefaultUpdateStyle() defaultAllowMaintainerEdit = config.DefaultAllowMaintainerEdit } hasProjects := false @@ -231,6 +233,7 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR AllowRebaseUpdate: allowRebaseUpdate, DefaultDeleteBranchAfterMerge: defaultDeleteBranchAfterMerge, DefaultMergeStyle: string(defaultMergeStyle), + DefaultUpdateStyle: string(defaultUpdateStyle), DefaultAllowMaintainerEdit: defaultAllowMaintainerEdit, AvatarURL: repo.AvatarLink(ctx), Internal: !repo.IsPrivate && repo.Owner.Visibility == api.VisibleTypePrivate, diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index f6e184fcb6..1ce9b298ad 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -189,6 +189,7 @@ type RepoUnitSettingForm struct { PullsAllowFastForwardOnly bool PullsAllowManualMerge bool PullsDefaultMergeStyle string + PullsDefaultUpdateStyle string EnableAutodetectManualMerge bool PullsAllowRebaseUpdate bool DefaultDeleteBranchAfterMerge bool diff --git a/services/webhook/notifier.go b/services/webhook/notifier.go index 8bfd03024f..fed33d8008 100644 --- a/services/webhook/notifier.go +++ b/services/webhook/notifier.go @@ -409,6 +409,10 @@ func (m *webhookNotifier) CreateIssueComment(ctx context.Context, doer *user_mod var pullRequest *api.PullRequest if issue.IsPull { eventType = webhook_module.HookEventPullRequestComment + if err := issue.LoadPullRequest(ctx); err != nil { + log.Error("LoadPullRequest: %v", err) + return + } pullRequest = convert.ToAPIPullRequest(ctx, issue.PullRequest, doer) } else { eventType = webhook_module.HookEventIssueComment diff --git a/templates/package/content/container.tmpl b/templates/package/content/container.tmpl index 78fdac3bad..dd1c24269b 100644 --- a/templates/package/content/container.tmpl +++ b/templates/package/content/container.tmpl @@ -36,11 +36,13 @@ </thead> <tbody> {{range .PackageDescriptor.Metadata.Manifests}} - <tr> + {{if ne .Platform "unknown/unknown"}} + <tr> <td><a href="{{$.PackageDescriptor.PackageWebLink}}/{{PathEscape .Digest}}">{{.Digest}}</a></td> <td>{{.Platform}}</td> <td>{{ctx.Locale.TrSize .Size}}</td> - </tr> + </tr> + {{end}} {{end}} </tbody> </table> diff --git a/templates/repo/actions/status.tmpl b/templates/repo/actions/status.tmpl index a0e02cf8d7..99fa74ac17 100644 --- a/templates/repo/actions/status.tmpl +++ b/templates/repo/actions/status.tmpl @@ -17,13 +17,15 @@ {{svg "octicon-check-circle-fill" $size (printf "text green %s" $className)}} {{else if eq .status "skipped"}} {{svg "octicon-skip" $size (printf "text grey %s" $className)}} +{{else if eq .status "cancelled"}} + {{svg "octicon-stop" $size (printf "text grey %s" $className)}} {{else if eq .status "waiting"}} {{svg "octicon-clock" $size (printf "text yellow %s" $className)}} {{else if eq .status "blocked"}} {{svg "octicon-blocked" $size (printf "text yellow %s" $className)}} {{else if eq .status "running"}} {{svg "octicon-meter" $size (printf "text yellow job-status-rotate %s" $className)}} -{{else if or (eq .status "failure") or (eq .status "cancelled") or (eq .status "unknown")}} +{{else}}{{/*failure, unknown*/}} {{svg "octicon-x-circle-fill" $size (printf "text red %s" $className)}} {{end}} </span> diff --git a/templates/repo/issue/view_content/update_branch_by_merge.tmpl b/templates/repo/issue/view_content/update_branch_by_merge.tmpl index adce052dee..9fbc8b018b 100644 --- a/templates/repo/issue/view_content/update_branch_by_merge.tmpl +++ b/templates/repo/issue/view_content/update_branch_by_merge.tmpl @@ -9,23 +9,31 @@ {{if and $.UpdateAllowed $.UpdateByRebaseAllowed}} <div class="tw-inline-block"> <div class="ui buttons update-button"> - <button class="ui button" data-do="{{$.Link}}/update" data-redirect="{{$.Link}}"> + <button class="ui button" data-do="{{$.Link}}/update?style={{$.UpdateStyle}}" data-redirect="{{$.Link}}"> <span class="button-text"> - {{ctx.Locale.Tr "repo.pulls.update_branch"}} + {{if eq $.UpdateStyle "rebase"}} + {{ctx.Locale.Tr "repo.pulls.update_branch_rebase"}} + {{else}} + {{ctx.Locale.Tr "repo.pulls.update_branch"}} + {{end}} </span> </button> <div class="ui dropdown icon button"> {{svg "octicon-triangle-down"}} <div class="menu"> - <a class="item active selected" data-do="{{$.Link}}/update">{{ctx.Locale.Tr "repo.pulls.update_branch"}}</a> - <a class="item" data-do="{{$.Link}}/update?style=rebase">{{ctx.Locale.Tr "repo.pulls.update_branch_rebase"}}</a> + <a class="item {{if ne $.UpdateStyle "rebase"}}active selected{{end}}" data-do="{{$.Link}}/update?style=merge"> + {{ctx.Locale.Tr "repo.pulls.update_branch"}} + </a> + <a class="item {{if eq $.UpdateStyle "rebase"}}active selected{{end}}" data-do="{{$.Link}}/update?style=rebase"> + {{ctx.Locale.Tr "repo.pulls.update_branch_rebase"}} + </a> </div> </div> </div> </div> {{end}} {{if and $.UpdateAllowed (not $.UpdateByRebaseAllowed)}} - <form action="{{$.Link}}/update" method="post" class="ui update-branch-form"> + <form action="{{$.Link}}/update?style=merge" method="post" class="ui update-branch-form"> {{$.CsrfTokenHtml}} <button class="ui compact button"> <span class="ui text">{{ctx.Locale.Tr "repo.pulls.update_branch"}}</span> diff --git a/templates/repo/settings/units/pulls.tmpl b/templates/repo/settings/units/pulls.tmpl index 4e9c53e0f4..2e637d4e0b 100644 --- a/templates/repo/settings/units/pulls.tmpl +++ b/templates/repo/settings/units/pulls.tmpl @@ -106,6 +106,29 @@ </div> </div> <div class="field"> + <p> + {{ctx.Locale.Tr "repo.settings.default_update_style_desc"}} + </p> + <div class="ui dropdown selection"> + <select name="pulls_default_update_style"> + <option value="merge" {{if or (not $pullRequestEnabled) (eq $prUnit.PullRequestsConfig.DefaultUpdateStyle "merge")}}selected{{end}}>{{ctx.Locale.Tr "repo.pulls.update_branch"}}</option> + <option value="rebase" {{if or (not $pullRequestEnabled) (eq $prUnit.PullRequestsConfig.DefaultUpdateStyle "rebase")}}selected{{end}}>{{ctx.Locale.Tr "repo.pulls.update_branch_rebase"}}</option> + </select>{{svg "octicon-triangle-down" 14 "dropdown icon"}} + <div class="default text"> + {{if (eq $prUnit.PullRequestsConfig.DefaultUpdateStyle "merge")}} + {{ctx.Locale.Tr "repo.pulls.update_branch"}} + {{end}} + {{if (eq $prUnit.PullRequestsConfig.DefaultUpdateStyle "rebase")}} + {{ctx.Locale.Tr "repo.pulls.update_branch_rebase"}} + {{end}} + </div> + <div class="menu"> + <div class="item" data-value="merge">{{ctx.Locale.Tr "repo.pulls.update_branch"}}</div> + <div class="item" data-value="rebase">{{ctx.Locale.Tr "repo.pulls.update_branch_rebase"}}</div> + </div> + </div> + </div> + <div class="field"> <div class="ui checkbox"> <input name="default_delete_branch_after_merge" type="checkbox" {{if or (not $pullRequestEnabled) ($prUnit.PullRequestsConfig.DefaultDeleteBranchAfterMerge)}}checked{{end}}> <label>{{ctx.Locale.Tr "repo.settings.pulls.default_delete_branch_after_merge"}}</label> diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index f0a0b86622..1feeebba98 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -23321,6 +23321,11 @@ "type": "string", "x-go-name": "DefaultMergeStyle" }, + "default_update_style": { + "description": "set to a update style to be used by this repository: \"rebase\" or \"merge\"", + "type": "string", + "x-go-name": "DefaultUpdateStyle" + }, "description": { "description": "a short description of the repository.", "type": "string", @@ -26605,6 +26610,10 @@ "type": "string", "x-go-name": "DefaultMergeStyle" }, + "default_update_style": { + "type": "string", + "x-go-name": "DefaultUpdateStyle" + }, "description": { "type": "string", "x-go-name": "Description" diff --git a/tests/integration/pull_update_test.go b/tests/integration/pull_update_test.go index 08041f0717..f36ea88c2b 100644 --- a/tests/integration/pull_update_test.go +++ b/tests/integration/pull_update_test.go @@ -4,6 +4,7 @@ package integration import ( + "fmt" "net/http" "net/url" "strings" @@ -16,6 +17,9 @@ import ( "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/setting" + api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/test" pull_service "code.gitea.io/gitea/services/pull" repo_service "code.gitea.io/gitea/services/repository" files_service "code.gitea.io/gitea/services/repository/files" @@ -83,6 +87,102 @@ func TestAPIPullUpdateByRebase(t *testing.T) { }) } +func TestAPIViewUpdateSettings(t *testing.T) { + onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { + defer tests.PrepareTestEnv(t)() + // Create PR to test + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + org26 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 26}) + pr := createOutdatedPR(t, user, org26) + + // Test GetDiverging + diffCount, err := pull_service.GetDiverging(git.DefaultContext, pr) + require.NoError(t, err) + assert.EqualValues(t, 1, diffCount.Behind) + assert.EqualValues(t, 1, diffCount.Ahead) + require.NoError(t, pr.LoadBaseRepo(db.DefaultContext)) + require.NoError(t, pr.LoadIssue(db.DefaultContext)) + + session := loginUser(t, "user2") + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAll) + + defaultUpdateStyle := "rebase" + editOption := api.EditRepoOption{ + DefaultUpdateStyle: &defaultUpdateStyle, + } + + req := NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", pr.BaseRepo.OwnerName, pr.BaseRepo.Name), editOption).AddTokenAuth(token) + session.MakeRequest(t, req, http.StatusOK) + assertViewPullUpdate(t, pr, session, "rebase", true) + + defaultUpdateStyle = "merge" + req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", pr.BaseRepo.OwnerName, pr.BaseRepo.Name), editOption).AddTokenAuth(token) + session.MakeRequest(t, req, http.StatusOK) + assertViewPullUpdate(t, pr, session, "merge", true) + }) +} + +func TestViewPullUpdateByMerge(t *testing.T) { + onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { + testViewPullUpdate(t, "merge") + }) +} + +func TestViewPullUpdateByRebase(t *testing.T) { + onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { + testViewPullUpdate(t, "rebase") + }) +} + +func testViewPullUpdate(t *testing.T, updateStyle string) { + defer test.MockVariableValue(&setting.Repository.PullRequest.DefaultUpdateStyle, updateStyle)() + defer tests.PrepareTestEnv(t)() + // Create PR to test + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + org26 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 26}) + pr := createOutdatedPR(t, user, org26) + + // Test GetDiverging + diffCount, err := pull_service.GetDiverging(git.DefaultContext, pr) + require.NoError(t, err) + assert.EqualValues(t, 1, diffCount.Behind) + assert.EqualValues(t, 1, diffCount.Ahead) + require.NoError(t, pr.LoadBaseRepo(db.DefaultContext)) + require.NoError(t, pr.LoadIssue(db.DefaultContext)) + + session := loginUser(t, "user2") + assertViewPullUpdate(t, pr, session, updateStyle, true) +} + +func assertViewPullUpdate(t *testing.T, pr *issues_model.PullRequest, session *TestSession, expectedStyle string, dropdownExpected bool) { + req := NewRequest(t, "GET", fmt.Sprintf("%s/%s/pulls/%d", pr.BaseRepo.OwnerName, pr.BaseRepo.Name, pr.Issue.Index)) + resp := session.MakeRequest(t, req, http.StatusOK) + + htmlDoc := NewHTMLParser(t, resp.Body) + // Verify that URL of the update button is shown correctly. + var mainExpectedURL string + mergeExpectedURL := fmt.Sprintf("/%s/%s/pulls/%d/update?style=merge", pr.BaseRepo.OwnerName, pr.BaseRepo.Name, pr.Issue.Index) + rebaseExpectedURL := fmt.Sprintf("/%s/%s/pulls/%d/update?style=rebase", pr.BaseRepo.OwnerName, pr.BaseRepo.Name, pr.Issue.Index) + if expectedStyle == "rebase" { + mainExpectedURL = rebaseExpectedURL + if dropdownExpected { + htmlDoc.AssertElement(t, fmt.Sprintf(".update-button .dropdown .menu .item[data-do=\"%s\"]:not(.active.selected)", mergeExpectedURL), true) + htmlDoc.AssertElement(t, fmt.Sprintf(".update-button .dropdown .menu .active.selected.item[data-do=\"%s\"]", rebaseExpectedURL), true) + } + } else { + mainExpectedURL = mergeExpectedURL + if dropdownExpected { + htmlDoc.AssertElement(t, fmt.Sprintf(".update-button .dropdown .menu .active.selected.item[data-do=\"%s\"]", mergeExpectedURL), true) + htmlDoc.AssertElement(t, fmt.Sprintf(".update-button .dropdown .menu .item[data-do=\"%s\"]:not(.active.selected)", rebaseExpectedURL), true) + } + } + if dropdownExpected { + htmlDoc.AssertElement(t, fmt.Sprintf(".update-button .button[data-do=\"%s\"]", mainExpectedURL), true) + } else { + htmlDoc.AssertElement(t, fmt.Sprintf("form[action=\"%s\"]", mainExpectedURL), true) + } +} + func createOutdatedPR(t *testing.T, actor, forkOrg *user_model.User) *issues_model.PullRequest { baseRepo, _, _ := tests.CreateDeclarativeRepo(t, actor, "repo-pr-update", nil, nil, nil) diff --git a/web_src/js/components/ActionRunStatus.vue b/web_src/js/components/ActionRunStatus.vue index 7ada543fea..eb7f780fbe 100644 --- a/web_src/js/components/ActionRunStatus.vue +++ b/web_src/js/components/ActionRunStatus.vue @@ -28,12 +28,13 @@ export default { }; </script> <template> - <span class="tw-flex tw-items-center" :data-tooltip-content="localeStatus" v-if="status"> + <span class="tw-flex tw-items-center" :data-tooltip-content="localeStatus ?? status" v-if="status"> <SvgIcon name="octicon-check-circle-fill" class="text green" :size="size" :class-name="className" v-if="status === 'success'"/> <SvgIcon name="octicon-skip" class="text grey" :size="size" :class-name="className" v-else-if="status === 'skipped'"/> + <SvgIcon name="octicon-stop" class="text yellow" :size="size" :class-name="className" v-else-if="status === 'cancelled'"/> <SvgIcon name="octicon-clock" class="text yellow" :size="size" :class-name="className" v-else-if="status === 'waiting'"/> <SvgIcon name="octicon-blocked" class="text yellow" :size="size" :class-name="className" v-else-if="status === 'blocked'"/> <SvgIcon name="octicon-meter" class="text yellow" :size="size" :class-name="'job-status-rotate ' + className" v-else-if="status === 'running'"/> - <SvgIcon name="octicon-x-circle-fill" class="text red" :size="size" v-else-if="['failure', 'cancelled', 'unknown'].includes(status)"/> + <SvgIcon name="octicon-x-circle-fill" class="text red" :size="size" v-else/><!-- failure, unknown --> </span> </template> diff --git a/web_src/js/components/RepoActionView.vue b/web_src/js/components/RepoActionView.vue index 13ac9427f3..136ad3693d 100644 --- a/web_src/js/components/RepoActionView.vue +++ b/web_src/js/components/RepoActionView.vue @@ -570,11 +570,13 @@ export function initRepositoryActionView() { .action-info-summary-title { display: flex; + align-items: center; + gap: 0.5em; } .action-info-summary-title-text { font-size: 20px; - margin: 0 0 0 8px; + margin: 0; flex: 1; overflow-wrap: anywhere; } diff --git a/web_src/js/svg.js b/web_src/js/svg.js index 9ef5f28e2f..76df7ff211 100644 --- a/web_src/js/svg.js +++ b/web_src/js/svg.js @@ -64,6 +64,7 @@ import octiconSidebarCollapse from '../../public/assets/img/svg/octicon-sidebar- import octiconSidebarExpand from '../../public/assets/img/svg/octicon-sidebar-expand.svg'; import octiconSkip from '../../public/assets/img/svg/octicon-skip.svg'; import octiconStar from '../../public/assets/img/svg/octicon-star.svg'; +import octiconStop from '../../public/assets/img/svg/octicon-stop.svg'; import octiconStrikethrough from '../../public/assets/img/svg/octicon-strikethrough.svg'; import octiconSync from '../../public/assets/img/svg/octicon-sync.svg'; import octiconTable from '../../public/assets/img/svg/octicon-table.svg'; @@ -138,6 +139,7 @@ const svgs = { 'octicon-sidebar-expand': octiconSidebarExpand, 'octicon-skip': octiconSkip, 'octicon-star': octiconStar, + 'octicon-stop': octiconStop, 'octicon-strikethrough': octiconStrikethrough, 'octicon-sync': octiconSync, 'octicon-table': octiconTable, |