summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDenys Konovalov <kontakt@denyskon.de>2023-11-18 12:37:08 +0100
committerGitHub <noreply@github.com>2023-11-18 13:37:08 +0200
commit816e46ee7ce4b2649479554a940ecbe1cc505a3d (patch)
treecc00f0fa563bde5fe6706659b0a102eb4799577a
parente88377470a05f7b57a5f421f7af7c03ed66e7983 (diff)
downloadgitea-816e46ee7ce4b2649479554a940ecbe1cc505a3d.tar.gz
gitea-816e46ee7ce4b2649479554a940ecbe1cc505a3d.zip
add skip ci functionality (#28075)
Adds the possibility to skip workflow execution if the commit message contains a string like [skip ci] or similar. The default strings are the same as on GitHub, users can also set custom ones in app.ini Reference: https://docs.github.com/en/actions/managing-workflow-runs/skipping-workflow-runs Close #28020
-rw-r--r--custom/conf/app.example.ini4
-rw-r--r--docs/content/administration/config-cheat-sheet.en-us.md1
-rw-r--r--modules/setting/actions.go6
-rw-r--r--services/actions/notifier_helper.go25
-rw-r--r--tests/integration/actions_trigger_test.go91
5 files changed, 124 insertions, 3 deletions
diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini
index 325e31af39..18d6fe37a8 100644
--- a/custom/conf/app.example.ini
+++ b/custom/conf/app.example.ini
@@ -1017,7 +1017,7 @@ LEVEL = Info
;ALLOWED_TYPES =
;;
;; Max size of each file in megabytes. Defaults to 50MB
-;FILE_MAX_SIZE = 50
+;FILE_MAX_SIZE = 50
;;
;; Max number of files per upload. Defaults to 5
;MAX_FILES = 5
@@ -2583,6 +2583,8 @@ LEVEL = Info
;ENDLESS_TASK_TIMEOUT = 3h
;; Timeout to cancel the jobs which have waiting status, but haven't been picked by a runner for a long time
;ABANDONED_JOB_TIMEOUT = 24h
+;; Strings committers can place inside a commit message to skip executing the corresponding actions workflow
+;SKIP_WORKFLOW_STRINGS = [skip ci],[ci skip],[no ci],[skip actions],[actions skip]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
diff --git a/docs/content/administration/config-cheat-sheet.en-us.md b/docs/content/administration/config-cheat-sheet.en-us.md
index 385830e5d8..e7ce7f05b5 100644
--- a/docs/content/administration/config-cheat-sheet.en-us.md
+++ b/docs/content/administration/config-cheat-sheet.en-us.md
@@ -1396,6 +1396,7 @@ PROXY_HOSTS = *.github.com
- `ZOMBIE_TASK_TIMEOUT`: **10m**: Timeout to stop the task which have running status, but haven't been updated for a long time
- `ENDLESS_TASK_TIMEOUT`: **3h**: Timeout to stop the tasks which have running status and continuous updates, but don't end for a long time
- `ABANDONED_JOB_TIMEOUT`: **24h**: Timeout to cancel the jobs which have waiting status, but haven't been picked by a runner for a long time
+- `SKIP_WORKFLOW_STRINGS`: **[skip ci],[ci skip],[no ci],[skip actions],[actions skip]**: Strings committers can place inside a commit message to skip executing the corresponding actions workflow
`DEFAULT_ACTIONS_URL` indicates where the Gitea Actions runners should find the actions with relative path.
For example, `uses: actions/checkout@v3` means `https://github.com/actions/checkout@v3` since the value of `DEFAULT_ACTIONS_URL` is `github`.
diff --git a/modules/setting/actions.go b/modules/setting/actions.go
index 026bab4bfc..9fd484c3b8 100644
--- a/modules/setting/actions.go
+++ b/modules/setting/actions.go
@@ -22,9 +22,11 @@ var (
ZombieTaskTimeout time.Duration `ini:"ZOMBIE_TASK_TIMEOUT"`
EndlessTaskTimeout time.Duration `ini:"ENDLESS_TASK_TIMEOUT"`
AbandonedJobTimeout time.Duration `ini:"ABANDONED_JOB_TIMEOUT"`
+ SkipWorkflowStrings []string `ìni:"SKIP_WORKFLOW_STRINGS"`
}{
- Enabled: true,
- DefaultActionsURL: defaultActionsURLGitHub,
+ Enabled: true,
+ DefaultActionsURL: defaultActionsURLGitHub,
+ SkipWorkflowStrings: []string{"[skip ci]", "[ci skip]", "[no ci]", "[skip actions]", "[actions skip]"},
}
)
diff --git a/services/actions/notifier_helper.go b/services/actions/notifier_helper.go
index 7d5f6c6c0a..1c08cec007 100644
--- a/services/actions/notifier_helper.go
+++ b/services/actions/notifier_helper.go
@@ -7,6 +7,7 @@ import (
"bytes"
"context"
"fmt"
+ "slices"
"strings"
actions_model "code.gitea.io/gitea/models/actions"
@@ -20,6 +21,7 @@ import (
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
webhook_module "code.gitea.io/gitea/modules/webhook"
"code.gitea.io/gitea/services/convert"
@@ -144,6 +146,10 @@ func notify(ctx context.Context, input *notifyInput) error {
return fmt.Errorf("gitRepo.GetCommit: %w", err)
}
+ if skipWorkflowsForCommit(input, commit) {
+ return nil
+ }
+
var detectedWorkflows []*actions_module.DetectedWorkflow
actionsConfig := input.Repo.MustGetUnit(ctx, unit_model.TypeActions).ActionsConfig()
workflows, schedules, err := actions_module.DetectWorkflows(gitRepo, commit, input.Event, input.Payload)
@@ -195,6 +201,25 @@ func notify(ctx context.Context, input *notifyInput) error {
return handleWorkflows(ctx, detectedWorkflows, commit, input, ref)
}
+func skipWorkflowsForCommit(input *notifyInput, commit *git.Commit) bool {
+ // skip workflow runs with a configured skip-ci string in commit message if the event is push or pull_request(_sync)
+ // https://docs.github.com/en/actions/managing-workflow-runs/skipping-workflow-runs
+ skipWorkflowEvents := []webhook_module.HookEventType{
+ webhook_module.HookEventPush,
+ webhook_module.HookEventPullRequest,
+ webhook_module.HookEventPullRequestSync,
+ }
+ if slices.Contains(skipWorkflowEvents, input.Event) {
+ for _, s := range setting.Actions.SkipWorkflowStrings {
+ if strings.Contains(commit.CommitMessage, s) {
+ log.Debug("repo %s with commit %s: skipped run because of %s string", input.Repo.RepoPath(), commit.ID, s)
+ return true
+ }
+ }
+ }
+ return false
+}
+
func handleWorkflows(
ctx context.Context,
detectedWorkflows []*actions_module.DetectedWorkflow,
diff --git a/tests/integration/actions_trigger_test.go b/tests/integration/actions_trigger_test.go
index 253de70326..684b93ed1d 100644
--- a/tests/integration/actions_trigger_test.go
+++ b/tests/integration/actions_trigger_test.go
@@ -4,6 +4,7 @@
package integration
import (
+ "fmt"
"net/url"
"strings"
"testing"
@@ -18,6 +19,7 @@ import (
user_model "code.gitea.io/gitea/models/user"
actions_module "code.gitea.io/gitea/modules/actions"
"code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/setting"
pull_service "code.gitea.io/gitea/services/pull"
repo_service "code.gitea.io/gitea/services/repository"
files_service "code.gitea.io/gitea/services/repository/files"
@@ -194,3 +196,92 @@ func TestPullRequestTargetEvent(t *testing.T) {
assert.Equal(t, 1, unittest.GetCount(t, &actions_model.ActionRun{RepoID: baseRepo.ID}))
})
}
+
+func TestSkipCI(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: "skip-ci",
+ Description: "test skip ci functionality",
+ AutoInit: true,
+ Gitignores: "Go",
+ License: "MIT",
+ Readme: "Default",
+ DefaultBranch: "main",
+ IsPrivate: false,
+ })
+ assert.NoError(t, err)
+ assert.NotEmpty(t, repo)
+
+ // enable actions
+ err = repo_model.UpdateRepositoryUnits(db.DefaultContext, repo, []repo_model.RepoUnit{{
+ RepoID: repo.ID,
+ Type: unit_model.TypeActions,
+ }}, nil)
+ assert.NoError(t, err)
+
+ // 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/pr.yml",
+ ContentReader: strings.NewReader("name: test\non:\n push:\njobs:\n test:\n runs-on: ubuntu-latest\n steps:\n - run: echo helloworld\n"),
+ },
+ },
+ Message: "add workflow",
+ OldBranch: "main",
+ NewBranch: "main",
+ Author: &files_service.IdentityOptions{
+ Name: user2.Name,
+ Email: user2.Email,
+ },
+ Committer: &files_service.IdentityOptions{
+ Name: user2.Name,
+ Email: user2.Email,
+ },
+ Dates: &files_service.CommitDateOptions{
+ Author: time.Now(),
+ Committer: time.Now(),
+ },
+ })
+ assert.NoError(t, err)
+ assert.NotEmpty(t, addWorkflowToBaseResp)
+
+ // a run has been created
+ assert.Equal(t, 1, unittest.GetCount(t, &actions_model.ActionRun{RepoID: repo.ID}))
+
+ // add a file with a configured skip-ci string in commit message
+ addFileResp, err := files_service.ChangeRepoFiles(git.DefaultContext, repo, user2, &files_service.ChangeRepoFilesOptions{
+ Files: []*files_service.ChangeRepoFile{
+ {
+ Operation: "create",
+ TreePath: "bar.txt",
+ ContentReader: strings.NewReader("bar"),
+ },
+ },
+ Message: fmt.Sprintf("%s add bar", setting.Actions.SkipWorkflowStrings[0]),
+ OldBranch: "main",
+ NewBranch: "main",
+ Author: &files_service.IdentityOptions{
+ Name: user2.Name,
+ Email: user2.Email,
+ },
+ Committer: &files_service.IdentityOptions{
+ Name: user2.Name,
+ Email: user2.Email,
+ },
+ Dates: &files_service.CommitDateOptions{
+ Author: time.Now(),
+ Committer: time.Now(),
+ },
+ })
+ assert.NoError(t, err)
+ assert.NotEmpty(t, addFileResp)
+
+ // the commit message contains a configured skip-ci string, so there is still only 1 record
+ assert.Equal(t, 1, unittest.GetCount(t, &actions_model.ActionRun{RepoID: repo.ID}))
+ })
+}