mirror of
https://github.com/go-gitea/gitea
synced 2026-02-10 00:38:09 +00:00
Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ec4fa231c7 | ||
|
|
33d4d32096 | ||
|
|
17c6a3862d | ||
|
|
30a593dfbb | ||
|
|
6c235f4959 | ||
|
|
1389fa8a99 | ||
|
|
87bd47cff3 | ||
|
|
0f2035d52f | ||
|
|
954962ca61 | ||
|
|
aa3c76159a | ||
|
|
224c48e16b | ||
|
|
7aba99af5e | ||
|
|
c5f37af8cd | ||
|
|
7aa8b8e2d2 | ||
|
|
ddf64b84e4 | ||
|
|
03518d3e18 | ||
|
|
fcdc57d811 | ||
|
|
f394cd70ac | ||
|
|
32f895f2d7 | ||
|
|
d95408bd5d | ||
|
|
1c1c94b061 | ||
|
|
0e20ccfe32 | ||
|
|
08822651a6 | ||
|
|
79484e1cb7 | ||
|
|
667427b4b9 | ||
|
|
2566f6ce8a | ||
|
|
944f1ec54c | ||
|
|
a05d098a37 | ||
|
|
332ee359ce | ||
|
|
a82bf022bf |
@@ -58,9 +58,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: npm
|
||||
cache-dependency-path: package-lock.json
|
||||
- run: make deps-frontend
|
||||
- run: make lint-swagger
|
||||
|
||||
@@ -127,9 +129,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: npm
|
||||
cache-dependency-path: package-lock.json
|
||||
- run: make deps-frontend
|
||||
- run: make lint-frontend
|
||||
- run: make checks-frontend
|
||||
@@ -174,9 +178,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: npm
|
||||
cache-dependency-path: package-lock.json
|
||||
- run: make deps-frontend
|
||||
- run: make lint-md
|
||||
- run: make docs
|
||||
|
||||
@@ -21,9 +21,11 @@ jobs:
|
||||
with:
|
||||
go-version: "~1.21"
|
||||
check-latest: true
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: npm
|
||||
cache-dependency-path: package-lock.json
|
||||
- run: make deps-frontend frontend deps-backend
|
||||
- run: npx playwright install --with-deps
|
||||
- run: make test-e2e-sqlite
|
||||
|
||||
@@ -20,9 +20,11 @@ jobs:
|
||||
with:
|
||||
go-version: "~1.21"
|
||||
check-latest: true
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: npm
|
||||
cache-dependency-path: package-lock.json
|
||||
- run: make deps-frontend deps-backend
|
||||
# xgo build
|
||||
- run: make release
|
||||
|
||||
@@ -21,9 +21,11 @@ jobs:
|
||||
with:
|
||||
go-version: "~1.21"
|
||||
check-latest: true
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: npm
|
||||
cache-dependency-path: package-lock.json
|
||||
- run: make deps-frontend deps-backend
|
||||
# xgo build
|
||||
- run: make release
|
||||
|
||||
@@ -23,9 +23,11 @@ jobs:
|
||||
with:
|
||||
go-version: "~1.21"
|
||||
check-latest: true
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: npm
|
||||
cache-dependency-path: package-lock.json
|
||||
- run: make deps-frontend deps-backend
|
||||
# xgo build
|
||||
- run: make release
|
||||
|
||||
+2
-3
@@ -9,6 +9,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.com).
|
||||
* SECURITY
|
||||
* Use go1.21.9 to include Golang security fix
|
||||
* Fix possible renderer security problem (#30136) (#30315)
|
||||
* Performance optimization for git push and check permissions for push options (#30104) (#30354)
|
||||
* BUGFIXES
|
||||
* Fix close file in the Upload func (#30262) (#30269)
|
||||
* Fix inline math blocks can't be preceeded/followed by alphanumerical characters (#30175) (#30250)
|
||||
@@ -18,12 +19,10 @@ been added to each release, please refer to the [blog](https://blog.gitea.com).
|
||||
* Load attachments for code comments (#30124) (#30126)
|
||||
* Fix gitea doctor will remove repo-avatar files when executing command storage-archives (#30094) (#30120)
|
||||
* Fix possible data race on tests (#30093) (#30108)
|
||||
* Performance optimization for git push (#30104)
|
||||
* Fix duplicate migrated milestones (#30102) (#30105)
|
||||
* Fix panic for fixBrokenRepoUnits16961 (#30068) (#30100)
|
||||
* Fix incorrect SVGs (#30087)
|
||||
* Fix incorrect SVGs (#30086) (#30087)
|
||||
* Fix create commit status (#30225) (#30340)
|
||||
* Performance optimization for git push (#30104) (#30354)
|
||||
* Fix misuse of unsupported global variables (#30402)
|
||||
* Fix to delete the cookie when AppSubURL is non-empty (#30375) (#30468)
|
||||
* Avoid user does not exist error when detecting schedule actions when the commit author is an external user (#30357) (#30408)
|
||||
|
||||
@@ -959,8 +959,9 @@ webpack: $(WEBPACK_DEST)
|
||||
|
||||
$(WEBPACK_DEST): $(WEBPACK_SOURCES) $(WEBPACK_CONFIGS) package-lock.json
|
||||
@$(MAKE) -s node-check node_modules
|
||||
rm -rf $(WEBPACK_DEST_ENTRIES)
|
||||
npx webpack
|
||||
@rm -rf $(WEBPACK_DEST_ENTRIES)
|
||||
@echo "Running webpack..."
|
||||
@BROWSERSLIST_IGNORE_OLD_DATA=true npx webpack
|
||||
@touch $(WEBPACK_DEST)
|
||||
|
||||
.PHONY: svg
|
||||
@@ -1009,7 +1010,7 @@ generate-gitignore:
|
||||
|
||||
.PHONY: generate-images
|
||||
generate-images: | node_modules
|
||||
npm install --no-save --no-package-lock fabric@5 imagemin-zopfli@7
|
||||
npm install --no-save fabric@6.0.0-rc1 imagemin-zopfli@7
|
||||
node build/generate-images.js $(TAGS)
|
||||
|
||||
.PHONY: generate-manpage
|
||||
|
||||
Generated
+5
-5
File diff suppressed because one or more lines are too long
+15
-18
@@ -1,20 +1,13 @@
|
||||
#!/usr/bin/env node
|
||||
import imageminZopfli from 'imagemin-zopfli';
|
||||
import {loadSVGFromString, Canvas, Rect, util} from 'fabric/node';
|
||||
import {optimize} from 'svgo';
|
||||
import {fabric} from 'fabric';
|
||||
import {readFile, writeFile} from 'node:fs/promises';
|
||||
import {argv, exit} from 'node:process';
|
||||
|
||||
function exit(err) {
|
||||
function doExit(err) {
|
||||
if (err) console.error(err);
|
||||
process.exit(err ? 1 : 0);
|
||||
}
|
||||
|
||||
function loadSvg(svg) {
|
||||
return new Promise((resolve) => {
|
||||
fabric.loadSVGFromString(svg, (objects, options) => {
|
||||
resolve({objects, options});
|
||||
});
|
||||
});
|
||||
exit(err ? 1 : 0);
|
||||
}
|
||||
|
||||
async function generate(svg, path, {size, bg}) {
|
||||
@@ -27,7 +20,7 @@ async function generate(svg, path, {size, bg}) {
|
||||
'removeDimensions',
|
||||
{
|
||||
name: 'addAttributesToSVGElement',
|
||||
params: {attributes: [{width: size}, {height: size}]}
|
||||
params: {attributes: [{width: size}, {height: size}]},
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -35,14 +28,14 @@ async function generate(svg, path, {size, bg}) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {objects, options} = await loadSvg(svg);
|
||||
const canvas = new fabric.Canvas();
|
||||
const {objects, options} = await loadSVGFromString(svg);
|
||||
const canvas = new Canvas();
|
||||
canvas.setDimensions({width: size, height: size});
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.scale(options.width ? (size / options.width) : 1, options.height ? (size / options.height) : 1);
|
||||
|
||||
if (bg) {
|
||||
canvas.add(new fabric.Rect({
|
||||
canvas.add(new Rect({
|
||||
left: 0,
|
||||
top: 0,
|
||||
height: size * (1 / (size / options.height)),
|
||||
@@ -51,7 +44,7 @@ async function generate(svg, path, {size, bg}) {
|
||||
}));
|
||||
}
|
||||
|
||||
canvas.add(fabric.util.groupSVGElements(objects, options));
|
||||
canvas.add(util.groupSVGElements(objects, options));
|
||||
canvas.renderAll();
|
||||
|
||||
let png = Buffer.from([]);
|
||||
@@ -64,7 +57,7 @@ async function generate(svg, path, {size, bg}) {
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const gitea = process.argv.slice(2).includes('gitea');
|
||||
const gitea = argv.slice(2).includes('gitea');
|
||||
const logoSvg = await readFile(new URL('../assets/logo.svg', import.meta.url), 'utf8');
|
||||
const faviconSvg = await readFile(new URL('../assets/favicon.svg', import.meta.url), 'utf8');
|
||||
|
||||
@@ -79,4 +72,8 @@ async function main() {
|
||||
]);
|
||||
}
|
||||
|
||||
main().then(exit).catch(exit);
|
||||
try {
|
||||
doExit(await main());
|
||||
} catch (err) {
|
||||
doExit(err);
|
||||
}
|
||||
|
||||
@@ -1430,7 +1430,7 @@ LEVEL = Info
|
||||
;; Batch size to send for batched queues
|
||||
;BATCH_LENGTH = 20
|
||||
;;
|
||||
;; Connection string for redis queues this will store the redis or redis-cluster connection string.
|
||||
;; Connection string for redis queues this will store the redis (or Redis cluster) connection string.
|
||||
;; When `TYPE` is `persistable-channel`, this provides a directory for the underlying leveldb
|
||||
;; or additional options of the form `leveldb://path/to/db?option=value&....`, and will override `DATADIR`.
|
||||
;CONN_STR = "redis://127.0.0.1:6379/0"
|
||||
@@ -1702,9 +1702,8 @@ LEVEL = Info
|
||||
;; For "memory" only, GC interval in seconds, default is 60
|
||||
;INTERVAL = 60
|
||||
;;
|
||||
;; For "redis", "redis-cluster" and "memcache", connection host address
|
||||
;; redis: `redis://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s`
|
||||
;; redis-cluster: `redis+cluster://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s`
|
||||
;; For "redis" and "memcache", connection host address
|
||||
;; redis: `redis://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s` (or `redis+cluster://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s` for a Redis cluster)
|
||||
;; memcache: `127.0.0.1:11211`
|
||||
;; twoqueue: `{"size":50000,"recent_ratio":0.25,"ghost_ratio":0.5}` or `50000`
|
||||
;HOST =
|
||||
@@ -1736,15 +1735,14 @@ LEVEL = Info
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;
|
||||
;; Either "memory", "file", "redis", "redis-cluster", "db", "mysql", "couchbase", "memcache" or "postgres"
|
||||
;; Either "memory", "file", "redis", "db", "mysql", "couchbase", "memcache" or "postgres"
|
||||
;; Default is "memory". "db" will reuse the configuration in [database]
|
||||
;PROVIDER = memory
|
||||
;;
|
||||
;; Provider config options
|
||||
;; memory: doesn't have any config yet
|
||||
;; file: session file path, e.g. `data/sessions`
|
||||
;; redis: `redis://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s`
|
||||
;; redis-cluster: `redis+cluster://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s`
|
||||
;; redis: `redis://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s` (or `redis+cluster://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s` for a Redis cluster)
|
||||
;; mysql: go-sql-driver/mysql dsn config string, e.g. `root:password@/session_table`
|
||||
;PROVIDER_CONFIG = data/sessions ; Relative paths will be made absolute against _`AppWorkPath`_.
|
||||
;;
|
||||
@@ -2340,22 +2338,6 @@ LEVEL = Info
|
||||
;; Enable issue by repository metrics; default is false
|
||||
;ENABLED_ISSUE_BY_REPOSITORY = false
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;[task]
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;
|
||||
;; Task queue type, could be `channel` or `redis`.
|
||||
;QUEUE_TYPE = channel
|
||||
;;
|
||||
;; Task queue length, available only when `QUEUE_TYPE` is `channel`.
|
||||
;QUEUE_LENGTH = 1000
|
||||
;;
|
||||
;; Task queue connection string, available only when `QUEUE_TYPE` is `redis`.
|
||||
;; If there is a password of redis, use `redis://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s` or `redis+cluster://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s` for `redis-clsuter`.
|
||||
;QUEUE_CONN_STR = "redis://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s"
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;[migrations]
|
||||
|
||||
@@ -487,7 +487,7 @@ Configuration at `[queue]` will set defaults for queues with overrides for indiv
|
||||
- `DATADIR`: **queues/common**: Base DataDir for storing level queues. `DATADIR` for individual queues can be set in `queue.name` sections. Relative paths will be made absolute against `%(APP_DATA_PATH)s`.
|
||||
- `LENGTH`: **100000**: Maximal queue size before channel queues block
|
||||
- `BATCH_LENGTH`: **20**: Batch data before passing to the handler
|
||||
- `CONN_STR`: **redis://127.0.0.1:6379/0**: Connection string for the redis queue type. For `redis-cluster` use `redis+cluster://127.0.0.1:6379/0`. Options can be set using query params. Similarly, LevelDB options can also be set using: **leveldb://relative/path?option=value** or **leveldb:///absolute/path?option=value**, and will override `DATADIR`
|
||||
- `CONN_STR`: **redis://127.0.0.1:6379/0**: Connection string for the redis queue type. If you're running a Redis cluster, use `redis+cluster://127.0.0.1:6379/0`. Options can be set using query params. Similarly, LevelDB options can also be set using: **leveldb://relative/path?option=value** or **leveldb:///absolute/path?option=value**, and will override `DATADIR`
|
||||
- `QUEUE_NAME`: **_queue**: The suffix for default redis and disk queue name. Individual queues will default to **`name`**`QUEUE_NAME` but can be overridden in the specific `queue.name` section.
|
||||
- `SET_NAME`: **_unique**: The suffix that will be added to the default redis and disk queue `set` name for unique queues. Individual queues will default to **`name`**`QUEUE_NAME`_`SET_NAME`_ but can be overridden in the specific `queue.name` section.
|
||||
- `MAX_WORKERS`: **(dynamic)**: Maximum number of worker go-routines for the queue. Default value is "CpuNum/2" clipped to between 1 and 10.
|
||||
@@ -760,12 +760,11 @@ and
|
||||
|
||||
## Cache (`cache`)
|
||||
|
||||
- `ENABLED`: **true**: Enable the cache.
|
||||
- `ADAPTER`: **memory**: Cache engine adapter, either `memory`, `redis`, `redis-cluster`, `twoqueue` or `memcache`. (`twoqueue` represents a size limited LRU cache.)
|
||||
- `ADAPTER`: **memory**: Cache engine adapter, either `memory`, `redis`, `twoqueue` or `memcache`. (`twoqueue` represents a size limited LRU cache.)
|
||||
- `INTERVAL`: **60**: Garbage Collection interval (sec), for memory and twoqueue cache only.
|
||||
- `HOST`: **_empty_**: Connection string for `redis`, `redis-cluster` and `memcache`. For `twoqueue` sets configuration for the queue.
|
||||
- `HOST`: **_empty_**: Connection string for `redis` and `memcache`. For `twoqueue` sets configuration for the queue.
|
||||
- Redis: `redis://:macaron@127.0.0.1:6379/0?pool_size=100&idle_timeout=180s`
|
||||
- Redis-cluster `redis+cluster://:macaron@127.0.0.1:6379/0?pool_size=100&idle_timeout=180s`
|
||||
- For a Redis cluster: `redis+cluster://:macaron@127.0.0.1:6379/0?pool_size=100&idle_timeout=180s`
|
||||
- Memcache: `127.0.0.1:9090;127.0.0.1:9091`
|
||||
- TwoQueue LRU cache: `{"size":50000,"recent_ratio":0.25,"ghost_ratio":0.5}` or `50000` representing the maximum number of objects stored in the cache.
|
||||
- `ITEM_TTL`: **16h**: Time to keep items in cache if not used, Setting it to -1 disables caching.
|
||||
@@ -778,7 +777,7 @@ and
|
||||
|
||||
## Session (`session`)
|
||||
|
||||
- `PROVIDER`: **memory**: Session engine provider \[memory, file, redis, redis-cluster, db, mysql, couchbase, memcache, postgres\]. Setting `db` will reuse the configuration in `[database]`
|
||||
- `PROVIDER`: **memory**: Session engine provider \[memory, file, redis, db, mysql, couchbase, memcache, postgres\]. Setting `db` will reuse the configuration in `[database]`
|
||||
- `PROVIDER_CONFIG`: **data/sessions**: For file, the root path; for db, empty (database config will be used); for others, the connection string. Relative paths will be made absolute against _`AppWorkPath`_.
|
||||
- `COOKIE_SECURE`:**_empty_**: `true` or `false`. Enable this to force using HTTPS for all session access. If not set, it defaults to `true` if the ROOT_URL is an HTTPS URL.
|
||||
- `COOKIE_NAME`: **i\_like\_gitea**: The name of the cookie used for the session ID.
|
||||
@@ -1182,14 +1181,6 @@ in this mapping or the filetype using heuristics.
|
||||
|
||||
- `DEFAULT_UI_LOCATION`: Default location of time on the UI, so that we can display correct user's time on UI. i.e. Asia/Shanghai
|
||||
|
||||
## Task (`task`)
|
||||
|
||||
Task queue configuration has been moved to `queue.task`. However, the below configuration values are kept for backwards compatibility:
|
||||
|
||||
- `QUEUE_TYPE`: **channel**: Task queue type, could be `channel` or `redis`.
|
||||
- `QUEUE_LENGTH`: **1000**: Task queue length, available only when `QUEUE_TYPE` is `channel`.
|
||||
- `QUEUE_CONN_STR`: **redis://127.0.0.1:6379/0**: Task queue connection string, available only when `QUEUE_TYPE` is `redis`. If redis needs a password, use `redis://123@127.0.0.1:6379/0` or `redis+cluster://123@127.0.0.1:6379/0`.
|
||||
|
||||
## Migrations (`migrations`)
|
||||
|
||||
- `MAX_ATTEMPTS`: **3**: Max attempts per http/https request on migrations.
|
||||
|
||||
@@ -1127,15 +1127,6 @@ ALLOW_DATA_URI_IMAGES = true
|
||||
|
||||
- `DEFAULT_UI_LOCATION`:在 UI 上的默认时间位置,以便我们可以在 UI 上显示正确的用户时间。例如:Asia/Shanghai
|
||||
|
||||
## 任务 (`task`)
|
||||
|
||||
任务队列配置已移动到 `queue.task`。然而,以下配置值仍保留以确保向后兼容:
|
||||
|
||||
- `QUEUE_TYPE`:**channel**:任务队列类型,可以是 `channel` 或 `redis`。
|
||||
- `QUEUE_LENGTH`:**1000**:任务队列长度,仅在 `QUEUE_TYPE` 为 `channel` 时可用。
|
||||
- `QUEUE_CONN_STR`:**redis://127.0.0.1:6379/0**:任务队列连接字符串,仅在 `QUEUE_TYPE` 为 `redis` 时可用。
|
||||
如果 redis 需要密码,使用 `redis://123@127.0.0.1:6379/0` 或 `redis+cluster://123@127.0.0.1:6379/0`。
|
||||
|
||||
## 迁移 (`migrations`)
|
||||
|
||||
- `MAX_ATTEMPTS`:**3**:每次 http/https 请求的最大尝试次数(用于迁移)。
|
||||
|
||||
@@ -350,6 +350,8 @@ Match User git
|
||||
AuthorizedKeysCommand /usr/bin/docker exec -i gitea /usr/local/bin/gitea keys -c /etc/gitea/app.ini -e git -u %u -t %t -k %k
|
||||
```
|
||||
|
||||
For this to work, the user `git` has to be allowed to run the `docker` cli command. Please read through the [security considerations](https://docs.docker.com/engine/security/#docker-daemon-attack-surface) of providing non-root linux users access to the docker daemon.
|
||||
|
||||
(From 1.16.0 you will not need to set the `-c /etc/gitea/app.ini` option.)
|
||||
|
||||
All that is left to do is restart the SSH server:
|
||||
|
||||
@@ -7,7 +7,7 @@ require (
|
||||
code.gitea.io/gitea-vet v0.2.3
|
||||
code.gitea.io/sdk/gitea v0.16.0
|
||||
codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570
|
||||
gitea.com/go-chi/binding v0.0.0-20230415142243-04b515c6d669
|
||||
gitea.com/go-chi/binding v0.0.0-20240430071103-39a851e106ed
|
||||
gitea.com/go-chi/cache v0.2.0
|
||||
gitea.com/go-chi/captcha v0.0.0-20230415143339-2c0754df4384
|
||||
gitea.com/go-chi/session v0.0.0-20230613035928-39541325faa3
|
||||
@@ -15,7 +15,6 @@ require (
|
||||
gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4
|
||||
github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358
|
||||
github.com/NYTimes/gziphandler v1.1.1
|
||||
github.com/PuerkitoBio/goquery v1.8.1
|
||||
github.com/alecthomas/chroma/v2 v2.13.0
|
||||
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
|
||||
|
||||
@@ -55,8 +55,8 @@ git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 h1:cliQ4H
|
||||
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078/go.mod h1:g/V2Hjas6Z1UHUp4yIx6bATpNzJ7DYtD0FG3+xARWxs=
|
||||
gitea.com/gitea/act v0.259.1 h1:8GG1o/xtUHl3qjn5f0h/2FXrT5ubBn05TJOM5ry+FBw=
|
||||
gitea.com/gitea/act v0.259.1/go.mod h1:UxZWRYqQG2Yj4+4OqfGWW5a3HELwejyWFQyU7F1jUD8=
|
||||
gitea.com/go-chi/binding v0.0.0-20230415142243-04b515c6d669 h1:RUBX+MK/TsDxpHmymaOaydfigEbbzqUnG1OTZU/HAeo=
|
||||
gitea.com/go-chi/binding v0.0.0-20230415142243-04b515c6d669/go.mod h1:77TZu701zMXWJFvB8gvTbQ92zQ3DQq/H7l5wAEjQRKc=
|
||||
gitea.com/go-chi/binding v0.0.0-20240430071103-39a851e106ed h1:EZZBtilMLSZNWtHHcgq2mt6NSGhJSZBuduAlinMEmso=
|
||||
gitea.com/go-chi/binding v0.0.0-20240430071103-39a851e106ed/go.mod h1:E3i3cgB04dDx0v3CytCgRTTn9Z/9x891aet3r456RVw=
|
||||
gitea.com/go-chi/cache v0.0.0-20210110083709-82c4c9ce2d5e/go.mod h1:k2V/gPDEtXGjjMGuBJiapffAXTv76H4snSmlJRLUhH0=
|
||||
gitea.com/go-chi/cache v0.2.0 h1:E0npuTfDW6CT1yD8NMDVc1SK6IeRjfmRL2zlEsCEd7w=
|
||||
gitea.com/go-chi/cache v0.2.0/go.mod h1:iQlVK2aKTZ/rE9UcHyz9pQWGvdP9i1eI2spOpzgCrtE=
|
||||
@@ -99,8 +99,6 @@ github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBa
|
||||
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
||||
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
||||
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
|
||||
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
|
||||
github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78=
|
||||
github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
|
||||
github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM=
|
||||
@@ -448,7 +446,6 @@ github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/V
|
||||
github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=
|
||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/goccy/go-json v0.9.5/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/goccy/go-json v0.9.6/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
|
||||
+16
-6
@@ -95,13 +95,10 @@ func (run *ActionRun) LoadAttributes(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if run.Repo == nil {
|
||||
repo, err := repo_model.GetRepositoryByID(ctx, run.RepoID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
run.Repo = repo
|
||||
if err := run.LoadRepo(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := run.Repo.LoadAttributes(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -117,6 +114,19 @@ func (run *ActionRun) LoadAttributes(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (run *ActionRun) LoadRepo(ctx context.Context) error {
|
||||
if run == nil || run.Repo != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
repo, err := repo_model.GetRepositoryByID(ctx, run.RepoID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
run.Repo = repo
|
||||
return nil
|
||||
}
|
||||
|
||||
func (run *ActionRun) Duration() time.Duration {
|
||||
return calculateDuration(run.Started, run.Stopped, run.Status)
|
||||
}
|
||||
|
||||
@@ -284,7 +284,7 @@ func CountRunnersWithoutBelongingOwner(ctx context.Context) (int64, error) {
|
||||
// Only affect action runners were a owner ID is set, as actions runners
|
||||
// could also be created on a repository.
|
||||
return db.GetEngine(ctx).Table("action_runner").
|
||||
Join("LEFT", "user", "`action_runner`.owner_id = `user`.id").
|
||||
Join("LEFT", "`user`", "`action_runner`.owner_id = `user`.id").
|
||||
Where("`action_runner`.owner_id != ?", 0).
|
||||
And(builder.IsNull{"`user`.id"}).
|
||||
Count(new(ActionRunner))
|
||||
@@ -293,7 +293,7 @@ func CountRunnersWithoutBelongingOwner(ctx context.Context) (int64, error) {
|
||||
func FixRunnersWithoutBelongingOwner(ctx context.Context) (int64, error) {
|
||||
subQuery := builder.Select("`action_runner`.id").
|
||||
From("`action_runner`").
|
||||
Join("LEFT", "user", "`action_runner`.owner_id = `user`.id").
|
||||
Join("LEFT", "`user`", "`action_runner`.owner_id = `user`.id").
|
||||
Where(builder.Neq{"`action_runner`.owner_id": 0}).
|
||||
And(builder.IsNull{"`user`.id"})
|
||||
b := builder.Delete(builder.In("id", subQuery)).From("`action_runner`")
|
||||
@@ -303,3 +303,25 @@ func FixRunnersWithoutBelongingOwner(ctx context.Context) (int64, error) {
|
||||
}
|
||||
return res.RowsAffected()
|
||||
}
|
||||
|
||||
func CountRunnersWithoutBelongingRepo(ctx context.Context) (int64, error) {
|
||||
return db.GetEngine(ctx).Table("action_runner").
|
||||
Join("LEFT", "`repository`", "`action_runner`.repo_id = `repository`.id").
|
||||
Where("`action_runner`.repo_id != ?", 0).
|
||||
And(builder.IsNull{"`repository`.id"}).
|
||||
Count(new(ActionRunner))
|
||||
}
|
||||
|
||||
func FixRunnersWithoutBelongingRepo(ctx context.Context) (int64, error) {
|
||||
subQuery := builder.Select("`action_runner`.id").
|
||||
From("`action_runner`").
|
||||
Join("LEFT", "`repository`", "`action_runner`.repo_id = `repository`.id").
|
||||
Where(builder.Neq{"`action_runner`.repo_id": 0}).
|
||||
And(builder.IsNull{"`repository`.id"})
|
||||
b := builder.Delete(builder.In("id", subQuery)).From("`action_runner`")
|
||||
res, err := db.GetEngine(ctx).Exec(b)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return res.RowsAffected()
|
||||
}
|
||||
|
||||
@@ -100,6 +100,11 @@ func UpdateVariable(ctx context.Context, variable *ActionVariable) (bool, error)
|
||||
func GetVariablesOfRun(ctx context.Context, run *ActionRun) (map[string]string, error) {
|
||||
variables := map[string]string{}
|
||||
|
||||
if err := run.LoadRepo(ctx); err != nil {
|
||||
log.Error("LoadRepo: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Org / User level
|
||||
ownerVariables, err := FindVariables(ctx, FindVariablesOpts{OwnerID: run.Repo.OwnerID})
|
||||
if err != nil {
|
||||
|
||||
@@ -13,8 +13,6 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
//////////////////// Application
|
||||
|
||||
func TestOAuth2Application_GenerateClientSecret(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
app := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: 1})
|
||||
|
||||
@@ -410,36 +410,16 @@ func GetLatestCommitStatusForRepoCommitIDs(ctx context.Context, repoID int64, co
|
||||
|
||||
// FindRepoRecentCommitStatusContexts returns repository's recent commit status contexts
|
||||
func FindRepoRecentCommitStatusContexts(ctx context.Context, repoID int64, before time.Duration) ([]string, error) {
|
||||
type result struct {
|
||||
Index int64
|
||||
SHA string
|
||||
}
|
||||
getBase := func() *xorm.Session {
|
||||
return db.GetEngine(ctx).Table(&CommitStatus{}).Where("repo_id = ?", repoID)
|
||||
}
|
||||
|
||||
start := timeutil.TimeStampNow().AddDuration(-before)
|
||||
results := make([]result, 0, 10)
|
||||
|
||||
sess := getBase().And("updated_unix >= ?", start).
|
||||
Select("max( `index` ) as `index`, sha").
|
||||
GroupBy("context_hash, sha").OrderBy("max( `index` ) desc")
|
||||
|
||||
err := sess.Find(&results)
|
||||
if err != nil {
|
||||
var contexts []string
|
||||
if err := db.GetEngine(ctx).Table("commit_status").
|
||||
Where("repo_id = ?", repoID).And("updated_unix >= ?", start).
|
||||
Cols("context").Distinct().Find(&contexts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
contexts := make([]string, 0, len(results))
|
||||
if len(results) == 0 {
|
||||
return contexts, nil
|
||||
}
|
||||
|
||||
conds := make([]builder.Cond, 0, len(results))
|
||||
for _, result := range results {
|
||||
conds = append(conds, builder.Eq{"`index`": result.Index, "sha": result.SHA})
|
||||
}
|
||||
return contexts, getBase().And(builder.Or(conds...)).Select("context").Find(&contexts)
|
||||
return contexts, nil
|
||||
}
|
||||
|
||||
// NewCommitStatusOptions holds options for creating a CommitStatus
|
||||
|
||||
@@ -5,11 +5,14 @@ package git_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
git_model "code.gitea.io/gitea/models/git"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -60,3 +63,55 @@ func TestGetCommitStatuses(t *testing.T) {
|
||||
assert.Equal(t, int(maxResults), 5)
|
||||
assert.Empty(t, statuses)
|
||||
}
|
||||
|
||||
func TestFindRepoRecentCommitStatusContexts(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
|
||||
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
gitRepo, err := git.OpenRepository(git.DefaultContext, repo2.RepoPath())
|
||||
assert.NoError(t, err)
|
||||
defer gitRepo.Close()
|
||||
|
||||
commit, err := gitRepo.GetBranchCommit(repo2.DefaultBranch)
|
||||
assert.NoError(t, err)
|
||||
|
||||
defer func() {
|
||||
_, err := db.DeleteByBean(db.DefaultContext, &git_model.CommitStatus{
|
||||
RepoID: repo2.ID,
|
||||
CreatorID: user2.ID,
|
||||
SHA: commit.ID.String(),
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
}()
|
||||
|
||||
err = git_model.NewCommitStatus(db.DefaultContext, git_model.NewCommitStatusOptions{
|
||||
Repo: repo2,
|
||||
Creator: user2,
|
||||
SHA: commit.ID.String(),
|
||||
CommitStatus: &git_model.CommitStatus{
|
||||
State: structs.CommitStatusFailure,
|
||||
TargetURL: "https://example.com/tests/",
|
||||
Context: "compliance/lint-backend",
|
||||
},
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = git_model.NewCommitStatus(db.DefaultContext, git_model.NewCommitStatusOptions{
|
||||
Repo: repo2,
|
||||
Creator: user2,
|
||||
SHA: commit.ID.String(),
|
||||
CommitStatus: &git_model.CommitStatus{
|
||||
State: structs.CommitStatusSuccess,
|
||||
TargetURL: "https://example.com/tests/",
|
||||
Context: "compliance/lint-backend",
|
||||
},
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
contexts, err := git_model.FindRepoRecentCommitStatusContexts(db.DefaultContext, repo2.ID, time.Hour)
|
||||
assert.NoError(t, err)
|
||||
if assert.Len(t, contexts, 1) {
|
||||
assert.Equal(t, "compliance/lint-backend", contexts[0])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,9 +9,9 @@ import (
|
||||
|
||||
// AddConfidentialColumnToOAuth2ApplicationTable: add ConfidentialClient column, setting existing rows to true
|
||||
func AddConfidentialClientColumnToOAuth2ApplicationTable(x *xorm.Engine) error {
|
||||
type OAuth2Application struct {
|
||||
type oauth2Application struct {
|
||||
ID int64
|
||||
ConfidentialClient bool `xorm:"NOT NULL DEFAULT TRUE"`
|
||||
}
|
||||
|
||||
return x.Sync(new(OAuth2Application))
|
||||
return x.Sync(new(oauth2Application))
|
||||
}
|
||||
|
||||
@@ -13,12 +13,12 @@ import (
|
||||
|
||||
func Test_AddConfidentialClientColumnToOAuth2ApplicationTable(t *testing.T) {
|
||||
// premigration
|
||||
type OAuth2Application struct {
|
||||
type oauth2Application struct {
|
||||
ID int64
|
||||
}
|
||||
|
||||
// Prepare and load the testing database
|
||||
x, deferable := base.PrepareTestEnv(t, 0, new(OAuth2Application))
|
||||
x, deferable := base.PrepareTestEnv(t, 0, new(oauth2Application))
|
||||
defer deferable()
|
||||
if x == nil || t.Failed() {
|
||||
return
|
||||
@@ -36,7 +36,7 @@ func Test_AddConfidentialClientColumnToOAuth2ApplicationTable(t *testing.T) {
|
||||
}
|
||||
|
||||
got := []ExpectedOAuth2Application{}
|
||||
if err := x.Table("o_auth2_application").Select("id, confidential_client").Find(&got); !assert.NoError(t, err) {
|
||||
if err := x.Table("oauth2_application").Select("id, confidential_client").Find(&got); !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -95,7 +95,10 @@ func GetRepoAssignees(ctx context.Context, repo *Repository) (_ []*user_model.Us
|
||||
// and just waste 1 unit is cheaper than re-allocate memory once.
|
||||
users := make([]*user_model.User, 0, len(uniqueUserIDs)+1)
|
||||
if len(userIDs) > 0 {
|
||||
if err = e.In("id", uniqueUserIDs.Values()).OrderBy(user_model.GetOrderByName()).Find(&users); err != nil {
|
||||
if err = e.In("id", uniqueUserIDs.Values()).
|
||||
Where(builder.Eq{"`user`.is_active": true}).
|
||||
OrderBy(user_model.GetOrderByName()).
|
||||
Find(&users); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
@@ -117,7 +120,8 @@ func GetReviewers(ctx context.Context, repo *Repository, doerID, posterID int64)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cond := builder.And(builder.Neq{"`user`.id": posterID})
|
||||
cond := builder.And(builder.Neq{"`user`.id": posterID}).
|
||||
And(builder.Eq{"`user`.is_active": true})
|
||||
|
||||
if repo.IsPrivate || repo.Owner.Visibility == api.VisibleTypePrivate {
|
||||
// This a private repository:
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"code.gitea.io/gitea/models/db"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
@@ -25,10 +26,17 @@ func TestRepoAssignees(t *testing.T) {
|
||||
repo21 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 21})
|
||||
users, err = repo_model.GetRepoAssignees(db.DefaultContext, repo21)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, users, 3)
|
||||
assert.Equal(t, users[0].ID, int64(15))
|
||||
assert.Equal(t, users[1].ID, int64(18))
|
||||
assert.Equal(t, users[2].ID, int64(16))
|
||||
if assert.Len(t, users, 3) {
|
||||
assert.ElementsMatch(t, []int64{15, 16, 18}, []int64{users[0].ID, users[1].ID, users[2].ID})
|
||||
}
|
||||
|
||||
// do not return deactivated users
|
||||
assert.NoError(t, user_model.UpdateUserCols(db.DefaultContext, &user_model.User{ID: 15, IsActive: false}, "is_active"))
|
||||
users, err = repo_model.GetRepoAssignees(db.DefaultContext, repo21)
|
||||
assert.NoError(t, err)
|
||||
if assert.Len(t, users, 2) {
|
||||
assert.NotContains(t, []int64{users[0].ID, users[1].ID}, 15)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoGetReviewers(t *testing.T) {
|
||||
@@ -40,17 +48,19 @@ func TestRepoGetReviewers(t *testing.T) {
|
||||
ctx := db.DefaultContext
|
||||
reviewers, err := repo_model.GetReviewers(ctx, repo1, 2, 2)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, reviewers, 4)
|
||||
if assert.Len(t, reviewers, 3) {
|
||||
assert.ElementsMatch(t, []int64{1, 4, 11}, []int64{reviewers[0].ID, reviewers[1].ID, reviewers[2].ID})
|
||||
}
|
||||
|
||||
// should include doer if doer is not PR poster.
|
||||
reviewers, err = repo_model.GetReviewers(ctx, repo1, 11, 2)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, reviewers, 4)
|
||||
assert.Len(t, reviewers, 3)
|
||||
|
||||
// should not include PR poster, if PR poster would be otherwise eligible
|
||||
reviewers, err = repo_model.GetReviewers(ctx, repo1, 11, 4)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, reviewers, 3)
|
||||
assert.Len(t, reviewers, 2)
|
||||
|
||||
// test private user repo
|
||||
repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
|
||||
|
||||
@@ -158,6 +158,12 @@ func checkDBConsistency(ctx context.Context, logger log.Logger, autofix bool) er
|
||||
Fixer: actions_model.FixRunnersWithoutBelongingOwner,
|
||||
FixedMessage: "Removed",
|
||||
},
|
||||
{
|
||||
Name: "Action Runners without existing repository",
|
||||
Counter: actions_model.CountRunnersWithoutBelongingRepo,
|
||||
Fixer: actions_model.FixRunnersWithoutBelongingRepo,
|
||||
FixedMessage: "Removed",
|
||||
},
|
||||
}
|
||||
|
||||
// TODO: function to recalc all counters
|
||||
|
||||
@@ -459,7 +459,7 @@ func parseCommitFileStatus(fileStatus *CommitFileStatus, stdout io.Reader) {
|
||||
_, _ = rd.Discard(1)
|
||||
}
|
||||
for {
|
||||
modifier, err := rd.ReadSlice('\x00')
|
||||
modifier, err := rd.ReadString('\x00')
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
log.Error("Unexpected error whilst reading from git log --name-status. Error: %v", err)
|
||||
|
||||
@@ -68,7 +68,7 @@ func ToSearchOptions(keyword string, opts *issues_model.IssuesOptions) *SearchOp
|
||||
searchOpt.Paginator = opts.Paginator
|
||||
|
||||
switch opts.SortType {
|
||||
case "":
|
||||
case "", "latest":
|
||||
searchOpt.SortBy = SortByCreatedDesc
|
||||
case "oldest":
|
||||
searchOpt.SortBy = SortByCreatedAsc
|
||||
@@ -86,7 +86,7 @@ func ToSearchOptions(keyword string, opts *issues_model.IssuesOptions) *SearchOp
|
||||
searchOpt.SortBy = SortByDeadlineDesc
|
||||
case "priority", "priorityrepo", "project-column-sorting":
|
||||
// Unsupported sort type for search
|
||||
searchOpt.SortBy = SortByUpdatedDesc
|
||||
fallthrough
|
||||
default:
|
||||
searchOpt.SortBy = SortByUpdatedDesc
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
var (
|
||||
backoffBegin = 50 * time.Millisecond
|
||||
backoffUpper = 2 * time.Second
|
||||
)
|
||||
@@ -18,6 +18,14 @@ type (
|
||||
backoffFuncErr func() (retry bool, err error)
|
||||
)
|
||||
|
||||
func mockBackoffDuration(d time.Duration) func() {
|
||||
oldBegin, oldUpper := backoffBegin, backoffUpper
|
||||
backoffBegin, backoffUpper = d, d
|
||||
return func() {
|
||||
backoffBegin, backoffUpper = oldBegin, oldUpper
|
||||
}
|
||||
}
|
||||
|
||||
func backoffRetErr[T any](ctx context.Context, begin, upper time.Duration, end <-chan time.Time, fn backoffFuncRetErr[T]) (ret T, err error) {
|
||||
d := begin
|
||||
for {
|
||||
|
||||
@@ -63,6 +63,8 @@ func (q *WorkerPoolQueue[T]) doDispatchBatchToWorker(wg *workerGroup[T], flushCh
|
||||
// TODO: the logic could be improved in the future, to avoid a data-race between "doStartNewWorker" and "workerNum"
|
||||
// The root problem is that if we skip "doStartNewWorker" here, the "workerNum" might be decreased by other workers later
|
||||
// So ideally, it should check whether there are enough workers by some approaches, and start new workers if necessary.
|
||||
// This data-race is not serious, as long as a new worker will be started soon to make sure there are enough workers,
|
||||
// so no need to hugely refactor at the moment.
|
||||
q.workerNumMu.Lock()
|
||||
noWorker := q.workerNum == 0
|
||||
if full || noWorker {
|
||||
@@ -136,6 +138,14 @@ func (q *WorkerPoolQueue[T]) basePushForShutdown(items ...T) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func resetIdleTicker(t *time.Ticker, dur time.Duration) {
|
||||
t.Reset(dur)
|
||||
select {
|
||||
case <-t.C:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
// doStartNewWorker starts a new worker for the queue, the worker reads from worker's channel and handles the items.
|
||||
func (q *WorkerPoolQueue[T]) doStartNewWorker(wp *workerGroup[T]) {
|
||||
wp.wg.Add(1)
|
||||
@@ -146,8 +156,6 @@ func (q *WorkerPoolQueue[T]) doStartNewWorker(wp *workerGroup[T]) {
|
||||
log.Debug("Queue %q starts new worker", q.GetName())
|
||||
defer log.Debug("Queue %q stops idle worker", q.GetName())
|
||||
|
||||
atomic.AddInt32(&q.workerStartedCounter, 1) // Only increase counter, used for debugging
|
||||
|
||||
t := time.NewTicker(workerIdleDuration)
|
||||
defer t.Stop()
|
||||
|
||||
@@ -169,11 +177,7 @@ func (q *WorkerPoolQueue[T]) doStartNewWorker(wp *workerGroup[T]) {
|
||||
}
|
||||
q.doWorkerHandle(batch)
|
||||
// reset the idle ticker, and drain the tick after reset in case a tick is already triggered
|
||||
t.Reset(workerIdleDuration)
|
||||
select {
|
||||
case <-t.C:
|
||||
default:
|
||||
}
|
||||
resetIdleTicker(t, workerIdleDuration) // key code for TestWorkerPoolQueueWorkerIdleReset
|
||||
case <-t.C:
|
||||
q.workerNumMu.Lock()
|
||||
keepWorking = q.workerNum <= 1 // keep the last worker running
|
||||
|
||||
@@ -40,8 +40,6 @@ type WorkerPoolQueue[T any] struct {
|
||||
workerMaxNum int
|
||||
workerActiveNum int
|
||||
workerNumMu sync.Mutex
|
||||
|
||||
workerStartedCounter int32
|
||||
}
|
||||
|
||||
type flushType chan struct{}
|
||||
|
||||
@@ -5,8 +5,10 @@ package queue
|
||||
|
||||
import (
|
||||
"context"
|
||||
"slices"
|
||||
"strconv"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -250,22 +252,34 @@ func TestWorkerPoolQueueShutdown(t *testing.T) {
|
||||
|
||||
func TestWorkerPoolQueueWorkerIdleReset(t *testing.T) {
|
||||
defer test.MockVariableValue(&workerIdleDuration, 10*time.Millisecond)()
|
||||
defer mockBackoffDuration(5 * time.Millisecond)()
|
||||
|
||||
var q *WorkerPoolQueue[int]
|
||||
var handledCount atomic.Int32
|
||||
var hasOnlyOneWorkerRunning atomic.Bool
|
||||
handler := func(items ...int) (unhandled []int) {
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
handledCount.Add(int32(len(items)))
|
||||
// make each work have different duration, and check the active worker number periodically
|
||||
var activeNums []int
|
||||
for i := 0; i < 5-items[0]%2; i++ {
|
||||
time.Sleep(workerIdleDuration * 2)
|
||||
activeNums = append(activeNums, q.GetWorkerActiveNumber())
|
||||
}
|
||||
// When the queue never becomes empty, the existing workers should keep working
|
||||
// It is not 100% true at the moment because the data-race in workergroup.go is not resolved, see that TODO */
|
||||
// If the "active worker numbers" is like [2 2 ... 1 1], it means that an existing worker exited and the no new worker is started.
|
||||
if slices.Equal([]int{1, 1}, activeNums[len(activeNums)-2:]) {
|
||||
hasOnlyOneWorkerRunning.Store(true)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
q, _ := newWorkerPoolQueueForTest("test-workpoolqueue", setting.QueueSettings{Type: "channel", BatchLength: 1, MaxWorkers: 2, Length: 100}, handler, false)
|
||||
q, _ = newWorkerPoolQueueForTest("test-workpoolqueue", setting.QueueSettings{Type: "channel", BatchLength: 1, MaxWorkers: 2, Length: 100}, handler, false)
|
||||
stop := runWorkerPoolQueue(q)
|
||||
for i := 0; i < 20; i++ {
|
||||
for i := 0; i < 100; i++ {
|
||||
assert.NoError(t, q.Push(i))
|
||||
}
|
||||
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
assert.EqualValues(t, 2, q.GetWorkerNumber())
|
||||
assert.EqualValues(t, 2, q.GetWorkerActiveNumber())
|
||||
// when the queue never becomes empty, the existing workers should keep working
|
||||
assert.EqualValues(t, 2, q.workerStartedCounter)
|
||||
assert.Greater(t, int(handledCount.Load()), 4) // make sure there are enough items handled during the test
|
||||
assert.False(t, hasOnlyOneWorkerRunning.Load(), "a slow handler should not block other workers from starting")
|
||||
stop()
|
||||
}
|
||||
|
||||
@@ -6,9 +6,6 @@ package session
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/web/middleware"
|
||||
|
||||
"gitea.com/go-chi/session"
|
||||
)
|
||||
|
||||
@@ -21,10 +18,12 @@ type Store interface {
|
||||
|
||||
// RegenerateSession regenerates the underlying session and returns the new store
|
||||
func RegenerateSession(resp http.ResponseWriter, req *http.Request) (Store, error) {
|
||||
// Ensure that a cookie with a trailing slash does not take precedence over
|
||||
// the cookie written by the middleware.
|
||||
middleware.DeleteLegacySiteCookie(resp, setting.SessionConfig.CookieName)
|
||||
|
||||
for _, f := range BeforeRegenerateSession {
|
||||
f(resp, req)
|
||||
}
|
||||
s, err := session.RegenerateSession(resp, req)
|
||||
return s, err
|
||||
}
|
||||
|
||||
// BeforeRegenerateSession is a list of functions that are called before a session is regenerated.
|
||||
var BeforeRegenerateSession []func(http.ResponseWriter, *http.Request)
|
||||
|
||||
+13
-7
@@ -128,6 +128,16 @@ func hasResponseBeenWritten(argsIn []reflect.Value) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func wrapHandlerProvider[T http.Handler](hp func(next http.Handler) T, funcInfo *routing.FuncInfo) func(next http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
h := hp(next) // this handle could be dynamically generated, so we can't use it for debug info
|
||||
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
||||
routing.UpdateFuncInfo(req.Context(), funcInfo)
|
||||
h.ServeHTTP(resp, req)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// toHandlerProvider converts a handler to a handler provider
|
||||
// A handler provider is a function that takes a "next" http.Handler, it can be used as a middleware
|
||||
func toHandlerProvider(handler any) func(next http.Handler) http.Handler {
|
||||
@@ -138,13 +148,9 @@ func toHandlerProvider(handler any) func(next http.Handler) http.Handler {
|
||||
}
|
||||
|
||||
if hp, ok := handler.(func(next http.Handler) http.Handler); ok {
|
||||
return func(next http.Handler) http.Handler {
|
||||
h := hp(next) // this handle could be dynamically generated, so we can't use it for debug info
|
||||
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
||||
routing.UpdateFuncInfo(req.Context(), funcInfo)
|
||||
h.ServeHTTP(resp, req)
|
||||
})
|
||||
}
|
||||
return wrapHandlerProvider(hp, funcInfo)
|
||||
} else if hp, ok := handler.(func(http.Handler) http.HandlerFunc); ok {
|
||||
return wrapHandlerProvider(hp, funcInfo)
|
||||
}
|
||||
|
||||
provider := func(next http.Handler) http.Handler {
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/session"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
|
||||
@@ -48,12 +49,12 @@ func SetSiteCookie(resp http.ResponseWriter, name, value string, maxAge int) {
|
||||
// Previous versions would use a cookie path with a trailing /.
|
||||
// These are more specific than cookies without a trailing /, so
|
||||
// we need to delete these if they exist.
|
||||
DeleteLegacySiteCookie(resp, name)
|
||||
deleteLegacySiteCookie(resp, name)
|
||||
}
|
||||
|
||||
// DeleteLegacySiteCookie deletes the cookie with the given name at the cookie
|
||||
// deleteLegacySiteCookie deletes the cookie with the given name at the cookie
|
||||
// path with a trailing /, which would unintentionally override the cookie.
|
||||
func DeleteLegacySiteCookie(resp http.ResponseWriter, name string) {
|
||||
func deleteLegacySiteCookie(resp http.ResponseWriter, name string) {
|
||||
if setting.SessionConfig.CookiePath == "" || strings.HasSuffix(setting.SessionConfig.CookiePath, "/") {
|
||||
// If the cookie path ends with /, no legacy cookies will take
|
||||
// precedence, so do nothing. The exception is that cookies with no
|
||||
@@ -74,3 +75,11 @@ func DeleteLegacySiteCookie(resp http.ResponseWriter, name string) {
|
||||
}
|
||||
resp.Header().Add("Set-Cookie", cookie.String())
|
||||
}
|
||||
|
||||
func init() {
|
||||
session.BeforeRegenerateSession = append(session.BeforeRegenerateSession, func(resp http.ResponseWriter, _ *http.Request) {
|
||||
// Ensure that a cookie with a trailing slash does not take precedence over
|
||||
// the cookie written by the middleware.
|
||||
deleteLegacySiteCookie(resp, setting.SessionConfig.CookieName)
|
||||
})
|
||||
}
|
||||
|
||||
Generated
+64
-96
@@ -30,7 +30,7 @@
|
||||
"katex": "0.16.10",
|
||||
"license-checker-webpack-plugin": "0.2.1",
|
||||
"lightningcss-loader": "2.1.0",
|
||||
"mermaid": "10.6.1",
|
||||
"mermaid": "10.9.0",
|
||||
"mini-css-extract-plugin": "2.7.6",
|
||||
"minimatch": "9.0.3",
|
||||
"monaco-editor": "0.44.0",
|
||||
@@ -1904,27 +1904,27 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-scale": {
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.5.tgz",
|
||||
"integrity": "sha512-w/C++3W394MHzcLKO2kdsIn5KKNTOqeQVzyPSGPLzQbkPw/jpeaGtSRlakcKevGgGsjJxGsbqS0fPrVFDbHrDA==",
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz",
|
||||
"integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==",
|
||||
"dependencies": {
|
||||
"@types/d3-time": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-scale-chromatic": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz",
|
||||
"integrity": "sha512-dsoJGEIShosKVRBZB0Vo3C8nqSDqVGujJU6tPznsBJxNJNwMF8utmS83nvCBKQYPpjCzaaHcrf66iTRpZosLPw=="
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.0.3.tgz",
|
||||
"integrity": "sha512-laXM4+1o5ImZv3RpFAsTRn3TEkzqkytiOY0Dz0sq5cnd1dtNlk6sHLon4OvqaiJb28T0S/TdsBI3Sjsy+keJrw=="
|
||||
},
|
||||
"node_modules/@types/d3-time": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.1.tgz",
|
||||
"integrity": "sha512-5j/AnefKAhCw4HpITmLDTPlf4vhi8o/dES+zbegfPb7LaGfNyqkLxBR6E+4yvTAgnJLmhe80EXFMzUs38fw4oA=="
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.3.tgz",
|
||||
"integrity": "sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw=="
|
||||
},
|
||||
"node_modules/@types/debug": {
|
||||
"version": "4.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.9.tgz",
|
||||
"integrity": "sha512-8Hz50m2eoS56ldRlepxSBa6PWEVCtzUo/92HgLc2qTMnotJNIm7xP+UZhyWoYsyOdd5dxZ+NZLb24rsKyFs2ow==",
|
||||
"version": "4.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
|
||||
"integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==",
|
||||
"dependencies": {
|
||||
"@types/ms": "*"
|
||||
}
|
||||
@@ -1978,9 +1978,9 @@
|
||||
"integrity": "sha512-a79Yc3TOk6dGdituy8hmTTJXjOkZ7zsFYV10L337ttq/rec8lRMDBpV7fL3uLx6TgbFCa5DU/h8FmIBQPSbU0w=="
|
||||
},
|
||||
"node_modules/@types/mdast": {
|
||||
"version": "3.0.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.13.tgz",
|
||||
"integrity": "sha512-HjiGiWedR0DVFkeNljpa6Lv4/IZU1+30VY5d747K7lBudFc3R0Ibr6yJ9lN3BE28VnZyDfLF/VB1Ql1ZIbKrmg==",
|
||||
"version": "3.0.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz",
|
||||
"integrity": "sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==",
|
||||
"dependencies": {
|
||||
"@types/unist": "^2"
|
||||
}
|
||||
@@ -1992,9 +1992,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/ms": {
|
||||
"version": "0.7.32",
|
||||
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.32.tgz",
|
||||
"integrity": "sha512-xPSg0jm4mqgEkNhowKgZFBNtwoEwF6gJ4Dhww+GFpm3IgtNseHQZ5IqdNwnquZEoANxyDAKDRAdVo4Z72VvD/g=="
|
||||
"version": "0.7.34",
|
||||
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz",
|
||||
"integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g=="
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.8.3",
|
||||
@@ -2022,9 +2022,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/unist": {
|
||||
"version": "2.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.8.tgz",
|
||||
"integrity": "sha512-d0XxK3YTObnWVp6rZuev3c49+j4Lo8g4L1ZRm9z5L0xpoZycUPshHgczK5gsUMaZOstjVYYi09p5gYvUtfChYw=="
|
||||
"version": "2.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz",
|
||||
"integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA=="
|
||||
},
|
||||
"node_modules/@types/urijs": {
|
||||
"version": "1.19.20",
|
||||
@@ -3558,13 +3558,9 @@
|
||||
"integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ=="
|
||||
},
|
||||
"node_modules/cytoscape": {
|
||||
"version": "3.26.0",
|
||||
"resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.26.0.tgz",
|
||||
"integrity": "sha512-IV+crL+KBcrCnVVUCZW+zRRRFUZQcrtdOPXki+o4CFUWLdAEYvuZLcBSJC9EBK++suamERKzeY7roq2hdovV3w==",
|
||||
"dependencies": {
|
||||
"heap": "^0.2.6",
|
||||
"lodash": "^4.17.21"
|
||||
},
|
||||
"version": "3.29.2",
|
||||
"resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.29.2.tgz",
|
||||
"integrity": "sha512-2G1ycU28Nh7OHT9rkXRLpCDP30MKH1dXJORZuBhtEhEW7pKwgPi77ImqlCWinouyE1PNepIOGZBOrE84DG7LyQ==",
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
@@ -3580,34 +3576,10 @@
|
||||
"cytoscape": "^3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/cytoscape-fcose": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz",
|
||||
"integrity": "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==",
|
||||
"dependencies": {
|
||||
"cose-base": "^2.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"cytoscape": "^3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/cytoscape-fcose/node_modules/cose-base": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/cose-base/-/cose-base-2.2.0.tgz",
|
||||
"integrity": "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==",
|
||||
"dependencies": {
|
||||
"layout-base": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/cytoscape-fcose/node_modules/layout-base": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/layout-base/-/layout-base-2.0.1.tgz",
|
||||
"integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg=="
|
||||
},
|
||||
"node_modules/d3": {
|
||||
"version": "7.8.5",
|
||||
"resolved": "https://registry.npmjs.org/d3/-/d3-7.8.5.tgz",
|
||||
"integrity": "sha512-JgoahDG51ncUfJu6wX/1vWQEqOflgXyl4MaHqlcSruTez7yhaRKR9i8VjjcQGeS2en/jnFivXuaIMnseMMt0XA==",
|
||||
"version": "7.9.0",
|
||||
"resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz",
|
||||
"integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==",
|
||||
"dependencies": {
|
||||
"d3-array": "3",
|
||||
"d3-axis": "3",
|
||||
@@ -3812,9 +3784,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/d3-geo": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.0.tgz",
|
||||
"integrity": "sha512-JEo5HxXDdDYXCaWdwLRt79y7giK8SbhZJbFWXqbRTolCHFI5jRqteLzCsq51NKbUoX0PjBVSohxrx+NoOUujYA==",
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz",
|
||||
"integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==",
|
||||
"dependencies": {
|
||||
"d3-array": "2.5.0 - 3"
|
||||
},
|
||||
@@ -3924,9 +3896,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/d3-scale-chromatic": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz",
|
||||
"integrity": "sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==",
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz",
|
||||
"integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==",
|
||||
"dependencies": {
|
||||
"d3-color": "1 - 3",
|
||||
"d3-interpolate": "1 - 3"
|
||||
@@ -4199,11 +4171,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/delaunator": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.0.tgz",
|
||||
"integrity": "sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==",
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz",
|
||||
"integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==",
|
||||
"dependencies": {
|
||||
"robust-predicates": "^3.0.0"
|
||||
"robust-predicates": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/delayed-stream": {
|
||||
@@ -4244,9 +4216,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/diff": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz",
|
||||
"integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==",
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz",
|
||||
"integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==",
|
||||
"engines": {
|
||||
"node": ">=0.3.1"
|
||||
}
|
||||
@@ -4350,9 +4322,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/dompurify": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.6.tgz",
|
||||
"integrity": "sha512-ilkD8YEnnGh1zJ240uJsW7AzE+2qpbOUYjacomn3AvJ6J4JhKGSZ2nh4wUIXPZrEPppaCLx5jFe8T89Rk8tQ7w=="
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.0.tgz",
|
||||
"integrity": "sha512-yoU4rhgPKCo+p5UrWWWNKiIq+ToGqmVVhk0PmMYBK4kRsR3/qhemNFL8f6CFmBd4gMwm3F4T7HBoydP5uY07fA=="
|
||||
},
|
||||
"node_modules/domutils": {
|
||||
"version": "3.1.0",
|
||||
@@ -4400,9 +4372,9 @@
|
||||
"integrity": "sha512-54z7squS1FyFRSUqq/knOFSptjjogLZXbKcYk3B0qkE1KZzvqASwRZnY2KzZQJqIYLVD38XZeoiMRflYSwyO4w=="
|
||||
},
|
||||
"node_modules/elkjs": {
|
||||
"version": "0.8.2",
|
||||
"resolved": "https://registry.npmjs.org/elkjs/-/elkjs-0.8.2.tgz",
|
||||
"integrity": "sha512-L6uRgvZTH+4OF5NE/MBbzQx/WYpru1xCBE9respNj6qznEewGUIfhzmm7horWWxbNO2M0WckQypGctR8lH79xQ=="
|
||||
"version": "0.9.3",
|
||||
"resolved": "https://registry.npmjs.org/elkjs/-/elkjs-0.9.3.tgz",
|
||||
"integrity": "sha512-f/ZeWvW/BCXbhGEf1Ujp29EASo/lk1FDnETgNKwJrsVvGZhUWCZyg3xLJjAsxfOmt8KjswHmI5EwCQcPMpOYhQ=="
|
||||
},
|
||||
"node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
@@ -5862,11 +5834,6 @@
|
||||
"resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-2.0.0.tgz",
|
||||
"integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg=="
|
||||
},
|
||||
"node_modules/heap": {
|
||||
"version": "0.2.7",
|
||||
"resolved": "https://registry.npmjs.org/heap/-/heap-0.2.7.tgz",
|
||||
"integrity": "sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg=="
|
||||
},
|
||||
"node_modules/hosted-git-info": {
|
||||
"version": "2.8.9",
|
||||
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
|
||||
@@ -6751,9 +6718,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/khroma": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/khroma/-/khroma-2.0.0.tgz",
|
||||
"integrity": "sha512-2J8rDNlQWbtiNYThZRvmMv5yt44ZakX+Tz5ZIp/mN1pt4snn+m030Va5Z4v8xA0cQFDXBwO/8i42xL4QPsVk3g=="
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz",
|
||||
"integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw=="
|
||||
},
|
||||
"node_modules/kind-of": {
|
||||
"version": "6.0.3",
|
||||
@@ -7148,7 +7115,8 @@
|
||||
"node_modules/lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/lodash-es": {
|
||||
"version": "4.17.21",
|
||||
@@ -7531,22 +7499,22 @@
|
||||
}
|
||||
},
|
||||
"node_modules/mermaid": {
|
||||
"version": "10.6.1",
|
||||
"resolved": "https://registry.npmmirror.com/mermaid/-/mermaid-10.6.1.tgz",
|
||||
"integrity": "sha512-Hky0/RpOw/1il9X8AvzOEChfJtVvmXm+y7JML5C//ePYMy0/9jCEmW1E1g86x9oDfW9+iVEdTV/i+M6KWRNs4A==",
|
||||
"version": "10.9.0",
|
||||
"resolved": "https://registry.npmjs.org/mermaid/-/mermaid-10.9.0.tgz",
|
||||
"integrity": "sha512-swZju0hFox/B/qoLKK0rOxxgh8Cf7rJSfAUc1u8fezVihYMvrJAS45GzAxTVf4Q+xn9uMgitBcmWk7nWGXOs/g==",
|
||||
"dependencies": {
|
||||
"@braintree/sanitize-url": "^6.0.1",
|
||||
"@types/d3-scale": "^4.0.3",
|
||||
"@types/d3-scale-chromatic": "^3.0.0",
|
||||
"cytoscape": "^3.23.0",
|
||||
"cytoscape": "^3.28.1",
|
||||
"cytoscape-cose-bilkent": "^4.1.0",
|
||||
"cytoscape-fcose": "^2.1.0",
|
||||
"d3": "^7.4.0",
|
||||
"d3-sankey": "^0.12.3",
|
||||
"dagre-d3-es": "7.0.10",
|
||||
"dayjs": "^1.11.7",
|
||||
"dompurify": "^3.0.5",
|
||||
"elkjs": "^0.8.2",
|
||||
"elkjs": "^0.9.0",
|
||||
"katex": "^0.16.9",
|
||||
"khroma": "^2.0.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"mdast-util-from-markdown": "^1.3.0",
|
||||
@@ -10152,9 +10120,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/stylis": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.0.tgz",
|
||||
"integrity": "sha512-E87pIogpwUsUwXw7dNyU4QDjdgVMy52m+XEOPEKUn161cCzWjjhPSQhByfd1CcNvrOLnXQ6OnnZDwnJrz/Z4YQ=="
|
||||
"version": "4.3.2",
|
||||
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.2.tgz",
|
||||
"integrity": "sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg=="
|
||||
},
|
||||
"node_modules/stylus": {
|
||||
"version": "0.57.0",
|
||||
@@ -11549,9 +11517,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/web-worker": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.2.0.tgz",
|
||||
"integrity": "sha512-PgF341avzqyx60neE9DD+XS26MMNMoUQRz9NOZwW32nPQrF6p77f1htcnjBSEV8BGMKZ16choqUG4hyI0Hx7mA=="
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.3.0.tgz",
|
||||
"integrity": "sha512-BSR9wyRsy/KOValMgd5kMyr3JzpdeoR9KVId8u5GVlTTAtNChlsE4yTxeY7zMdNSyOmoKBv8NH2qeRY9Tg+IaA=="
|
||||
},
|
||||
"node_modules/webidl-conversions": {
|
||||
"version": "7.0.0",
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@
|
||||
"katex": "0.16.10",
|
||||
"license-checker-webpack-plugin": "0.2.1",
|
||||
"lightningcss-loader": "2.1.0",
|
||||
"mermaid": "10.6.1",
|
||||
"mermaid": "10.9.0",
|
||||
"mini-css-extract-plugin": "2.7.6",
|
||||
"minimatch": "9.0.3",
|
||||
"monaco-editor": "0.44.0",
|
||||
|
||||
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640" style="enable-background:new 0 0 640 640" xml:space="preserve" width="32" height="32"><path style="fill:#fff" d="m395.9 484.2-126.9-61c-12.5-6-17.9-21.2-11.8-33.8l61-126.9c6-12.5 21.2-17.9 33.8-11.8 17.2 8.3 27.1 13 27.1 13l-.1-109.2 16.7-.1.1 117.1s57.4 24.2 83.1 40.1c3.7 2.3 10.2 6.8 12.9 14.4 2.1 6.1 2 13.1-1 19.3l-61 126.9c-6.2 12.7-21.4 18.1-33.9 12z"/><path style="fill:#609926" d="M622.7 149.8c-4.1-4.1-9.6-4-9.6-4s-117.2 6.6-177.9 8c-13.3.3-26.5.6-39.6.7v117.2c-5.5-2.6-11.1-5.3-16.6-7.9 0-36.4-.1-109.2-.1-109.2-29 .4-89.2-2.2-89.2-2.2s-141.4-7.1-156.8-8.5c-9.8-.6-22.5-2.1-39 1.5-8.7 1.8-33.5 7.4-53.8 26.9C-4.9 212.4 6.6 276.2 8 285.8c1.7 11.7 6.9 44.2 31.7 72.5 45.8 56.1 144.4 54.8 144.4 54.8s12.1 28.9 30.6 55.5c25 33.1 50.7 58.9 75.7 62 63 0 188.9-.1 188.9-.1s12 .1 28.3-10.3c14-8.5 26.5-23.4 26.5-23.4S547 483 565 451.5c5.5-9.7 10.1-19.1 14.1-28 0 0 55.2-117.1 55.2-231.1-1.1-34.5-9.6-40.6-11.6-42.6zM125.6 353.9c-25.9-8.5-36.9-18.7-36.9-18.7S69.6 321.8 60 295.4c-16.5-44.2-1.4-71.2-1.4-71.2s8.4-22.5 38.5-30c13.8-3.7 31-3.1 31-3.1s7.1 59.4 15.7 94.2c7.2 29.2 24.8 77.7 24.8 77.7s-26.1-3.1-43-9.1zm300.3 107.6s-6.1 14.5-19.6 15.4c-5.8.4-10.3-1.2-10.3-1.2s-.3-.1-5.3-2.1l-112.9-55s-10.9-5.7-12.8-15.6c-2.2-8.1 2.7-18.1 2.7-18.1L322 273s4.8-9.7 12.2-13c.6-.3 2.3-1 4.5-1.5 8.1-2.1 18 2.8 18 2.8L467.4 315s12.6 5.7 15.3 16.2c1.9 7.4-.5 14-1.8 17.2-6.3 15.4-55 113.1-55 113.1z"/><path style="fill:#609926" d="M326.8 380.1c-8.2.1-15.4 5.8-17.3 13.8-1.9 8 2 16.3 9.1 20 7.7 4 17.5 1.8 22.7-5.4 5.1-7.1 4.3-16.9-1.8-23.1l24-49.1c1.5.1 3.7.2 6.2-.5 4.1-.9 7.1-3.6 7.1-3.6 4.2 1.8 8.6 3.8 13.2 6.1 4.8 2.4 9.3 4.9 13.4 7.3.9.5 1.8 1.1 2.8 1.9 1.6 1.3 3.4 3.1 4.7 5.5 1.9 5.5-1.9 14.9-1.9 14.9-2.3 7.6-18.4 40.6-18.4 40.6-8.1-.2-15.3 5-17.7 12.5-2.6 8.1 1.1 17.3 8.9 21.3 7.8 4 17.4 1.7 22.5-5.3 5-6.8 4.6-16.3-1.1-22.6 1.9-3.7 3.7-7.4 5.6-11.3 5-10.4 13.5-30.4 13.5-30.4.9-1.7 5.7-10.3 2.7-21.3-2.5-11.4-12.6-16.7-12.6-16.7-12.2-7.9-29.2-15.2-29.2-15.2s0-4.1-1.1-7.1c-1.1-3.1-2.8-5.1-3.9-6.3 4.7-9.7 9.4-19.3 14.1-29-4.1-2-8.1-4-12.2-6.1-4.8 9.8-9.7 19.7-14.5 29.5-6.7-.1-12.9 3.5-16.1 9.4-3.4 6.3-2.7 14.1 1.9 19.8l-24.6 50.4z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" style="enable-background:new 0 0 640 640" viewBox="0 0 640 640" width="32" height="32"><path d="m395.9 484.2-126.9-61c-12.5-6-17.9-21.2-11.8-33.8l61-126.9c6-12.5 21.2-17.9 33.8-11.8 17.2 8.3 27.1 13 27.1 13l-.1-109.2 16.7-.1.1 117.1s57.4 24.2 83.1 40.1c3.7 2.3 10.2 6.8 12.9 14.4 2.1 6.1 2 13.1-1 19.3l-61 126.9c-6.2 12.7-21.4 18.1-33.9 12z" style="fill:#fff"/><path d="M622.7 149.8c-4.1-4.1-9.6-4-9.6-4s-117.2 6.6-177.9 8c-13.3.3-26.5.6-39.6.7v117.2c-5.5-2.6-11.1-5.3-16.6-7.9 0-36.4-.1-109.2-.1-109.2-29 .4-89.2-2.2-89.2-2.2s-141.4-7.1-156.8-8.5c-9.8-.6-22.5-2.1-39 1.5-8.7 1.8-33.5 7.4-53.8 26.9C-4.9 212.4 6.6 276.2 8 285.8c1.7 11.7 6.9 44.2 31.7 72.5 45.8 56.1 144.4 54.8 144.4 54.8s12.1 28.9 30.6 55.5c25 33.1 50.7 58.9 75.7 62 63 0 188.9-.1 188.9-.1s12 .1 28.3-10.3c14-8.5 26.5-23.4 26.5-23.4S547 483 565 451.5c5.5-9.7 10.1-19.1 14.1-28 0 0 55.2-117.1 55.2-231.1-1.1-34.5-9.6-40.6-11.6-42.6zM125.6 353.9c-25.9-8.5-36.9-18.7-36.9-18.7S69.6 321.8 60 295.4c-16.5-44.2-1.4-71.2-1.4-71.2s8.4-22.5 38.5-30c13.8-3.7 31-3.1 31-3.1s7.1 59.4 15.7 94.2c7.2 29.2 24.8 77.7 24.8 77.7s-26.1-3.1-43-9.1zm300.3 107.6s-6.1 14.5-19.6 15.4c-5.8.4-10.3-1.2-10.3-1.2s-.3-.1-5.3-2.1l-112.9-55s-10.9-5.7-12.8-15.6c-2.2-8.1 2.7-18.1 2.7-18.1L322 273s4.8-9.7 12.2-13c.6-.3 2.3-1 4.5-1.5 8.1-2.1 18 2.8 18 2.8L467.4 315s12.6 5.7 15.3 16.2c1.9 7.4-.5 14-1.8 17.2-6.3 15.4-55 113.1-55 113.1z" style="fill:#609926"/><path d="M326.8 380.1c-8.2.1-15.4 5.8-17.3 13.8-1.9 8 2 16.3 9.1 20 7.7 4 17.5 1.8 22.7-5.4 5.1-7.1 4.3-16.9-1.8-23.1l24-49.1c1.5.1 3.7.2 6.2-.5 4.1-.9 7.1-3.6 7.1-3.6 4.2 1.8 8.6 3.8 13.2 6.1 4.8 2.4 9.3 4.9 13.4 7.3.9.5 1.8 1.1 2.8 1.9 1.6 1.3 3.4 3.1 4.7 5.5 1.9 5.5-1.9 14.9-1.9 14.9-2.3 7.6-18.4 40.6-18.4 40.6-8.1-.2-15.3 5-17.7 12.5-2.6 8.1 1.1 17.3 8.9 21.3 7.8 4 17.4 1.7 22.5-5.3 5-6.8 4.6-16.3-1.1-22.6 1.9-3.7 3.7-7.4 5.6-11.3 5-10.4 13.5-30.4 13.5-30.4.9-1.7 5.7-10.3 2.7-21.3-2.5-11.4-12.6-16.7-12.6-16.7-12.2-7.9-29.2-15.2-29.2-15.2s0-4.1-1.1-7.1c-1.1-3.1-2.8-5.1-3.9-6.3 4.7-9.7 9.4-19.3 14.1-29-4.1-2-8.1-4-12.2-6.1-4.8 9.8-9.7 19.7-14.5 29.5-6.7-.1-12.9 3.5-16.1 9.4-3.4 6.3-2.7 14.1 1.9 19.8l-24.6 50.4z" style="fill:#609926"/></svg>
|
||||
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640" style="enable-background:new 0 0 640 640" xml:space="preserve" width="32" height="32"><path style="fill:#fff" d="m395.9 484.2-126.9-61c-12.5-6-17.9-21.2-11.8-33.8l61-126.9c6-12.5 21.2-17.9 33.8-11.8 17.2 8.3 27.1 13 27.1 13l-.1-109.2 16.7-.1.1 117.1s57.4 24.2 83.1 40.1c3.7 2.3 10.2 6.8 12.9 14.4 2.1 6.1 2 13.1-1 19.3l-61 126.9c-6.2 12.7-21.4 18.1-33.9 12z"/><path style="fill:#609926" d="M622.7 149.8c-4.1-4.1-9.6-4-9.6-4s-117.2 6.6-177.9 8c-13.3.3-26.5.6-39.6.7v117.2c-5.5-2.6-11.1-5.3-16.6-7.9 0-36.4-.1-109.2-.1-109.2-29 .4-89.2-2.2-89.2-2.2s-141.4-7.1-156.8-8.5c-9.8-.6-22.5-2.1-39 1.5-8.7 1.8-33.5 7.4-53.8 26.9C-4.9 212.4 6.6 276.2 8 285.8c1.7 11.7 6.9 44.2 31.7 72.5 45.8 56.1 144.4 54.8 144.4 54.8s12.1 28.9 30.6 55.5c25 33.1 50.7 58.9 75.7 62 63 0 188.9-.1 188.9-.1s12 .1 28.3-10.3c14-8.5 26.5-23.4 26.5-23.4S547 483 565 451.5c5.5-9.7 10.1-19.1 14.1-28 0 0 55.2-117.1 55.2-231.1-1.1-34.5-9.6-40.6-11.6-42.6zM125.6 353.9c-25.9-8.5-36.9-18.7-36.9-18.7S69.6 321.8 60 295.4c-16.5-44.2-1.4-71.2-1.4-71.2s8.4-22.5 38.5-30c13.8-3.7 31-3.1 31-3.1s7.1 59.4 15.7 94.2c7.2 29.2 24.8 77.7 24.8 77.7s-26.1-3.1-43-9.1zm300.3 107.6s-6.1 14.5-19.6 15.4c-5.8.4-10.3-1.2-10.3-1.2s-.3-.1-5.3-2.1l-112.9-55s-10.9-5.7-12.8-15.6c-2.2-8.1 2.7-18.1 2.7-18.1L322 273s4.8-9.7 12.2-13c.6-.3 2.3-1 4.5-1.5 8.1-2.1 18 2.8 18 2.8L467.4 315s12.6 5.7 15.3 16.2c1.9 7.4-.5 14-1.8 17.2-6.3 15.4-55 113.1-55 113.1z"/><path style="fill:#609926" d="M326.8 380.1c-8.2.1-15.4 5.8-17.3 13.8-1.9 8 2 16.3 9.1 20 7.7 4 17.5 1.8 22.7-5.4 5.1-7.1 4.3-16.9-1.8-23.1l24-49.1c1.5.1 3.7.2 6.2-.5 4.1-.9 7.1-3.6 7.1-3.6 4.2 1.8 8.6 3.8 13.2 6.1 4.8 2.4 9.3 4.9 13.4 7.3.9.5 1.8 1.1 2.8 1.9 1.6 1.3 3.4 3.1 4.7 5.5 1.9 5.5-1.9 14.9-1.9 14.9-2.3 7.6-18.4 40.6-18.4 40.6-8.1-.2-15.3 5-17.7 12.5-2.6 8.1 1.1 17.3 8.9 21.3 7.8 4 17.4 1.7 22.5-5.3 5-6.8 4.6-16.3-1.1-22.6 1.9-3.7 3.7-7.4 5.6-11.3 5-10.4 13.5-30.4 13.5-30.4.9-1.7 5.7-10.3 2.7-21.3-2.5-11.4-12.6-16.7-12.6-16.7-12.2-7.9-29.2-15.2-29.2-15.2s0-4.1-1.1-7.1c-1.1-3.1-2.8-5.1-3.9-6.3 4.7-9.7 9.4-19.3 14.1-29-4.1-2-8.1-4-12.2-6.1-4.8 9.8-9.7 19.7-14.5 29.5-6.7-.1-12.9 3.5-16.1 9.4-3.4 6.3-2.7 14.1 1.9 19.8l-24.6 50.4z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" style="enable-background:new 0 0 640 640" viewBox="0 0 640 640" width="32" height="32"><path d="m395.9 484.2-126.9-61c-12.5-6-17.9-21.2-11.8-33.8l61-126.9c6-12.5 21.2-17.9 33.8-11.8 17.2 8.3 27.1 13 27.1 13l-.1-109.2 16.7-.1.1 117.1s57.4 24.2 83.1 40.1c3.7 2.3 10.2 6.8 12.9 14.4 2.1 6.1 2 13.1-1 19.3l-61 126.9c-6.2 12.7-21.4 18.1-33.9 12z" style="fill:#fff"/><path d="M622.7 149.8c-4.1-4.1-9.6-4-9.6-4s-117.2 6.6-177.9 8c-13.3.3-26.5.6-39.6.7v117.2c-5.5-2.6-11.1-5.3-16.6-7.9 0-36.4-.1-109.2-.1-109.2-29 .4-89.2-2.2-89.2-2.2s-141.4-7.1-156.8-8.5c-9.8-.6-22.5-2.1-39 1.5-8.7 1.8-33.5 7.4-53.8 26.9C-4.9 212.4 6.6 276.2 8 285.8c1.7 11.7 6.9 44.2 31.7 72.5 45.8 56.1 144.4 54.8 144.4 54.8s12.1 28.9 30.6 55.5c25 33.1 50.7 58.9 75.7 62 63 0 188.9-.1 188.9-.1s12 .1 28.3-10.3c14-8.5 26.5-23.4 26.5-23.4S547 483 565 451.5c5.5-9.7 10.1-19.1 14.1-28 0 0 55.2-117.1 55.2-231.1-1.1-34.5-9.6-40.6-11.6-42.6zM125.6 353.9c-25.9-8.5-36.9-18.7-36.9-18.7S69.6 321.8 60 295.4c-16.5-44.2-1.4-71.2-1.4-71.2s8.4-22.5 38.5-30c13.8-3.7 31-3.1 31-3.1s7.1 59.4 15.7 94.2c7.2 29.2 24.8 77.7 24.8 77.7s-26.1-3.1-43-9.1zm300.3 107.6s-6.1 14.5-19.6 15.4c-5.8.4-10.3-1.2-10.3-1.2s-.3-.1-5.3-2.1l-112.9-55s-10.9-5.7-12.8-15.6c-2.2-8.1 2.7-18.1 2.7-18.1L322 273s4.8-9.7 12.2-13c.6-.3 2.3-1 4.5-1.5 8.1-2.1 18 2.8 18 2.8L467.4 315s12.6 5.7 15.3 16.2c1.9 7.4-.5 14-1.8 17.2-6.3 15.4-55 113.1-55 113.1z" style="fill:#609926"/><path d="M326.8 380.1c-8.2.1-15.4 5.8-17.3 13.8-1.9 8 2 16.3 9.1 20 7.7 4 17.5 1.8 22.7-5.4 5.1-7.1 4.3-16.9-1.8-23.1l24-49.1c1.5.1 3.7.2 6.2-.5 4.1-.9 7.1-3.6 7.1-3.6 4.2 1.8 8.6 3.8 13.2 6.1 4.8 2.4 9.3 4.9 13.4 7.3.9.5 1.8 1.1 2.8 1.9 1.6 1.3 3.4 3.1 4.7 5.5 1.9 5.5-1.9 14.9-1.9 14.9-2.3 7.6-18.4 40.6-18.4 40.6-8.1-.2-15.3 5-17.7 12.5-2.6 8.1 1.1 17.3 8.9 21.3 7.8 4 17.4 1.7 22.5-5.3 5-6.8 4.6-16.3-1.1-22.6 1.9-3.7 3.7-7.4 5.6-11.3 5-10.4 13.5-30.4 13.5-30.4.9-1.7 5.7-10.3 2.7-21.3-2.5-11.4-12.6-16.7-12.6-16.7-12.2-7.9-29.2-15.2-29.2-15.2s0-4.1-1.1-7.1c-1.1-3.1-2.8-5.1-3.9-6.3 4.7-9.7 9.4-19.3 14.1-29-4.1-2-8.1-4-12.2-6.1-4.8 9.8-9.7 19.7-14.5 29.5-6.7-.1-12.9 3.5-16.1 9.4-3.4 6.3-2.7 14.1 1.9 19.8l-24.6 50.4z" style="fill:#609926"/></svg>
|
||||
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
@@ -31,7 +31,7 @@ import (
|
||||
user_service "code.gitea.io/gitea/services/user"
|
||||
)
|
||||
|
||||
func parseAuthSource(ctx *context.APIContext, u *user_model.User, sourceID int64, loginName string) {
|
||||
func parseAuthSource(ctx *context.APIContext, u *user_model.User, sourceID int64) {
|
||||
if sourceID == 0 {
|
||||
return
|
||||
}
|
||||
@@ -48,7 +48,6 @@ func parseAuthSource(ctx *context.APIContext, u *user_model.User, sourceID int64
|
||||
|
||||
u.LoginType = source.Type
|
||||
u.LoginSource = source.ID
|
||||
u.LoginName = loginName
|
||||
}
|
||||
|
||||
// CreateUser create a user
|
||||
@@ -84,12 +83,13 @@ func CreateUser(ctx *context.APIContext) {
|
||||
Passwd: form.Password,
|
||||
MustChangePassword: true,
|
||||
LoginType: auth.Plain,
|
||||
LoginName: form.LoginName,
|
||||
}
|
||||
if form.MustChangePassword != nil {
|
||||
u.MustChangePassword = *form.MustChangePassword
|
||||
}
|
||||
|
||||
parseAuthSource(ctx, u, form.SourceID, form.LoginName)
|
||||
parseAuthSource(ctx, u, form.SourceID)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
@@ -192,7 +192,7 @@ func EditUser(ctx *context.APIContext) {
|
||||
|
||||
form := web.GetForm(ctx).(*api.EditUserOption)
|
||||
|
||||
parseAuthSource(ctx, ctx.ContextUser, form.SourceID, form.LoginName)
|
||||
parseAuthSource(ctx, ctx.ContextUser, form.SourceID)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -468,8 +468,9 @@ func AuthorizeOAuth(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// Redirect if user already granted access
|
||||
if grant != nil {
|
||||
// Redirect if user already granted access and the application is confidential.
|
||||
// I.e. always require authorization for public clients as recommended by RFC 6749 Section 10.2
|
||||
if app.ConfidentialClient && grant != nil {
|
||||
code, err := grant.GenerateNewAuthorizationCode(ctx, form.RedirectURI, form.CodeChallenge, form.CodeChallengeMethod)
|
||||
if err != nil {
|
||||
handleServerError(ctx, form.State, form.RedirectURI)
|
||||
@@ -543,15 +544,30 @@ func GrantApplicationOAuth(ctx *context.Context) {
|
||||
ctx.ServerError("GetOAuth2ApplicationByClientID", err)
|
||||
return
|
||||
}
|
||||
grant, err := app.CreateGrant(ctx, ctx.Doer.ID, form.Scope)
|
||||
grant, err := app.GetGrantByUserID(ctx, ctx.Doer.ID)
|
||||
if err != nil {
|
||||
handleServerError(ctx, form.State, form.RedirectURI)
|
||||
return
|
||||
}
|
||||
if grant == nil {
|
||||
grant, err = app.CreateGrant(ctx, ctx.Doer.ID, form.Scope)
|
||||
if err != nil {
|
||||
handleAuthorizeError(ctx, AuthorizeError{
|
||||
State: form.State,
|
||||
ErrorDescription: "cannot create grant for user",
|
||||
ErrorCode: ErrorCodeServerError,
|
||||
}, form.RedirectURI)
|
||||
return
|
||||
}
|
||||
} else if grant.Scope != form.Scope {
|
||||
handleAuthorizeError(ctx, AuthorizeError{
|
||||
State: form.State,
|
||||
ErrorDescription: "cannot create grant for user",
|
||||
ErrorDescription: "a grant exists with different scope",
|
||||
ErrorCode: ErrorCodeServerError,
|
||||
}, form.RedirectURI)
|
||||
return
|
||||
}
|
||||
|
||||
if len(form.Nonce) > 0 {
|
||||
err := grant.SetNonce(ctx, form.Nonce)
|
||||
if err != nil {
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -19,6 +18,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/templates"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/modules/web"
|
||||
shared_user "code.gitea.io/gitea/routers/web/shared/user"
|
||||
@@ -100,7 +100,7 @@ func Projects(ctx *context.Context) {
|
||||
}
|
||||
|
||||
for _, project := range projects {
|
||||
project.RenderedContent = project.Description
|
||||
project.RenderedContent = string(templates.RenderMarkdownToHtml(ctx, project.Description))
|
||||
}
|
||||
|
||||
err = shared_user.LoadHeaderCount(ctx)
|
||||
@@ -190,14 +190,15 @@ func NewProjectPost(ctx *context.Context) {
|
||||
|
||||
// ChangeProjectStatus updates the status of a project between "open" and "close"
|
||||
func ChangeProjectStatus(ctx *context.Context) {
|
||||
toClose := false
|
||||
var toClose bool
|
||||
switch ctx.Params(":action") {
|
||||
case "open":
|
||||
toClose = false
|
||||
case "close":
|
||||
toClose = true
|
||||
default:
|
||||
ctx.Redirect(ctx.ContextUser.HomeLink() + "/-/projects")
|
||||
ctx.JSONRedirect(ctx.ContextUser.HomeLink() + "/-/projects")
|
||||
return
|
||||
}
|
||||
id := ctx.ParamsInt64(":id")
|
||||
|
||||
@@ -209,7 +210,7 @@ func ChangeProjectStatus(ctx *context.Context) {
|
||||
}
|
||||
return
|
||||
}
|
||||
ctx.Redirect(ctx.ContextUser.HomeLink() + "/-/projects?state=" + url.QueryEscape(ctx.Params(":action")))
|
||||
ctx.JSONRedirect(fmt.Sprintf("%s/-/projects/%d", ctx.ContextUser.HomeLink(), id))
|
||||
}
|
||||
|
||||
// DeleteProject delete a project
|
||||
@@ -391,7 +392,7 @@ func ViewProject(ctx *context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
project.RenderedContent = project.Description
|
||||
project.RenderedContent = string(templates.RenderMarkdownToHtml(ctx, project.Description))
|
||||
ctx.Data["LinkedPRs"] = linkedPrsMap
|
||||
ctx.Data["PageIsViewProjects"] = true
|
||||
ctx.Data["CanWriteProjects"] = canWriteProjects(ctx)
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
@@ -176,14 +175,10 @@ func ChangeProjectStatus(ctx *context.Context) {
|
||||
id := ctx.ParamsInt64(":id")
|
||||
|
||||
if err := project_model.ChangeProjectStatusByRepoIDAndID(ctx, ctx.Repo.Repository.ID, id, toClose); err != nil {
|
||||
if project_model.IsErrProjectNotExist(err) {
|
||||
ctx.NotFound("", err)
|
||||
} else {
|
||||
ctx.ServerError("ChangeProjectStatusByIDAndRepoID", err)
|
||||
}
|
||||
ctx.NotFoundOrServerError("ChangeProjectStatusByRepoIDAndID", project_model.IsErrProjectNotExist, err)
|
||||
return
|
||||
}
|
||||
ctx.JSONRedirect(ctx.Repo.RepoLink + "/projects?state=" + url.QueryEscape(ctx.Params(":action")))
|
||||
ctx.JSONRedirect(fmt.Sprintf("%s/projects/%d", ctx.Repo.RepoLink, id))
|
||||
}
|
||||
|
||||
// DeleteProject delete a project
|
||||
|
||||
+13
-11
@@ -48,9 +48,9 @@ import (
|
||||
_ "code.gitea.io/gitea/modules/session" // to registers all internal adapters
|
||||
|
||||
"gitea.com/go-chi/captcha"
|
||||
"github.com/NYTimes/gziphandler"
|
||||
chi_middleware "github.com/go-chi/chi/v5/middleware"
|
||||
"github.com/go-chi/cors"
|
||||
"github.com/klauspost/compress/gzhttp"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
@@ -100,14 +100,14 @@ func optionsCorsHandler() func(next http.Handler) http.Handler {
|
||||
// The Session plugin is expected to be executed second, in order to skip authentication
|
||||
// for users that have already signed in.
|
||||
func buildAuthGroup() *auth_service.Group {
|
||||
group := auth_service.NewGroup(
|
||||
&auth_service.OAuth2{}, // FIXME: this should be removed and only applied in download and oauth related routers
|
||||
&auth_service.Basic{}, // FIXME: this should be removed and only applied in download and git/lfs routers
|
||||
&auth_service.Session{},
|
||||
)
|
||||
group := auth_service.NewGroup()
|
||||
group.Add(&auth_service.OAuth2{}) // FIXME: this should be removed and only applied in download and oauth related routers
|
||||
group.Add(&auth_service.Basic{}) // FIXME: this should be removed and only applied in download and git/lfs routers
|
||||
|
||||
if setting.Service.EnableReverseProxyAuth {
|
||||
group.Add(&auth_service.ReverseProxy{})
|
||||
group.Add(&auth_service.ReverseProxy{}) // reverseproxy should before Session, otherwise the header will be ignored if user has login
|
||||
}
|
||||
group.Add(&auth_service.Session{})
|
||||
|
||||
if setting.IsWindows && auth_model.IsSSPIEnabled() {
|
||||
group.Add(&auth_service.SSPI{}) // it MUST be the last, see the comment of SSPI
|
||||
@@ -244,11 +244,13 @@ func Routes() *web.Route {
|
||||
var mid []any
|
||||
|
||||
if setting.EnableGzip {
|
||||
h, err := gziphandler.GzipHandlerWithOpts(gziphandler.MinSize(GzipMinSize))
|
||||
// random jitter is recommended by: https://pkg.go.dev/github.com/klauspost/compress/gzhttp#readme-breach-mitigation
|
||||
// compression level 6 is the gzip default and a good general tradeoff between speed, CPU usage, and compression
|
||||
wrapper, err := gzhttp.NewWrapper(gzhttp.RandomJitter(32, 0, false), gzhttp.MinSize(GzipMinSize), gzhttp.CompressionLevel(6))
|
||||
if err != nil {
|
||||
log.Fatal("GzipHandlerWithOpts failed: %v", err)
|
||||
log.Fatal("gzhttp.NewWrapper failed: %v", err)
|
||||
}
|
||||
mid = append(mid, h)
|
||||
mid = append(mid, wrapper)
|
||||
}
|
||||
|
||||
if setting.Service.EnableCaptcha {
|
||||
@@ -261,7 +263,7 @@ func Routes() *web.Route {
|
||||
routes.Get("/metrics", append(mid, Metrics)...)
|
||||
}
|
||||
|
||||
routes.Get("/robots.txt", append(mid, misc.RobotsTxt)...)
|
||||
routes.Methods("GET,HEAD", "/robots.txt", append(mid, misc.RobotsTxt)...)
|
||||
routes.Get("/ssh_info", misc.SSHInfo)
|
||||
routes.Get("/api/healthz", healthcheck.Check)
|
||||
|
||||
|
||||
@@ -74,6 +74,11 @@ func newNotifyInput(repo *repo_model.Repository, doer *user_model.User, event we
|
||||
}
|
||||
}
|
||||
|
||||
func newNotifyInputForSchedules(repo *repo_model.Repository) *notifyInput {
|
||||
// the doer here will be ignored as we force using action user when handling schedules
|
||||
return newNotifyInput(repo, user_model.NewActionsUser(), webhook_module.HookEventSchedule)
|
||||
}
|
||||
|
||||
func (input *notifyInput) WithDoer(doer *user_model.User) *notifyInput {
|
||||
input.Doer = doer
|
||||
return input
|
||||
@@ -454,7 +459,7 @@ func handleSchedules(
|
||||
RepoID: input.Repo.ID,
|
||||
OwnerID: input.Repo.OwnerID,
|
||||
WorkflowID: dwf.EntryName,
|
||||
TriggerUserID: input.Doer.ID,
|
||||
TriggerUserID: user_model.ActionsUserID,
|
||||
Ref: ref,
|
||||
CommitSHA: commit.ID.String(),
|
||||
Event: input.Event,
|
||||
@@ -496,7 +501,7 @@ func DetectAndHandleSchedules(ctx context.Context, repo *repo_model.Repository)
|
||||
// We need a notifyInput to call handleSchedules
|
||||
// if repo is a mirror, commit author maybe an external user,
|
||||
// so we use action user as the Doer of the notifyInput
|
||||
notifyInput := newNotifyInput(repo, user_model.NewActionsUser(), webhook_module.HookEventSchedule)
|
||||
notifyInput := newNotifyInputForSchedules(repo)
|
||||
|
||||
return handleSchedules(ctx, scheduleWorkflows, commit, notifyInput, repo.DefaultBranch)
|
||||
}
|
||||
|
||||
@@ -127,8 +127,14 @@ func CreateScheduleTask(ctx context.Context, cron *actions_model.ActionSchedule)
|
||||
Status: actions_model.StatusWaiting,
|
||||
}
|
||||
|
||||
vars, err := actions_model.GetVariablesOfRun(ctx, run)
|
||||
if err != nil {
|
||||
log.Error("GetVariablesOfRun: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Parse the workflow specification from the cron schedule
|
||||
workflows, err := jobparser.Parse(cron.Content)
|
||||
workflows, err := jobparser.Parse(cron.Content, jobparser.WithVars(vars))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -152,3 +152,19 @@ func (r *indexerNotifier) IssueChangeLabels(ctx context.Context, doer *user_mode
|
||||
func (r *indexerNotifier) IssueClearLabels(ctx context.Context, doer *user_model.User, issue *issues_model.Issue) {
|
||||
issue_indexer.UpdateIssueIndexer(ctx, issue.ID)
|
||||
}
|
||||
|
||||
func (r *indexerNotifier) MergePullRequest(ctx context.Context, doer *user_model.User, pr *issues_model.PullRequest) {
|
||||
if err := pr.LoadIssue(ctx); err != nil {
|
||||
log.Error("LoadIssue: %v", err)
|
||||
return
|
||||
}
|
||||
issue_indexer.UpdateIssueIndexer(ctx, pr.Issue.ID)
|
||||
}
|
||||
|
||||
func (r *indexerNotifier) AutoMergePullRequest(ctx context.Context, doer *user_model.User, pr *issues_model.PullRequest) {
|
||||
if err := pr.LoadIssue(ctx); err != nil {
|
||||
log.Error("LoadIssue: %v", err)
|
||||
return
|
||||
}
|
||||
issue_indexer.UpdateIssueIndexer(ctx, pr.Issue.ID)
|
||||
}
|
||||
|
||||
@@ -12,10 +12,12 @@
|
||||
{{ctx.Locale.Tr "repo.settings.hooks"}}
|
||||
</a>
|
||||
{{end}}
|
||||
{{if and (.Repository.UnitEnabled $.Context $.UnitTypeCode) (not .Repository.IsEmpty)}}
|
||||
<a class="{{if .PageIsSettingsBranches}}active {{end}}item" href="{{.RepoLink}}/settings/branches">
|
||||
{{ctx.Locale.Tr "repo.settings.branches"}}
|
||||
</a>
|
||||
{{if .Repository.UnitEnabled $.Context $.UnitTypeCode}}
|
||||
{{if not .Repository.IsEmpty}}
|
||||
<a class="{{if .PageIsSettingsBranches}}active {{end}}item" href="{{.RepoLink}}/settings/branches">
|
||||
{{ctx.Locale.Tr "repo.settings.branches"}}
|
||||
</a>
|
||||
{{end}}
|
||||
<a class="{{if .PageIsSettingsTags}}active {{end}}item" href="{{.RepoLink}}/settings/tags">
|
||||
{{ctx.Locale.Tr "repo.settings.tags"}}
|
||||
</a>
|
||||
|
||||
@@ -671,7 +671,9 @@ func TestAPIRepoGetReviewers(t *testing.T) {
|
||||
resp := MakeRequest(t, req, http.StatusOK)
|
||||
var reviewers []*api.User
|
||||
DecodeJSON(t, resp, &reviewers)
|
||||
assert.Len(t, reviewers, 4)
|
||||
if assert.Len(t, reviewers, 3) {
|
||||
assert.ElementsMatch(t, []int64{1, 4, 11}, []int64{reviewers[0].ID, reviewers[1].ID, reviewers[2].ID})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPIRepoGetAssignees(t *testing.T) {
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/models/webhook"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/queue"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/test"
|
||||
"code.gitea.io/gitea/modules/translation"
|
||||
@@ -435,3 +436,63 @@ func TestConflictChecking(t *testing.T) {
|
||||
assert.False(t, conflictingPR.Mergeable(db.DefaultContext))
|
||||
})
|
||||
}
|
||||
|
||||
func TestPullMergeIndexerNotifier(t *testing.T) {
|
||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||
// create a pull request
|
||||
session := loginUser(t, "user1")
|
||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
|
||||
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
|
||||
createPullResp := testPullCreate(t, session, "user1", "repo1", "master", "Indexer notifier test pull")
|
||||
|
||||
assert.NoError(t, queue.GetManager().FlushAll(context.Background(), 0))
|
||||
time.Sleep(time.Second)
|
||||
|
||||
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{
|
||||
OwnerName: "user2",
|
||||
Name: "repo1",
|
||||
})
|
||||
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{
|
||||
RepoID: repo1.ID,
|
||||
Title: "Indexer notifier test pull",
|
||||
IsPull: true,
|
||||
IsClosed: false,
|
||||
})
|
||||
|
||||
// build the request for searching issues
|
||||
link, _ := url.Parse("/api/v1/repos/issues/search")
|
||||
query := url.Values{}
|
||||
query.Add("state", "closed")
|
||||
query.Add("type", "pulls")
|
||||
query.Add("q", "notifier")
|
||||
link.RawQuery = query.Encode()
|
||||
|
||||
// search issues
|
||||
searchIssuesResp := session.MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK)
|
||||
var apiIssuesBefore []*api.Issue
|
||||
DecodeJSON(t, searchIssuesResp, &apiIssuesBefore)
|
||||
assert.Len(t, apiIssuesBefore, 0)
|
||||
|
||||
// merge the pull request
|
||||
elem := strings.Split(test.RedirectURL(createPullResp), "/")
|
||||
assert.EqualValues(t, "pulls", elem[3])
|
||||
testPullMerge(t, session, elem[1], elem[2], elem[4], repo_model.MergeStyleMerge)
|
||||
|
||||
// check if the issue is closed
|
||||
issue = unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{
|
||||
ID: issue.ID,
|
||||
})
|
||||
assert.True(t, issue.IsClosed)
|
||||
|
||||
assert.NoError(t, queue.GetManager().FlushAll(context.Background(), 0))
|
||||
time.Sleep(time.Second)
|
||||
|
||||
// search issues again
|
||||
searchIssuesResp = session.MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK)
|
||||
var apiIssuesAfter []*api.Issue
|
||||
DecodeJSON(t, searchIssuesResp, &apiIssuesAfter)
|
||||
if assert.Len(t, apiIssuesAfter, 1) {
|
||||
assert.Equal(t, issue.ID, apiIssuesAfter[0].ID)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -112,6 +112,10 @@ export async function createMonaco(textarea, filename, editorOpts) {
|
||||
...other,
|
||||
});
|
||||
|
||||
monaco.editor.addKeybindingRules([
|
||||
{keybinding: monaco.KeyCode.Enter, command: null}, // disable enter from accepting code completion
|
||||
]);
|
||||
|
||||
const model = editor.getModel();
|
||||
model.onDidChangeContent(() => {
|
||||
textarea.value = editor.getValue({preserveBOM: true});
|
||||
|
||||
Reference in New Issue
Block a user