From d81659d03947ac4533099089c5f442437e1d6887 Mon Sep 17 00:00:00 2001 From: Jason Song Date: Tue, 16 May 2023 14:49:40 +0800 Subject: [PATCH 01/18] Respect original content when creating secrets (#24745) Fix #24721. Follow what GitHub does: - Don't trim spaces for secrets. - Newline should be `\n` instead of `\r\n`. Did some tests with: ```yaml name: secrets on: push jobs: show_secrets: runs-on: ubuntu-latest steps: - name: Dump secrets context run: echo '${{ toJSON(secrets) }}' | base64 ``` `AAAAAA`: ```text AAAAAA AAAAAA ``` `BBBBBB`: ```text BBBBBB BBBBBB ``` On GitHub: image On Gitea (before): image On Gitea (after): image --- models/secret/secret.go | 2 +- routers/web/shared/secrets/secrets.go | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/models/secret/secret.go b/models/secret/secret.go index f970d5319e7e..8b23b6c35cf8 100644 --- a/models/secret/secret.go +++ b/models/secret/secret.go @@ -59,7 +59,7 @@ func newSecret(ownerID, repoID int64, name, data string) *Secret { // InsertEncryptedSecret Creates, encrypts, and validates a new secret with yet unencrypted data and insert into database func InsertEncryptedSecret(ctx context.Context, ownerID, repoID int64, name, data string) (*Secret, error) { - encrypted, err := secret_module.EncryptSecret(setting.SecretKey, strings.TrimSpace(data)) + encrypted, err := secret_module.EncryptSecret(setting.SecretKey, data) if err != nil { return nil, err } diff --git a/routers/web/shared/secrets/secrets.go b/routers/web/shared/secrets/secrets.go index 0e6fa247416f..a0d648f908fd 100644 --- a/routers/web/shared/secrets/secrets.go +++ b/routers/web/shared/secrets/secrets.go @@ -5,6 +5,7 @@ package secrets import ( "net/http" + "strings" "code.gitea.io/gitea/models/db" secret_model "code.gitea.io/gitea/models/secret" @@ -27,7 +28,15 @@ func SetSecretsContext(ctx *context.Context, ownerID, repoID int64) { func PerformSecretsPost(ctx *context.Context, ownerID, repoID int64, redirectURL string) { form := web.GetForm(ctx).(*forms.AddSecretForm) - s, err := secret_model.InsertEncryptedSecret(ctx, ownerID, repoID, form.Title, form.Content) + content := form.Content + // Since the content is from a form which is a textarea, the line endings are \r\n. + // It's a standard behavior of HTML. + // But we want to store them as \n like what GitHub does. + // And users are unlikely to really need to keep the \r. + // Other than this, we should respect the original content, even leading or trailing spaces. + content = strings.ReplaceAll(content, "\r\n", "\n") + + s, err := secret_model.InsertEncryptedSecret(ctx, ownerID, repoID, form.Title, content) if err != nil { log.Error("InsertEncryptedSecret: %v", err) ctx.Flash.Error(ctx.Tr("secrets.creation.failed")) From e720f492066c791b06ce92d13a2103b58c63dbf7 Mon Sep 17 00:00:00 2001 From: silverwind Date: Tue, 16 May 2023 14:42:16 +0200 Subject: [PATCH 02/18] Skip TestRepoCommitsStatusParallel on CI (#24741) Related: https://github.com/go-gitea/gitea/issues/22109 Co-authored-by: Giteabot --- tests/integration/repo_commits_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/integration/repo_commits_test.go b/tests/integration/repo_commits_test.go index 57ecc4da5f74..0cfd21485d19 100644 --- a/tests/integration/repo_commits_test.go +++ b/tests/integration/repo_commits_test.go @@ -7,6 +7,7 @@ import ( "fmt" "net/http" "net/http/httptest" + "os" "path" "sync" "testing" @@ -134,6 +135,9 @@ func TestRepoCommitsWithStatusRunning(t *testing.T) { } func TestRepoCommitsStatusParallel(t *testing.T) { + if os.Getenv("CI") != "" { + t.Skip("Skipping because test is flaky on CI") + } defer tests.PrepareTestEnv(t)() session := loginUser(t, "user2") From c367b63b7f931b58320c48388cc94e66f679d0a7 Mon Sep 17 00:00:00 2001 From: Gary Moon Date: Tue, 16 May 2023 11:46:46 -0400 Subject: [PATCH 03/18] Add @garymoon to MAINTAINERS (#24752) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hi all. I would like to apply to be a maintainer please. I have [four accepted PRs](https://github.com/go-gitea/gitea/pulls?q=is%3Apr+author%3Agarymoon) (such as they are), sign my commits, and have MFA enabled everywhere. My hope is to continue with code contributions where I can (I'm not a developer, just a wannabe), take some work off kdumont's hands, and contribute on the infrastructure side where there is room 💚 :tea: Thank you! --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index ef416639b0fe..f62660a5553c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -50,3 +50,4 @@ Eddie Yang <576951401@qq.com> (@yp05327) Dong Ge (@sillyguodong) Xinyi Gong (@HesterG) wxiaoguang (@wxiaoguang) +Gary Moon (@garymoon) From 584c0789fa72f5e21167ba5f1370b7918e604247 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Wed, 17 May 2023 04:55:51 +0800 Subject: [PATCH 04/18] Make mailer SMTP check have timed context (#24751) Make mailer SMTP check have timed context Otherwise Gitea may block for long time if the DNS request blocks. --------- Co-authored-by: Giteabot --- modules/setting/mailer.go | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/modules/setting/mailer.go b/modules/setting/mailer.go index 39afce7d4645..a2bc2df44499 100644 --- a/modules/setting/mailer.go +++ b/modules/setting/mailer.go @@ -4,6 +4,7 @@ package setting import ( + "context" "net" "net/mail" "strings" @@ -198,7 +199,7 @@ func loadMailerFrom(rootCfg ConfigProvider) { ips := tryResolveAddr(MailService.SMTPAddr) if MailService.Protocol == "smtp" { for _, ip := range ips { - if !ip.IsLoopback() { + if !ip.IP.IsLoopback() { log.Warn("connecting over insecure SMTP protocol to non-local address is not recommended") break } @@ -258,20 +259,21 @@ func loadNotifyMailFrom(rootCfg ConfigProvider) { log.Info("Notify Mail Service Enabled") } -func tryResolveAddr(addr string) []net.IP { +func tryResolveAddr(addr string) []net.IPAddr { if strings.HasPrefix(addr, "[") && strings.HasSuffix(addr, "]") { addr = addr[1 : len(addr)-1] } ip := net.ParseIP(addr) if ip != nil { - ips := make([]net.IP, 1) - ips[0] = ip - return ips + return []net.IPAddr{{IP: ip}} } - ips, err := net.LookupIP(addr) + + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + ips, err := net.DefaultResolver.LookupIPAddr(ctx, addr) if err != nil { log.Warn("could not look up mailer.SMTP_ADDR: %v", err) - return make([]net.IP, 0) + return nil } return ips } From 0a3c4d4a595cc7e12462dde393ed64186260f26b Mon Sep 17 00:00:00 2001 From: Yarden Shoham Date: Wed, 17 May 2023 03:12:37 +0300 Subject: [PATCH 05/18] Fix team members API endpoint pagination (#24754) Now it's 1-based instead of 0-based - Fixes #24747 ### Before ![image](https://github.com/go-gitea/gitea/assets/20454870/9b58ecfa-666c-4b78-bd0f-93233efeecbd) ### After ![image](https://github.com/go-gitea/gitea/assets/20454870/103b767a-e02e-4473-9f9f-5a676a61c174) ## :warning: BREAKING :warning: Previous API consumers may have relied on the 0-based pagination of this endpoint. The page numbering now starts at 1, as documented. Signed-off-by: Yarden Shoham --- models/organization/team_user.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/models/organization/team_user.go b/models/organization/team_user.go index 816daf3d34b3..768dc24c507b 100644 --- a/models/organization/team_user.go +++ b/models/organization/team_user.go @@ -63,8 +63,8 @@ func GetTeamMembers(ctx context.Context, opts *SearchMembersOptions) ([]*user_mo Where(builder.Eq{"team_id": opts.TeamID}), ) } - if opts.PageSize > 0 && opts.Page > -1 { - sess = sess.Limit(opts.PageSize, opts.Page*opts.PageSize) + if opts.PageSize > 0 && opts.Page > 0 { + sess = sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize) } if err := sess.OrderBy("full_name, name").Find(&members); err != nil { return nil, err From 473dee7c7a017f21f9c6dff38812b0f31dbed3c8 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 17 May 2023 13:42:08 +0800 Subject: [PATCH 06/18] Ignore build for docs only (#24761) Fix https://github.com/go-gitea/gitea/pull/24530#issuecomment-1550227919 --- .../workflows/pull-compliance-docsignore.yml | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/.github/workflows/pull-compliance-docsignore.yml b/.github/workflows/pull-compliance-docsignore.yml index 1599e19207ad..ea0619e122eb 100644 --- a/.github/workflows/pull-compliance-docsignore.yml +++ b/.github/workflows/pull-compliance-docsignore.yml @@ -11,3 +11,33 @@ jobs: runs-on: ubuntu-latest steps: - run: echo "No build required" + + lint-backend: + runs-on: ubuntu-latest + steps: + - run: echo "No build required" + + lint-go-windows: + runs-on: ubuntu-latest + steps: + - run: echo "No build required" + + lint-go-gogit: + runs-on: ubuntu-latest + steps: + - run: echo "No build required" + + checks-backend: + runs-on: ubuntu-latest + steps: + - run: echo "No build required" + + frontend: + runs-on: ubuntu-latest + steps: + - run: echo "No build required" + + backend: + runs-on: ubuntu-latest + steps: + - run: echo "No build required" From 9fb0945a0959f1c0f0c9e75980e8a0cc5355184c Mon Sep 17 00:00:00 2001 From: Alejandro Leal Date: Wed, 17 May 2023 01:45:26 -0400 Subject: [PATCH 07/18] Updates to doc (#24757) ## Misspelling fixes to: - docs/content/doc/administration/config-cheat-sheet.en-us.md - docs/content/doc/installation/from-source.en-us.md - docs/content/doc/usage/packages/cargo.en-us.md - docs/content/doc/usage/packages/storage.en-us.md --------- Co-authored-by: delvh --- docs/content/doc/administration/config-cheat-sheet.en-us.md | 2 +- docs/content/doc/installation/from-source.en-us.md | 2 +- docs/content/doc/usage/packages/cargo.en-us.md | 4 ++-- docs/content/doc/usage/packages/storage.en-us.md | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/content/doc/administration/config-cheat-sheet.en-us.md b/docs/content/doc/administration/config-cheat-sheet.en-us.md index 624c59e87cf5..5bf5d6fbf9d6 100644 --- a/docs/content/doc/administration/config-cheat-sheet.en-us.md +++ b/docs/content/doc/administration/config-cheat-sheet.en-us.md @@ -220,7 +220,7 @@ The following configuration set `Content-Type: application/vnd.android.package-a - `SHOW_USER_EMAIL`: **true**: Whether the email of the user should be shown in the Explore Users page. - `THEMES`: **auto,gitea,arc-green**: All available themes. Allow users select personalized themes. regardless of the value of `DEFAULT_THEME`. -- `THEME_COLOR_META_TAG`: **\**: Value of `theme-color` meta tag, used by some mobile browers for chrome and out-of-viewport areas. Default is unset which uses body color. +- `THEME_COLOR_META_TAG`: **\**: Value of `theme-color` meta tag, used by some mobile browsers for chrome and out-of-viewport areas. Default is unset which uses body color. - `MAX_DISPLAY_FILE_SIZE`: **8388608**: Max size of files to be displayed (default is 8MiB) - `REACTIONS`: All available reactions users can choose on issues/prs and comments Values can be emoji alias (:smile:) or a unicode emoji. diff --git a/docs/content/doc/installation/from-source.en-us.md b/docs/content/doc/installation/from-source.en-us.md index 72a3e1247265..57db1a36aec4 100644 --- a/docs/content/doc/installation/from-source.en-us.md +++ b/docs/content/doc/installation/from-source.en-us.md @@ -201,7 +201,7 @@ This can be combined with `CC`, `GOOS`, and `GOARCH` as above. A script to enable bash-completion can be found at [`contrib/autocompletion/bash_autocomplete`](https://raw.githubusercontent.com/go-gitea/gitea/main/contrib/autocompletion/bash_autocomplete). This should be altered as appropriate and can be `source` in your `.bashrc` or copied as `/usr/share/bash-completion/completions/gitea`. -Similary a script for zsh-completion can be found at [`contrib/autocompletion/zsh_autocomplete`](https://raw.githubusercontent.com/go-gitea/gitea/main/contrib/autocompletion/zsh_autocomplete). This can be copied to `/usr/share/zsh/_gitea` or sourced within your +Similarly, a script for zsh-completion can be found at [`contrib/autocompletion/zsh_autocomplete`](https://raw.githubusercontent.com/go-gitea/gitea/main/contrib/autocompletion/zsh_autocomplete). This can be copied to `/usr/share/zsh/_gitea` or sourced within your `.zshrc`. YMMV and these scripts may need further improvement. diff --git a/docs/content/doc/usage/packages/cargo.en-us.md b/docs/content/doc/usage/packages/cargo.en-us.md index a74384ccefe9..f16d914224af 100644 --- a/docs/content/doc/usage/packages/cargo.en-us.md +++ b/docs/content/doc/usage/packages/cargo.en-us.md @@ -25,13 +25,13 @@ Publish [Cargo](https://doc.rust-lang.org/stable/cargo/) packages for your user To work with the Cargo package registry, you need [Rust and Cargo](https://www.rust-lang.org/tools/install). -Cargo stores informations about the available packages in a package index stored in a git repository. +Cargo stores information about the available packages in a package index stored in a git repository. This repository is needed to work with the registry. The following section describes how to create it. ## Index Repository -Cargo stores informations about the available packages in a package index stored in a git repository. +Cargo stores information about the available packages in a package index stored in a git repository. In Gitea this repository has the special name `_cargo-index`. After a package was uploaded, its metadata is automatically written to the index. The content of this repository should not be manually modified. diff --git a/docs/content/doc/usage/packages/storage.en-us.md b/docs/content/doc/usage/packages/storage.en-us.md index 598a636f5e30..74bd53b52e39 100644 --- a/docs/content/doc/usage/packages/storage.en-us.md +++ b/docs/content/doc/usage/packages/storage.en-us.md @@ -28,7 +28,7 @@ If two identical files are uploaded only one blob is saved on the filesystem. This ensures no space is wasted for duplicated files. If two packages are uploaded with identical files, both packages will display the same size but on the filesystem they require only half of the size. -Whenever a package gets deleted only the references to the underlaying blobs are removed. +Whenever a package gets deleted, only the references to the underlying blobs are removed. The blobs get not removed at this moment, so they still require space on the filesystem. When a new package gets uploaded the existing blobs may get referenced again. From e7c2231dee356df5cbe5a47c07e31e3a8d090a6f Mon Sep 17 00:00:00 2001 From: Zettat123 Date: Wed, 17 May 2023 16:11:13 +0800 Subject: [PATCH 08/18] Support for status check pattern (#24633) This PR is to allow users to specify status checks by patterns. Users can enter patterns in the "Status Check Pattern" `textarea` to match status checks and each line specifies a pattern. If "Status Check" is enabled, patterns cannot be empty and user must enter at least one pattern. Users will no longer be able to choose status checks from the table. But a __*`Matched`*__ mark will be added to the matched checks to help users enter patterns. Benefits: - Even if no status checks have been completed, users can specify necessary status checks in advance. - More flexible. Users can specify a series of status checks by one pattern. Before: ![image](https://github.com/go-gitea/gitea/assets/15528715/635738ad-580c-49cd-941d-c721e5b99be4) After: ![image](https://github.com/go-gitea/gitea/assets/15528715/16aa7b1b-abf1-4170-9bfa-ae6fc9803a82) --------- Co-authored-by: silverwind --- options/locale/locale_en-US.ini | 7 +- package-lock.json | 197 +++++++++++++++--- package.json | 1 + routers/web/repo/pull.go | 4 +- routers/web/repo/setting_protected_branch.go | 40 ++-- services/forms/repo_form.go | 2 +- services/pull/commit_status.go | 54 +++-- templates/repo/settings/protected_branch.tmpl | 11 +- web_src/css/repo.css | 5 + web_src/js/features/repo-settings.js | 24 +++ 10 files changed, 267 insertions(+), 78 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 906a32dc2d73..2167471aace5 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -2190,8 +2190,13 @@ settings.protect_merge_whitelist_committers_desc = Allow only whitelisted users settings.protect_merge_whitelist_users = Whitelisted users for merging: settings.protect_merge_whitelist_teams = Whitelisted teams for merging: settings.protect_check_status_contexts = Enable Status Check -settings.protect_check_status_contexts_desc = Require status checks to pass before merging. Choose which status checks must pass before branches can be merged into a branch that matches this rule. When enabled, commits must first be pushed to another branch, then merged or pushed directly to a branch that matches this rule after status checks have passed. If no contexts are selected, the last commit must be successful regardless of context. +settings.protect_status_check_patterns = Status check patterns: +settings.protect_status_check_patterns_desc = Enter patterns to specify which status checks must pass before branches can be merged into a branch that matches this rule. Each line specifies a pattern. Patterns cannot be empty. +settings.protect_check_status_contexts_desc = Require status checks to pass before merging. When enabled, commits must first be pushed to another branch, then merged or pushed directly to a branch that matches this rule after status checks have passed. If no contexts are matched, the last commit must be successful regardless of context. settings.protect_check_status_contexts_list = Status checks found in the last week for this repository +settings.protect_status_check_matched = Matched +settings.protect_invalid_status_check_pattern = Invalid status check pattern: "%s". +settings.protect_no_valid_status_check_patterns = No valid status check patterns. settings.protect_required_approvals = Required approvals: settings.protect_required_approvals_desc = Allow only to merge pull request with enough positive reviews. settings.protect_approvals_whitelist_enabled = Restrict approvals to whitelisted users or teams diff --git a/package-lock.json b/package-lock.json index d00dda5cd7e7..7f30fe5ebc7b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,6 +35,7 @@ "license-checker-webpack-plugin": "0.2.1", "mermaid": "10.1.0", "mini-css-extract-plugin": "2.7.5", + "minimatch": "9.0.0", "monaco-editor": "0.38.0", "monaco-editor-webpack-plugin": "7.0.1", "pretty-ms": "8.0.0", @@ -855,12 +856,34 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/@eslint/js": { "version": "8.40.0", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.40.0.tgz", @@ -907,6 +930,28 @@ "node": ">=10.10.0" } }, + "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -1480,6 +1525,28 @@ "node": "^12.20 || >=14.13" } }, + "node_modules/@stoplight/spectral-core/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@stoplight/spectral-core/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/@stoplight/spectral-formats": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@stoplight/spectral-formats/-/spectral-formats-1.5.0.tgz", @@ -2668,12 +2735,11 @@ "dev": true }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^1.0.0" } }, "node_modules/braces": { @@ -4574,6 +4640,16 @@ "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" } }, + "node_modules/eslint-plugin-import/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/eslint-plugin-import/node_modules/debug": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", @@ -4595,6 +4671,18 @@ "node": ">=0.10.0" } }, + "node_modules/eslint-plugin-import/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/eslint-plugin-import/node_modules/semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -4767,12 +4855,34 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/eslint/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/espree": { "version": "9.5.2", "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", @@ -5273,6 +5383,26 @@ "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/global-modules": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", @@ -6396,6 +6526,26 @@ "webpack": "^4.4.0 || ^5.4.0" } }, + "node_modules/license-checker-webpack-plugin/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/license-checker-webpack-plugin/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/license-checker-webpack-plugin/node_modules/semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -6645,15 +6795,6 @@ "node": ">=14" } }, - "node_modules/markdownlint-cli/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, "node_modules/markdownlint-cli/node_modules/commander": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", @@ -6691,21 +6832,6 @@ "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", "dev": true }, - "node_modules/markdownlint-cli/node_modules/minimatch": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.0.tgz", - "integrity": "sha512-0jJj8AvgKqWN05mrwuqi8QYKx1WmYSUoKSxu5Qhs9prezTz10sxAHGNZe9J9cqIJzta8DWsleh2KaVaLl6Ru2w==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/markdownlint-micromark": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/markdownlint-micromark/-/markdownlint-micromark-0.1.2.tgz", @@ -6939,14 +7065,17 @@ } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.0.tgz", + "integrity": "sha512-0jJj8AvgKqWN05mrwuqi8QYKx1WmYSUoKSxu5Qhs9prezTz10sxAHGNZe9J9cqIJzta8DWsleh2KaVaLl6Ru2w==", "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "*" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/minimist": { diff --git a/package.json b/package.json index cf3f5af99abe..6ec0d755dcb1 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "license-checker-webpack-plugin": "0.2.1", "mermaid": "10.1.0", "mini-css-extract-plugin": "2.7.5", + "minimatch": "9.0.0", "monaco-editor": "0.38.0", "monaco-editor-webpack-plugin": "7.0.1", "pretty-ms": "8.0.0", diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index 070fc109dcec..8821e74c951e 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -45,6 +45,8 @@ import ( "code.gitea.io/gitea/services/gitdiff" pull_service "code.gitea.io/gitea/services/pull" repo_service "code.gitea.io/gitea/services/repository" + + "github.com/gobwas/glob" ) const ( @@ -575,7 +577,7 @@ func PrepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C if pb != nil && pb.EnableStatusCheck { ctx.Data["is_context_required"] = func(context string) bool { for _, c := range pb.StatusCheckContexts { - if c == context { + if gp, err := glob.Compile(c); err == nil && gp.Match(context) { return true } } diff --git a/routers/web/repo/setting_protected_branch.go b/routers/web/repo/setting_protected_branch.go index 932bd373d7ae..1a944799c23f 100644 --- a/routers/web/repo/setting_protected_branch.go +++ b/routers/web/repo/setting_protected_branch.go @@ -6,6 +6,7 @@ package repo import ( "fmt" "net/http" + "net/url" "strings" "time" @@ -23,6 +24,8 @@ import ( "code.gitea.io/gitea/services/forms" pull_service "code.gitea.io/gitea/services/pull" "code.gitea.io/gitea/services/repository" + + "github.com/gobwas/glob" ) const ( @@ -115,21 +118,10 @@ func SettingsProtectedBranch(c *context.Context) { c.Data["whitelist_users"] = strings.Join(base.Int64sToStrings(rule.WhitelistUserIDs), ",") c.Data["merge_whitelist_users"] = strings.Join(base.Int64sToStrings(rule.MergeWhitelistUserIDs), ",") c.Data["approvals_whitelist_users"] = strings.Join(base.Int64sToStrings(rule.ApprovalsWhitelistUserIDs), ",") + c.Data["status_check_contexts"] = strings.Join(rule.StatusCheckContexts, "\n") contexts, _ := git_model.FindRepoRecentCommitStatusContexts(c, c.Repo.Repository.ID, 7*24*time.Hour) // Find last week status check contexts - for _, ctx := range rule.StatusCheckContexts { - var found bool - for i := range contexts { - if contexts[i] == ctx { - found = true - break - } - } - if !found { - contexts = append(contexts, ctx) - } - } + c.Data["recent_status_checks"] = contexts - c.Data["branch_status_check_contexts"] = contexts if c.Repo.Owner.IsOrganization() { teams, err := organization.OrgFromUser(c.Repo.Owner).TeamsWithAccessToRepo(c.Repo.Repository.ID, perm.AccessModeRead) if err != nil { @@ -237,7 +229,27 @@ func SettingsProtectedBranchPost(ctx *context.Context) { protectBranch.EnableStatusCheck = f.EnableStatusCheck if f.EnableStatusCheck { - protectBranch.StatusCheckContexts = f.StatusCheckContexts + patterns := strings.Split(strings.ReplaceAll(f.StatusCheckContexts, "\r", "\n"), "\n") + validPatterns := make([]string, 0, len(patterns)) + for _, pattern := range patterns { + trimmed := strings.TrimSpace(pattern) + if trimmed == "" { + continue + } + if _, err := glob.Compile(trimmed); err != nil { + ctx.Flash.Error(ctx.Tr("repo.settings.protect_invalid_status_check_pattern", pattern)) + ctx.Redirect(fmt.Sprintf("%s/settings/branches/edit?rule_name=%s", ctx.Repo.RepoLink, url.QueryEscape(protectBranch.RuleName))) + return + } + validPatterns = append(validPatterns, trimmed) + } + if len(validPatterns) == 0 { + // if status check is enabled, patterns slice is not allowed to be empty + ctx.Flash.Error(ctx.Tr("repo.settings.protect_no_valid_status_check_patterns")) + ctx.Redirect(fmt.Sprintf("%s/settings/branches/edit?rule_name=%s", ctx.Repo.RepoLink, url.QueryEscape(protectBranch.RuleName))) + return + } + protectBranch.StatusCheckContexts = validPatterns } else { protectBranch.StatusCheckContexts = nil } diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index 41d7dc7d2b0a..d705ecad3f7d 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -199,7 +199,7 @@ type ProtectBranchForm struct { MergeWhitelistUsers string MergeWhitelistTeams string EnableStatusCheck bool - StatusCheckContexts []string + StatusCheckContexts string RequiredApprovals int64 EnableApprovalsWhitelist bool ApprovalsWhitelistUsers string diff --git a/services/pull/commit_status.go b/services/pull/commit_status.go index bfdb3f7291b8..51ba06da2758 100644 --- a/services/pull/commit_status.go +++ b/services/pull/commit_status.go @@ -11,43 +11,53 @@ import ( git_model "code.gitea.io/gitea/models/git" issues_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/structs" + "github.com/gobwas/glob" "github.com/pkg/errors" ) // MergeRequiredContextsCommitStatus returns a commit status state for given required contexts func MergeRequiredContextsCommitStatus(commitStatuses []*git_model.CommitStatus, requiredContexts []string) structs.CommitStatusState { - if len(requiredContexts) == 0 { - status := git_model.CalcCommitStatus(commitStatuses) - if status != nil { - return status.State + // matchedCount is the number of `CommitStatus.Context` that match any context of `requiredContexts` + matchedCount := 0 + returnedStatus := structs.CommitStatusSuccess + + if len(requiredContexts) > 0 { + requiredContextsGlob := make(map[string]glob.Glob, len(requiredContexts)) + for _, ctx := range requiredContexts { + if gp, err := glob.Compile(ctx); err != nil { + log.Error("glob.Compile %s failed. Error: %v", ctx, err) + } else { + requiredContextsGlob[ctx] = gp + } } - return structs.CommitStatusSuccess - } - returnedStatus := structs.CommitStatusSuccess - for _, ctx := range requiredContexts { - var targetStatus structs.CommitStatusState for _, commitStatus := range commitStatuses { - if commitStatus.Context == ctx { - targetStatus = commitStatus.State - break + var targetStatus structs.CommitStatusState + for _, gp := range requiredContextsGlob { + if gp.Match(commitStatus.Context) { + targetStatus = commitStatus.State + matchedCount++ + break + } } - } - if targetStatus == "" { - targetStatus = structs.CommitStatusPending - commitStatuses = append(commitStatuses, &git_model.CommitStatus{ - State: targetStatus, - Context: ctx, - Description: "Pending", - }) + if targetStatus != "" && targetStatus.NoBetterThan(returnedStatus) { + returnedStatus = targetStatus + } } - if targetStatus.NoBetterThan(returnedStatus) { - returnedStatus = targetStatus + } + + if matchedCount == 0 { + status := git_model.CalcCommitStatus(commitStatuses) + if status != nil { + return status.State } + return structs.CommitStatusSuccess } + return returnedStatus } diff --git a/templates/repo/settings/protected_branch.tmpl b/templates/repo/settings/protected_branch.tmpl index 494cadfcc1be..cad98f8a7f69 100644 --- a/templates/repo/settings/protected_branch.tmpl +++ b/templates/repo/settings/protected_branch.tmpl @@ -157,6 +157,9 @@
+ + +

{{.locale.Tr "repo.settings.protect_status_check_patterns_desc"}}

@@ -164,13 +167,11 @@ - {{range $.branch_status_check_contexts}} + {{range $.recent_status_checks}} {{else}} diff --git a/web_src/css/repo.css b/web_src/css/repo.css index eafe022cee66..73096a643465 100644 --- a/web_src/css/repo.css +++ b/web_src/css/repo.css @@ -1990,6 +1990,11 @@ padding-left: 26px; } +.repository.settings.branches .branch-protection .status-check-matched-mark { + font-weight: var(--font-weight-bold); + font-style: italic; +} + .repository.settings.webhook .events .column { padding-bottom: 0; } diff --git a/web_src/js/features/repo-settings.js b/web_src/js/features/repo-settings.js index 7105a14b3026..8cc016fdc27e 100644 --- a/web_src/js/features/repo-settings.js +++ b/web_src/js/features/repo-settings.js @@ -1,5 +1,7 @@ import $ from 'jquery'; +import {minimatch} from 'minimatch'; import {createMonaco} from './codeeditor.js'; +import {onInputDebounce, toggleElem} from '../utils/dom.js'; const {appSubUrl, csrfToken} = window.config; @@ -81,4 +83,26 @@ export function initRepoSettingBranches() { const $target = $($(this).attr('data-target')); if (this.checked) $target.addClass('disabled'); // only disable, do not auto enable }); + + // show the `Matched` mark for the status checks that match the pattern + const markMatchedStatusChecks = () => { + const patterns = (document.getElementById('status_check_contexts').value || '').split(/[\r\n]+/); + const validPatterns = patterns.map((item) => item.trim()).filter(Boolean); + const marks = document.getElementsByClassName('status-check-matched-mark'); + + for (const el of marks) { + let matched = false; + const statusCheck = el.getAttribute('data-status-check'); + for (const pattern of validPatterns) { + if (minimatch(statusCheck, pattern)) { + matched = true; + break; + } + } + + toggleElem(el, matched); + } + }; + markMatchedStatusChecks(); + document.getElementById('status_check_contexts').addEventListener('input', onInputDebounce(markMatchedStatusChecks)); } From b807d2f6205bf1ba60d3a543e8e1a16f7be956df Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 17 May 2023 17:21:35 +0800 Subject: [PATCH 09/18] Support no label/assignee filter and batch clearing labels/assignees (#24707) Since milestones has been implemented, this PR will fix #3407 --------- Co-authored-by: Jason Song --- models/issues/issue.go | 40 +++++++++++++++++++++------------ options/locale/locale_en-US.ini | 2 ++ routers/web/repo/issue.go | 5 ++++- templates/repo/issue/list.tmpl | 8 +++++++ 4 files changed, 40 insertions(+), 15 deletions(-) diff --git a/models/issues/issue.go b/models/issues/issue.go index 8c173433f254..df38e68519ed 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -1251,6 +1251,8 @@ func (opts *IssuesOptions) setupSessionNoLimit(sess *xorm.Session) { if opts.AssigneeID > 0 { applyAssigneeCondition(sess, opts.AssigneeID) + } else if opts.AssigneeID == db.NoConditionID { + sess.Where("issue.id NOT IN (SELECT issue_id FROM issue_assignees)") } if opts.PosterID > 0 { @@ -1312,13 +1314,17 @@ func (opts *IssuesOptions) setupSessionNoLimit(sess *xorm.Session) { sess.And(builder.Eq{"repository.is_archived": opts.IsArchived.IsTrue()}) } - if opts.LabelIDs != nil { - for i, labelID := range opts.LabelIDs { - if labelID > 0 { - sess.Join("INNER", fmt.Sprintf("issue_label il%d", i), - fmt.Sprintf("issue.id = il%[1]d.issue_id AND il%[1]d.label_id = %[2]d", i, labelID)) - } else { - sess.Where("issue.id not in (select issue_id from issue_label where label_id = ?)", -labelID) + if len(opts.LabelIDs) > 0 { + if opts.LabelIDs[0] == 0 { + sess.Where("issue.id NOT IN (SELECT issue_id FROM issue_label)") + } else { + for i, labelID := range opts.LabelIDs { + if labelID > 0 { + sess.Join("INNER", fmt.Sprintf("issue_label il%d", i), + fmt.Sprintf("issue.id = il%[1]d.issue_id AND il%[1]d.label_id = %[2]d", i, labelID)) + } else if labelID < 0 { // 0 is not supported here, so just ignore it + sess.Where("issue.id not in (select issue_id from issue_label where label_id = ?)", -labelID) + } } } } @@ -1705,17 +1711,21 @@ func getIssueStatsChunk(opts *IssueStatsOptions, issueIDs []int64) (*IssueStats, sess.In("issue.id", issueIDs) } - if len(opts.Labels) > 0 && opts.Labels != "0" { + if len(opts.Labels) > 0 { labelIDs, err := base.StringsToInt64s(strings.Split(opts.Labels, ",")) if err != nil { log.Warn("Malformed Labels argument: %s", opts.Labels) } else { - for i, labelID := range labelIDs { - if labelID > 0 { - sess.Join("INNER", fmt.Sprintf("issue_label il%d", i), - fmt.Sprintf("issue.id = il%[1]d.issue_id AND il%[1]d.label_id = %[2]d", i, labelID)) - } else { - sess.Where("issue.id NOT IN (SELECT issue_id FROM issue_label WHERE label_id = ?)", -labelID) + if labelIDs[0] == 0 { + sess.Where("issue.id NOT IN (SELECT issue_id FROM issue_label)") + } else { + for i, labelID := range labelIDs { + if labelID > 0 { + sess.Join("INNER", fmt.Sprintf("issue_label il%d", i), + fmt.Sprintf("issue.id = il%[1]d.issue_id AND il%[1]d.label_id = %[2]d", i, labelID)) + } else if labelID < 0 { // 0 is not supported here, so just ignore it + sess.Where("issue.id NOT IN (SELECT issue_id FROM issue_label WHERE label_id = ?)", -labelID) + } } } } @@ -1734,6 +1744,8 @@ func getIssueStatsChunk(opts *IssueStatsOptions, issueIDs []int64) (*IssueStats, if opts.AssigneeID > 0 { applyAssigneeCondition(sess, opts.AssigneeID) + } else if opts.AssigneeID == db.NoConditionID { + sess.Where("id NOT IN (SELECT issue_id FROM issue_assignees)") } if opts.PosterID > 0 { diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 2167471aace5..78dbb3c9c2f4 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1361,6 +1361,7 @@ issues.delete_branch_at = `deleted branch %s %s` issues.filter_label = Label issues.filter_label_exclude = `Use alt + click/enter to exclude labels` issues.filter_label_no_select = All labels +issues.filter_label_select_no_label = No Label issues.filter_milestone = Milestone issues.filter_milestone_all = All milestones issues.filter_milestone_none = No milestones @@ -1371,6 +1372,7 @@ issues.filter_project_all = All projects issues.filter_project_none = No project issues.filter_assignee = Assignee issues.filter_assginee_no_select = All assignees +issues.filter_assginee_no_assignee = No assignee issues.filter_poster = Author issues.filter_poster_no_select = All authors issues.filter_type = Type diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index c2f30a01f473..66a498613975 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -170,8 +170,11 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti repo := ctx.Repo.Repository var labelIDs []int64 + // 1,-2 means including label 1 and excluding label 2 + // 0 means issues with no label + // blank means labels will not be filtered for issues selectLabels := ctx.FormString("labels") - if len(selectLabels) > 0 && selectLabels != "0" { + if len(selectLabels) > 0 { labelIDs, err = base.StringsToInt64s(strings.Split(selectLabels, ",")) if err != nil { ctx.ServerError("StringsToInt64s", err) diff --git a/templates/repo/issue/list.tmpl b/templates/repo/issue/list.tmpl index 68d40ffea70e..7c2f73ca5929 100644 --- a/templates/repo/issue/list.tmpl +++ b/templates/repo/issue/list.tmpl @@ -38,6 +38,7 @@ {{.locale.Tr "repo.issues.filter_label_exclude" | Safe}} + {{.locale.Tr "repo.issues.filter_label_select_no_label"}} {{.locale.Tr "repo.issues.filter_label_no_select"}} {{$previousExclusiveScope := "_no_scope"}} {{range .Labels}} @@ -156,6 +157,7 @@ {{svg "octicon-search" 16}} + {{.locale.Tr "repo.issues.filter_assginee_no_assignee"}} {{.locale.Tr "repo.issues.filter_assginee_no_select"}} {{range .Assignees}} @@ -226,6 +228,9 @@ {{svg "octicon-triangle-down" 14 "dropdown icon"}}
- - - - + {{.}} + {{$.locale.Tr "repo.settings.protect_status_check_matched"}}