mirror of
https://github.com/go-gitea/gitea.git
synced 2025-08-08 12:22:32 +00:00
Merge branch 'main' into dev/hezi/add-raw-diff-patch
This commit is contained in:
commit
f4a0fe369f
@ -230,18 +230,24 @@ func (status *CommitStatus) HideActionsURL(ctx context.Context) {
|
||||
|
||||
// CalcCommitStatus returns commit status state via some status, the commit statues should order by id desc
|
||||
func CalcCommitStatus(statuses []*CommitStatus) *CommitStatus {
|
||||
// This function is widely used, but it is not quite right.
|
||||
// Ideally it should return something like "CommitStatusSummary" with properly aggregated state.
|
||||
// GitHub's behavior: if all statuses are "skipped", GitHub will return "success" as the combined status.
|
||||
var lastStatus *CommitStatus
|
||||
state := api.CommitStatusSuccess
|
||||
for _, status := range statuses {
|
||||
if status.State.NoBetterThan(state) {
|
||||
if state == status.State || status.State.HasHigherPriorityThan(state) {
|
||||
state = status.State
|
||||
lastStatus = status
|
||||
}
|
||||
}
|
||||
if lastStatus == nil {
|
||||
if len(statuses) > 0 {
|
||||
// FIXME: a bad case: Gitea just returns the first commit status, its status is "skipped" in this case.
|
||||
lastStatus = statuses[0]
|
||||
} else {
|
||||
// FIXME: another bad case: if the "statuses" slice is empty, the returned value is an invalid CommitStatus, all its fields are empty.
|
||||
// Frontend code (tmpl&vue) sometimes depend on the empty fields to skip rendering commit status elements (need to double check in the future)
|
||||
lastStatus = &CommitStatus{}
|
||||
}
|
||||
}
|
||||
|
@ -59,7 +59,11 @@ func UpdateCommitStatusSummary(ctx context.Context, repoID int64, sha string) er
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
state := CalcCommitStatus(commitStatuses)
|
||||
// it guarantees that commitStatuses is not empty because this function is always called after a commit status is created
|
||||
if len(commitStatuses) == 0 {
|
||||
setting.PanicInDevOrTesting("no commit statuses found for repo %d and sha %s", repoID, sha)
|
||||
}
|
||||
state := CalcCommitStatus(commitStatuses) // non-empty commitStatuses is guaranteed
|
||||
// mysql will return 0 when update a record which state hasn't been changed which behaviour is different from other database,
|
||||
// so we need to use insert in on duplicate
|
||||
if setting.Database.Type.IsMySQL() {
|
||||
|
@ -12,9 +12,7 @@ import (
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/organization"
|
||||
access_model "code.gitea.io/gitea/models/perm/access"
|
||||
project_model "code.gitea.io/gitea/models/project"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
system_model "code.gitea.io/gitea/models/system"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
@ -715,138 +713,13 @@ func UpdateReactionsMigrationsByType(ctx context.Context, gitServiceType api.Git
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteIssuesByRepoID deletes issues by repositories id
|
||||
func DeleteIssuesByRepoID(ctx context.Context, repoID int64) (attachmentPaths []string, err error) {
|
||||
// MariaDB has a performance bug: https://jira.mariadb.org/browse/MDEV-16289
|
||||
// so here it uses "DELETE ... WHERE IN" with pre-queried IDs.
|
||||
sess := db.GetEngine(ctx)
|
||||
|
||||
for {
|
||||
issueIDs := make([]int64, 0, db.DefaultMaxInSize)
|
||||
|
||||
err := sess.Table(&Issue{}).Where("repo_id = ?", repoID).OrderBy("id").Limit(db.DefaultMaxInSize).Cols("id").Find(&issueIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(issueIDs) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
// Delete content histories
|
||||
_, err = sess.In("issue_id", issueIDs).Delete(&ContentHistory{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Delete comments and attachments
|
||||
_, err = sess.In("issue_id", issueIDs).Delete(&Comment{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Dependencies for issues in this repository
|
||||
_, err = sess.In("issue_id", issueIDs).Delete(&IssueDependency{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Delete dependencies for issues in other repositories
|
||||
_, err = sess.In("dependency_id", issueIDs).Delete(&IssueDependency{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = sess.In("issue_id", issueIDs).Delete(&IssueUser{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = sess.In("issue_id", issueIDs).Delete(&Reaction{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = sess.In("issue_id", issueIDs).Delete(&IssueWatch{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = sess.In("issue_id", issueIDs).Delete(&Stopwatch{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = sess.In("issue_id", issueIDs).Delete(&TrackedTime{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = sess.In("issue_id", issueIDs).Delete(&project_model.ProjectIssue{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = sess.In("dependent_issue_id", issueIDs).Delete(&Comment{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var attachments []*repo_model.Attachment
|
||||
err = sess.In("issue_id", issueIDs).Find(&attachments)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for j := range attachments {
|
||||
attachmentPaths = append(attachmentPaths, attachments[j].RelativePath())
|
||||
}
|
||||
|
||||
_, err = sess.In("issue_id", issueIDs).Delete(&repo_model.Attachment{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = sess.In("id", issueIDs).Delete(&Issue{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
func GetOrphanedIssueRepoIDs(ctx context.Context) ([]int64, error) {
|
||||
var repoIDs []int64
|
||||
if err := db.GetEngine(ctx).Table("issue").Distinct("issue.repo_id").
|
||||
Join("LEFT", "repository", "issue.repo_id=repository.id").
|
||||
Where(builder.IsNull{"repository.id"}).
|
||||
Find(&repoIDs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return attachmentPaths, err
|
||||
}
|
||||
|
||||
// DeleteOrphanedIssues delete issues without a repo
|
||||
func DeleteOrphanedIssues(ctx context.Context) error {
|
||||
var attachmentPaths []string
|
||||
err := db.WithTx(ctx, func(ctx context.Context) error {
|
||||
var ids []int64
|
||||
|
||||
if err := db.GetEngine(ctx).Table("issue").Distinct("issue.repo_id").
|
||||
Join("LEFT", "repository", "issue.repo_id=repository.id").
|
||||
Where(builder.IsNull{"repository.id"}).GroupBy("issue.repo_id").
|
||||
Find(&ids); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := range ids {
|
||||
paths, err := DeleteIssuesByRepoID(ctx, ids[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
attachmentPaths = append(attachmentPaths, paths...)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Remove issue attachment files.
|
||||
for i := range attachmentPaths {
|
||||
// FIXME: it's not right, because the attachment might not be on local filesystem
|
||||
system_model.RemoveAllWithNotice(ctx, "Delete issue attachment", attachmentPaths[i])
|
||||
}
|
||||
return nil
|
||||
return repoIDs, nil
|
||||
}
|
||||
|
@ -43,21 +43,23 @@ func IsWorkflow(path string) bool {
|
||||
return strings.HasPrefix(path, ".gitea/workflows") || strings.HasPrefix(path, ".github/workflows")
|
||||
}
|
||||
|
||||
func ListWorkflows(commit *git.Commit) (git.Entries, error) {
|
||||
tree, err := commit.SubTree(".gitea/workflows")
|
||||
func ListWorkflows(commit *git.Commit) (string, git.Entries, error) {
|
||||
rpath := ".gitea/workflows"
|
||||
tree, err := commit.SubTree(rpath)
|
||||
if _, ok := err.(git.ErrNotExist); ok {
|
||||
tree, err = commit.SubTree(".github/workflows")
|
||||
rpath = ".github/workflows"
|
||||
tree, err = commit.SubTree(rpath)
|
||||
}
|
||||
if _, ok := err.(git.ErrNotExist); ok {
|
||||
return nil, nil
|
||||
return "", nil, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
entries, err := tree.ListEntriesRecursiveFast()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
ret := make(git.Entries, 0, len(entries))
|
||||
@ -66,7 +68,7 @@ func ListWorkflows(commit *git.Commit) (git.Entries, error) {
|
||||
ret = append(ret, entry)
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
return rpath, ret, nil
|
||||
}
|
||||
|
||||
func GetContentFromEntry(entry *git.TreeEntry) ([]byte, error) {
|
||||
@ -102,7 +104,7 @@ func DetectWorkflows(
|
||||
payload api.Payloader,
|
||||
detectSchedule bool,
|
||||
) ([]*DetectedWorkflow, []*DetectedWorkflow, error) {
|
||||
entries, err := ListWorkflows(commit)
|
||||
_, entries, err := ListWorkflows(commit)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@ -147,7 +149,7 @@ func DetectWorkflows(
|
||||
}
|
||||
|
||||
func DetectScheduledWorkflows(gitRepo *git.Repository, commit *git.Commit) ([]*DetectedWorkflow, error) {
|
||||
entries, err := ListWorkflows(commit)
|
||||
_, entries, err := ListWorkflows(commit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -18,6 +18,8 @@ const (
|
||||
CommitStatusFailure CommitStatusState = "failure"
|
||||
// CommitStatusWarning is for when the CommitStatus is Warning
|
||||
CommitStatusWarning CommitStatusState = "warning"
|
||||
// CommitStatusSkipped is for when CommitStatus is Skipped
|
||||
CommitStatusSkipped CommitStatusState = "skipped"
|
||||
)
|
||||
|
||||
var commitStatusPriorities = map[CommitStatusState]int{
|
||||
@ -26,25 +28,17 @@ var commitStatusPriorities = map[CommitStatusState]int{
|
||||
CommitStatusWarning: 2,
|
||||
CommitStatusPending: 3,
|
||||
CommitStatusSuccess: 4,
|
||||
CommitStatusSkipped: 5,
|
||||
}
|
||||
|
||||
func (css CommitStatusState) String() string {
|
||||
return string(css)
|
||||
}
|
||||
|
||||
// NoBetterThan returns true if this State is no better than the given State
|
||||
// This function only handles the states defined in CommitStatusPriorities
|
||||
func (css CommitStatusState) NoBetterThan(css2 CommitStatusState) bool {
|
||||
// NoBetterThan only handles the 5 states above
|
||||
if _, exist := commitStatusPriorities[css]; !exist {
|
||||
return false
|
||||
}
|
||||
|
||||
if _, exist := commitStatusPriorities[css2]; !exist {
|
||||
return false
|
||||
}
|
||||
|
||||
return commitStatusPriorities[css] <= commitStatusPriorities[css2]
|
||||
// HasHigherPriorityThan returns true if this state has higher priority than the other
|
||||
// Undefined states are considered to have the highest priority like CommitStatusError(0)
|
||||
func (css CommitStatusState) HasHigherPriorityThan(other CommitStatusState) bool {
|
||||
return commitStatusPriorities[css] < commitStatusPriorities[other]
|
||||
}
|
||||
|
||||
// IsPending represents if commit status state is pending
|
||||
|
@ -10,165 +10,21 @@ import (
|
||||
)
|
||||
|
||||
func TestNoBetterThan(t *testing.T) {
|
||||
type args struct {
|
||||
css CommitStatusState
|
||||
css2 CommitStatusState
|
||||
}
|
||||
var unExpectedState CommitStatusState
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want bool
|
||||
s1, s2 CommitStatusState
|
||||
higher bool
|
||||
}{
|
||||
{
|
||||
name: "success is no better than success",
|
||||
args: args{
|
||||
css: CommitStatusSuccess,
|
||||
css2: CommitStatusSuccess,
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "success is no better than pending",
|
||||
args: args{
|
||||
css: CommitStatusSuccess,
|
||||
css2: CommitStatusPending,
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "success is no better than failure",
|
||||
args: args{
|
||||
css: CommitStatusSuccess,
|
||||
css2: CommitStatusFailure,
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "success is no better than error",
|
||||
args: args{
|
||||
css: CommitStatusSuccess,
|
||||
css2: CommitStatusError,
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "pending is no better than success",
|
||||
args: args{
|
||||
css: CommitStatusPending,
|
||||
css2: CommitStatusSuccess,
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "pending is no better than pending",
|
||||
args: args{
|
||||
css: CommitStatusPending,
|
||||
css2: CommitStatusPending,
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "pending is no better than failure",
|
||||
args: args{
|
||||
css: CommitStatusPending,
|
||||
css2: CommitStatusFailure,
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "pending is no better than error",
|
||||
args: args{
|
||||
css: CommitStatusPending,
|
||||
css2: CommitStatusError,
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "failure is no better than success",
|
||||
args: args{
|
||||
css: CommitStatusFailure,
|
||||
css2: CommitStatusSuccess,
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "failure is no better than pending",
|
||||
args: args{
|
||||
css: CommitStatusFailure,
|
||||
css2: CommitStatusPending,
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "failure is no better than failure",
|
||||
args: args{
|
||||
css: CommitStatusFailure,
|
||||
css2: CommitStatusFailure,
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "failure is no better than error",
|
||||
args: args{
|
||||
css: CommitStatusFailure,
|
||||
css2: CommitStatusError,
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "error is no better than success",
|
||||
args: args{
|
||||
css: CommitStatusError,
|
||||
css2: CommitStatusSuccess,
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "error is no better than pending",
|
||||
args: args{
|
||||
css: CommitStatusError,
|
||||
css2: CommitStatusPending,
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "error is no better than failure",
|
||||
args: args{
|
||||
css: CommitStatusError,
|
||||
css2: CommitStatusFailure,
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "error is no better than error",
|
||||
args: args{
|
||||
css: CommitStatusError,
|
||||
css2: CommitStatusError,
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "unExpectedState is no better than success",
|
||||
args: args{
|
||||
css: unExpectedState,
|
||||
css2: CommitStatusSuccess,
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "unExpectedState is no better than unExpectedState",
|
||||
args: args{
|
||||
css: unExpectedState,
|
||||
css2: unExpectedState,
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{CommitStatusError, CommitStatusFailure, true},
|
||||
{CommitStatusFailure, CommitStatusWarning, true},
|
||||
{CommitStatusWarning, CommitStatusPending, true},
|
||||
{CommitStatusPending, CommitStatusSuccess, true},
|
||||
{CommitStatusSuccess, CommitStatusSkipped, true},
|
||||
|
||||
{CommitStatusError, "unknown-xxx", false},
|
||||
{"unknown-xxx", CommitStatusFailure, true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := tt.args.css.NoBetterThan(tt.args.css2)
|
||||
assert.Equal(t, tt.want, result)
|
||||
})
|
||||
assert.Equal(t, tt.higher, tt.s1.HasHigherPriorityThan(tt.s2), "s1=%s, s2=%s, expected=%v", tt.s1, tt.s2, tt.higher)
|
||||
}
|
||||
assert.False(t, CommitStatusError.HasHigherPriorityThan(CommitStatusError))
|
||||
}
|
||||
|
@ -3815,6 +3815,7 @@ runs.expire_log_message = Logs have been purged because they were too old.
|
||||
runs.delete = Delete workflow run
|
||||
runs.delete.description = Are you sure you want to permanently delete this workflow run? This action cannot be undone.
|
||||
runs.not_done = This workflow run is not done.
|
||||
runs.view_workflow_file = View workflow file
|
||||
|
||||
workflow.disable = Disable Workflow
|
||||
workflow.disable_success = Workflow '%s' disabled successfully.
|
||||
|
@ -1228,6 +1228,7 @@ migrate.migrating_issues=Migration des tickets
|
||||
migrate.migrating_pulls=Migration des demandes d'ajout
|
||||
migrate.cancel_migrating_title=Annuler la migration
|
||||
migrate.cancel_migrating_confirm=Voulez-vous abandonner cette migration ?
|
||||
migrating_status=Migration des statuts
|
||||
|
||||
mirror_from=miroir de
|
||||
forked_from=bifurqué depuis
|
||||
@ -3813,6 +3814,7 @@ runs.expire_log_message=Les journaux ont été supprimés car ils étaient trop
|
||||
runs.delete=Supprimer cette exécution
|
||||
runs.delete.description=Êtes-vous sûr de vouloir supprimer définitivement cette exécution ? Cette action ne peut pas être annulée.
|
||||
runs.not_done=Cette exécution du flux de travail n’est pas terminée.
|
||||
runs.view_workflow_file=Voir le fichier du flux de travail
|
||||
|
||||
workflow.disable=Désactiver le flux de travail
|
||||
workflow.disable_success=Le flux de travail « %s » a bien été désactivé.
|
||||
|
@ -3814,6 +3814,7 @@ runs.expire_log_message=Glanadh logaí toisc go raibh siad ró-sean.
|
||||
runs.delete=Scrios rith sreabha oibre
|
||||
runs.delete.description=An bhfuil tú cinnte gur mian leat an rith sreabha oibre seo a scriosadh go buan? Ní féidir an gníomh seo a chealú.
|
||||
runs.not_done=Níl an rith sreabha oibre seo críochnaithe.
|
||||
runs.view_workflow_file=Féach ar chomhad sreabha oibre
|
||||
|
||||
workflow.disable=Díchumasaigh sreabhadh oibre
|
||||
workflow.disable_success=D'éirigh le sreabhadh oibre '%s' a dhíchumasú.
|
||||
|
@ -130,6 +130,7 @@ pin=ピン留め
|
||||
unpin=ピン留め解除
|
||||
|
||||
artifacts=成果物
|
||||
expired=期限切れ
|
||||
confirm_delete_artifact=アーティファクト %s を削除してよろしいですか?
|
||||
|
||||
archived=アーカイブ
|
||||
@ -1227,6 +1228,7 @@ migrate.migrating_issues=イシュー移行中
|
||||
migrate.migrating_pulls=プルリクエスト移行中
|
||||
migrate.cancel_migrating_title=移行のキャンセル
|
||||
migrate.cancel_migrating_confirm=この移行をキャンセルしますか?
|
||||
migrating_status=移行状況
|
||||
|
||||
mirror_from=ミラー元
|
||||
forked_from=フォーク元
|
||||
@ -3812,6 +3814,7 @@ runs.expire_log_message=ログは古すぎるため消去されています。
|
||||
runs.delete=ワークフローの実行を削除
|
||||
runs.delete.description=このワークフローを完全に削除してもよろしいですか?この操作は元に戻せません。
|
||||
runs.not_done=このワークフローの実行は完了していません。
|
||||
runs.view_workflow_file=ワークフローファイルを表示
|
||||
|
||||
workflow.disable=ワークフローを無効にする
|
||||
workflow.disable_success=ワークフロー '%s' が無効になりました。
|
||||
|
@ -3813,6 +3813,7 @@ runs.expire_log_message=Os registros foram removidos porque eram muito antigos.
|
||||
runs.delete=Eliminar execução da sequência de trabalho
|
||||
runs.delete.description=Tem a certeza que pretende eliminar permanentemente a execução desta sequência de trabalho? Esta operação não poderá ser desfeita.
|
||||
runs.not_done=A execução desta sequência de trabalho ainda não terminou.
|
||||
runs.view_workflow_file=Ver ficheiro da sequência de trabalho
|
||||
|
||||
workflow.disable=Desabilitar sequência de trabalho
|
||||
workflow.disable_success=A sequência de trabalho '%s' foi desabilitada com sucesso.
|
||||
|
@ -126,7 +126,7 @@ func prepareWorkflowDispatchTemplate(ctx *context.Context, commit *git.Commit) (
|
||||
|
||||
var curWorkflow *model.Workflow
|
||||
|
||||
entries, err := actions.ListWorkflows(commit)
|
||||
_, entries, err := actions.ListWorkflows(commit)
|
||||
if err != nil {
|
||||
ctx.ServerError("ListWorkflows", err)
|
||||
return nil
|
||||
|
@ -64,6 +64,36 @@ func View(ctx *context_module.Context) {
|
||||
ctx.HTML(http.StatusOK, tplViewActions)
|
||||
}
|
||||
|
||||
func ViewWorkflowFile(ctx *context_module.Context) {
|
||||
runIndex := getRunIndex(ctx)
|
||||
run, err := actions_model.GetRunByIndex(ctx, ctx.Repo.Repository.ID, runIndex)
|
||||
if err != nil {
|
||||
ctx.NotFoundOrServerError("GetRunByIndex", func(err error) bool {
|
||||
return errors.Is(err, util.ErrNotExist)
|
||||
}, err)
|
||||
return
|
||||
}
|
||||
commit, err := ctx.Repo.GitRepo.GetCommit(run.CommitSHA)
|
||||
if err != nil {
|
||||
ctx.NotFoundOrServerError("GetCommit", func(err error) bool {
|
||||
return errors.Is(err, util.ErrNotExist)
|
||||
}, err)
|
||||
return
|
||||
}
|
||||
rpath, entries, err := actions.ListWorkflows(commit)
|
||||
if err != nil {
|
||||
ctx.ServerError("ListWorkflows", err)
|
||||
return
|
||||
}
|
||||
for _, entry := range entries {
|
||||
if entry.Name() == run.WorkflowID {
|
||||
ctx.Redirect(fmt.Sprintf("%s/src/commit/%s/%s/%s", ctx.Repo.RepoLink, url.PathEscape(run.CommitSHA), util.PathEscapeSegments(rpath), util.PathEscapeSegments(run.WorkflowID)))
|
||||
return
|
||||
}
|
||||
}
|
||||
ctx.NotFound(nil)
|
||||
}
|
||||
|
||||
type LogCursor struct {
|
||||
Step int `json:"step"`
|
||||
Cursor int64 `json:"cursor"`
|
||||
|
@ -1445,6 +1445,7 @@ func registerWebRoutes(m *web.Router) {
|
||||
m.Post("/rerun", reqRepoActionsWriter, actions.Rerun)
|
||||
m.Get("/logs", actions.Logs)
|
||||
})
|
||||
m.Get("/workflow", actions.ViewWorkflowFile)
|
||||
m.Post("/cancel", reqRepoActionsWriter, actions.Cancel)
|
||||
m.Post("/approve", reqRepoActionsWriter, actions.Approve)
|
||||
m.Post("/delete", reqRepoActionsWriter, actions.Delete)
|
||||
|
@ -149,12 +149,14 @@ func createCommitStatus(ctx context.Context, job *actions_model.ActionRunJob) er
|
||||
|
||||
func toCommitStatus(status actions_model.Status) api.CommitStatusState {
|
||||
switch status {
|
||||
case actions_model.StatusSuccess, actions_model.StatusSkipped:
|
||||
case actions_model.StatusSuccess:
|
||||
return api.CommitStatusSuccess
|
||||
case actions_model.StatusFailure, actions_model.StatusCancelled:
|
||||
return api.CommitStatusFailure
|
||||
case actions_model.StatusWaiting, actions_model.StatusBlocked, actions_model.StatusRunning:
|
||||
return api.CommitStatusPending
|
||||
case actions_model.StatusSkipped:
|
||||
return api.CommitStatusSkipped
|
||||
default:
|
||||
return api.CommitStatusError
|
||||
}
|
||||
|
@ -31,16 +31,6 @@ import (
|
||||
"github.com/nektos/act/pkg/model"
|
||||
)
|
||||
|
||||
func getActionWorkflowPath(commit *git.Commit) string {
|
||||
paths := []string{".gitea/workflows", ".github/workflows"}
|
||||
for _, treePath := range paths {
|
||||
if _, err := commit.SubTree(treePath); err == nil {
|
||||
return treePath
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func getActionWorkflowEntry(ctx *context.APIContext, commit *git.Commit, folder string, entry *git.TreeEntry) *api.ActionWorkflow {
|
||||
cfgUnit := ctx.Repo.Repository.MustGetUnit(ctx, unit.TypeActions)
|
||||
cfg := cfgUnit.ActionsConfig()
|
||||
@ -109,14 +99,12 @@ func ListActionWorkflows(ctx *context.APIContext) ([]*api.ActionWorkflow, error)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
entries, err := actions.ListWorkflows(defaultBranchCommit)
|
||||
folder, entries, err := actions.ListWorkflows(defaultBranchCommit)
|
||||
if err != nil {
|
||||
ctx.APIError(http.StatusNotFound, err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
folder := getActionWorkflowPath(defaultBranchCommit)
|
||||
|
||||
workflows := make([]*api.ActionWorkflow, len(entries))
|
||||
for i, entry := range entries {
|
||||
workflows[i] = getActionWorkflowEntry(ctx, defaultBranchCommit, folder, entry)
|
||||
@ -185,7 +173,7 @@ func DispatchActionWorkflow(ctx reqctx.RequestContext, doer *user_model.User, re
|
||||
}
|
||||
|
||||
// get workflow entry from runTargetCommit
|
||||
entries, err := actions.ListWorkflows(runTargetCommit)
|
||||
_, entries, err := actions.ListWorkflows(runTargetCommit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -42,13 +42,14 @@ func ToCombinedStatus(ctx context.Context, statuses []*git_model.CommitStatus, r
|
||||
SHA: statuses[0].SHA,
|
||||
TotalCount: len(statuses),
|
||||
Repository: repo,
|
||||
URL: "",
|
||||
URL: "", // never set or used?
|
||||
State: api.CommitStatusSuccess,
|
||||
}
|
||||
|
||||
retStatus.Statuses = make([]*api.CommitStatus, 0, len(statuses))
|
||||
for _, status := range statuses {
|
||||
retStatus.Statuses = append(retStatus.Statuses, ToCommitStatus(ctx, status))
|
||||
if retStatus.State == "" || status.State.NoBetterThan(retStatus.State) {
|
||||
if status.State.HasHigherPriorityThan(retStatus.State) {
|
||||
retStatus.State = status.State
|
||||
}
|
||||
}
|
||||
@ -57,9 +58,13 @@ func ToCombinedStatus(ctx context.Context, statuses []*git_model.CommitStatus, r
|
||||
// > failure if any of the contexts report as error or failure
|
||||
// > pending if there are no statuses or a context is pending
|
||||
// > success if the latest status for all contexts is success
|
||||
if retStatus.State.IsError() {
|
||||
retStatus.State = api.CommitStatusFailure
|
||||
switch retStatus.State {
|
||||
case api.CommitStatusSkipped:
|
||||
retStatus.State = api.CommitStatusSuccess // all skipped means success
|
||||
case api.CommitStatusPending, api.CommitStatusSuccess:
|
||||
// use the current state for pending or success
|
||||
default:
|
||||
retStatus.State = api.CommitStatusFailure // otherwise, it is a failure
|
||||
}
|
||||
|
||||
return retStatus
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ import (
|
||||
secret_model "code.gitea.io/gitea/models/secret"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
issue_service "code.gitea.io/gitea/services/issue"
|
||||
)
|
||||
|
||||
type consistencyCheck struct {
|
||||
@ -93,7 +94,7 @@ func prepareDBConsistencyChecks() []consistencyCheck {
|
||||
// find issues without existing repository
|
||||
Name: "Orphaned Issues without existing repository",
|
||||
Counter: issues_model.CountOrphanedIssues,
|
||||
Fixer: asFixer(issues_model.DeleteOrphanedIssues),
|
||||
Fixer: asFixer(issue_service.DeleteOrphanedIssues),
|
||||
},
|
||||
// find releases without existing repository
|
||||
genericOrphanCheck("Orphaned Releases without existing repository",
|
||||
|
@ -190,9 +190,13 @@ func DeleteIssue(ctx context.Context, doer *user_model.User, gitRepo *git.Reposi
|
||||
}
|
||||
|
||||
// delete entries in database
|
||||
if err := deleteIssue(ctx, issue); err != nil {
|
||||
attachmentPaths, err := deleteIssue(ctx, issue)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, attachmentPath := range attachmentPaths {
|
||||
system_model.RemoveStorageWithNotice(ctx, storage.Attachments, "Delete issue attachment", attachmentPath)
|
||||
}
|
||||
|
||||
// delete pull request related git data
|
||||
if issue.IsPull && gitRepo != nil {
|
||||
@ -256,45 +260,45 @@ func GetRefEndNamesAndURLs(issues []*issues_model.Issue, repoLink string) (map[i
|
||||
}
|
||||
|
||||
// deleteIssue deletes the issue
|
||||
func deleteIssue(ctx context.Context, issue *issues_model.Issue) error {
|
||||
func deleteIssue(ctx context.Context, issue *issues_model.Issue) ([]string, error) {
|
||||
ctx, committer, err := db.TxContext(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
defer committer.Close()
|
||||
|
||||
e := db.GetEngine(ctx)
|
||||
if _, err := e.ID(issue.ID).NoAutoCondition().Delete(issue); err != nil {
|
||||
return err
|
||||
if _, err := db.GetEngine(ctx).ID(issue.ID).NoAutoCondition().Delete(issue); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// update the total issue numbers
|
||||
if err := repo_model.UpdateRepoIssueNumbers(ctx, issue.RepoID, issue.IsPull, false); err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
// if the issue is closed, update the closed issue numbers
|
||||
if issue.IsClosed {
|
||||
if err := repo_model.UpdateRepoIssueNumbers(ctx, issue.RepoID, issue.IsPull, true); err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := issues_model.UpdateMilestoneCounters(ctx, issue.MilestoneID); err != nil {
|
||||
return fmt.Errorf("error updating counters for milestone id %d: %w",
|
||||
return nil, fmt.Errorf("error updating counters for milestone id %d: %w",
|
||||
issue.MilestoneID, err)
|
||||
}
|
||||
|
||||
if err := activities_model.DeleteIssueActions(ctx, issue.RepoID, issue.ID, issue.Index); err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// find attachments related to this issue and remove them
|
||||
if err := issue.LoadAttributes(ctx); err != nil {
|
||||
return err
|
||||
if err := issue.LoadAttachments(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var attachmentPaths []string
|
||||
for i := range issue.Attachments {
|
||||
system_model.RemoveStorageWithNotice(ctx, storage.Attachments, "Delete issue attachment", issue.Attachments[i].RelativePath())
|
||||
attachmentPaths = append(attachmentPaths, issue.Attachments[i].RelativePath())
|
||||
}
|
||||
|
||||
// delete all database data still assigned to this issue
|
||||
@ -318,8 +322,68 @@ func deleteIssue(ctx context.Context, issue *issues_model.Issue) error {
|
||||
&issues_model.Comment{DependentIssueID: issue.ID},
|
||||
&issues_model.IssuePin{IssueID: issue.ID},
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := committer.Commit(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return attachmentPaths, nil
|
||||
}
|
||||
|
||||
// DeleteOrphanedIssues delete issues without a repo
|
||||
func DeleteOrphanedIssues(ctx context.Context) error {
|
||||
var attachmentPaths []string
|
||||
err := db.WithTx(ctx, func(ctx context.Context) error {
|
||||
repoIDs, err := issues_model.GetOrphanedIssueRepoIDs(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range repoIDs {
|
||||
paths, err := DeleteIssuesByRepoID(ctx, repoIDs[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
attachmentPaths = append(attachmentPaths, paths...)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return committer.Commit()
|
||||
// Remove issue attachment files.
|
||||
for i := range attachmentPaths {
|
||||
system_model.RemoveStorageWithNotice(ctx, storage.Attachments, "Delete issue attachment", attachmentPaths[i])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteIssuesByRepoID deletes issues by repositories id
|
||||
func DeleteIssuesByRepoID(ctx context.Context, repoID int64) (attachmentPaths []string, err error) {
|
||||
for {
|
||||
issues := make([]*issues_model.Issue, 0, db.DefaultMaxInSize)
|
||||
if err := db.GetEngine(ctx).
|
||||
Where("repo_id = ?", repoID).
|
||||
OrderBy("id").
|
||||
Limit(db.DefaultMaxInSize).
|
||||
Find(&issues); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(issues) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
for _, issue := range issues {
|
||||
issueAttachPaths, err := deleteIssue(ctx, issue)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("deleteIssue [issue_id: %d]: %w", issue.ID, err)
|
||||
}
|
||||
|
||||
attachmentPaths = append(attachmentPaths, issueAttachPaths...)
|
||||
}
|
||||
}
|
||||
|
||||
return attachmentPaths, err
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ func TestIssue_DeleteIssue(t *testing.T) {
|
||||
ID: issueIDs[2],
|
||||
}
|
||||
|
||||
err = deleteIssue(db.DefaultContext, issue)
|
||||
_, err = deleteIssue(db.DefaultContext, issue)
|
||||
assert.NoError(t, err)
|
||||
issueIDs, err = issues_model.GetIssueIDsByRepoID(db.DefaultContext, 1)
|
||||
assert.NoError(t, err)
|
||||
@ -55,7 +55,7 @@ func TestIssue_DeleteIssue(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
issue, err = issues_model.GetIssueByID(db.DefaultContext, 4)
|
||||
assert.NoError(t, err)
|
||||
err = deleteIssue(db.DefaultContext, issue)
|
||||
_, err = deleteIssue(db.DefaultContext, issue)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, attachments, 2)
|
||||
for i := range attachments {
|
||||
@ -78,7 +78,7 @@ func TestIssue_DeleteIssue(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, left)
|
||||
|
||||
err = deleteIssue(db.DefaultContext, issue2)
|
||||
_, err = deleteIssue(db.DefaultContext, issue2)
|
||||
assert.NoError(t, err)
|
||||
left, err = issues_model.IssueNoDependenciesLeft(db.DefaultContext, issue1)
|
||||
assert.NoError(t, err)
|
||||
|
@ -46,59 +46,33 @@ func MergeRequiredContextsCommitStatus(commitStatuses []*git_model.CommitStatus,
|
||||
|
||||
// If required rule not match any action, then it is pending
|
||||
if targetStatus == "" {
|
||||
if structs.CommitStatusPending.NoBetterThan(returnedStatus) {
|
||||
if structs.CommitStatusPending.HasHigherPriorityThan(returnedStatus) {
|
||||
returnedStatus = structs.CommitStatusPending
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if targetStatus.NoBetterThan(returnedStatus) {
|
||||
if targetStatus.HasHigherPriorityThan(returnedStatus) {
|
||||
returnedStatus = targetStatus
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if matchedCount == 0 && returnedStatus == structs.CommitStatusSuccess {
|
||||
status := git_model.CalcCommitStatus(commitStatuses)
|
||||
if status != nil {
|
||||
return status.State
|
||||
if len(commitStatuses) == 0 {
|
||||
// "no statuses" should mean "pending"
|
||||
return structs.CommitStatusPending
|
||||
}
|
||||
return structs.CommitStatusSuccess
|
||||
status := git_model.CalcCommitStatus(commitStatuses)
|
||||
if status.State == structs.CommitStatusSkipped {
|
||||
return structs.CommitStatusSuccess // if all statuses are skipped, return success
|
||||
}
|
||||
return status.State
|
||||
}
|
||||
|
||||
return returnedStatus
|
||||
}
|
||||
|
||||
// IsCommitStatusContextSuccess returns true if all required status check contexts succeed.
|
||||
func IsCommitStatusContextSuccess(commitStatuses []*git_model.CommitStatus, requiredContexts []string) bool {
|
||||
// If no specific context is required, require that last commit status is a success
|
||||
if len(requiredContexts) == 0 {
|
||||
status := git_model.CalcCommitStatus(commitStatuses)
|
||||
if status == nil || status.State != structs.CommitStatusSuccess {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
for _, ctx := range requiredContexts {
|
||||
var found bool
|
||||
for _, commitStatus := range commitStatuses {
|
||||
if commitStatus.Context == ctx {
|
||||
if commitStatus.State != structs.CommitStatusSuccess {
|
||||
return false
|
||||
}
|
||||
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// IsPullCommitStatusPass returns if all required status checks PASS
|
||||
func IsPullCommitStatusPass(ctx context.Context, pr *issues_model.PullRequest) (bool, error) {
|
||||
pb, err := git_model.GetFirstMatchProtectedBranchRule(ctx, pr.BaseRepoID, pr.BaseBranch)
|
||||
|
@ -14,52 +14,70 @@ import (
|
||||
)
|
||||
|
||||
func TestMergeRequiredContextsCommitStatus(t *testing.T) {
|
||||
testCases := [][]*git_model.CommitStatus{
|
||||
cases := []struct {
|
||||
commitStatuses []*git_model.CommitStatus
|
||||
requiredContexts []string
|
||||
expected structs.CommitStatusState
|
||||
}{
|
||||
{
|
||||
{Context: "Build 1", State: structs.CommitStatusSuccess},
|
||||
{Context: "Build 2", State: structs.CommitStatusSuccess},
|
||||
{Context: "Build 3", State: structs.CommitStatusSuccess},
|
||||
commitStatuses: []*git_model.CommitStatus{},
|
||||
requiredContexts: []string{},
|
||||
expected: structs.CommitStatusPending,
|
||||
},
|
||||
{
|
||||
{Context: "Build 1", State: structs.CommitStatusSuccess},
|
||||
{Context: "Build 2", State: structs.CommitStatusSuccess},
|
||||
{Context: "Build 2t", State: structs.CommitStatusPending},
|
||||
commitStatuses: []*git_model.CommitStatus{
|
||||
{Context: "Build xxx", State: structs.CommitStatusSkipped},
|
||||
},
|
||||
requiredContexts: []string{"Build*"},
|
||||
expected: structs.CommitStatusSuccess,
|
||||
},
|
||||
{
|
||||
{Context: "Build 1", State: structs.CommitStatusSuccess},
|
||||
{Context: "Build 2", State: structs.CommitStatusSuccess},
|
||||
{Context: "Build 2t", State: structs.CommitStatusFailure},
|
||||
commitStatuses: []*git_model.CommitStatus{
|
||||
{Context: "Build 1", State: structs.CommitStatusSkipped},
|
||||
{Context: "Build 2", State: structs.CommitStatusSuccess},
|
||||
{Context: "Build 3", State: structs.CommitStatusSuccess},
|
||||
},
|
||||
requiredContexts: []string{"Build*"},
|
||||
expected: structs.CommitStatusSuccess,
|
||||
},
|
||||
{
|
||||
{Context: "Build 1", State: structs.CommitStatusSuccess},
|
||||
{Context: "Build 2", State: structs.CommitStatusSuccess},
|
||||
{Context: "Build 2t", State: structs.CommitStatusSuccess},
|
||||
commitStatuses: []*git_model.CommitStatus{
|
||||
{Context: "Build 1", State: structs.CommitStatusSuccess},
|
||||
{Context: "Build 2", State: structs.CommitStatusSuccess},
|
||||
{Context: "Build 2t", State: structs.CommitStatusPending},
|
||||
},
|
||||
requiredContexts: []string{"Build*", "Build 2t*"},
|
||||
expected: structs.CommitStatusPending,
|
||||
},
|
||||
{
|
||||
{Context: "Build 1", State: structs.CommitStatusSuccess},
|
||||
{Context: "Build 2", State: structs.CommitStatusSuccess},
|
||||
{Context: "Build 2t", State: structs.CommitStatusSuccess},
|
||||
commitStatuses: []*git_model.CommitStatus{
|
||||
{Context: "Build 1", State: structs.CommitStatusSuccess},
|
||||
{Context: "Build 2", State: structs.CommitStatusSuccess},
|
||||
{Context: "Build 2t", State: structs.CommitStatusFailure},
|
||||
},
|
||||
requiredContexts: []string{"Build*", "Build 2t*"},
|
||||
expected: structs.CommitStatusFailure,
|
||||
},
|
||||
{
|
||||
commitStatuses: []*git_model.CommitStatus{
|
||||
{Context: "Build 1", State: structs.CommitStatusSuccess},
|
||||
{Context: "Build 2", State: structs.CommitStatusSuccess},
|
||||
{Context: "Build 2t", State: structs.CommitStatusSuccess},
|
||||
},
|
||||
requiredContexts: []string{"Build*", "Build 2t*", "Build 3*"},
|
||||
expected: structs.CommitStatusPending,
|
||||
},
|
||||
{
|
||||
commitStatuses: []*git_model.CommitStatus{
|
||||
{Context: "Build 1", State: structs.CommitStatusSuccess},
|
||||
{Context: "Build 2", State: structs.CommitStatusSuccess},
|
||||
{Context: "Build 2t", State: structs.CommitStatusSuccess},
|
||||
},
|
||||
requiredContexts: []string{"Build*", "Build *", "Build 2t*", "Build 1*"},
|
||||
expected: structs.CommitStatusSuccess,
|
||||
},
|
||||
}
|
||||
testCasesRequiredContexts := [][]string{
|
||||
{"Build*"},
|
||||
{"Build*", "Build 2t*"},
|
||||
{"Build*", "Build 2t*"},
|
||||
{"Build*", "Build 2t*", "Build 3*"},
|
||||
{"Build*", "Build *", "Build 2t*", "Build 1*"},
|
||||
}
|
||||
|
||||
testCasesExpected := []structs.CommitStatusState{
|
||||
structs.CommitStatusSuccess,
|
||||
structs.CommitStatusPending,
|
||||
structs.CommitStatusFailure,
|
||||
structs.CommitStatusPending,
|
||||
structs.CommitStatusSuccess,
|
||||
}
|
||||
|
||||
for i, commitStatuses := range testCases {
|
||||
if MergeRequiredContextsCommitStatus(commitStatuses, testCasesRequiredContexts[i]) != testCasesExpected[i] {
|
||||
assert.Fail(t, "Test case failed", "Test case %d failed", i+1)
|
||||
}
|
||||
for i, c := range cases {
|
||||
assert.Equal(t, c.expected, MergeRequiredContextsCommitStatus(c.commitStatuses, c.requiredContexts), "case %d", i)
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/storage"
|
||||
actions_service "code.gitea.io/gitea/services/actions"
|
||||
asymkey_service "code.gitea.io/gitea/services/asymkey"
|
||||
issue_service "code.gitea.io/gitea/services/issue"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
@ -193,7 +194,7 @@ func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, repoID
|
||||
|
||||
// Delete Issues and related objects
|
||||
var attachmentPaths []string
|
||||
if attachmentPaths, err = issues_model.DeleteIssuesByRepoID(ctx, repoID); err != nil {
|
||||
if attachmentPaths, err = issue_service.DeleteIssuesByRepoID(ctx, repoID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -39,10 +39,7 @@
|
||||
<div class="ui dropdown jump tw-p-2">
|
||||
{{svg "octicon-kebab-horizontal"}}
|
||||
<div class="menu flex-items-menu">
|
||||
<!-- TODO: This redundant link should be replaced by something else in future,
|
||||
because have not figured out how to add "View Workflow" or anything similar to GitHub.
|
||||
Related: https://github.com/go-gitea/gitea/pull/34530 -->
|
||||
<a class="item" href="{{$run.Link}}">{{svg "octicon-play"}}{{ctx.Locale.Tr "view"}}</a>
|
||||
<a class="item" href="{{$run.Link}}/workflow">{{svg "octicon-play"}}{{ctx.Locale.Tr "actions.runs.view_workflow_file"}}</a>
|
||||
{{if and $.AllowDeleteWorkflowRuns $run.Status.IsDone}}
|
||||
<a class="item link-action"
|
||||
data-url="{{$run.Link}}/delete"
|
||||
|
@ -14,3 +14,6 @@
|
||||
{{if eq .State "warning"}}
|
||||
{{svg "gitea-exclamation" 18 "commit-status icon text yellow"}}
|
||||
{{end}}
|
||||
{{if eq .State "skipped"}}
|
||||
{{svg "octicon-skip" 18 "commit-status icon text grey"}}
|
||||
{{end}}
|
||||
|
@ -815,10 +815,6 @@ overflow-menu .ui.label {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.code-view .lines-num span::after {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.lines-type-marker {
|
||||
vertical-align: top;
|
||||
white-space: nowrap;
|
||||
@ -855,39 +851,13 @@ overflow-menu .ui.label {
|
||||
.lines-escape {
|
||||
width: 0;
|
||||
white-space: nowrap;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.lines-code {
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.file-view tr.active {
|
||||
color: inherit !important;
|
||||
background: inherit !important;
|
||||
}
|
||||
|
||||
.file-view tr.active .lines-num,
|
||||
.file-view tr.active .lines-code {
|
||||
background: var(--color-highlight-bg) !important;
|
||||
}
|
||||
|
||||
.file-view tr.active:last-of-type .lines-code {
|
||||
border-bottom-right-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
.file-view tr.active .lines-num {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.file-view tr.active .lines-num::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 2px;
|
||||
height: 100%;
|
||||
background: var(--color-highlight-fg);
|
||||
}
|
||||
|
||||
.code-inner {
|
||||
font: 12px var(--fonts-monospace);
|
||||
white-space: pre-wrap;
|
||||
@ -938,12 +908,12 @@ overflow-menu .ui.label {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.top-line-blame {
|
||||
tr.top-line-blame {
|
||||
border-top: 1px solid var(--color-secondary);
|
||||
}
|
||||
|
||||
.code-view tr.top-line-blame:first-of-type {
|
||||
border-top: none;
|
||||
tr.top-line-blame:first-of-type {
|
||||
border-top: none; /* merge code lines belonging to the same commit into one block */
|
||||
}
|
||||
|
||||
.lines-code .bottom-line,
|
||||
@ -951,15 +921,6 @@ overflow-menu .ui.label {
|
||||
border-bottom: 1px solid var(--color-secondary);
|
||||
}
|
||||
|
||||
.code-view {
|
||||
background: var(--color-code-bg);
|
||||
border-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
.code-view table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.migrate .svg.gitea-git {
|
||||
color: var(--color-git);
|
||||
}
|
||||
|
@ -62,7 +62,7 @@
|
||||
@import "./repo/issue-label.css";
|
||||
@import "./repo/issue-list.css";
|
||||
@import "./repo/list-header.css";
|
||||
@import "./repo/linebutton.css";
|
||||
@import "./repo/file-view.css";
|
||||
@import "./repo/wiki.css";
|
||||
@import "./repo/header.css";
|
||||
@import "./repo/home.css";
|
||||
|
@ -309,10 +309,18 @@
|
||||
box-sizing: initial;
|
||||
}
|
||||
|
||||
.file-view.markup {
|
||||
padding: 1em 2em;
|
||||
}
|
||||
|
||||
.file-view.markup:has(.file-not-rendered-prompt) {
|
||||
padding: 0; /* let the file-not-rendered-prompt layout itself */
|
||||
}
|
||||
|
||||
/* this background ensures images can break <hr>. We can only do this on
|
||||
cases where the background is known and not transparent. */
|
||||
.markup.file-view img,
|
||||
.markup.file-view video,
|
||||
.file-view.markup img,
|
||||
.file-view.markup video,
|
||||
.comment-body .markup img, /* regular comment */
|
||||
.comment-body .markup video,
|
||||
.comment-content .markup img, /* code comment */
|
||||
|
@ -1238,21 +1238,6 @@ td .commit-summary {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.file-view.markup {
|
||||
padding: 1em 2em;
|
||||
}
|
||||
|
||||
.file-view.markup:has(.file-not-rendered-prompt) {
|
||||
padding: 0; /* let the file-not-rendered-prompt layout itself */
|
||||
}
|
||||
|
||||
.file-not-rendered-prompt {
|
||||
padding: 1rem;
|
||||
text-align: center;
|
||||
font-size: 1rem !important; /* use consistent styles for various containers (code, markup, etc) */
|
||||
line-height: var(--line-height-default) !important; /* same as above */
|
||||
}
|
||||
|
||||
.repository .activity-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
58
web_src/css/repo/file-view.css
Normal file
58
web_src/css/repo/file-view.css
Normal file
@ -0,0 +1,58 @@
|
||||
.file-view tr.active {
|
||||
background: var(--color-highlight-bg);
|
||||
}
|
||||
|
||||
.file-view tr.active:last-of-type .lines-code {
|
||||
border-bottom-right-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
.file-view tr.active .lines-num {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.file-view tr.active .lines-num::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 2px;
|
||||
height: 100%;
|
||||
background: var(--color-highlight-fg);
|
||||
}
|
||||
|
||||
.file-view .file-not-rendered-prompt {
|
||||
padding: 1rem;
|
||||
text-align: center;
|
||||
font-size: 1rem !important; /* use consistent styles for various containers (code, markup, etc) */
|
||||
line-height: var(--line-height-default) !important; /* same as above */
|
||||
}
|
||||
|
||||
/* ".code-view" is always used with ".file-view", to show the code of a file */
|
||||
.file-view.code-view {
|
||||
background: var(--color-code-bg);
|
||||
border-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
.file-view.code-view table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.file-view.code-view .lines-num span::after {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.file-view.code-view .lines-num:hover {
|
||||
color: var(--color-text-dark);
|
||||
}
|
||||
|
||||
.file-view.code-view .ui.button.code-line-button {
|
||||
border: 1px solid var(--color-secondary);
|
||||
padding: 1px 4px;
|
||||
margin: 0;
|
||||
min-height: 0;
|
||||
position: absolute;
|
||||
left: 6px;
|
||||
}
|
||||
|
||||
.file-view.code-view .ui.button.code-line-button:hover {
|
||||
background: var(--color-secondary);
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
.code-view .lines-num:hover {
|
||||
color: var(--color-text-dark) !important;
|
||||
}
|
||||
|
||||
.ui.button.code-line-button {
|
||||
border: 1px solid var(--color-secondary);
|
||||
padding: 1px 4px;
|
||||
margin: 0;
|
||||
min-height: 0;
|
||||
position: absolute;
|
||||
left: 6px;
|
||||
}
|
||||
|
||||
.ui.button.code-line-button:hover {
|
||||
background: var(--color-secondary);
|
||||
}
|
@ -6,7 +6,7 @@ import {fomanticQuery} from '../modules/fomantic/base.ts';
|
||||
|
||||
const {appSubUrl, assetUrlPrefix, pageData} = window.config;
|
||||
|
||||
type CommitStatus = 'pending' | 'success' | 'error' | 'failure' | 'warning';
|
||||
type CommitStatus = 'pending' | 'success' | 'error' | 'failure' | 'warning' | 'skipped';
|
||||
|
||||
type CommitStatusMap = {
|
||||
[status in CommitStatus]: {
|
||||
@ -22,6 +22,7 @@ const commitStatus: CommitStatusMap = {
|
||||
error: {name: 'gitea-exclamation', color: 'red'},
|
||||
failure: {name: 'octicon-x', color: 'red'},
|
||||
warning: {name: 'gitea-exclamation', color: 'yellow'},
|
||||
skipped: {name: 'octicon-skip', color: 'grey'},
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
|
Loading…
Reference in New Issue
Block a user