From b6bf128f1e1a943df85c1ce276d0317c8689ffca Mon Sep 17 00:00:00 2001 From: GiteaBot Date: Wed, 14 May 2025 00:36:37 +0000 Subject: [PATCH 01/16] [skip ci] Updated translations via Crowdin --- options/locale/locale_ga-IE.ini | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/options/locale/locale_ga-IE.ini b/options/locale/locale_ga-IE.ini index cdde7e015d..2c38ad83bf 100644 --- a/options/locale/locale_ga-IE.ini +++ b/options/locale/locale_ga-IE.ini @@ -3728,7 +3728,11 @@ creation.name_placeholder=carachtair alfanumair nó íoslaghda amháin nach féi creation.value_placeholder=Ionchur ábhar ar bith. Fágfar spás bán ag tús agus ag deireadh ar lár. creation.description_placeholder=Cuir isteach cur síos gairid (roghnach). +save_success=Tá an rún "%s" sábháilte. +save_failed=Theip ar an rún a shábháil. +add_secret=Cuir rún leis +edit_secret=Cuir rún in eagar deletion=Bain rún deletion.description=Is buan rún a bhaint agus ní féidir é a chealú. Lean ort? deletion.success=Tá an rún bainte. From dd500ce5598848e4f50999b627155ec8520932d3 Mon Sep 17 00:00:00 2001 From: NorthRealm <155140859+NorthRealm@users.noreply.github.com> Date: Thu, 15 May 2025 05:40:10 +0800 Subject: [PATCH 02/16] Fix Workflow run Not Found page (#34459) Related: https://github.com/go-gitea/gitea/pull/34337#issuecomment-2863593738 https://github.com/go-gitea/gitea/pull/34337#discussion_r2086332493 --- routers/web/repo/actions/view.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/routers/web/repo/actions/view.go b/routers/web/repo/actions/view.go index 9840465bff..13b19862ff 100644 --- a/routers/web/repo/actions/view.go +++ b/routers/web/repo/actions/view.go @@ -611,20 +611,20 @@ func getRunJobs(ctx *context_module.Context, runIndex, jobIndex int64) (*actions run, err := actions_model.GetRunByIndex(ctx, ctx.Repo.Repository.ID, runIndex) if err != nil { if errors.Is(err, util.ErrNotExist) { - ctx.HTTPError(http.StatusNotFound, err.Error()) + ctx.NotFound(nil) return nil, nil } - ctx.HTTPError(http.StatusInternalServerError, err.Error()) + ctx.ServerError("GetRunByIndex", err) return nil, nil } run.Repo = ctx.Repo.Repository jobs, err := actions_model.GetRunJobsByRunID(ctx, run.ID) if err != nil { - ctx.HTTPError(http.StatusInternalServerError, err.Error()) + ctx.ServerError("GetRunJobsByRunID", err) return nil, nil } if len(jobs) == 0 { - ctx.HTTPError(http.StatusNotFound) + ctx.NotFound(nil) return nil, nil } From 319d03fbc049de34a6fa0bc3019107ec5b724ff2 Mon Sep 17 00:00:00 2001 From: GiteaBot Date: Thu, 15 May 2025 00:35:31 +0000 Subject: [PATCH 03/16] [skip ci] Updated translations via Crowdin --- options/locale/locale_fr-FR.ini | 7 +++++++ options/locale/locale_ja-JP.ini | 8 ++++++++ options/locale/locale_pt-PT.ini | 3 +++ 3 files changed, 18 insertions(+) diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini index eeb5e31965..16fe024968 100644 --- a/options/locale/locale_fr-FR.ini +++ b/options/locale/locale_fr-FR.ini @@ -3728,7 +3728,11 @@ creation.name_placeholder=Caractères alphanumériques ou tirets bas uniquement, creation.value_placeholder=Entrez n’importe quoi. Les blancs cernant seront taillés. creation.description_placeholder=Décrire brièvement votre dépôt (optionnel). +save_success=Le secret « %s » a été enregistré. +save_failed=Impossible d’enregistrer le secret. +add_secret=Ajouter un secret +edit_secret=Modifier le secret deletion=Supprimer le secret deletion.description=La suppression d'un secret est permanente et irréversible. Continuer ? deletion.success=Le secret a été supprimé. @@ -3806,6 +3810,9 @@ runs.no_workflows.documentation=Pour plus d’informations sur les actions Gitea runs.no_runs=Le flux de travail n'a pas encore d'exécution. runs.empty_commit_message=(message de révision vide) runs.expire_log_message=Les journaux ont été supprimés car ils étaient trop anciens. +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. workflow.disable=Désactiver le flux de travail workflow.disable_success=Le flux de travail « %s » a bien été désactivé. diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini index 7790dccd6b..2a871da6aa 100644 --- a/options/locale/locale_ja-JP.ini +++ b/options/locale/locale_ja-JP.ini @@ -450,6 +450,7 @@ use_scratch_code=スクラッチコードを使う twofa_scratch_used=あなたはスクラッチコードを使用しました。 2要素認証の設定ページにリダイレクトしましたので、デバイスの登録を解除するか、新しいスクラッチコードを生成しましょう。 twofa_passcode_incorrect=パスコードが正しくありません。デバイスを紛失した場合は、スクラッチコードを使ってサインインしてください。 twofa_scratch_token_incorrect=スクラッチコードが正しくありません。 +twofa_required=リポジトリにアクセスするには2段階認証を設定するか、再度ログインしてください。 login_userpass=サインイン login_openid=OpenID oauth_signup_tab=新規アカウント登録 @@ -1878,6 +1879,7 @@ pulls.add_prefix=先頭に %s を追加 pulls.remove_prefix=先頭の %s を除去 pulls.data_broken=このプルリクエストは、フォークの情報が見つからないため壊れています。 pulls.files_conflicted=このプルリクエストは、ターゲットブランチと競合する変更を含んでいます。 +pulls.is_checking=マージの競合を確認しています... pulls.is_ancestor=このブランチは既にターゲットブランチに含まれています。マージするものはありません。 pulls.is_empty=このブランチの変更は既にターゲットブランチにあります。これは空のコミットになります。 pulls.required_status_check_failed=いくつかの必要なステータスチェックが成功していません。 @@ -3725,7 +3727,11 @@ creation.name_placeholder=大文字小文字の区別なし、英数字とアン creation.value_placeholder=内容を入力してください。前後の空白は除去されます。 creation.description_placeholder=簡単な説明を入力してください。 (オプション) +save_success=シークレット "%s" を保存しました。 +save_failed=シークレットの保存に失敗しました。 +add_secret=シークレットを追加 +edit_secret=シークレットを編集 deletion=シークレットの削除 deletion.description=シークレットの削除は恒久的で元に戻すことはできません。 続行しますか? deletion.success=シークレットを削除しました。 @@ -3842,6 +3848,8 @@ deleted.display_name=削除されたプロジェクト type-1.display_name=個人プロジェクト type-2.display_name=リポジトリ プロジェクト type-3.display_name=組織プロジェクト +enter_fullscreen=フルスクリーン +exit_fullscreen=フルスクリーンを終了 [git.filemode] changed_filemode=%[1]s → %[2]s diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini index b47b61f6bd..8da4216272 100644 --- a/options/locale/locale_pt-PT.ini +++ b/options/locale/locale_pt-PT.ini @@ -3810,6 +3810,9 @@ runs.no_workflows.documentation=Para mais informação sobre o Gitea Actions vej runs.no_runs=A sequência de trabalho ainda não foi executada. runs.empty_commit_message=(mensagem de cometimento vazia) 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. workflow.disable=Desabilitar sequência de trabalho workflow.disable_success=A sequência de trabalho '%s' foi desabilitada com sucesso. From bf338bb9e231a8f9ccef7de2e13a0fdf871fc680 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Thu, 15 May 2025 23:25:46 +0800 Subject: [PATCH 04/16] Fix project board view (#34470) Fix #34469 --- web_src/css/features/projects.css | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/web_src/css/features/projects.css b/web_src/css/features/projects.css index 80c9d89638..1d09e9c7ea 100644 --- a/web_src/css/features/projects.css +++ b/web_src/css/features/projects.css @@ -10,7 +10,7 @@ .project-header { padding: 0.5em 0; - overflow-x: auto; /* in fullscreen mode, the position is fixed, so we can't use "flex wrap" which would change the height */ + flex-wrap: wrap; } .project-header h2 { @@ -101,17 +101,11 @@ opacity: 0; } -.fullscreen.projects-view .project-header { - position: fixed; - z-index: 1000; - top: 0; - left: 0; - right: 0; - padding: 0.5em; - width: 100%; - max-width: 100%; - background-color: var(--color-body); - border-bottom: 1px solid var(--color-secondary); +.fullscreen.projects-view { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; } /* Hide project-description in full-screen due to its variable height. No need to show it for better space use. */ @@ -120,9 +114,7 @@ } .fullscreen.projects-view #project-board { - position: absolute; - top: 60px; - left: 0; - right: 0; - max-height: calc(100vh - 70px); + flex: 1; + max-height: unset; + padding-bottom: 0.5em; } From c24f4b3d2912224164ee3a75850a6a31bdd041f1 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 15 May 2025 09:28:31 -0700 Subject: [PATCH 05/16] Add migrations tests (#34456) Fix #34455 Co-authored-by: wxiaoguang --- models/migrations/v1_23/v302.go | 5 ++- models/migrations/v1_23/v302_test.go | 51 ++++++++++++++++++++++++++++ models/migrations/v1_23/v304.go | 5 ++- models/migrations/v1_23/v304_test.go | 40 ++++++++++++++++++++++ 4 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 models/migrations/v1_23/v302_test.go create mode 100644 models/migrations/v1_23/v304_test.go diff --git a/models/migrations/v1_23/v302.go b/models/migrations/v1_23/v302.go index d7ea03eb3d..5d2e9b1438 100644 --- a/models/migrations/v1_23/v302.go +++ b/models/migrations/v1_23/v302.go @@ -14,5 +14,8 @@ func AddIndexToActionTaskStoppedLogExpired(x *xorm.Engine) error { Stopped timeutil.TimeStamp `xorm:"index(stopped_log_expired)"` LogExpired bool `xorm:"index(stopped_log_expired)"` } - return x.Sync(new(ActionTask)) + _, err := x.SyncWithOptions(xorm.SyncOptions{ + IgnoreDropIndices: true, + }, new(ActionTask)) + return err } diff --git a/models/migrations/v1_23/v302_test.go b/models/migrations/v1_23/v302_test.go new file mode 100644 index 0000000000..29e85ae9d9 --- /dev/null +++ b/models/migrations/v1_23/v302_test.go @@ -0,0 +1,51 @@ +// Copyright 2025 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_23 //nolint + +import ( + "testing" + + "code.gitea.io/gitea/models/migrations/base" + "code.gitea.io/gitea/modules/timeutil" + + "github.com/stretchr/testify/assert" +) + +func Test_AddIndexToActionTaskStoppedLogExpired(t *testing.T) { + type ActionTask struct { + ID int64 + JobID int64 + Attempt int64 + RunnerID int64 `xorm:"index"` + Status int `xorm:"index"` + Started timeutil.TimeStamp `xorm:"index"` + Stopped timeutil.TimeStamp `xorm:"index(stopped_log_expired)"` + + RepoID int64 `xorm:"index"` + OwnerID int64 `xorm:"index"` + CommitSHA string `xorm:"index"` + IsForkPullRequest bool + + Token string `xorm:"-"` + TokenHash string `xorm:"UNIQUE"` // sha256 of token + TokenSalt string + TokenLastEight string `xorm:"index token_last_eight"` + + LogFilename string // file name of log + LogInStorage bool // read log from database or from storage + LogLength int64 // lines count + LogSize int64 // blob size + LogIndexes []int64 `xorm:"LONGBLOB"` // line number to offset + LogExpired bool `xorm:"index(stopped_log_expired)"` // files that are too old will be deleted + + Created timeutil.TimeStamp `xorm:"created"` + Updated timeutil.TimeStamp `xorm:"updated index"` + } + + // Prepare and load the testing database + x, deferable := base.PrepareTestEnv(t, 0, new(ActionTask)) + defer deferable() + + assert.NoError(t, AddIndexToActionTaskStoppedLogExpired(x)) +} diff --git a/models/migrations/v1_23/v304.go b/models/migrations/v1_23/v304.go index 65cffedbd9..e108f47779 100644 --- a/models/migrations/v1_23/v304.go +++ b/models/migrations/v1_23/v304.go @@ -9,5 +9,8 @@ func AddIndexForReleaseSha1(x *xorm.Engine) error { type Release struct { Sha1 string `xorm:"INDEX VARCHAR(64)"` } - return x.Sync(new(Release)) + _, err := x.SyncWithOptions(xorm.SyncOptions{ + IgnoreDropIndices: true, + }, new(Release)) + return err } diff --git a/models/migrations/v1_23/v304_test.go b/models/migrations/v1_23/v304_test.go new file mode 100644 index 0000000000..955219d3f9 --- /dev/null +++ b/models/migrations/v1_23/v304_test.go @@ -0,0 +1,40 @@ +// Copyright 2025 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_23 //nolint + +import ( + "testing" + + "code.gitea.io/gitea/models/migrations/base" + "code.gitea.io/gitea/modules/timeutil" + + "github.com/stretchr/testify/assert" +) + +func Test_AddIndexForReleaseSha1(t *testing.T) { + type Release struct { + ID int64 `xorm:"pk autoincr"` + RepoID int64 `xorm:"INDEX UNIQUE(n)"` + PublisherID int64 `xorm:"INDEX"` + TagName string `xorm:"INDEX UNIQUE(n)"` + OriginalAuthor string + OriginalAuthorID int64 `xorm:"index"` + LowerTagName string + Target string + Title string + Sha1 string `xorm:"VARCHAR(64)"` + NumCommits int64 + Note string `xorm:"TEXT"` + IsDraft bool `xorm:"NOT NULL DEFAULT false"` + IsPrerelease bool `xorm:"NOT NULL DEFAULT false"` + IsTag bool `xorm:"NOT NULL DEFAULT false"` // will be true only if the record is a tag and has no related releases + CreatedUnix timeutil.TimeStamp `xorm:"INDEX"` + } + + // Prepare and load the testing database + x, deferable := base.PrepareTestEnv(t, 0, new(Release)) + defer deferable() + + assert.NoError(t, AddIndexForReleaseSha1(x)) +} From 7b518bc6c79035a53c0b752680d833fce5e1a2fe Mon Sep 17 00:00:00 2001 From: Sebastian Weigand Date: Fri, 16 May 2025 05:56:26 +0200 Subject: [PATCH 06/16] Change "rejected" to "changes requested" in 3rd party PR review notification (#34481) This PR changes 3rd party notifications wording on a PR review that requests changes and can be considered a follow up for #5858 to also fix #5857 in 3rd party notifications. The difference in the actual notification would be the following: ```diff - Pull request review rejected + Pull request review changes requested ``` While this is a simple string change at first look, it has a deeper UX meaning. # Motivation We could observe that some developers are hesitant to press the "Request changes" button since their peers first see that their changes were rejected, thus a more appropriate wording that also falls in line with the meaning and UI would be beneficial. ## Meaning Pressing the `Request changes` button in a PR review means that as a reviewer you are willing to merge the general change in a PR if changes requested review comments are implemented. Rejecting a PR on the other hand would be equivalent with closing it since that change isn't welcome at all (e.g. out of scope feature). ## Sync with UI The UI button says `request changes` and the other options ![image](https://github.com/user-attachments/assets/3766cc89-40d7-4c5e-9ff7-a0e1f6991ea6) ## Considered Problems This might break some automation for users who rely on string matching. --- services/webhook/discord.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/webhook/discord.go b/services/webhook/discord.go index 0e8a9aa67c..7b68430508 100644 --- a/services/webhook/discord.go +++ b/services/webhook/discord.go @@ -305,7 +305,7 @@ func parseHookPullRequestEventType(event webhook_module.HookEventType) (string, case webhook_module.HookEventPullRequestReviewApproved: return "approved", nil case webhook_module.HookEventPullRequestReviewRejected: - return "rejected", nil + return "requested changes", nil case webhook_module.HookEventPullRequestReviewComment: return "comment", nil default: From 59df03b5542f05a1a927996fbf6483f7da363e03 Mon Sep 17 00:00:00 2001 From: ChristopherHX Date: Fri, 16 May 2025 08:44:29 +0200 Subject: [PATCH 07/16] Fix get / delete runner to use consistent http 404 and 500 status (#34480) * previously deleting an already deleted runner returned http 500 * previously any database error for the get endpoint was http 404 and never 500 --- routers/api/v1/shared/runners.go | 45 ++++++++++++-------- tests/integration/api_actions_runner_test.go | 8 ++++ 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/routers/api/v1/shared/runners.go b/routers/api/v1/shared/runners.go index d42f330d1c..e9834aff9f 100644 --- a/routers/api/v1/shared/runners.go +++ b/routers/api/v1/shared/runners.go @@ -67,6 +67,28 @@ func ListRunners(ctx *context.APIContext, ownerID, repoID int64) { ctx.JSON(http.StatusOK, &res) } +func getRunnerByID(ctx *context.APIContext, ownerID, repoID, runnerID int64) (*actions_model.ActionRunner, bool) { + if ownerID != 0 && repoID != 0 { + setting.PanicInDevOrTesting("ownerID and repoID should not be both set") + } + + runner, err := actions_model.GetRunnerByID(ctx, runnerID) + if err != nil { + if errors.Is(err, util.ErrNotExist) { + ctx.APIErrorNotFound("Runner not found") + } else { + ctx.APIErrorInternal(err) + } + return nil, false + } + + if !runner.EditableInContext(ownerID, repoID) { + ctx.APIErrorNotFound("No permission to access this runner") + return nil, false + } + return runner, true +} + // GetRunner get the runner for api route validated ownerID and repoID // ownerID == 0 and repoID == 0 means any runner including global runners // ownerID == 0 and repoID != 0 means any runner for the given repo @@ -77,13 +99,8 @@ func GetRunner(ctx *context.APIContext, ownerID, repoID, runnerID int64) { if ownerID != 0 && repoID != 0 { setting.PanicInDevOrTesting("ownerID and repoID should not be both set") } - runner, err := actions_model.GetRunnerByID(ctx, runnerID) - if err != nil { - ctx.APIErrorNotFound(err) - return - } - if !runner.EditableInContext(ownerID, repoID) { - ctx.APIErrorNotFound("No permission to get this runner") + runner, ok := getRunnerByID(ctx, ownerID, repoID, runnerID) + if !ok { return } ctx.JSON(http.StatusOK, convert.ToActionRunner(ctx, runner)) @@ -96,20 +113,12 @@ func GetRunner(ctx *context.APIContext, ownerID, repoID, runnerID int64) { // ownerID != 0 and repoID != 0 undefined behavior // Access rights are checked at the API route level func DeleteRunner(ctx *context.APIContext, ownerID, repoID, runnerID int64) { - if ownerID != 0 && repoID != 0 { - setting.PanicInDevOrTesting("ownerID and repoID should not be both set") - } - runner, err := actions_model.GetRunnerByID(ctx, runnerID) - if err != nil { - ctx.APIErrorInternal(err) - return - } - if !runner.EditableInContext(ownerID, repoID) { - ctx.APIErrorNotFound("No permission to delete this runner") + runner, ok := getRunnerByID(ctx, ownerID, repoID, runnerID) + if !ok { return } - err = actions_model.DeleteRunner(ctx, runner.ID) + err := actions_model.DeleteRunner(ctx, runner.ID) if err != nil { ctx.APIErrorInternal(err) return diff --git a/tests/integration/api_actions_runner_test.go b/tests/integration/api_actions_runner_test.go index ace7aa381a..87b82e2ce9 100644 --- a/tests/integration/api_actions_runner_test.go +++ b/tests/integration/api_actions_runner_test.go @@ -329,4 +329,12 @@ func testActionsRunnerRepo(t *testing.T) { req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/repos/user2/repo1/actions/runners/%d", 34349)).AddTokenAuth(token) MakeRequest(t, req, http.StatusNotFound) }) + + t.Run("DeleteAdminRunnerNotFoundUnknownID", func(t *testing.T) { + userUsername := "user2" + token := getUserToken(t, userUsername, auth_model.AccessTokenScopeWriteRepository) + // Verify delete a runner by unknown id is not found + req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/repos/user2/repo1/actions/runners/%d", 4384797347934)).AddTokenAuth(token) + MakeRequest(t, req, http.StatusNotFound) + }) } From 6fbf0e67383dd2970d8e6c309ebc5c634732866c Mon Sep 17 00:00:00 2001 From: techknowlogick Date: Fri, 16 May 2025 10:09:45 -0400 Subject: [PATCH 08/16] nix flake update (#34476) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 2f7b86359b..da3f19bbd2 100644 --- a/flake.lock +++ b/flake.lock @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1739214665, - "narHash": "sha256-26L8VAu3/1YRxS8MHgBOyOM8xALdo6N0I04PgorE7UM=", + "lastModified": 1747179050, + "narHash": "sha256-qhFMmDkeJX9KJwr5H32f1r7Prs7XbQWtO0h3V0a0rFY=", "owner": "nixos", "repo": "nixpkgs", - "rev": "64e75cd44acf21c7933d61d7721e812eac1b5a0a", + "rev": "adaa24fbf46737f3f1b5497bf64bae750f82942e", "type": "github" }, "original": { From e92c4f18083ed312b69591ebb77e0f504ee77025 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 16 May 2025 07:35:20 -0700 Subject: [PATCH 09/16] Add missing setting load in dump-repo command (#34479) Fix #34465 --- cmd/dump_repo.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cmd/dump_repo.go b/cmd/dump_repo.go index 3a24cf6c5f..11d0270404 100644 --- a/cmd/dump_repo.go +++ b/cmd/dump_repo.go @@ -80,6 +80,11 @@ wiki, issues, labels, releases, release_assets, milestones, pull_requests, comme } func runDumpRepository(ctx *cli.Context) error { + setupConsoleLogger(log.INFO, log.CanColorStderr, os.Stderr) + + setting.DisableLoggerInit() + setting.LoadSettings() // cannot access skip_tls_verify settings otherwise + stdCtx, cancel := installSignals() defer cancel() From b6c066747400927407a6b4807165d2de40c7495b Mon Sep 17 00:00:00 2001 From: Ryo Hanafusa Date: Sun, 18 May 2025 01:58:27 +0900 Subject: [PATCH 10/16] Add R-HNF to the TRANSLATORS file (#34494) I would like to be added to the TRANSLATORS file. Here are my related activities: * https://crowdin.com/profile/R-HNF/activity * commit: [skip ci] Updated translations via Crowdin 319d03fbc049de34a6fa0bc3019107ec5b724ff2 I also referred to the following PRs: * #8451 * #8292 --- options/locale/TRANSLATORS | 1 + 1 file changed, 1 insertion(+) diff --git a/options/locale/TRANSLATORS b/options/locale/TRANSLATORS index e67255f2fb..4eee2b26c1 100644 --- a/options/locale/TRANSLATORS +++ b/options/locale/TRANSLATORS @@ -66,6 +66,7 @@ Piotr Orzechowski Richard Bukovansky Robert Nuske Robin Hübner +Ryo Hanafusa SeongJae Park Thiago Avelino Thomas Fanninger From 972381097c6b3488bf9d5c3c4fde04775b2e3a3c Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sat, 17 May 2025 13:05:55 -0700 Subject: [PATCH 11/16] Fix url validation in webhook add/edit API (#34492) --- routers/api/v1/utils/hook.go | 9 ++++ routers/api/v1/utils/hook_test.go | 82 +++++++++++++++++++++++++++++++ routers/api/v1/utils/main_test.go | 21 ++++++++ 3 files changed, 112 insertions(+) create mode 100644 routers/api/v1/utils/hook_test.go create mode 100644 routers/api/v1/utils/main_test.go diff --git a/routers/api/v1/utils/hook.go b/routers/api/v1/utils/hook.go index 532d157e35..b6e650bdc0 100644 --- a/routers/api/v1/utils/hook.go +++ b/routers/api/v1/utils/hook.go @@ -15,6 +15,7 @@ import ( "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/validation" webhook_module "code.gitea.io/gitea/modules/webhook" "code.gitea.io/gitea/services/context" webhook_service "code.gitea.io/gitea/services/webhook" @@ -92,6 +93,10 @@ func checkCreateHookOption(ctx *context.APIContext, form *api.CreateHookOption) ctx.APIError(http.StatusUnprocessableEntity, "Invalid content type") return false } + if !validation.IsValidURL(form.Config["url"]) { + ctx.APIError(http.StatusUnprocessableEntity, "Invalid url") + return false + } return true } @@ -324,6 +329,10 @@ func EditRepoHook(ctx *context.APIContext, form *api.EditHookOption, hookID int6 func editHook(ctx *context.APIContext, form *api.EditHookOption, w *webhook.Webhook) bool { if form.Config != nil { if url, ok := form.Config["url"]; ok { + if !validation.IsValidURL(url) { + ctx.APIError(http.StatusUnprocessableEntity, "Invalid url") + return false + } w.URL = url } if ct, ok := form.Config["content_type"]; ok { diff --git a/routers/api/v1/utils/hook_test.go b/routers/api/v1/utils/hook_test.go new file mode 100644 index 0000000000..e5e8ce07ce --- /dev/null +++ b/routers/api/v1/utils/hook_test.go @@ -0,0 +1,82 @@ +// Copyright 2025 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package utils + +import ( + "net/http" + "testing" + + "code.gitea.io/gitea/models/unittest" + "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/services/contexttest" + + "github.com/stretchr/testify/assert" +) + +func TestTestHookValidation(t *testing.T) { + unittest.PrepareTestEnv(t) + + t.Run("Test Validation", func(t *testing.T) { + ctx, _ := contexttest.MockAPIContext(t, "user2/repo1/hooks") + contexttest.LoadRepo(t, ctx, 1) + contexttest.LoadRepoCommit(t, ctx) + contexttest.LoadUser(t, ctx, 2) + + checkCreateHookOption(ctx, &structs.CreateHookOption{ + Type: "gitea", + Config: map[string]string{ + "content_type": "json", + "url": "https://example.com/webhook", + }, + }) + assert.Equal(t, 0, ctx.Resp.WrittenStatus()) // not written yet + }) + + t.Run("Test Validation with invalid URL", func(t *testing.T) { + ctx, _ := contexttest.MockAPIContext(t, "user2/repo1/hooks") + contexttest.LoadRepo(t, ctx, 1) + contexttest.LoadRepoCommit(t, ctx) + contexttest.LoadUser(t, ctx, 2) + + checkCreateHookOption(ctx, &structs.CreateHookOption{ + Type: "gitea", + Config: map[string]string{ + "content_type": "json", + "url": "example.com/webhook", + }, + }) + assert.Equal(t, http.StatusUnprocessableEntity, ctx.Resp.WrittenStatus()) + }) + + t.Run("Test Validation with invalid webhook type", func(t *testing.T) { + ctx, _ := contexttest.MockAPIContext(t, "user2/repo1/hooks") + contexttest.LoadRepo(t, ctx, 1) + contexttest.LoadRepoCommit(t, ctx) + contexttest.LoadUser(t, ctx, 2) + + checkCreateHookOption(ctx, &structs.CreateHookOption{ + Type: "unknown", + Config: map[string]string{ + "content_type": "json", + "url": "example.com/webhook", + }, + }) + assert.Equal(t, http.StatusUnprocessableEntity, ctx.Resp.WrittenStatus()) + }) + + t.Run("Test Validation with empty content type", func(t *testing.T) { + ctx, _ := contexttest.MockAPIContext(t, "user2/repo1/hooks") + contexttest.LoadRepo(t, ctx, 1) + contexttest.LoadRepoCommit(t, ctx) + contexttest.LoadUser(t, ctx, 2) + + checkCreateHookOption(ctx, &structs.CreateHookOption{ + Type: "unknown", + Config: map[string]string{ + "url": "https://example.com/webhook", + }, + }) + assert.Equal(t, http.StatusUnprocessableEntity, ctx.Resp.WrittenStatus()) + }) +} diff --git a/routers/api/v1/utils/main_test.go b/routers/api/v1/utils/main_test.go new file mode 100644 index 0000000000..4eace1f369 --- /dev/null +++ b/routers/api/v1/utils/main_test.go @@ -0,0 +1,21 @@ +// Copyright 2018 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package utils + +import ( + "testing" + + "code.gitea.io/gitea/models/unittest" + "code.gitea.io/gitea/modules/setting" + webhook_service "code.gitea.io/gitea/services/webhook" +) + +func TestMain(m *testing.M) { + unittest.MainTest(m, &unittest.TestOptions{ + SetUp: func() error { + setting.LoadQueueSettings() + return webhook_service.Init() + }, + }) +} From d89eed998f9e4dc84d0ef02451703590d7a8ef51 Mon Sep 17 00:00:00 2001 From: ChristopherHX Date: Sun, 18 May 2025 18:43:56 +0200 Subject: [PATCH 12/16] Fix edithook api can not update package, status and workflow_job events (#34495) * the origin of this problem is duplicated code --- routers/api/v1/utils/hook.go | 92 +++++++++++++++--------------------- 1 file changed, 37 insertions(+), 55 deletions(-) diff --git a/routers/api/v1/utils/hook.go b/routers/api/v1/utils/hook.go index b6e650bdc0..3bbd292718 100644 --- a/routers/api/v1/utils/hook.go +++ b/routers/api/v1/utils/hook.go @@ -159,6 +159,41 @@ func pullHook(events []string, event string) bool { return util.SliceContainsString(events, event, true) || util.SliceContainsString(events, string(webhook_module.HookEventPullRequest), true) } +func updateHookEvents(events []string) webhook_module.HookEvents { + if len(events) == 0 { + events = []string{"push"} + } + hookEvents := make(webhook_module.HookEvents) + hookEvents[webhook_module.HookEventCreate] = util.SliceContainsString(events, string(webhook_module.HookEventCreate), true) + hookEvents[webhook_module.HookEventPush] = util.SliceContainsString(events, string(webhook_module.HookEventPush), true) + hookEvents[webhook_module.HookEventDelete] = util.SliceContainsString(events, string(webhook_module.HookEventDelete), true) + hookEvents[webhook_module.HookEventFork] = util.SliceContainsString(events, string(webhook_module.HookEventFork), true) + hookEvents[webhook_module.HookEventRepository] = util.SliceContainsString(events, string(webhook_module.HookEventRepository), true) + hookEvents[webhook_module.HookEventWiki] = util.SliceContainsString(events, string(webhook_module.HookEventWiki), true) + hookEvents[webhook_module.HookEventRelease] = util.SliceContainsString(events, string(webhook_module.HookEventRelease), true) + hookEvents[webhook_module.HookEventPackage] = util.SliceContainsString(events, string(webhook_module.HookEventPackage), true) + hookEvents[webhook_module.HookEventStatus] = util.SliceContainsString(events, string(webhook_module.HookEventStatus), true) + hookEvents[webhook_module.HookEventWorkflowJob] = util.SliceContainsString(events, string(webhook_module.HookEventWorkflowJob), true) + + // Issues + hookEvents[webhook_module.HookEventIssues] = issuesHook(events, "issues_only") + hookEvents[webhook_module.HookEventIssueAssign] = issuesHook(events, string(webhook_module.HookEventIssueAssign)) + hookEvents[webhook_module.HookEventIssueLabel] = issuesHook(events, string(webhook_module.HookEventIssueLabel)) + hookEvents[webhook_module.HookEventIssueMilestone] = issuesHook(events, string(webhook_module.HookEventIssueMilestone)) + hookEvents[webhook_module.HookEventIssueComment] = issuesHook(events, string(webhook_module.HookEventIssueComment)) + + // Pull requests + hookEvents[webhook_module.HookEventPullRequest] = pullHook(events, "pull_request_only") + hookEvents[webhook_module.HookEventPullRequestAssign] = pullHook(events, string(webhook_module.HookEventPullRequestAssign)) + hookEvents[webhook_module.HookEventPullRequestLabel] = pullHook(events, string(webhook_module.HookEventPullRequestLabel)) + hookEvents[webhook_module.HookEventPullRequestMilestone] = pullHook(events, string(webhook_module.HookEventPullRequestMilestone)) + hookEvents[webhook_module.HookEventPullRequestComment] = pullHook(events, string(webhook_module.HookEventPullRequestComment)) + hookEvents[webhook_module.HookEventPullRequestReview] = pullHook(events, "pull_request_review") + hookEvents[webhook_module.HookEventPullRequestReviewRequest] = pullHook(events, string(webhook_module.HookEventPullRequestReviewRequest)) + hookEvents[webhook_module.HookEventPullRequestSync] = pullHook(events, string(webhook_module.HookEventPullRequestSync)) + return hookEvents +} + // addHook add the hook specified by `form`, `ownerID` and `repoID`. If there is // an error, write to `ctx` accordingly. Return (webhook, ok) func addHook(ctx *context.APIContext, form *api.CreateHookOption, ownerID, repoID int64) (*webhook.Webhook, bool) { @@ -167,9 +202,6 @@ func addHook(ctx *context.APIContext, form *api.CreateHookOption, ownerID, repoI return nil, false } - if len(form.Events) == 0 { - form.Events = []string{"push"} - } if form.Config["is_system_webhook"] != "" { sw, err := strconv.ParseBool(form.Config["is_system_webhook"]) if err != nil { @@ -188,31 +220,7 @@ func addHook(ctx *context.APIContext, form *api.CreateHookOption, ownerID, repoI IsSystemWebhook: isSystemWebhook, HookEvent: &webhook_module.HookEvent{ ChooseEvents: true, - HookEvents: webhook_module.HookEvents{ - webhook_module.HookEventCreate: util.SliceContainsString(form.Events, string(webhook_module.HookEventCreate), true), - webhook_module.HookEventDelete: util.SliceContainsString(form.Events, string(webhook_module.HookEventDelete), true), - webhook_module.HookEventFork: util.SliceContainsString(form.Events, string(webhook_module.HookEventFork), true), - webhook_module.HookEventIssues: issuesHook(form.Events, "issues_only"), - webhook_module.HookEventIssueAssign: issuesHook(form.Events, string(webhook_module.HookEventIssueAssign)), - webhook_module.HookEventIssueLabel: issuesHook(form.Events, string(webhook_module.HookEventIssueLabel)), - webhook_module.HookEventIssueMilestone: issuesHook(form.Events, string(webhook_module.HookEventIssueMilestone)), - webhook_module.HookEventIssueComment: issuesHook(form.Events, string(webhook_module.HookEventIssueComment)), - webhook_module.HookEventPush: util.SliceContainsString(form.Events, string(webhook_module.HookEventPush), true), - webhook_module.HookEventPullRequest: pullHook(form.Events, "pull_request_only"), - webhook_module.HookEventPullRequestAssign: pullHook(form.Events, string(webhook_module.HookEventPullRequestAssign)), - webhook_module.HookEventPullRequestLabel: pullHook(form.Events, string(webhook_module.HookEventPullRequestLabel)), - webhook_module.HookEventPullRequestMilestone: pullHook(form.Events, string(webhook_module.HookEventPullRequestMilestone)), - webhook_module.HookEventPullRequestComment: pullHook(form.Events, string(webhook_module.HookEventPullRequestComment)), - webhook_module.HookEventPullRequestReview: pullHook(form.Events, "pull_request_review"), - webhook_module.HookEventPullRequestReviewRequest: pullHook(form.Events, string(webhook_module.HookEventPullRequestReviewRequest)), - webhook_module.HookEventPullRequestSync: pullHook(form.Events, string(webhook_module.HookEventPullRequestSync)), - webhook_module.HookEventWiki: util.SliceContainsString(form.Events, string(webhook_module.HookEventWiki), true), - webhook_module.HookEventRepository: util.SliceContainsString(form.Events, string(webhook_module.HookEventRepository), true), - webhook_module.HookEventRelease: util.SliceContainsString(form.Events, string(webhook_module.HookEventRelease), true), - webhook_module.HookEventPackage: util.SliceContainsString(form.Events, string(webhook_module.HookEventPackage), true), - webhook_module.HookEventStatus: util.SliceContainsString(form.Events, string(webhook_module.HookEventStatus), true), - webhook_module.HookEventWorkflowJob: util.SliceContainsString(form.Events, string(webhook_module.HookEventWorkflowJob), true), - }, + HookEvents: updateHookEvents(form.Events), BranchFilter: form.BranchFilter, }, IsActive: form.Active, @@ -361,19 +369,10 @@ func editHook(ctx *context.APIContext, form *api.EditHookOption, w *webhook.Webh } // Update events - if len(form.Events) == 0 { - form.Events = []string{"push"} - } + w.HookEvents = updateHookEvents(form.Events) w.PushOnly = false w.SendEverything = false w.ChooseEvents = true - w.HookEvents[webhook_module.HookEventCreate] = util.SliceContainsString(form.Events, string(webhook_module.HookEventCreate), true) - w.HookEvents[webhook_module.HookEventPush] = util.SliceContainsString(form.Events, string(webhook_module.HookEventPush), true) - w.HookEvents[webhook_module.HookEventDelete] = util.SliceContainsString(form.Events, string(webhook_module.HookEventDelete), true) - w.HookEvents[webhook_module.HookEventFork] = util.SliceContainsString(form.Events, string(webhook_module.HookEventFork), true) - w.HookEvents[webhook_module.HookEventRepository] = util.SliceContainsString(form.Events, string(webhook_module.HookEventRepository), true) - w.HookEvents[webhook_module.HookEventWiki] = util.SliceContainsString(form.Events, string(webhook_module.HookEventWiki), true) - w.HookEvents[webhook_module.HookEventRelease] = util.SliceContainsString(form.Events, string(webhook_module.HookEventRelease), true) w.BranchFilter = form.BranchFilter err := w.SetHeaderAuthorization(form.AuthorizationHeader) @@ -382,23 +381,6 @@ func editHook(ctx *context.APIContext, form *api.EditHookOption, w *webhook.Webh return false } - // Issues - w.HookEvents[webhook_module.HookEventIssues] = issuesHook(form.Events, "issues_only") - w.HookEvents[webhook_module.HookEventIssueAssign] = issuesHook(form.Events, string(webhook_module.HookEventIssueAssign)) - w.HookEvents[webhook_module.HookEventIssueLabel] = issuesHook(form.Events, string(webhook_module.HookEventIssueLabel)) - w.HookEvents[webhook_module.HookEventIssueMilestone] = issuesHook(form.Events, string(webhook_module.HookEventIssueMilestone)) - w.HookEvents[webhook_module.HookEventIssueComment] = issuesHook(form.Events, string(webhook_module.HookEventIssueComment)) - - // Pull requests - w.HookEvents[webhook_module.HookEventPullRequest] = pullHook(form.Events, "pull_request_only") - w.HookEvents[webhook_module.HookEventPullRequestAssign] = pullHook(form.Events, string(webhook_module.HookEventPullRequestAssign)) - w.HookEvents[webhook_module.HookEventPullRequestLabel] = pullHook(form.Events, string(webhook_module.HookEventPullRequestLabel)) - w.HookEvents[webhook_module.HookEventPullRequestMilestone] = pullHook(form.Events, string(webhook_module.HookEventPullRequestMilestone)) - w.HookEvents[webhook_module.HookEventPullRequestComment] = pullHook(form.Events, string(webhook_module.HookEventPullRequestComment)) - w.HookEvents[webhook_module.HookEventPullRequestReview] = pullHook(form.Events, "pull_request_review") - w.HookEvents[webhook_module.HookEventPullRequestReviewRequest] = pullHook(form.Events, string(webhook_module.HookEventPullRequestReviewRequest)) - w.HookEvents[webhook_module.HookEventPullRequestSync] = pullHook(form.Events, string(webhook_module.HookEventPullRequestSync)) - if err := w.UpdateEvent(); err != nil { ctx.APIErrorInternal(err) return false From ec10c6ba5a6c4c3a5ab9bdf47616d6058c6fb59c Mon Sep 17 00:00:00 2001 From: GiteaBot Date: Mon, 19 May 2025 00:38:52 +0000 Subject: [PATCH 13/16] [skip ci] Updated translations via Crowdin --- options/locale/locale_ga-IE.ini | 3 +++ 1 file changed, 3 insertions(+) diff --git a/options/locale/locale_ga-IE.ini b/options/locale/locale_ga-IE.ini index 2c38ad83bf..fac7e15fc4 100644 --- a/options/locale/locale_ga-IE.ini +++ b/options/locale/locale_ga-IE.ini @@ -3810,6 +3810,9 @@ runs.no_workflows.documentation=Le haghaidh tuilleadh eolais ar Gitea Actions, f runs.no_runs=Níl aon rith ag an sreabhadh oibre fós. runs.empty_commit_message=(teachtaireacht tiomantas folamh) 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. workflow.disable=Díchumasaigh sreabhadh oibre workflow.disable_success=D'éirigh le sreabhadh oibre '%s' a dhíchumasú. From 9cfcc079c7353d635e1e0d38006c3f925944e1b6 Mon Sep 17 00:00:00 2001 From: Adam Majer Date: Mon, 19 May 2025 19:08:00 +0200 Subject: [PATCH 14/16] Export repo's manual merge settings (#34502) --- modules/structs/repo.go | 2 ++ services/convert/repository.go | 6 ++++++ templates/swagger/v1_json.tmpl | 8 ++++++++ 3 files changed, 16 insertions(+) diff --git a/modules/structs/repo.go b/modules/structs/repo.go index fb784bd8b3..c48e71bc4e 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -101,6 +101,8 @@ type Repository struct { AllowSquash bool `json:"allow_squash_merge"` AllowFastForwardOnly bool `json:"allow_fast_forward_only_merge"` AllowRebaseUpdate bool `json:"allow_rebase_update"` + AllowManualMerge bool `json:"allow_manual_merge"` + AutodetectManualMerge bool `json:"autodetect_manual_merge"` DefaultDeleteBranchAfterMerge bool `json:"default_delete_branch_after_merge"` DefaultMergeStyle string `json:"default_merge_style"` DefaultAllowMaintainerEdit bool `json:"default_allow_maintainer_edit"` diff --git a/services/convert/repository.go b/services/convert/repository.go index 7dfdfd2179..614eb58a88 100644 --- a/services/convert/repository.go +++ b/services/convert/repository.go @@ -98,6 +98,8 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR allowSquash := false allowFastForwardOnly := false allowRebaseUpdate := false + allowManualMerge := true + autodetectManualMerge := false defaultDeleteBranchAfterMerge := false defaultMergeStyle := repo_model.MergeStyleMerge defaultAllowMaintainerEdit := false @@ -111,6 +113,8 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR allowSquash = config.AllowSquash allowFastForwardOnly = config.AllowFastForwardOnly allowRebaseUpdate = config.AllowRebaseUpdate + allowManualMerge = config.AllowManualMerge + autodetectManualMerge = config.AutodetectManualMerge defaultDeleteBranchAfterMerge = config.DefaultDeleteBranchAfterMerge defaultMergeStyle = config.GetDefaultMergeStyle() defaultAllowMaintainerEdit = config.DefaultAllowMaintainerEdit @@ -235,6 +239,8 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR AllowSquash: allowSquash, AllowFastForwardOnly: allowFastForwardOnly, AllowRebaseUpdate: allowRebaseUpdate, + AllowManualMerge: allowManualMerge, + AutodetectManualMerge: autodetectManualMerge, DefaultDeleteBranchAfterMerge: defaultDeleteBranchAfterMerge, DefaultMergeStyle: string(defaultMergeStyle), DefaultAllowMaintainerEdit: defaultAllowMaintainerEdit, diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index e28fecaec0..3512b647c2 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -26184,6 +26184,10 @@ "type": "boolean", "x-go-name": "AllowFastForwardOnly" }, + "allow_manual_merge": { + "type": "boolean", + "x-go-name": "AllowManualMerge" + }, "allow_merge_commits": { "type": "boolean", "x-go-name": "AllowMerge" @@ -26213,6 +26217,10 @@ "format": "date-time", "x-go-name": "ArchivedAt" }, + "autodetect_manual_merge": { + "type": "boolean", + "x-go-name": "AutodetectManualMerge" + }, "avatar_url": { "type": "string", "x-go-name": "AvatarURL" From d06eb8d8014b1f179187926b97b92b6f42c65038 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Tue, 20 May 2025 09:57:58 +0800 Subject: [PATCH 15/16] feat(api): add date range filtering to commit retrieval endpoints (#34497) - Add support for filtering commits by date range via new "since" and "until" parameters - Update API endpoints and command logic to handle the new parameters for fetching commits within given dates - Extend API documentation and Swagger specs to describe the new "since" and "until" query parameters - Refactor related function signatures and implementations to accept and pass "since" and "until" values --------- Signed-off-by: appleboy Signed-off-by: Bo-Yi Wu --- modules/git/commit.go | 6 +++-- modules/git/repo_commit.go | 17 +++++++++++++- modules/git/repo_stats.go | 7 ++++-- modules/repository/commits_test.go | 2 -- routers/api/v1/repo/commits.go | 36 +++++++++++++++++++++++++++++- routers/web/feed/branch.go | 2 +- routers/web/repo/commit.go | 2 +- templates/swagger/v1_json.tmpl | 14 ++++++++++++ 8 files changed, 76 insertions(+), 10 deletions(-) diff --git a/modules/git/commit.go b/modules/git/commit.go index 3e790e89d9..cd50c51151 100644 --- a/modules/git/commit.go +++ b/modules/git/commit.go @@ -166,6 +166,8 @@ type CommitsCountOptions struct { Not string Revision []string RelPath []string + Since string + Until string } // CommitsCount returns number of total commits of until given revision. @@ -199,8 +201,8 @@ func (c *Commit) CommitsCount() (int64, error) { } // CommitsByRange returns the specific page commits before current revision, every page's number default by CommitsRangeSize -func (c *Commit) CommitsByRange(page, pageSize int, not string) ([]*Commit, error) { - return c.repo.commitsByRange(c.ID, page, pageSize, not) +func (c *Commit) CommitsByRange(page, pageSize int, not, since, until string) ([]*Commit, error) { + return c.repo.commitsByRangeWithTime(c.ID, page, pageSize, not, since, until) } // CommitsBefore returns all the commits before current revision diff --git a/modules/git/repo_commit.go b/modules/git/repo_commit.go index 72f35711f0..a44fd8c0e1 100644 --- a/modules/git/repo_commit.go +++ b/modules/git/repo_commit.go @@ -89,7 +89,8 @@ func (repo *Repository) GetCommitByPath(relpath string) (*Commit, error) { return commits[0], nil } -func (repo *Repository) commitsByRange(id ObjectID, page, pageSize int, not string) ([]*Commit, error) { +// commitsByRangeWithTime returns the specific page commits before current revision, with not, since, until support +func (repo *Repository) commitsByRangeWithTime(id ObjectID, page, pageSize int, not, since, until string) ([]*Commit, error) { cmd := NewCommand("log"). AddOptionFormat("--skip=%d", (page-1)*pageSize). AddOptionFormat("--max-count=%d", pageSize). @@ -99,6 +100,12 @@ func (repo *Repository) commitsByRange(id ObjectID, page, pageSize int, not stri if not != "" { cmd.AddOptionValues("--not", not) } + if since != "" { + cmd.AddOptionFormat("--since=%s", since) + } + if until != "" { + cmd.AddOptionFormat("--until=%s", until) + } stdout, _, err := cmd.RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path}) if err != nil { @@ -212,6 +219,8 @@ type CommitsByFileAndRangeOptions struct { File string Not string Page int + Since string + Until string } // CommitsByFileAndRange return the commits according revision file and the page @@ -231,6 +240,12 @@ func (repo *Repository) CommitsByFileAndRange(opts CommitsByFileAndRangeOptions) if opts.Not != "" { gitCmd.AddOptionValues("--not", opts.Not) } + if opts.Since != "" { + gitCmd.AddOptionFormat("--since=%s", opts.Since) + } + if opts.Until != "" { + gitCmd.AddOptionFormat("--until=%s", opts.Until) + } gitCmd.AddDashesAndList(opts.File) err := gitCmd.Run(repo.Ctx, &RunOpts{ diff --git a/modules/git/repo_stats.go b/modules/git/repo_stats.go index 76fe92bb34..8c6f31c38c 100644 --- a/modules/git/repo_stats.go +++ b/modules/git/repo_stats.go @@ -40,7 +40,9 @@ func (repo *Repository) GetCodeActivityStats(fromTime time.Time, branch string) since := fromTime.Format(time.RFC3339) - stdout, _, runErr := NewCommand("rev-list", "--count", "--no-merges", "--branches=*", "--date=iso").AddOptionFormat("--since='%s'", since).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path}) + stdout, _, runErr := NewCommand("rev-list", "--count", "--no-merges", "--branches=*", "--date=iso"). + AddOptionFormat("--since=%s", since). + RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path}) if runErr != nil { return nil, runErr } @@ -60,7 +62,8 @@ func (repo *Repository) GetCodeActivityStats(fromTime time.Time, branch string) _ = stdoutWriter.Close() }() - gitCmd := NewCommand("log", "--numstat", "--no-merges", "--pretty=format:---%n%h%n%aN%n%aE%n", "--date=iso").AddOptionFormat("--since='%s'", since) + gitCmd := NewCommand("log", "--numstat", "--no-merges", "--pretty=format:---%n%h%n%aN%n%aE%n", "--date=iso"). + AddOptionFormat("--since=%s", since) if len(branch) == 0 { gitCmd.AddArguments("--branches=*") } else { diff --git a/modules/repository/commits_test.go b/modules/repository/commits_test.go index 6e407015c2..030cd7714d 100644 --- a/modules/repository/commits_test.go +++ b/modules/repository/commits_test.go @@ -200,5 +200,3 @@ func TestListToPushCommits(t *testing.T) { assert.Equal(t, now, pushCommits.Commits[1].Timestamp) } } - -// TODO TestPushUpdate diff --git a/routers/api/v1/repo/commits.go b/routers/api/v1/repo/commits.go index 20258064a0..6a93be624f 100644 --- a/routers/api/v1/repo/commits.go +++ b/routers/api/v1/repo/commits.go @@ -8,6 +8,7 @@ import ( "math" "net/http" "strconv" + "time" issues_model "code.gitea.io/gitea/models/issues" user_model "code.gitea.io/gitea/models/user" @@ -116,6 +117,16 @@ func GetAllCommits(ctx *context.APIContext) { // in: query // description: filepath of a file/dir // type: string + // - name: since + // in: query + // description: Only commits after this date will be returned (ISO 8601 format) + // type: string + // format: date-time + // - name: until + // in: query + // description: Only commits before this date will be returned (ISO 8601 format) + // type: string + // format: date-time // - name: stat // in: query // description: include diff stats for every commit (disable for speedup, default 'true') @@ -148,6 +159,23 @@ func GetAllCommits(ctx *context.APIContext) { // "409": // "$ref": "#/responses/EmptyRepository" + since := ctx.FormString("since") + until := ctx.FormString("until") + + // Validate since/until as ISO 8601 (RFC3339) + if since != "" { + if _, err := time.Parse(time.RFC3339, since); err != nil { + ctx.APIError(http.StatusUnprocessableEntity, "invalid 'since' format, expected ISO 8601 (RFC3339)") + return + } + } + if until != "" { + if _, err := time.Parse(time.RFC3339, until); err != nil { + ctx.APIError(http.StatusUnprocessableEntity, "invalid 'until' format, expected ISO 8601 (RFC3339)") + return + } + } + if ctx.Repo.Repository.IsEmpty { ctx.JSON(http.StatusConflict, api.APIError{ Message: "Git Repository is empty.", @@ -198,6 +226,8 @@ func GetAllCommits(ctx *context.APIContext) { RepoPath: ctx.Repo.GitRepo.Path, Not: not, Revision: []string{baseCommit.ID.String()}, + Since: since, + Until: until, }) if err != nil { ctx.APIErrorInternal(err) @@ -205,7 +235,7 @@ func GetAllCommits(ctx *context.APIContext) { } // Query commits - commits, err = baseCommit.CommitsByRange(listOptions.Page, listOptions.PageSize, not) + commits, err = baseCommit.CommitsByRange(listOptions.Page, listOptions.PageSize, not, since, until) if err != nil { ctx.APIErrorInternal(err) return @@ -221,6 +251,8 @@ func GetAllCommits(ctx *context.APIContext) { Not: not, Revision: []string{sha}, RelPath: []string{path}, + Since: since, + Until: until, }) if err != nil { @@ -237,6 +269,8 @@ func GetAllCommits(ctx *context.APIContext) { File: path, Not: not, Page: listOptions.Page, + Since: since, + Until: until, }) if err != nil { ctx.APIErrorInternal(err) diff --git a/routers/web/feed/branch.go b/routers/web/feed/branch.go index 4a6fe9603d..094fd987ac 100644 --- a/routers/web/feed/branch.go +++ b/routers/web/feed/branch.go @@ -15,7 +15,7 @@ import ( // ShowBranchFeed shows tags and/or releases on the repo as RSS / Atom feed func ShowBranchFeed(ctx *context.Context, repo *repo.Repository, formatType string) { - commits, err := ctx.Repo.Commit.CommitsByRange(0, 10, "") + commits, err := ctx.Repo.Commit.CommitsByRange(0, 10, "", "", "") if err != nil { ctx.ServerError("ShowBranchFeed", err) return diff --git a/routers/web/repo/commit.go b/routers/web/repo/commit.go index ae5baa9c47..01a6cbc319 100644 --- a/routers/web/repo/commit.go +++ b/routers/web/repo/commit.go @@ -78,7 +78,7 @@ func Commits(ctx *context.Context) { } // Both `git log branchName` and `git log commitId` work. - commits, err := ctx.Repo.Commit.CommitsByRange(page, pageSize, "") + commits, err := ctx.Repo.Commit.CommitsByRange(page, pageSize, "", "", "") if err != nil { ctx.ServerError("CommitsByRange", err) return diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 3512b647c2..c97e525660 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -6604,6 +6604,20 @@ "name": "path", "in": "query" }, + { + "type": "string", + "format": "date-time", + "description": "Only commits after this date will be returned (ISO 8601 format)", + "name": "since", + "in": "query" + }, + { + "type": "string", + "format": "date-time", + "description": "Only commits before this date will be returned (ISO 8601 format)", + "name": "until", + "in": "query" + }, { "type": "boolean", "description": "include diff stats for every commit (disable for speedup, default 'true')", From 0534eddd16a604fabfec464e36458e540244382d Mon Sep 17 00:00:00 2001 From: badhezi Date: Tue, 20 May 2025 05:24:10 +0300 Subject: [PATCH 16/16] Use run-name and evaluate workflow variables (#34301) This addresses https://github.com/go-gitea/gitea/issues/34247 depends on https://gitea.com/gitea/act/pulls/137 I couldn't find any previous implementation for `run-name` support on workflows so I created one. Key points: All dispatched workflows, scheduled workflows and detected workflows (from different hooks) will use and evaluate `run-name` if exists, with the corresponding gitea context and variables. This will be used as the Action run title and replace the default commit message being used today. Had to change act package jobparser (see link above) and create two helpers https://github.com/go-gitea/gitea/blob/3a1320c70df82ae88e286612c1646cffb8e2c343/models/actions/utils.go#L86 and https://github.com/go-gitea/gitea/blob/3a1320c70df82ae88e286612c1646cffb8e2c343/services/actions/context.go#L169 to pass the correct types to [GenerateGiteaContext](https://github.com/go-gitea/gitea/pull/34301/files#diff-9c9c27cb61a33e55ad33dc2c2e6a3521957a3e5cc50ddf652fdcd1def87b044dR86) and [WithGitContext](https://gitea.com/gitea/act/src/commit/65c232c4a5a40e59e257ab5d956b32585f0405d7/pkg/jobparser/jobparser.go#L84) respectively. Screenshot 2025-04-28 at 17 13 01 --- go.mod | 2 +- go.sum | 4 +- models/actions/utils.go | 19 +++ services/actions/context.go | 42 +++++- services/actions/notifier_helper.go | 31 ++++- services/actions/workflow.go | 73 ++++++---- tests/integration/actions_trigger_test.go | 155 ++++++++++++++++++++++ 7 files changed, 293 insertions(+), 33 deletions(-) diff --git a/go.mod b/go.mod index a43a0a3111..a99e9b8214 100644 --- a/go.mod +++ b/go.mod @@ -317,7 +317,7 @@ replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1 replace github.com/shurcooL/vfsgen => github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0 -replace github.com/nektos/act => gitea.com/gitea/act v0.261.4 +replace github.com/nektos/act => gitea.com/gitea/act v0.261.6 // TODO: the only difference is in `PutObject`: the fork doesn't use `NewVerifyingReader(r, sha256.New(), oid, expectedSize)`, need to figure out why replace github.com/charmbracelet/git-lfs-transfer => gitea.com/gitea/git-lfs-transfer v0.2.0 diff --git a/go.sum b/go.sum index 9b200cc2d9..24abf49099 100644 --- a/go.sum +++ b/go.sum @@ -14,8 +14,8 @@ dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= -gitea.com/gitea/act v0.261.4 h1:Tf9eLlvsYFtKcpuxlMvf9yT3g4Hshb2Beqw6C1STuH8= -gitea.com/gitea/act v0.261.4/go.mod h1:Pg5C9kQY1CEA3QjthjhlrqOC/QOT5NyWNjOjRHw23Ok= +gitea.com/gitea/act v0.261.6 h1:CjZwKOyejonNFDmsXOw3wGm5Vet573hHM6VMLsxtvPY= +gitea.com/gitea/act v0.261.6/go.mod h1:Pg5C9kQY1CEA3QjthjhlrqOC/QOT5NyWNjOjRHw23Ok= gitea.com/gitea/git-lfs-transfer v0.2.0 h1:baHaNoBSRaeq/xKayEXwiDQtlIjps4Ac/Ll4KqLMB40= gitea.com/gitea/git-lfs-transfer v0.2.0/go.mod h1:UrXUCm3xLQkq15fu7qlXHUMlrhdlXHoi13KH2Dfiits= gitea.com/gitea/go-xsd-duration v0.0.0-20220703122237-02e73435a078 h1:BAFmdZpRW7zMQZQDClaCWobRj9uL1MR3MzpCVJvc5s4= diff --git a/models/actions/utils.go b/models/actions/utils.go index 12657942fc..f6ba661ae3 100644 --- a/models/actions/utils.go +++ b/models/actions/utils.go @@ -82,3 +82,22 @@ func calculateDuration(started, stopped timeutil.TimeStamp, status Status) time. } return timeSince(s).Truncate(time.Second) } + +// best effort function to convert an action schedule to action run, to be used in GenerateGiteaContext +func (s *ActionSchedule) ToActionRun() *ActionRun { + return &ActionRun{ + Title: s.Title, + RepoID: s.RepoID, + Repo: s.Repo, + OwnerID: s.OwnerID, + WorkflowID: s.WorkflowID, + TriggerUserID: s.TriggerUserID, + TriggerUser: s.TriggerUser, + Ref: s.Ref, + CommitSHA: s.CommitSHA, + Event: s.Event, + EventPayload: s.EventPayload, + Created: s.Created, + Updated: s.Updated, + } +} diff --git a/services/actions/context.go b/services/actions/context.go index 2667e18337..8f686a688a 100644 --- a/services/actions/context.go +++ b/services/actions/context.go @@ -15,11 +15,15 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/setting" + + "github.com/nektos/act/pkg/model" ) +type GiteaContext map[string]any + // GenerateGiteaContext generate the gitea context without token and gitea_runtime_token // job can be nil when generating a context for parsing workflow-level expressions -func GenerateGiteaContext(run *actions_model.ActionRun, job *actions_model.ActionRunJob) map[string]any { +func GenerateGiteaContext(run *actions_model.ActionRun, job *actions_model.ActionRunJob) GiteaContext { event := map[string]any{} _ = json.Unmarshal([]byte(run.EventPayload), &event) @@ -42,7 +46,7 @@ func GenerateGiteaContext(run *actions_model.ActionRun, job *actions_model.Actio refName := git.RefName(ref) - gitContext := map[string]any{ + gitContext := GiteaContext{ // standard contexts, see https://docs.github.com/en/actions/learn-github-actions/contexts#github-context "action": "", // string, The name of the action currently running, or the id of a step. GitHub removes special characters, and uses the name __run when the current step runs a script without an id. If you use the same action more than once in the same job, the name will include a suffix with the sequence number with underscore before it. For example, the first script you run will have the name __run, and the second script will be named __run_2. Similarly, the second invocation of actions/checkout will be actionscheckout2. "action_path": "", // string, The path where an action is located. This property is only supported in composite actions. You can use this path to access files located in the same repository as the action. @@ -160,3 +164,37 @@ func mergeTwoOutputs(o1, o2 map[string]string) map[string]string { } return ret } + +func (g *GiteaContext) ToGitHubContext() *model.GithubContext { + return &model.GithubContext{ + Event: (*g)["event"].(map[string]any), + EventPath: (*g)["event_path"].(string), + Workflow: (*g)["workflow"].(string), + RunID: (*g)["run_id"].(string), + RunNumber: (*g)["run_number"].(string), + Actor: (*g)["actor"].(string), + Repository: (*g)["repository"].(string), + EventName: (*g)["event_name"].(string), + Sha: (*g)["sha"].(string), + Ref: (*g)["ref"].(string), + RefName: (*g)["ref_name"].(string), + RefType: (*g)["ref_type"].(string), + HeadRef: (*g)["head_ref"].(string), + BaseRef: (*g)["base_ref"].(string), + Token: "", // deliberately omitted for security + Workspace: (*g)["workspace"].(string), + Action: (*g)["action"].(string), + ActionPath: (*g)["action_path"].(string), + ActionRef: (*g)["action_ref"].(string), + ActionRepository: (*g)["action_repository"].(string), + Job: (*g)["job"].(string), + JobName: "", // not present in GiteaContext + RepositoryOwner: (*g)["repository_owner"].(string), + RetentionDays: (*g)["retention_days"].(string), + RunnerPerflog: "", // not present in GiteaContext + RunnerTrackingID: "", // not present in GiteaContext + ServerURL: (*g)["server_url"].(string), + APIURL: (*g)["api_url"].(string), + GraphQLURL: (*g)["graphql_url"].(string), + } +} diff --git a/services/actions/notifier_helper.go b/services/actions/notifier_helper.go index d179134798..54f4346a66 100644 --- a/services/actions/notifier_helper.go +++ b/services/actions/notifier_helper.go @@ -302,9 +302,11 @@ func handleWorkflows( run := &actions_model.ActionRun{ Title: strings.SplitN(commit.CommitMessage, "\n", 2)[0], RepoID: input.Repo.ID, + Repo: input.Repo, OwnerID: input.Repo.OwnerID, WorkflowID: dwf.EntryName, TriggerUserID: input.Doer.ID, + TriggerUser: input.Doer, Ref: ref, CommitSHA: commit.ID.String(), IsForkPullRequest: isForkPullRequest, @@ -333,12 +335,18 @@ func handleWorkflows( continue } - jobs, err := jobparser.Parse(dwf.Content, jobparser.WithVars(vars)) + giteaCtx := GenerateGiteaContext(run, nil) + + jobs, err := jobparser.Parse(dwf.Content, jobparser.WithVars(vars), jobparser.WithGitContext(giteaCtx.ToGitHubContext())) if err != nil { log.Error("jobparser.Parse: %v", err) continue } + if len(jobs) > 0 && jobs[0].RunName != "" { + run.Title = jobs[0].RunName + } + // cancel running jobs if the event is push or pull_request_sync if run.Event == webhook_module.HookEventPush || run.Event == webhook_module.HookEventPullRequestSync { @@ -508,9 +516,11 @@ func handleSchedules( run := &actions_model.ActionSchedule{ Title: strings.SplitN(commit.CommitMessage, "\n", 2)[0], RepoID: input.Repo.ID, + Repo: input.Repo, OwnerID: input.Repo.OwnerID, WorkflowID: dwf.EntryName, TriggerUserID: user_model.ActionsUserID, + TriggerUser: user_model.NewActionsUser(), Ref: ref, CommitSHA: commit.ID.String(), Event: input.Event, @@ -518,6 +528,25 @@ func handleSchedules( Specs: schedules, Content: dwf.Content, } + + vars, err := actions_model.GetVariablesOfRun(ctx, run.ToActionRun()) + if err != nil { + log.Error("GetVariablesOfRun: %v", err) + continue + } + + giteaCtx := GenerateGiteaContext(run.ToActionRun(), nil) + + jobs, err := jobparser.Parse(dwf.Content, jobparser.WithVars(vars), jobparser.WithGitContext(giteaCtx.ToGitHubContext())) + if err != nil { + log.Error("jobparser.Parse: %v", err) + continue + } + + if len(jobs) > 0 && jobs[0].RunName != "" { + run.Title = jobs[0].RunName + } + crons = append(crons, run) } diff --git a/services/actions/workflow.go b/services/actions/workflow.go index dc8a1dd349..15cebf6847 100644 --- a/services/actions/workflow.go +++ b/services/actions/workflow.go @@ -192,22 +192,55 @@ func DispatchActionWorkflow(ctx reqctx.RequestContext, doer *user_model.User, re // find workflow from commit var workflows []*jobparser.SingleWorkflow - for _, entry := range entries { - if entry.Name() != workflowID { + var entry *git.TreeEntry + + run := &actions_model.ActionRun{ + Title: strings.SplitN(runTargetCommit.CommitMessage, "\n", 2)[0], + RepoID: repo.ID, + Repo: repo, + OwnerID: repo.OwnerID, + WorkflowID: workflowID, + TriggerUserID: doer.ID, + TriggerUser: doer, + Ref: string(refName), + CommitSHA: runTargetCommit.ID.String(), + IsForkPullRequest: false, + Event: "workflow_dispatch", + TriggerEvent: "workflow_dispatch", + Status: actions_model.StatusWaiting, + } + + for _, e := range entries { + if e.Name() != workflowID { continue } - - content, err := actions.GetContentFromEntry(entry) - if err != nil { - return err - } - workflows, err = jobparser.Parse(content) - if err != nil { - return err - } + entry = e break } + if entry == nil { + return util.ErrorWrapLocale( + util.NewNotExistErrorf("workflow %q doesn't exist", workflowID), + "actions.workflow.not_found", workflowID, + ) + } + + content, err := actions.GetContentFromEntry(entry) + if err != nil { + return err + } + + giteaCtx := GenerateGiteaContext(run, nil) + + workflows, err = jobparser.Parse(content, jobparser.WithGitContext(giteaCtx.ToGitHubContext())) + if err != nil { + return err + } + + if len(workflows) > 0 && workflows[0].RunName != "" { + run.Title = workflows[0].RunName + } + if len(workflows) == 0 { return util.ErrorWrapLocale( util.NewNotExistErrorf("workflow %q doesn't exist", workflowID), @@ -236,25 +269,12 @@ func DispatchActionWorkflow(ctx reqctx.RequestContext, doer *user_model.User, re Inputs: inputsWithDefaults, Sender: convert.ToUserWithAccessMode(ctx, doer, perm.AccessModeNone), } + var eventPayload []byte if eventPayload, err = workflowDispatchPayload.JSONPayload(); err != nil { return fmt.Errorf("JSONPayload: %w", err) } - - run := &actions_model.ActionRun{ - Title: strings.SplitN(runTargetCommit.CommitMessage, "\n", 2)[0], - RepoID: repo.ID, - OwnerID: repo.OwnerID, - WorkflowID: workflowID, - TriggerUserID: doer.ID, - Ref: string(refName), - CommitSHA: runTargetCommit.ID.String(), - IsForkPullRequest: false, - Event: "workflow_dispatch", - TriggerEvent: "workflow_dispatch", - EventPayload: string(eventPayload), - Status: actions_model.StatusWaiting, - } + run.EventPayload = string(eventPayload) // cancel running jobs of the same workflow if err := CancelPreviousJobs( @@ -280,6 +300,5 @@ func DispatchActionWorkflow(ctx reqctx.RequestContext, doer *user_model.User, re for _, job := range allJobs { notify_service.WorkflowJobStatusUpdate(ctx, repo, doer, job, nil) } - return nil } diff --git a/tests/integration/actions_trigger_test.go b/tests/integration/actions_trigger_test.go index f576dc38ab..e755481d09 100644 --- a/tests/integration/actions_trigger_test.go +++ b/tests/integration/actions_trigger_test.go @@ -1156,6 +1156,7 @@ jobs: run := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{ Title: "add workflow", RepoID: repo.ID, + Repo: repo, Event: "workflow_dispatch", Ref: "refs/heads/dispatch", WorkflowID: "dispatch.yml", @@ -1448,3 +1449,157 @@ jobs: assert.Equal(t, pullRequest.MergedCommitID, actionRun.CommitSHA) }) } + +func TestActionRunNameWithContextVariables(t *testing.T) { + onGiteaRun(t, func(t *testing.T, u *url.URL) { + user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + + // create the repo + repo, err := repo_service.CreateRepository(db.DefaultContext, user2, user2, repo_service.CreateRepoOptions{ + Name: "action-run-name-with-variables", + Description: "test action run name", + AutoInit: true, + Gitignores: "Go", + License: "MIT", + Readme: "Default", + DefaultBranch: "main", + IsPrivate: false, + }) + assert.NoError(t, err) + assert.NotEmpty(t, repo) + + // add workflow file to the repo + addWorkflowToBaseResp, err := files_service.ChangeRepoFiles(git.DefaultContext, repo, user2, &files_service.ChangeRepoFilesOptions{ + Files: []*files_service.ChangeRepoFile{ + { + Operation: "create", + TreePath: ".gitea/workflows/runname.yml", + ContentReader: strings.NewReader(`name: test +on: + [create,delete] +run-name: ${{ gitea.actor }} is running this workflow +jobs: + test: + runs-on: ubuntu-latest + steps: + - run: echo helloworld +`), + }, + }, + Message: "add workflow with run-name", + OldBranch: "main", + NewBranch: "main", + Author: &files_service.IdentityOptions{ + GitUserName: user2.Name, + GitUserEmail: user2.Email, + }, + Committer: &files_service.IdentityOptions{ + GitUserName: user2.Name, + GitUserEmail: user2.Email, + }, + Dates: &files_service.CommitDateOptions{ + Author: time.Now(), + Committer: time.Now(), + }, + }) + assert.NoError(t, err) + assert.NotEmpty(t, addWorkflowToBaseResp) + + // Get the commit ID of the default branch + gitRepo, err := gitrepo.OpenRepository(git.DefaultContext, repo) + assert.NoError(t, err) + defer gitRepo.Close() + branch, err := git_model.GetBranch(db.DefaultContext, repo.ID, repo.DefaultBranch) + assert.NoError(t, err) + + // create a branch + err = repo_service.CreateNewBranchFromCommit(db.DefaultContext, user2, repo, gitRepo, branch.CommitID, "test-action-run-name-with-variables") + assert.NoError(t, err) + run := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{ + Title: user2.LoginName + " is running this workflow", + RepoID: repo.ID, + Event: "create", + Ref: "refs/heads/test-action-run-name-with-variables", + WorkflowID: "runname.yml", + CommitSHA: branch.CommitID, + }) + assert.NotNil(t, run) + }) +} + +func TestActionRunName(t *testing.T) { + onGiteaRun(t, func(t *testing.T, u *url.URL) { + user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + + // create the repo + repo, err := repo_service.CreateRepository(db.DefaultContext, user2, user2, repo_service.CreateRepoOptions{ + Name: "action-run-name", + Description: "test action run-name", + AutoInit: true, + Gitignores: "Go", + License: "MIT", + Readme: "Default", + DefaultBranch: "main", + IsPrivate: false, + }) + assert.NoError(t, err) + assert.NotEmpty(t, repo) + + // add workflow file to the repo + addWorkflowToBaseResp, err := files_service.ChangeRepoFiles(git.DefaultContext, repo, user2, &files_service.ChangeRepoFilesOptions{ + Files: []*files_service.ChangeRepoFile{ + { + Operation: "create", + TreePath: ".gitea/workflows/runname.yml", + ContentReader: strings.NewReader(`name: test +on: + [create,delete] +run-name: run name without variables +jobs: + test: + runs-on: ubuntu-latest + steps: + - run: echo helloworld +`), + }, + }, + Message: "add workflow with run name", + OldBranch: "main", + NewBranch: "main", + Author: &files_service.IdentityOptions{ + GitUserName: user2.Name, + GitUserEmail: user2.Email, + }, + Committer: &files_service.IdentityOptions{ + GitUserName: user2.Name, + GitUserEmail: user2.Email, + }, + Dates: &files_service.CommitDateOptions{ + Author: time.Now(), + Committer: time.Now(), + }, + }) + assert.NoError(t, err) + assert.NotEmpty(t, addWorkflowToBaseResp) + + // Get the commit ID of the default branch + gitRepo, err := gitrepo.OpenRepository(git.DefaultContext, repo) + assert.NoError(t, err) + defer gitRepo.Close() + branch, err := git_model.GetBranch(db.DefaultContext, repo.ID, repo.DefaultBranch) + assert.NoError(t, err) + + // create a branch + err = repo_service.CreateNewBranchFromCommit(db.DefaultContext, user2, repo, gitRepo, branch.CommitID, "test-action-run-name") + assert.NoError(t, err) + run := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{ + Title: "run name without variables", + RepoID: repo.ID, + Event: "create", + Ref: "refs/heads/test-action-run-name", + WorkflowID: "runname.yml", + CommitSHA: branch.CommitID, + }) + assert.NotNil(t, run) + }) +}