Skip to content

Commit

Permalink
Merge branch 'main' into astick
Browse files Browse the repository at this point in the history
  • Loading branch information
GiteaBot authored Mar 14, 2024
2 parents a6b52b2 + 7a90e59 commit 3a00332
Show file tree
Hide file tree
Showing 11 changed files with 190 additions and 73 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ GOFUMPT_PACKAGE ?= mvdan.cc/[email protected]
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/[email protected]
GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/[email protected]
MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/[email protected]
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.30.5
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@db51e79a0e37c572d8b59ae0c58bf2bbbbe53285
XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest
GO_LICENSES_PACKAGE ?= github.com/google/[email protected]
GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/[email protected]
Expand Down
2 changes: 1 addition & 1 deletion custom/conf/app.example.ini
Original file line number Diff line number Diff line change
Expand Up @@ -2608,7 +2608,7 @@ 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
;; Strings committers can place inside a commit message or PR title to skip executing the corresponding actions workflow
;SKIP_WORKFLOW_STRINGS = [skip ci],[ci skip],[no ci],[skip actions],[actions skip]

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Expand Down
2 changes: 1 addition & 1 deletion docs/content/administration/config-cheat-sheet.en-us.md
Original file line number Diff line number Diff line change
Expand Up @@ -1406,7 +1406,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
- `SKIP_WORKFLOW_STRINGS`: **[skip ci],[ci skip],[no ci],[skip actions],[actions skip]**: Strings committers can place inside a commit message or PR title 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@v4` means `https://github.com/actions/checkout@v4` since the value of `DEFAULT_ACTIONS_URL` is `github`.
Expand Down
57 changes: 45 additions & 12 deletions modules/markup/csv/csv.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,29 +77,62 @@ func writeField(w io.Writer, element, class, field string) error {
}

// Render implements markup.Renderer
func (Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error {
func (r Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error {
tmpBlock := bufio.NewWriter(output)
maxSize := setting.UI.CSV.MaxFileSize

// FIXME: don't read all to memory
rawBytes, err := io.ReadAll(input)
if maxSize == 0 {
return r.tableRender(ctx, input, tmpBlock)
}

rawBytes, err := io.ReadAll(io.LimitReader(input, maxSize+1))
if err != nil {
return err
}

if setting.UI.CSV.MaxFileSize != 0 && setting.UI.CSV.MaxFileSize < int64(len(rawBytes)) {
if _, err := tmpBlock.WriteString("<pre>"); err != nil {
return err
}
if _, err := tmpBlock.WriteString(html.EscapeString(string(rawBytes))); err != nil {
return err
if int64(len(rawBytes)) <= maxSize {
return r.tableRender(ctx, bytes.NewReader(rawBytes), tmpBlock)
}
return r.fallbackRender(io.MultiReader(bytes.NewReader(rawBytes), input), tmpBlock)
}

func (Renderer) fallbackRender(input io.Reader, tmpBlock *bufio.Writer) error {
_, err := tmpBlock.WriteString("<pre>")
if err != nil {
return err
}

scan := bufio.NewScanner(input)
scan.Split(bufio.ScanRunes)
for scan.Scan() {
switch scan.Text() {
case `&`:
_, err = tmpBlock.WriteString("&amp;")
case `'`:
_, err = tmpBlock.WriteString("&#39;") // "&#39;" is shorter than "&apos;" and apos was not in HTML until HTML5.
case `<`:
_, err = tmpBlock.WriteString("&lt;")
case `>`:
_, err = tmpBlock.WriteString("&gt;")
case `"`:
_, err = tmpBlock.WriteString("&#34;") // "&#34;" is shorter than "&quot;".
default:
_, err = tmpBlock.Write(scan.Bytes())
}
if _, err := tmpBlock.WriteString("</pre>"); err != nil {
if err != nil {
return err
}
return tmpBlock.Flush()
}

rd, err := csv.CreateReaderAndDetermineDelimiter(ctx, bytes.NewReader(rawBytes))
_, err = tmpBlock.WriteString("</pre>")
if err != nil {
return err
}
return tmpBlock.Flush()
}

func (Renderer) tableRender(ctx *markup.RenderContext, input io.Reader, tmpBlock *bufio.Writer) error {
rd, err := csv.CreateReaderAndDetermineDelimiter(ctx, input)
if err != nil {
return err
}
Expand Down
10 changes: 10 additions & 0 deletions modules/markup/csv/csv_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
package markup

import (
"bufio"
"bytes"
"strings"
"testing"

Expand All @@ -29,4 +31,12 @@ func TestRenderCSV(t *testing.T) {
assert.NoError(t, err)
assert.EqualValues(t, v, buf.String())
}

t.Run("fallbackRender", func(t *testing.T) {
var buf bytes.Buffer
err := render.fallbackRender(strings.NewReader("1,<a>\n2,<b>"), bufio.NewWriter(&buf))
assert.NoError(t, err)
want := "<pre>1,&lt;a&gt;\n2,&lt;b&gt;</pre>"
assert.Equal(t, want, buf.String())
})
}
9 changes: 9 additions & 0 deletions modules/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,3 +212,12 @@ func ToFloat64(number any) (float64, error) {
func ToPointer[T any](val T) *T {
return &val
}

// IfZero returns "def" if "v" is a zero value, otherwise "v"
func IfZero[T comparable](v, def T) T {
var zero T
if v == zero {
return def
}
return v
}
1 change: 1 addition & 0 deletions routers/web/repo/setting/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,7 @@ func checkWebhook(ctx *context.Context) (*ownerRepoCtx, *webhook.Webhook) {
return nil, nil
}
ctx.Data["BaseLink"] = orCtx.Link
ctx.Data["BaseLinkNew"] = orCtx.LinkNew

var w *webhook.Webhook
if orCtx.RepoID > 0 {
Expand Down
10 changes: 7 additions & 3 deletions services/actions/notifier_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ func notify(ctx context.Context, input *notifyInput) error {
return fmt.Errorf("gitRepo.GetCommit: %w", err)
}

if skipWorkflowsForCommit(input, commit) {
if skipWorkflows(input, commit) {
return nil
}

Expand Down Expand Up @@ -223,8 +223,8 @@ 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)
func skipWorkflows(input *notifyInput, commit *git.Commit) bool {
// skip workflow runs with a configured skip-ci string in commit message or pr title 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,
Expand All @@ -233,6 +233,10 @@ func skipWorkflowsForCommit(input *notifyInput, commit *git.Commit) bool {
}
if slices.Contains(skipWorkflowEvents, input.Event) {
for _, s := range setting.Actions.SkipWorkflowStrings {
if input.PullRequest != nil && strings.Contains(input.PullRequest.Issue.Title, s) {
log.Debug("repo %s: skipped run for pr %v because of %s string", input.Repo.RepoPath(), input.PullRequest.Issue.ID, s)
return true
}
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
Expand Down
78 changes: 29 additions & 49 deletions services/webhook/deliver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
webhook_model "code.gitea.io/gitea/models/webhook"
"code.gitea.io/gitea/modules/hostmatcher"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
webhook_module "code.gitea.io/gitea/modules/webhook"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -226,69 +227,47 @@ func TestWebhookDeliverSpecificTypes(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())

type hookCase struct {
gotBody chan []byte
gotBody chan []byte
httpMethod string // default to POST
}

cases := map[string]hookCase{
webhook_module.SLACK: {
gotBody: make(chan []byte, 1),
},
webhook_module.DISCORD: {
gotBody: make(chan []byte, 1),
},
webhook_module.DINGTALK: {
gotBody: make(chan []byte, 1),
},
webhook_module.TELEGRAM: {
gotBody: make(chan []byte, 1),
},
webhook_module.MSTEAMS: {
gotBody: make(chan []byte, 1),
},
webhook_module.FEISHU: {
gotBody: make(chan []byte, 1),
},
webhook_module.MATRIX: {
gotBody: make(chan []byte, 1),
},
webhook_module.WECHATWORK: {
gotBody: make(chan []byte, 1),
},
webhook_module.PACKAGIST: {
gotBody: make(chan []byte, 1),
},
cases := map[string]*hookCase{
webhook_module.SLACK: {},
webhook_module.DISCORD: {},
webhook_module.DINGTALK: {},
webhook_module.TELEGRAM: {},
webhook_module.MSTEAMS: {},
webhook_module.FEISHU: {},
webhook_module.MATRIX: {httpMethod: "PUT"},
webhook_module.WECHATWORK: {},
webhook_module.PACKAGIST: {},
}

s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
typ := strings.Split(r.URL.Path, "/")[1] // URL: "/{webhook_type}/other-path"
assert.Equal(t, "application/json", r.Header.Get("Content-Type"), r.URL.Path)

typ := strings.Split(r.URL.Path, "/")[1] // take first segment (after skipping leading slash)
hc := cases[typ]
require.NotNil(t, hc.gotBody, r.URL.Path)
body, err := io.ReadAll(r.Body)
assert.NoError(t, err)
w.WriteHeader(200)
hc.gotBody <- body
assert.Equal(t, util.IfZero(cases[typ].httpMethod, "POST"), r.Method, "webhook test request %q", r.URL.Path)
body, _ := io.ReadAll(r.Body) // read request and send it back to the test by testcase's chan
cases[typ].gotBody <- body
w.WriteHeader(http.StatusNoContent)
}))
t.Cleanup(s.Close)

p := pushTestPayload()
data, err := p.JSONPayload()
assert.NoError(t, err)

for typ, hc := range cases {
typ := typ
hc := hc
for typ := range cases {
cases[typ].gotBody = make(chan []byte, 1)
typ := typ // TODO: remove this workaround when Go >= 1.22
t.Run(typ, func(t *testing.T) {
t.Parallel()
hook := &webhook_model.Webhook{
RepoID: 3,
IsActive: true,
Type: typ,
URL: s.URL + "/" + typ,
HTTPMethod: "POST",
ContentType: 0, // set to 0 so that falling back to default request fails with "invalid content type"
Meta: "{}",
RepoID: 3,
IsActive: true,
Type: typ,
URL: s.URL + "/" + typ,
Meta: "{}",
}
assert.NoError(t, webhook_model.CreateWebhook(db.DefaultContext, hook))

Expand All @@ -304,10 +283,11 @@ func TestWebhookDeliverSpecificTypes(t *testing.T) {
assert.NotNil(t, hookTask)

assert.NoError(t, Deliver(context.Background(), hookTask))

select {
case gotBody := <-hc.gotBody:
case gotBody := <-cases[typ].gotBody:
assert.NotEqual(t, string(data), string(gotBody), "request body must be different from the event payload")
assert.Equal(t, hookTask.RequestInfo.Body, string(gotBody), "request body was not saved")
assert.Equal(t, hookTask.RequestInfo.Body, string(gotBody), "delivered webhook payload doesn't match saved request")
case <-time.After(5 * time.Second):
t.Fatal("waited to long for request to happen")
}
Expand Down
51 changes: 45 additions & 6 deletions tests/integration/actions_trigger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
actions_module "code.gitea.io/gitea/modules/actions"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/test"
pull_service "code.gitea.io/gitea/services/pull"
repo_service "code.gitea.io/gitea/services/repository"
files_service "code.gitea.io/gitea/services/repository/files"
Expand Down Expand Up @@ -199,6 +200,7 @@ func TestPullRequestTargetEvent(t *testing.T) {

func TestSkipCI(t *testing.T) {
onGiteaRun(t, func(t *testing.T, u *url.URL) {
session := loginUser(t, "user2")
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})

// create the repo
Expand All @@ -209,7 +211,7 @@ func TestSkipCI(t *testing.T) {
Gitignores: "Go",
License: "MIT",
Readme: "Default",
DefaultBranch: "main",
DefaultBranch: "master",
IsPrivate: false,
})
assert.NoError(t, err)
Expand All @@ -228,12 +230,12 @@ func TestSkipCI(t *testing.T) {
{
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"),
ContentReader: strings.NewReader("name: test\non:\n push:\n branches: [master]\n pull_request:\njobs:\n test:\n runs-on: ubuntu-latest\n steps:\n - run: echo helloworld\n"),
},
},
Message: "add workflow",
OldBranch: "main",
NewBranch: "main",
OldBranch: "master",
NewBranch: "master",
Author: &files_service.IdentityOptions{
Name: user2.Name,
Email: user2.Email,
Expand Down Expand Up @@ -263,8 +265,8 @@ func TestSkipCI(t *testing.T) {
},
},
Message: fmt.Sprintf("%s add bar", setting.Actions.SkipWorkflowStrings[0]),
OldBranch: "main",
NewBranch: "main",
OldBranch: "master",
NewBranch: "master",
Author: &files_service.IdentityOptions{
Name: user2.Name,
Email: user2.Email,
Expand All @@ -283,5 +285,42 @@ func TestSkipCI(t *testing.T) {

// 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}))

// add file to new branch
addFileToBranchResp, err := files_service.ChangeRepoFiles(git.DefaultContext, repo, user2, &files_service.ChangeRepoFilesOptions{
Files: []*files_service.ChangeRepoFile{
{
Operation: "create",
TreePath: "test-skip-ci",
ContentReader: strings.NewReader("test-skip-ci"),
},
},
Message: "add test file",
OldBranch: "master",
NewBranch: "test-skip-ci",
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, addFileToBranchResp)

resp := testPullCreate(t, session, "user2", "skip-ci", true, "master", "test-skip-ci", "[skip ci] test-skip-ci")

// check the redirected URL
url := test.RedirectURL(resp)
assert.Regexp(t, "^/user2/skip-ci/pulls/[0-9]*$", url)

// the pr title 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}))
})
}
Loading

0 comments on commit 3a00332

Please sign in to comment.