mirror of https://github.com/go-gitea/gitea
Merge branch 'main' into sync-issue-pr-and-more
This commit is contained in:
commit
4886e180a8
|
@ -18,7 +18,7 @@ jobs:
|
|||
- run: make generate-license generate-gitignore
|
||||
timeout-minutes: 40
|
||||
- name: push translations to repo
|
||||
uses: appleboy/git-push-action@v0.0.2
|
||||
uses: appleboy/git-push-action@v0.0.3
|
||||
with:
|
||||
author_email: "teabot@gitea.io"
|
||||
author_name: GiteaBot
|
||||
|
|
|
@ -22,7 +22,7 @@ jobs:
|
|||
- name: update locales
|
||||
run: ./build/update-locales.sh
|
||||
- name: push translations to repo
|
||||
uses: appleboy/git-push-action@v0.0.2
|
||||
uses: appleboy/git-push-action@v0.0.3
|
||||
with:
|
||||
author_email: "teabot@gitea.io"
|
||||
author_name: GiteaBot
|
||||
|
|
|
@ -58,7 +58,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
- run: make deps-frontend
|
||||
|
@ -115,7 +115,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
- run: make deps-frontend
|
||||
|
@ -162,7 +162,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
- run: make deps-frontend
|
||||
|
|
|
@ -16,8 +16,8 @@ jobs:
|
|||
needs: files-changed
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: docker/setup-buildx-action@v2
|
||||
- uses: docker/build-push-action@v4
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
- uses: docker/build-push-action@v5
|
||||
with:
|
||||
push: false
|
||||
tags: gitea/gitea:linux-amd64
|
||||
|
@ -27,8 +27,8 @@ jobs:
|
|||
needs: files-changed
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: docker/setup-buildx-action@v2
|
||||
- uses: docker/build-push-action@v4
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
- uses: docker/build-push-action@v5
|
||||
with:
|
||||
push: false
|
||||
file: Dockerfile.rootless
|
||||
|
|
|
@ -21,7 +21,7 @@ jobs:
|
|||
with:
|
||||
go-version-file: go.mod
|
||||
check-latest: true
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
- run: make deps-frontend frontend deps-backend
|
||||
|
|
|
@ -2,7 +2,7 @@ name: release-nightly
|
|||
|
||||
on:
|
||||
push:
|
||||
branches: [ main, release/v* ]
|
||||
branches: [main, release/v*]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
@ -22,7 +22,7 @@ jobs:
|
|||
with:
|
||||
go-version-file: go.mod
|
||||
check-latest: true
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
- run: make deps-frontend deps-backend
|
||||
|
@ -32,7 +32,7 @@ jobs:
|
|||
TAGS: bindata sqlite sqlite_unlock_notify
|
||||
- name: import gpg key
|
||||
id: import_gpg
|
||||
uses: crazy-max/ghaction-import-gpg@v5
|
||||
uses: crazy-max/ghaction-import-gpg@v6
|
||||
with:
|
||||
gpg_private_key: ${{ secrets.GPGSIGN_KEY }}
|
||||
passphrase: ${{ secrets.GPGSIGN_PASSPHRASE }}
|
||||
|
@ -68,8 +68,8 @@ jobs:
|
|||
with:
|
||||
go-version-file: go.mod
|
||||
check-latest: true
|
||||
- uses: docker/setup-qemu-action@v2
|
||||
- uses: docker/setup-buildx-action@v2
|
||||
- uses: docker/setup-qemu-action@v3
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
- name: Get cleaned branch name
|
||||
id: clean_name
|
||||
run: |
|
||||
|
@ -81,14 +81,14 @@ jobs:
|
|||
REF_NAME=$(echo "${{ github.ref }}" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\///' -e 's/release\/v//')
|
||||
echo "branch=${REF_NAME}-nightly" >> "$GITHUB_OUTPUT"
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: fetch go modules
|
||||
run: make vendor
|
||||
- name: build rootful docker image
|
||||
uses: docker/build-push-action@v4
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
|
@ -105,8 +105,8 @@ jobs:
|
|||
with:
|
||||
go-version-file: go.mod
|
||||
check-latest: true
|
||||
- uses: docker/setup-qemu-action@v2
|
||||
- uses: docker/setup-buildx-action@v2
|
||||
- uses: docker/setup-qemu-action@v3
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
- name: Get cleaned branch name
|
||||
id: clean_name
|
||||
run: |
|
||||
|
@ -118,14 +118,14 @@ jobs:
|
|||
REF_NAME=$(echo "${{ github.ref }}" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\///' -e 's/release\/v//')
|
||||
echo "branch=${REF_NAME}-nightly" >> "$GITHUB_OUTPUT"
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: fetch go modules
|
||||
run: make vendor
|
||||
- name: build rootless docker image
|
||||
uses: docker/build-push-action@v4
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
|
|
|
@ -3,7 +3,7 @@ name: release-tag-rc
|
|||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v1*-rc*'
|
||||
- "v1*-rc*"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
@ -21,7 +21,7 @@ jobs:
|
|||
with:
|
||||
go-version-file: go.mod
|
||||
check-latest: true
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
- run: make deps-frontend deps-backend
|
||||
|
@ -31,7 +31,7 @@ jobs:
|
|||
TAGS: bindata sqlite sqlite_unlock_notify
|
||||
- name: import gpg key
|
||||
id: import_gpg
|
||||
uses: crazy-max/ghaction-import-gpg@v5
|
||||
uses: crazy-max/ghaction-import-gpg@v6
|
||||
with:
|
||||
gpg_private_key: ${{ secrets.GPGSIGN_KEY }}
|
||||
passphrase: ${{ secrets.GPGSIGN_PASSPHRASE }}
|
||||
|
@ -68,8 +68,8 @@ jobs:
|
|||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
||||
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
|
||||
- run: git fetch --unshallow --quiet --tags --force
|
||||
- uses: docker/setup-qemu-action@v2
|
||||
- uses: docker/setup-buildx-action@v2
|
||||
- uses: docker/setup-qemu-action@v3
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
- uses: docker/metadata-action@v5
|
||||
id: meta
|
||||
with:
|
||||
|
@ -78,12 +78,12 @@ jobs:
|
|||
tags: |
|
||||
type=semver,pattern={{version}}
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: build rootful docker image
|
||||
uses: docker/build-push-action@v4
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
|
@ -97,8 +97,8 @@ jobs:
|
|||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
||||
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
|
||||
- run: git fetch --unshallow --quiet --tags --force
|
||||
- uses: docker/setup-qemu-action@v2
|
||||
- uses: docker/setup-buildx-action@v2
|
||||
- uses: docker/setup-qemu-action@v3
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
- uses: docker/metadata-action@v5
|
||||
id: meta
|
||||
with:
|
||||
|
@ -110,12 +110,12 @@ jobs:
|
|||
tags: |
|
||||
type=semver,pattern={{version}}
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: build rootless docker image
|
||||
uses: docker/build-push-action@v4
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
|
|
|
@ -3,9 +3,9 @@ name: release-tag-version
|
|||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v1.*'
|
||||
- '!v1*-rc*'
|
||||
- '!v1*-dev'
|
||||
- "v1.*"
|
||||
- "!v1*-rc*"
|
||||
- "!v1*-dev"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
@ -23,7 +23,7 @@ jobs:
|
|||
with:
|
||||
go-version-file: go.mod
|
||||
check-latest: true
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
- run: make deps-frontend deps-backend
|
||||
|
@ -33,7 +33,7 @@ jobs:
|
|||
TAGS: bindata sqlite sqlite_unlock_notify
|
||||
- name: import gpg key
|
||||
id: import_gpg
|
||||
uses: crazy-max/ghaction-import-gpg@v5
|
||||
uses: crazy-max/ghaction-import-gpg@v6
|
||||
with:
|
||||
gpg_private_key: ${{ secrets.GPGSIGN_KEY }}
|
||||
passphrase: ${{ secrets.GPGSIGN_PASSPHRASE }}
|
||||
|
@ -70,8 +70,8 @@ jobs:
|
|||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
||||
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
|
||||
- run: git fetch --unshallow --quiet --tags --force
|
||||
- uses: docker/setup-qemu-action@v2
|
||||
- uses: docker/setup-buildx-action@v2
|
||||
- uses: docker/setup-qemu-action@v3
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
- uses: docker/metadata-action@v5
|
||||
id: meta
|
||||
with:
|
||||
|
@ -87,12 +87,12 @@ jobs:
|
|||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{version}}
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: build rootful docker image
|
||||
uses: docker/build-push-action@v4
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
|
@ -106,8 +106,8 @@ jobs:
|
|||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
||||
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
|
||||
- run: git fetch --unshallow --quiet --tags --force
|
||||
- uses: docker/setup-qemu-action@v2
|
||||
- uses: docker/setup-buildx-action@v2
|
||||
- uses: docker/setup-qemu-action@v3
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
- uses: docker/metadata-action@v5
|
||||
id: meta
|
||||
with:
|
||||
|
@ -126,12 +126,12 @@ jobs:
|
|||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{version}}
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: build rootless docker image
|
||||
uses: docker/build-push-action@v4
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
|
|
|
@ -62,7 +62,7 @@ func runListAuth(c *cli.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
authSources, err := auth_model.Sources(ctx)
|
||||
authSources, err := auth_model.FindSources(ctx, auth_model.FindSourcesOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -424,7 +424,7 @@ The following configuration set `Content-Type: application/vnd.android.package-a
|
|||
## Database (`database`)
|
||||
|
||||
- `DB_TYPE`: **mysql**: The database type in use \[mysql, postgres, mssql, sqlite3\].
|
||||
- `HOST`: **127.0.0.1:3306**: Database host address and port or absolute path for unix socket \[mysql, postgres\] (ex: /var/run/mysqld/mysqld.sock).
|
||||
- `HOST`: **127.0.0.1:3306**: Database host address and port or absolute path for unix socket \[mysql, postgres[^1]\] (ex: /var/run/mysqld/mysqld.sock).
|
||||
- `NAME`: **gitea**: Database name.
|
||||
- `USER`: **root**: Database username.
|
||||
- `PASSWD`: **_empty_**: Database user password. Use \`your password\` or """your password""" for quoting if you use special characters in the password.
|
||||
|
@ -455,6 +455,8 @@ The following configuration set `Content-Type: application/vnd.android.package-a
|
|||
- `CONN_MAX_LIFETIME` **0 or 3s**: Sets the maximum amount of time a DB connection may be reused - default is 0, meaning there is no limit (except on MySQL where it is 3s - see #6804 & #7071).
|
||||
- `AUTO_MIGRATION` **true**: Whether execute database models migrations automatically.
|
||||
|
||||
[^1]: It may be necessary to specify a hostport even when listening on a unix socket, as the port is part of the socket name. see [#24552](https://github.com/go-gitea/gitea/issues/24552#issuecomment-1681649367) for additional details.
|
||||
|
||||
Please see #8540 & #8273 for further discussion of the appropriate values for `MAX_OPEN_CONNS`, `MAX_IDLE_CONNS` & `CONN_MAX_LIFETIME` and their
|
||||
relation to port exhaustion.
|
||||
|
||||
|
|
|
@ -114,6 +114,12 @@ If you cannot see the settings page, please make sure that you have the right pe
|
|||
|
||||
The format of the registration token is a random string `D0gvfu2iHfUjNqCYVljVyRV14fISpJxxxxxxxxxx`.
|
||||
|
||||
A registration token can also be obtained from the gitea [command-line interface](../../administration/command-line.en-us.md#actions-generate-runner-token):
|
||||
|
||||
```
|
||||
gitea --config /etc/gitea/app.ini actions generate-runner-token
|
||||
```
|
||||
|
||||
### Register the runner
|
||||
|
||||
The act runner can be registered by running the following command:
|
||||
|
@ -262,6 +268,40 @@ The runner will fetch jobs from the Gitea instance and run them automatically.
|
|||
|
||||
Since act runner is still in development, it is recommended to check the latest version and upgrade it regularly.
|
||||
|
||||
## Systemd service
|
||||
|
||||
It is also possible to run act-runner as a [systemd](https://en.wikipedia.org/wiki/Systemd) service. Create an unprivileged `act_runner` user on your system, and the following file in `/etc/systemd/system/act_runner.service`. The paths in `ExecStart` and `WorkingDirectory` may need to be adjusted depending on where you installed the `act_runner` binary, its configuration file, and the home directory of the `act_runner` user.
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=Gitea Actions runner
|
||||
Documentation=https://gitea.com/gitea/act_runner
|
||||
After=docker.service
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/local/bin/act_runner daemon --config /etc/act_runner/config.yaml
|
||||
ExecReload=/bin/kill -s HUP $MAINPID
|
||||
WorkingDirectory=/var/lib/act_runner
|
||||
TimeoutSec=0
|
||||
RestartSec=10
|
||||
Restart=always
|
||||
User=act_runner
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
Then:
|
||||
|
||||
```bash
|
||||
# load the new systemd unit file
|
||||
sudo systemctl daemon-reload
|
||||
# start the service and enable it at boot
|
||||
sudo systemctl enable act_runner --now
|
||||
```
|
||||
|
||||
If using Docker, the `act_runner` user should also be added to the `docker` group before starting the service. Keep in mind that this effectively gives `act_runner` root access to the system [[1]](https://docs.docker.com/engine/security/#docker-daemon-attack-surface).
|
||||
|
||||
## Configuration variable
|
||||
|
||||
You can create configuration variables on the user, organization and repository level.
|
||||
|
|
|
@ -113,6 +113,8 @@ Runner级别决定了从哪里获取注册令牌。
|
|||
|
||||
注册令牌的格式是一个随机字符串 `D0gvfu2iHfUjNqCYVljVyRV14fISpJxxxxxxxxxx`。
|
||||
|
||||
注册令牌也可以通过 Gitea 的 [命令行](../../administration/command-line.en-us.md#actions-generate-runner-token) 获得:
|
||||
|
||||
### 注册Runner
|
||||
|
||||
可以通过运行以下命令来注册Act Runner:
|
||||
|
|
2
go.mod
2
go.mod
|
@ -36,7 +36,7 @@ require (
|
|||
github.com/ethantkoenig/rupture v1.0.1
|
||||
github.com/felixge/fgprof v0.9.3
|
||||
github.com/fsnotify/fsnotify v1.6.0
|
||||
github.com/gliderlabs/ssh v0.3.5
|
||||
github.com/gliderlabs/ssh v0.3.6-0.20230927171611-ece6c7995e46
|
||||
github.com/go-ap/activitypub v0.0.0-20231003111253-1fba3772399b
|
||||
github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73
|
||||
github.com/go-chi/chi/v5 v5.0.10
|
||||
|
|
8
go.sum
8
go.sum
|
@ -329,8 +329,8 @@ github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4
|
|||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||
github.com/fxamacker/cbor/v2 v2.5.0 h1:oHsG0V/Q6E/wqTS2O1Cozzsy69nqCiguo5Q1a1ADivE=
|
||||
github.com/fxamacker/cbor/v2 v2.5.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
|
||||
github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY=
|
||||
github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4=
|
||||
github.com/gliderlabs/ssh v0.3.6-0.20230927171611-ece6c7995e46 h1:fYiA820jw7wmAvdXrHwMItxjJkra7dT9y8yiXhtzb94=
|
||||
github.com/gliderlabs/ssh v0.3.6-0.20230927171611-ece6c7995e46/go.mod h1:i/TCLcdiX9Up/vs+Rp8c3yMbqp2Y4Y7Nh9uzGFCa5pM=
|
||||
github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
|
||||
github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
|
||||
github.com/go-ap/activitypub v0.0.0-20231003111253-1fba3772399b h1:VLD6IPBDkqEsOZ+EfLO6MayuHycZ0cv4BStTlRoZduo=
|
||||
|
@ -1237,7 +1237,6 @@ golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qx
|
|||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
|
@ -1337,9 +1336,7 @@ golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
@ -1353,7 +1350,6 @@ golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||
|
|
|
@ -102,7 +102,7 @@ func GetStatistic(ctx context.Context) (stats Statistic) {
|
|||
stats.Counter.Follow, _ = e.Count(new(user_model.Follow))
|
||||
stats.Counter.Mirror, _ = e.Count(new(repo_model.Mirror))
|
||||
stats.Counter.Release, _ = e.Count(new(repo_model.Release))
|
||||
stats.Counter.AuthSource = auth.CountSources(ctx)
|
||||
stats.Counter.AuthSource = auth.CountSources(ctx, auth.FindSourcesOptions{})
|
||||
stats.Counter.Webhook, _ = e.Count(new(webhook.Webhook))
|
||||
stats.Counter.Milestone, _ = e.Count(new(issues_model.Milestone))
|
||||
stats.Counter.Label, _ = e.Count(new(issues_model.Label))
|
||||
|
|
|
@ -631,15 +631,6 @@ func (err ErrOAuthApplicationNotFound) Unwrap() error {
|
|||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// GetActiveOAuth2ProviderSources returns all actived LoginOAuth2 sources
|
||||
func GetActiveOAuth2ProviderSources(ctx context.Context) ([]*Source, error) {
|
||||
sources := make([]*Source, 0, 1)
|
||||
if err := db.GetEngine(ctx).Where("is_active = ? and type = ?", true, OAuth2).Find(&sources); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sources, nil
|
||||
}
|
||||
|
||||
// GetActiveOAuth2SourceByName returns a OAuth2 AuthSource based on the given name
|
||||
func GetActiveOAuth2SourceByName(ctx context.Context, name string) (*Source, error) {
|
||||
authSource := new(Source)
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"xorm.io/builder"
|
||||
"xorm.io/xorm"
|
||||
"xorm.io/xorm/convert"
|
||||
)
|
||||
|
@ -240,37 +241,26 @@ func CreateSource(ctx context.Context, source *Source) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// Sources returns a slice of all login sources found in DB.
|
||||
func Sources(ctx context.Context) ([]*Source, error) {
|
||||
type FindSourcesOptions struct {
|
||||
IsActive util.OptionalBool
|
||||
LoginType Type
|
||||
}
|
||||
|
||||
func (opts FindSourcesOptions) ToConds() builder.Cond {
|
||||
conds := builder.NewCond()
|
||||
if !opts.IsActive.IsNone() {
|
||||
conds = conds.And(builder.Eq{"is_active": opts.IsActive.IsTrue()})
|
||||
}
|
||||
if opts.LoginType != NoType {
|
||||
conds = conds.And(builder.Eq{"`type`": opts.LoginType})
|
||||
}
|
||||
return conds
|
||||
}
|
||||
|
||||
// FindSources returns a slice of login sources found in DB according to given conditions.
|
||||
func FindSources(ctx context.Context, opts FindSourcesOptions) ([]*Source, error) {
|
||||
auths := make([]*Source, 0, 6)
|
||||
return auths, db.GetEngine(ctx).Find(&auths)
|
||||
}
|
||||
|
||||
// SourcesByType returns all sources of the specified type
|
||||
func SourcesByType(ctx context.Context, loginType Type) ([]*Source, error) {
|
||||
sources := make([]*Source, 0, 1)
|
||||
if err := db.GetEngine(ctx).Where("type = ?", loginType).Find(&sources); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sources, nil
|
||||
}
|
||||
|
||||
// AllActiveSources returns all active sources
|
||||
func AllActiveSources(ctx context.Context) ([]*Source, error) {
|
||||
sources := make([]*Source, 0, 5)
|
||||
if err := db.GetEngine(ctx).Where("is_active = ?", true).Find(&sources); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sources, nil
|
||||
}
|
||||
|
||||
// ActiveSources returns all active sources of the specified type
|
||||
func ActiveSources(ctx context.Context, tp Type) ([]*Source, error) {
|
||||
sources := make([]*Source, 0, 1)
|
||||
if err := db.GetEngine(ctx).Where("is_active = ? and type = ?", true, tp).Find(&sources); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sources, nil
|
||||
return auths, db.GetEngine(ctx).Where(opts.ToConds()).Find(&auths)
|
||||
}
|
||||
|
||||
// IsSSPIEnabled returns true if there is at least one activated login
|
||||
|
@ -279,7 +269,10 @@ func IsSSPIEnabled(ctx context.Context) bool {
|
|||
if !db.HasEngine {
|
||||
return false
|
||||
}
|
||||
sources, err := ActiveSources(ctx, SSPI)
|
||||
sources, err := FindSources(ctx, FindSourcesOptions{
|
||||
IsActive: util.OptionalBoolTrue,
|
||||
LoginType: SSPI,
|
||||
})
|
||||
if err != nil {
|
||||
log.Error("ActiveSources: %v", err)
|
||||
return false
|
||||
|
@ -354,8 +347,8 @@ func UpdateSource(ctx context.Context, source *Source) error {
|
|||
}
|
||||
|
||||
// CountSources returns number of login sources.
|
||||
func CountSources(ctx context.Context) int64 {
|
||||
count, _ := db.GetEngine(ctx).Count(new(Source))
|
||||
func CountSources(ctx context.Context, opts FindSourcesOptions) int64 {
|
||||
count, _ := db.GetEngine(ctx).Where(opts.ToConds()).Count(new(Source))
|
||||
return count
|
||||
}
|
||||
|
||||
|
|
|
@ -142,22 +142,14 @@ type Issue struct {
|
|||
}
|
||||
|
||||
var (
|
||||
issueTasksPat *regexp.Regexp
|
||||
issueTasksDonePat *regexp.Regexp
|
||||
)
|
||||
|
||||
const (
|
||||
issueTasksRegexpStr = `(^\s*[-*]\s\[[\sxX]\]\s.)|(\n\s*[-*]\s\[[\sxX]\]\s.)`
|
||||
issueTasksDoneRegexpStr = `(^\s*[-*]\s\[[xX]\]\s.)|(\n\s*[-*]\s\[[xX]\]\s.)`
|
||||
issueTasksPat = regexp.MustCompile(`(^\s*[-*]\s\[[\sxX]\]\s.)|(\n\s*[-*]\s\[[\sxX]\]\s.)`)
|
||||
issueTasksDonePat = regexp.MustCompile(`(^\s*[-*]\s\[[xX]\]\s.)|(\n\s*[-*]\s\[[xX]\]\s.)`)
|
||||
)
|
||||
|
||||
// IssueIndex represents the issue index table
|
||||
type IssueIndex db.ResourceIndex
|
||||
|
||||
func init() {
|
||||
issueTasksPat = regexp.MustCompile(issueTasksRegexpStr)
|
||||
issueTasksDonePat = regexp.MustCompile(issueTasksDoneRegexpStr)
|
||||
|
||||
db.RegisterModel(new(Issue))
|
||||
db.RegisterModel(new(IssueIndex))
|
||||
}
|
||||
|
|
|
@ -548,6 +548,8 @@ var migrations = []Migration{
|
|||
NewMigration("Rename user themes", v1_22.RenameUserThemes),
|
||||
// v281 -> v282
|
||||
NewMigration("Add auth_token table", v1_22.CreateAuthTokenTable),
|
||||
// v282 -> v283
|
||||
NewMigration("Add Index to pull_auto_merge.doer_id", v1_22.AddIndexToPullAutoMergeDoerID),
|
||||
}
|
||||
|
||||
// GetCurrentDBVersion returns the current db version
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_22 //nolint
|
||||
|
||||
import (
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func AddIndexToPullAutoMergeDoerID(x *xorm.Engine) error {
|
||||
type PullAutoMerge struct {
|
||||
DoerID int64 `xorm:"INDEX NOT NULL"`
|
||||
}
|
||||
|
||||
return x.Sync(&PullAutoMerge{})
|
||||
}
|
|
@ -14,12 +14,11 @@ import (
|
|||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
)
|
||||
|
||||
func removeOrgUser(ctx context.Context, orgID, userID int64) error {
|
||||
// RemoveOrgUser removes user from given organization.
|
||||
func RemoveOrgUser(ctx context.Context, orgID, userID int64) error {
|
||||
ou := new(organization.OrgUser)
|
||||
|
||||
sess := db.GetEngine(ctx)
|
||||
|
||||
has, err := sess.
|
||||
has, err := db.GetEngine(ctx).
|
||||
Where("uid=?", userID).
|
||||
And("org_id=?", orgID).
|
||||
Get(ou)
|
||||
|
@ -52,7 +51,13 @@ func removeOrgUser(ctx context.Context, orgID, userID int64) error {
|
|||
}
|
||||
}
|
||||
|
||||
if _, err := sess.ID(ou.ID).Delete(ou); err != nil {
|
||||
ctx, committer, err := db.TxContext(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer committer.Close()
|
||||
|
||||
if _, err := db.GetEngine(ctx).ID(ou.ID).Delete(ou); err != nil {
|
||||
return err
|
||||
} else if _, err = db.Exec(ctx, "UPDATE `user` SET num_members=num_members-1 WHERE id=?", orgID); err != nil {
|
||||
return err
|
||||
|
@ -74,7 +79,7 @@ func removeOrgUser(ctx context.Context, orgID, userID int64) error {
|
|||
}
|
||||
|
||||
if len(repoIDs) > 0 {
|
||||
if _, err = sess.
|
||||
if _, err = db.GetEngine(ctx).
|
||||
Where("user_id = ?", userID).
|
||||
In("repo_id", repoIDs).
|
||||
Delete(new(access_model.Access)); err != nil {
|
||||
|
@ -93,18 +98,5 @@ func removeOrgUser(ctx context.Context, orgID, userID int64) error {
|
|||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveOrgUser removes user from given organization.
|
||||
func RemoveOrgUser(ctx context.Context, orgID, userID int64) error {
|
||||
ctx, committer, err := db.TxContext(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer committer.Close()
|
||||
if err := removeOrgUser(ctx, orgID, userID); err != nil {
|
||||
return err
|
||||
}
|
||||
return committer.Commit()
|
||||
}
|
||||
|
|
|
@ -502,7 +502,7 @@ func removeInvalidOrgUser(ctx context.Context, userID, orgID int64) error {
|
|||
}); err != nil {
|
||||
return err
|
||||
} else if count == 0 {
|
||||
return removeOrgUser(ctx, orgID, userID)
|
||||
return RemoveOrgUser(ctx, orgID, userID)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ import (
|
|||
type AutoMerge struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
PullID int64 `xorm:"UNIQUE"`
|
||||
DoerID int64 `xorm:"NOT NULL"`
|
||||
DoerID int64 `xorm:"INDEX NOT NULL"`
|
||||
Doer *user_model.User `xorm:"-"`
|
||||
MergeStyle repo_model.MergeStyle `xorm:"varchar(30)"`
|
||||
Message string `xorm:"LONGTEXT"`
|
||||
|
|
|
@ -157,7 +157,6 @@ func Contexter() func(next http.Handler) http.Handler {
|
|||
ctx.Data["Context"] = ctx // TODO: use "ctx" in template and remove this
|
||||
ctx.Data["CurrentURL"] = setting.AppSubURL + req.URL.RequestURI()
|
||||
ctx.Data["Link"] = ctx.Link
|
||||
ctx.Data["locale"] = ctx.Locale
|
||||
|
||||
// PageData is passed by reference, and it will be rendered to `window.config.pageData` in `head.tmpl` for JavaScript modules
|
||||
ctx.PageData = map[string]any{}
|
||||
|
|
|
@ -6,6 +6,7 @@ package setting
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
|
@ -135,15 +136,18 @@ func DBConnStr() (string, error) {
|
|||
// parsePostgreSQLHostPort parses given input in various forms defined in
|
||||
// https://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING
|
||||
// and returns proper host and port number.
|
||||
func parsePostgreSQLHostPort(info string) (string, string) {
|
||||
host, port := "127.0.0.1", "5432"
|
||||
if strings.Contains(info, ":") && !strings.HasSuffix(info, "]") {
|
||||
idx := strings.LastIndex(info, ":")
|
||||
host = info[:idx]
|
||||
port = info[idx+1:]
|
||||
} else if len(info) > 0 {
|
||||
func parsePostgreSQLHostPort(info string) (host, port string) {
|
||||
if h, p, err := net.SplitHostPort(info); err == nil {
|
||||
host, port = h, p
|
||||
} else {
|
||||
// treat the "info" as "host", if it's an IPv6 address, remove the wrapper
|
||||
host = info
|
||||
if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") {
|
||||
host = host[1 : len(host)-1]
|
||||
}
|
||||
}
|
||||
|
||||
// set fallback values
|
||||
if host == "" {
|
||||
host = "127.0.0.1"
|
||||
}
|
||||
|
@ -155,14 +159,22 @@ func parsePostgreSQLHostPort(info string) (string, string) {
|
|||
|
||||
func getPostgreSQLConnectionString(dbHost, dbUser, dbPasswd, dbName, dbParam, dbsslMode string) (connStr string) {
|
||||
host, port := parsePostgreSQLHostPort(dbHost)
|
||||
if host[0] == '/' { // looks like a unix socket
|
||||
connStr = fmt.Sprintf("postgres://%s:%s@:%s/%s%ssslmode=%s&host=%s",
|
||||
url.PathEscape(dbUser), url.PathEscape(dbPasswd), port, dbName, dbParam, dbsslMode, host)
|
||||
} else {
|
||||
connStr = fmt.Sprintf("postgres://%s:%s@%s:%s/%s%ssslmode=%s",
|
||||
url.PathEscape(dbUser), url.PathEscape(dbPasswd), host, port, dbName, dbParam, dbsslMode)
|
||||
connURL := url.URL{
|
||||
Scheme: "postgres",
|
||||
User: url.UserPassword(dbUser, dbPasswd),
|
||||
Host: net.JoinHostPort(host, port),
|
||||
Path: dbName,
|
||||
OmitHost: false,
|
||||
RawQuery: dbParam,
|
||||
}
|
||||
return connStr
|
||||
query := connURL.Query()
|
||||
if dbHost[0] == '/' { // looks like a unix socket
|
||||
query.Add("host", dbHost)
|
||||
connURL.Host = ":" + port
|
||||
}
|
||||
query.Set("sslmode", dbsslMode)
|
||||
connURL.RawQuery = query.Encode()
|
||||
return connURL.String()
|
||||
}
|
||||
|
||||
// ParseMSSQLHostPort splits the host into host and port
|
||||
|
|
|
@ -10,46 +10,49 @@ import (
|
|||
)
|
||||
|
||||
func Test_parsePostgreSQLHostPort(t *testing.T) {
|
||||
tests := []struct {
|
||||
tests := map[string]struct {
|
||||
HostPort string
|
||||
Host string
|
||||
Port string
|
||||
}{
|
||||
{
|
||||
"host-port": {
|
||||
HostPort: "127.0.0.1:1234",
|
||||
Host: "127.0.0.1",
|
||||
Port: "1234",
|
||||
},
|
||||
{
|
||||
"no-port": {
|
||||
HostPort: "127.0.0.1",
|
||||
Host: "127.0.0.1",
|
||||
Port: "5432",
|
||||
},
|
||||
{
|
||||
"ipv6-port": {
|
||||
HostPort: "[::1]:1234",
|
||||
Host: "[::1]",
|
||||
Host: "::1",
|
||||
Port: "1234",
|
||||
},
|
||||
{
|
||||
"ipv6-no-port": {
|
||||
HostPort: "[::1]",
|
||||
Host: "[::1]",
|
||||
Host: "::1",
|
||||
Port: "5432",
|
||||
},
|
||||
{
|
||||
"unix-socket": {
|
||||
HostPort: "/tmp/pg.sock:1234",
|
||||
Host: "/tmp/pg.sock",
|
||||
Port: "1234",
|
||||
},
|
||||
{
|
||||
"unix-socket-no-port": {
|
||||
HostPort: "/tmp/pg.sock",
|
||||
Host: "/tmp/pg.sock",
|
||||
Port: "5432",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
host, port := parsePostgreSQLHostPort(test.HostPort)
|
||||
assert.Equal(t, test.Host, host)
|
||||
assert.Equal(t, test.Port, port)
|
||||
for k, test := range tests {
|
||||
t.Run(k, func(t *testing.T) {
|
||||
t.Log(test.HostPort)
|
||||
host, port := parsePostgreSQLHostPort(test.HostPort)
|
||||
assert.Equal(t, test.Host, host)
|
||||
assert.Equal(t, test.Port, port)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -72,7 +75,7 @@ func Test_getPostgreSQLConnectionString(t *testing.T) {
|
|||
Name: "gitea",
|
||||
Param: "",
|
||||
SSLMode: "false",
|
||||
Output: "postgres://testuser:space%20space%20%21%23$%25%5E%5E%25%5E%60%60%60-=%3F=@:5432/giteasslmode=false&host=/tmp/pg.sock",
|
||||
Output: "postgres://testuser:space%20space%20%21%23$%25%5E%5E%25%5E%60%60%60-=%3F=@:5432/gitea?host=%2Ftmp%2Fpg.sock&sslmode=false",
|
||||
},
|
||||
{
|
||||
Host: "localhost",
|
||||
|
@ -82,7 +85,7 @@ func Test_getPostgreSQLConnectionString(t *testing.T) {
|
|||
Name: "gitea",
|
||||
Param: "",
|
||||
SSLMode: "true",
|
||||
Output: "postgres://pgsqlusername:I%20love%20Gitea%21@localhost:5432/giteasslmode=true",
|
||||
Output: "postgres://pgsqlusername:I%20love%20Gitea%21@localhost:5432/gitea?sslmode=true",
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// StorageType is a type of Storage
|
||||
|
@ -249,14 +250,24 @@ func getStorageForMinio(targetSec, overrideSec ConfigSection, tp targetSecType,
|
|||
return nil, fmt.Errorf("map minio config failed: %v", err)
|
||||
}
|
||||
|
||||
if storage.MinioConfig.BasePath == "" {
|
||||
storage.MinioConfig.BasePath = name + "/"
|
||||
var defaultPath string
|
||||
if storage.MinioConfig.BasePath != "" {
|
||||
if tp == targetSecIsStorage || tp == targetSecIsDefault {
|
||||
defaultPath = strings.TrimSuffix(storage.MinioConfig.BasePath, "/") + "/" + name + "/"
|
||||
} else {
|
||||
defaultPath = storage.MinioConfig.BasePath
|
||||
}
|
||||
}
|
||||
if defaultPath == "" {
|
||||
defaultPath = name + "/"
|
||||
}
|
||||
|
||||
if overrideSec != nil {
|
||||
storage.MinioConfig.ServeDirect = ConfigSectionKeyBool(overrideSec, "SERVE_DIRECT", storage.MinioConfig.ServeDirect)
|
||||
storage.MinioConfig.BasePath = ConfigSectionKeyString(overrideSec, "MINIO_BASE_PATH", storage.MinioConfig.BasePath)
|
||||
storage.MinioConfig.BasePath = ConfigSectionKeyString(overrideSec, "MINIO_BASE_PATH", defaultPath)
|
||||
storage.MinioConfig.Bucket = ConfigSectionKeyString(overrideSec, "MINIO_BUCKET", storage.MinioConfig.Bucket)
|
||||
} else {
|
||||
storage.MinioConfig.BasePath = defaultPath
|
||||
}
|
||||
return &storage, nil
|
||||
}
|
||||
|
|
|
@ -412,3 +412,56 @@ MINIO_USE_SSL = true
|
|||
assert.EqualValues(t, true, RepoArchive.Storage.MinioConfig.UseSSL)
|
||||
assert.EqualValues(t, "repo-archive/", RepoArchive.Storage.MinioConfig.BasePath)
|
||||
}
|
||||
|
||||
func Test_getStorageConfiguration28(t *testing.T) {
|
||||
cfg, err := NewConfigProviderFromData(`
|
||||
[storage]
|
||||
STORAGE_TYPE = minio
|
||||
MINIO_ACCESS_KEY_ID = my_access_key
|
||||
MINIO_SECRET_ACCESS_KEY = my_secret_key
|
||||
MINIO_USE_SSL = true
|
||||
MINIO_BASE_PATH = /prefix
|
||||
`)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, loadRepoArchiveFrom(cfg))
|
||||
assert.EqualValues(t, "my_access_key", RepoArchive.Storage.MinioConfig.AccessKeyID)
|
||||
assert.EqualValues(t, "my_secret_key", RepoArchive.Storage.MinioConfig.SecretAccessKey)
|
||||
assert.EqualValues(t, true, RepoArchive.Storage.MinioConfig.UseSSL)
|
||||
assert.EqualValues(t, "/prefix/repo-archive/", RepoArchive.Storage.MinioConfig.BasePath)
|
||||
|
||||
cfg, err = NewConfigProviderFromData(`
|
||||
[storage]
|
||||
STORAGE_TYPE = minio
|
||||
MINIO_ACCESS_KEY_ID = my_access_key
|
||||
MINIO_SECRET_ACCESS_KEY = my_secret_key
|
||||
MINIO_USE_SSL = true
|
||||
MINIO_BASE_PATH = /prefix
|
||||
|
||||
[lfs]
|
||||
MINIO_BASE_PATH = /lfs
|
||||
`)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, loadLFSFrom(cfg))
|
||||
assert.EqualValues(t, "my_access_key", LFS.Storage.MinioConfig.AccessKeyID)
|
||||
assert.EqualValues(t, "my_secret_key", LFS.Storage.MinioConfig.SecretAccessKey)
|
||||
assert.EqualValues(t, true, LFS.Storage.MinioConfig.UseSSL)
|
||||
assert.EqualValues(t, "/lfs", LFS.Storage.MinioConfig.BasePath)
|
||||
|
||||
cfg, err = NewConfigProviderFromData(`
|
||||
[storage]
|
||||
STORAGE_TYPE = minio
|
||||
MINIO_ACCESS_KEY_ID = my_access_key
|
||||
MINIO_SECRET_ACCESS_KEY = my_secret_key
|
||||
MINIO_USE_SSL = true
|
||||
MINIO_BASE_PATH = /prefix
|
||||
|
||||
[storage.lfs]
|
||||
MINIO_BASE_PATH = /lfs
|
||||
`)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, loadLFSFrom(cfg))
|
||||
assert.EqualValues(t, "my_access_key", LFS.Storage.MinioConfig.AccessKeyID)
|
||||
assert.EqualValues(t, "my_secret_key", LFS.Storage.MinioConfig.SecretAccessKey)
|
||||
assert.EqualValues(t, true, LFS.Storage.MinioConfig.UseSSL)
|
||||
assert.EqualValues(t, "/lfs", LFS.Storage.MinioConfig.BasePath)
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@ import (
|
|||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
@ -165,10 +164,6 @@ func sessionHandler(session ssh.Session) {
|
|||
}
|
||||
|
||||
func publicKeyHandler(ctx ssh.Context, key ssh.PublicKey) bool {
|
||||
// FIXME: the "ssh.Context" is not thread-safe, so db operations should use the immutable parent "Context"
|
||||
// TODO: Remove after https://github.com/gliderlabs/ssh/pull/211
|
||||
parentCtx := reflect.ValueOf(ctx).Elem().FieldByName("Context").Interface().(context.Context)
|
||||
|
||||
if log.IsDebug() { // <- FingerprintSHA256 is kinda expensive so only calculate it if necessary
|
||||
log.Debug("Handle Public Key: Fingerprint: %s from %s", gossh.FingerprintSHA256(key), ctx.RemoteAddr())
|
||||
}
|
||||
|
@ -200,7 +195,7 @@ func publicKeyHandler(ctx ssh.Context, key ssh.PublicKey) bool {
|
|||
// look for the exact principal
|
||||
principalLoop:
|
||||
for _, principal := range cert.ValidPrincipals {
|
||||
pkey, err := asymkey_model.SearchPublicKeyByContentExact(parentCtx, principal)
|
||||
pkey, err := asymkey_model.SearchPublicKeyByContentExact(ctx, principal)
|
||||
if err != nil {
|
||||
if asymkey_model.IsErrKeyNotExist(err) {
|
||||
log.Debug("Principal Rejected: %s Unknown Principal: %s", ctx.RemoteAddr(), principal)
|
||||
|
@ -257,7 +252,7 @@ func publicKeyHandler(ctx ssh.Context, key ssh.PublicKey) bool {
|
|||
log.Debug("Handle Public Key: %s Fingerprint: %s is not a certificate", ctx.RemoteAddr(), gossh.FingerprintSHA256(key))
|
||||
}
|
||||
|
||||
pkey, err := asymkey_model.SearchPublicKeyByContent(parentCtx, strings.TrimSpace(string(gossh.MarshalAuthorizedKey(key))))
|
||||
pkey, err := asymkey_model.SearchPublicKeyByContent(ctx, strings.TrimSpace(string(gossh.MarshalAuthorizedKey(key))))
|
||||
if err != nil {
|
||||
if asymkey_model.IsErrKeyNotExist(err) {
|
||||
log.Warn("Unknown public key: %s from %s", gossh.FingerprintSHA256(key), ctx.RemoteAddr())
|
||||
|
|
|
@ -16,6 +16,7 @@ type Package struct {
|
|||
Type string `json:"type"`
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
HTMLURL string `json:"html_url"`
|
||||
// swagger:strfmt date-time
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
|
|
|
@ -1786,6 +1786,8 @@ pulls.status_checks_failure = Some checks failed
|
|||
pulls.status_checks_error = Some checks reported errors
|
||||
pulls.status_checks_requested = Required
|
||||
pulls.status_checks_details = Details
|
||||
pulls.status_checks_hide_all = Hide all checks
|
||||
pulls.status_checks_show_all = Show all checks
|
||||
pulls.update_branch = Update branch by merge
|
||||
pulls.update_branch_rebase = Update branch by rebase
|
||||
pulls.update_branch_success = Branch update was successful
|
||||
|
|
|
@ -77,20 +77,24 @@ milestones=Míľniky
|
|||
|
||||
ok=OK
|
||||
cancel=Zrušiť
|
||||
retry=Opakovať
|
||||
save=Uložiť
|
||||
add=Pridať
|
||||
add_all=Pridať všetko
|
||||
remove=Odstrániť
|
||||
remove_all=Odstrániť všetko
|
||||
remove_label_str=Odstrániť položku „%s“
|
||||
edit=Upraviť
|
||||
|
||||
enabled=Povolené
|
||||
|
||||
copy=Kopírovať
|
||||
copy_url=Kopírovať URL
|
||||
copy_content=Kopírovať obsah
|
||||
copy_branch=Kopírovať meno vetvy
|
||||
copy_success=Skopírované!
|
||||
copy_error=Kopírovanie zlyhalo
|
||||
copy_type_unsupported=Tento typ súboru nie je možné skopírovať
|
||||
|
||||
write=Zapísať
|
||||
preview=Náhľad
|
||||
|
|
|
@ -58,7 +58,8 @@ func validateArtifactHash(ctx *ArtifactContext, artifactName string) bool {
|
|||
func parseArtifactItemPath(ctx *ArtifactContext) (string, string, bool) {
|
||||
// itemPath is generated from upload-artifact action
|
||||
// it's formatted as {artifact_name}/{artfict_path_in_runner}
|
||||
itemPath := util.PathJoinRel(ctx.Req.URL.Query().Get("itemPath"))
|
||||
// act_runner in host mode on Windows, itemPath is joined by Windows slash '\'
|
||||
itemPath := util.PathJoinRelX(ctx.Req.URL.Query().Get("itemPath"))
|
||||
artifactName := strings.Split(itemPath, "/")[0]
|
||||
artifactPath := strings.TrimPrefix(itemPath, artifactName+"/")
|
||||
if !validateArtifactHash(ctx, artifactName) {
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/httpcache"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
@ -35,20 +36,18 @@ func RenderPanicErrorPage(w http.ResponseWriter, req *http.Request, err any) {
|
|||
httpcache.SetCacheControlInHeader(w.Header(), 0, "no-transform")
|
||||
w.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions)
|
||||
|
||||
data := middleware.GetContextData(req.Context())
|
||||
if data["locale"] == nil {
|
||||
data = middleware.CommonTemplateContextData()
|
||||
data["locale"] = middleware.Locale(w, req)
|
||||
}
|
||||
tmplCtx := context.TemplateContext{}
|
||||
tmplCtx["Locale"] = middleware.Locale(w, req)
|
||||
ctxData := middleware.GetContextData(req.Context())
|
||||
|
||||
// This recovery handler could be called without Gitea's web context, so we shouldn't touch that context too much.
|
||||
// Otherwise, the 500-page may cause new panics, eg: cache.GetContextWithData, it makes the developer&users couldn't find the original panic.
|
||||
user, _ := data[middleware.ContextDataKeySignedUser].(*user_model.User)
|
||||
user, _ := ctxData[middleware.ContextDataKeySignedUser].(*user_model.User)
|
||||
if !setting.IsProd || (user != nil && user.IsAdmin) {
|
||||
data["ErrorMsg"] = "PANIC: " + combinedErr
|
||||
ctxData["ErrorMsg"] = "PANIC: " + combinedErr
|
||||
}
|
||||
|
||||
err = templates.HTMLRenderer().HTML(w, http.StatusInternalServerError, string(tplStatus500), data, nil)
|
||||
err = templates.HTMLRenderer().HTML(w, http.StatusInternalServerError, string(tplStatus500), ctxData, tmplCtx)
|
||||
if err != nil {
|
||||
log.Error("Error occurs again when rendering error page: %v", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
|
|
|
@ -26,6 +26,7 @@ func TestRenderPanicErrorPage(t *testing.T) {
|
|||
respContent := w.Body.String()
|
||||
assert.Contains(t, respContent, `class="page-content status-page-500"`)
|
||||
assert.Contains(t, respContent, `</html>`)
|
||||
assert.Contains(t, respContent, `lang="en-US"`) // make sure the locale work
|
||||
|
||||
// the 500 page doesn't have normal pages footer, it makes it easier to distinguish a normal page and a failed page.
|
||||
// especially when a sub-template causes page error, the HTTP response code is still 200,
|
||||
|
|
|
@ -48,13 +48,13 @@ func Authentications(ctx *context.Context) {
|
|||
ctx.Data["PageIsAdminAuthentications"] = true
|
||||
|
||||
var err error
|
||||
ctx.Data["Sources"], err = auth.Sources(ctx)
|
||||
ctx.Data["Sources"], err = auth.FindSources(ctx, auth.FindSourcesOptions{})
|
||||
if err != nil {
|
||||
ctx.ServerError("auth.Sources", err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Data["Total"] = auth.CountSources(ctx)
|
||||
ctx.Data["Total"] = auth.CountSources(ctx, auth.FindSourcesOptions{})
|
||||
ctx.HTML(http.StatusOK, tplAuths)
|
||||
}
|
||||
|
||||
|
@ -99,7 +99,7 @@ func NewAuthSource(ctx *context.Context) {
|
|||
ctx.Data["AuthSources"] = authSources
|
||||
ctx.Data["SecurityProtocols"] = securityProtocols
|
||||
ctx.Data["SMTPAuths"] = smtp.Authenticators
|
||||
oauth2providers := oauth2.GetOAuth2Providers()
|
||||
oauth2providers := oauth2.GetSupportedOAuth2Providers()
|
||||
ctx.Data["OAuth2Providers"] = oauth2providers
|
||||
|
||||
ctx.Data["SSPIAutoCreateUsers"] = true
|
||||
|
@ -242,7 +242,7 @@ func NewAuthSourcePost(ctx *context.Context) {
|
|||
ctx.Data["AuthSources"] = authSources
|
||||
ctx.Data["SecurityProtocols"] = securityProtocols
|
||||
ctx.Data["SMTPAuths"] = smtp.Authenticators
|
||||
oauth2providers := oauth2.GetOAuth2Providers()
|
||||
oauth2providers := oauth2.GetSupportedOAuth2Providers()
|
||||
ctx.Data["OAuth2Providers"] = oauth2providers
|
||||
|
||||
ctx.Data["SSPIAutoCreateUsers"] = true
|
||||
|
@ -284,7 +284,7 @@ func NewAuthSourcePost(ctx *context.Context) {
|
|||
ctx.RenderWithErr(err.Error(), tplAuthNew, form)
|
||||
return
|
||||
}
|
||||
existing, err := auth.SourcesByType(ctx, auth.SSPI)
|
||||
existing, err := auth.FindSources(ctx, auth.FindSourcesOptions{LoginType: auth.SSPI})
|
||||
if err != nil || len(existing) > 0 {
|
||||
ctx.Data["Err_Type"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("admin.auths.login_source_of_type_exist"), tplAuthNew, form)
|
||||
|
@ -334,7 +334,7 @@ func EditAuthSource(ctx *context.Context) {
|
|||
|
||||
ctx.Data["SecurityProtocols"] = securityProtocols
|
||||
ctx.Data["SMTPAuths"] = smtp.Authenticators
|
||||
oauth2providers := oauth2.GetOAuth2Providers()
|
||||
oauth2providers := oauth2.GetSupportedOAuth2Providers()
|
||||
ctx.Data["OAuth2Providers"] = oauth2providers
|
||||
|
||||
source, err := auth.GetSourceByID(ctx, ctx.ParamsInt64(":authid"))
|
||||
|
@ -368,7 +368,7 @@ func EditAuthSourcePost(ctx *context.Context) {
|
|||
ctx.Data["PageIsAdminAuthentications"] = true
|
||||
|
||||
ctx.Data["SMTPAuths"] = smtp.Authenticators
|
||||
oauth2providers := oauth2.GetOAuth2Providers()
|
||||
oauth2providers := oauth2.GetSupportedOAuth2Providers()
|
||||
ctx.Data["OAuth2Providers"] = oauth2providers
|
||||
|
||||
source, err := auth.GetSourceByID(ctx, ctx.ParamsInt64(":authid"))
|
||||
|
|
|
@ -90,7 +90,9 @@ func NewUser(ctx *context.Context) {
|
|||
|
||||
ctx.Data["login_type"] = "0-0"
|
||||
|
||||
sources, err := auth.Sources(ctx)
|
||||
sources, err := auth.FindSources(ctx, auth.FindSourcesOptions{
|
||||
IsActive: util.OptionalBoolTrue,
|
||||
})
|
||||
if err != nil {
|
||||
ctx.ServerError("auth.Sources", err)
|
||||
return
|
||||
|
@ -109,7 +111,9 @@ func NewUserPost(ctx *context.Context) {
|
|||
ctx.Data["DefaultUserVisibilityMode"] = setting.Service.DefaultUserVisibilityMode
|
||||
ctx.Data["AllowedUserVisibilityModes"] = setting.Service.AllowedUserVisibilityModesSlice.ToVisibleTypeSlice()
|
||||
|
||||
sources, err := auth.Sources(ctx)
|
||||
sources, err := auth.FindSources(ctx, auth.FindSourcesOptions{
|
||||
IsActive: util.OptionalBoolTrue,
|
||||
})
|
||||
if err != nil {
|
||||
ctx.ServerError("auth.Sources", err)
|
||||
return
|
||||
|
@ -230,7 +234,7 @@ func prepareUserInfo(ctx *context.Context) *user_model.User {
|
|||
ctx.Data["LoginSource"] = &auth.Source{}
|
||||
}
|
||||
|
||||
sources, err := auth.Sources(ctx)
|
||||
sources, err := auth.FindSources(ctx, auth.FindSourcesOptions{})
|
||||
if err != nil {
|
||||
ctx.ServerError("auth.Sources", err)
|
||||
return nil
|
||||
|
|
|
@ -160,12 +160,11 @@ func SignIn(ctx *context.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
orderedOAuth2Names, oauth2Providers, err := oauth2.GetActiveOAuth2Providers(ctx)
|
||||
oauth2Providers, err := oauth2.GetOAuth2Providers(ctx, util.OptionalBoolTrue)
|
||||
if err != nil {
|
||||
ctx.ServerError("UserSignIn", err)
|
||||
return
|
||||
}
|
||||
ctx.Data["OrderedOAuth2Names"] = orderedOAuth2Names
|
||||
ctx.Data["OAuth2Providers"] = oauth2Providers
|
||||
ctx.Data["Title"] = ctx.Tr("sign_in")
|
||||
ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login"
|
||||
|
@ -184,12 +183,11 @@ func SignIn(ctx *context.Context) {
|
|||
func SignInPost(ctx *context.Context) {
|
||||
ctx.Data["Title"] = ctx.Tr("sign_in")
|
||||
|
||||
orderedOAuth2Names, oauth2Providers, err := oauth2.GetActiveOAuth2Providers(ctx)
|
||||
oauth2Providers, err := oauth2.GetOAuth2Providers(ctx, util.OptionalBoolTrue)
|
||||
if err != nil {
|
||||
ctx.ServerError("UserSignIn", err)
|
||||
return
|
||||
}
|
||||
ctx.Data["OrderedOAuth2Names"] = orderedOAuth2Names
|
||||
ctx.Data["OAuth2Providers"] = oauth2Providers
|
||||
ctx.Data["Title"] = ctx.Tr("sign_in")
|
||||
ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login"
|
||||
|
@ -408,13 +406,12 @@ func SignUp(ctx *context.Context) {
|
|||
|
||||
ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/sign_up"
|
||||
|
||||
orderedOAuth2Names, oauth2Providers, err := oauth2.GetActiveOAuth2Providers(ctx)
|
||||
oauth2Providers, err := oauth2.GetOAuth2Providers(ctx, util.OptionalBoolTrue)
|
||||
if err != nil {
|
||||
ctx.ServerError("UserSignUp", err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Data["OrderedOAuth2Names"] = orderedOAuth2Names
|
||||
ctx.Data["OAuth2Providers"] = oauth2Providers
|
||||
context.SetCaptchaData(ctx)
|
||||
|
||||
|
@ -438,13 +435,12 @@ func SignUpPost(ctx *context.Context) {
|
|||
|
||||
ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/sign_up"
|
||||
|
||||
orderedOAuth2Names, oauth2Providers, err := oauth2.GetActiveOAuth2Providers(ctx)
|
||||
oauth2Providers, err := oauth2.GetOAuth2Providers(ctx, util.OptionalBoolTrue)
|
||||
if err != nil {
|
||||
ctx.ServerError("UserSignUp", err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Data["OrderedOAuth2Names"] = orderedOAuth2Names
|
||||
ctx.Data["OAuth2Providers"] = oauth2Providers
|
||||
context.SetCaptchaData(ctx)
|
||||
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package web
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/web"
|
||||
"code.gitea.io/gitea/routers/web/repo"
|
||||
context_service "code.gitea.io/gitea/services/context"
|
||||
)
|
||||
|
||||
func requireSignIn(ctx *context.Context) {
|
||||
if !setting.Service.RequireSignInView {
|
||||
return
|
||||
}
|
||||
|
||||
// rely on the results of Contexter
|
||||
if !ctx.IsSigned {
|
||||
// TODO: support digit auth - which would be Authorization header with digit
|
||||
ctx.Resp.Header().Set("WWW-Authenticate", `Basic realm="Gitea"`)
|
||||
ctx.Error(http.StatusUnauthorized)
|
||||
}
|
||||
}
|
||||
|
||||
func gitHTTPRouters(m *web.Route) {
|
||||
m.Group("", func() {
|
||||
m.PostOptions("/git-upload-pack", repo.ServiceUploadPack)
|
||||
m.PostOptions("/git-receive-pack", repo.ServiceReceivePack)
|
||||
m.GetOptions("/info/refs", repo.GetInfoRefs)
|
||||
m.GetOptions("/HEAD", repo.GetTextFile("HEAD"))
|
||||
m.GetOptions("/objects/info/alternates", repo.GetTextFile("objects/info/alternates"))
|
||||
m.GetOptions("/objects/info/http-alternates", repo.GetTextFile("objects/info/http-alternates"))
|
||||
m.GetOptions("/objects/info/packs", repo.GetInfoPacks)
|
||||
m.GetOptions("/objects/info/{file:[^/]*}", repo.GetTextFile(""))
|
||||
m.GetOptions("/objects/{head:[0-9a-f]{2}}/{hash:[0-9a-f]{38}}", repo.GetLooseObject)
|
||||
m.GetOptions("/objects/pack/pack-{file:[0-9a-f]{40}}.pack", repo.GetPackFile)
|
||||
m.GetOptions("/objects/pack/pack-{file:[0-9a-f]{40}}.idx", repo.GetIdxFile)
|
||||
}, ignSignInAndCsrf, requireSignIn, repo.HTTPGitEnabledHandler, repo.CorsHandler(), context_service.UserAssignmentWeb())
|
||||
}
|
|
@ -1773,7 +1773,7 @@ func ViewIssue(ctx *context.Context) {
|
|||
pull := issue.PullRequest
|
||||
pull.Issue = issue
|
||||
canDelete := false
|
||||
ctx.Data["AllowMerge"] = false
|
||||
allowMerge := false
|
||||
|
||||
if ctx.IsSigned {
|
||||
if err := pull.LoadHeadRepo(ctx); err != nil {
|
||||
|
@ -1806,7 +1806,7 @@ func ViewIssue(ctx *context.Context) {
|
|||
ctx.ServerError("GetUserRepoPermission", err)
|
||||
return
|
||||
}
|
||||
ctx.Data["AllowMerge"], err = pull_service.IsUserAllowedToMerge(ctx, pull, perm, ctx.Doer)
|
||||
allowMerge, err = pull_service.IsUserAllowedToMerge(ctx, pull, perm, ctx.Doer)
|
||||
if err != nil {
|
||||
ctx.ServerError("IsUserAllowedToMerge", err)
|
||||
return
|
||||
|
@ -1818,6 +1818,8 @@ func ViewIssue(ctx *context.Context) {
|
|||
}
|
||||
}
|
||||
|
||||
ctx.Data["AllowMerge"] = allowMerge
|
||||
|
||||
prUnit, err := repo.GetUnit(ctx, unit.TypePullRequests)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetUnit", err)
|
||||
|
@ -1927,7 +1929,7 @@ func ViewIssue(ctx *context.Context) {
|
|||
if pull.CanAutoMerge() || pull.IsWorkInProgress(ctx) || pull.IsChecking() {
|
||||
return false
|
||||
}
|
||||
if (ctx.Doer.IsAdmin || ctx.Repo.IsAdmin()) && prConfig.AllowManualMerge {
|
||||
if allowMerge && prConfig.AllowManualMerge {
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
|
@ -6,12 +6,14 @@ package security
|
|||
|
||||
import (
|
||||
"net/http"
|
||||
"sort"
|
||||
|
||||
auth_model "code.gitea.io/gitea/models/auth"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/services/auth/source/oauth2"
|
||||
)
|
||||
|
||||
|
@ -82,7 +84,7 @@ func loadSecurityData(ctx *context.Context) {
|
|||
// map the provider display name with the AuthSource
|
||||
sources := make(map[*auth_model.Source]string)
|
||||
for _, externalAccount := range accountLinks {
|
||||
if authSource, err := auth_model.GetSourceByID(ctx, externalAccount.LoginSourceID); err == nil {
|
||||
if authSource, err := auth_model.GetSourceByID(ctx, externalAccount.LoginSourceID); err == nil && authSource.IsActive {
|
||||
var providerDisplayName string
|
||||
|
||||
type DisplayNamed interface {
|
||||
|
@ -105,11 +107,31 @@ func loadSecurityData(ctx *context.Context) {
|
|||
}
|
||||
ctx.Data["AccountLinks"] = sources
|
||||
|
||||
orderedOAuth2Names, oauth2Providers, err := oauth2.GetActiveOAuth2Providers(ctx)
|
||||
authSources, err := auth_model.FindSources(ctx, auth_model.FindSourcesOptions{
|
||||
IsActive: util.OptionalBoolNone,
|
||||
LoginType: auth_model.OAuth2,
|
||||
})
|
||||
if err != nil {
|
||||
ctx.ServerError("GetActiveOAuth2Providers", err)
|
||||
ctx.ServerError("FindSources", err)
|
||||
return
|
||||
}
|
||||
|
||||
var orderedOAuth2Names []string
|
||||
oauth2Providers := make(map[string]oauth2.Provider)
|
||||
for _, source := range authSources {
|
||||
provider, err := oauth2.CreateProviderFromSource(source)
|
||||
if err != nil {
|
||||
ctx.ServerError("CreateProviderFromSource", err)
|
||||
return
|
||||
}
|
||||
oauth2Providers[source.Name] = provider
|
||||
if source.IsActive {
|
||||
orderedOAuth2Names = append(orderedOAuth2Names, source.Name)
|
||||
}
|
||||
}
|
||||
|
||||
sort.Strings(orderedOAuth2Names)
|
||||
|
||||
ctx.Data["OrderedOAuth2Names"] = orderedOAuth2Names
|
||||
ctx.Data["OAuth2Providers"] = oauth2Providers
|
||||
|
||||
|
|
|
@ -276,6 +276,8 @@ func Routes() *web.Route {
|
|||
return routes
|
||||
}
|
||||
|
||||
var ignSignInAndCsrf = verifyAuthWithOptions(&common.VerifyOptions{DisableCSRF: true})
|
||||
|
||||
// registerRoutes register routes
|
||||
func registerRoutes(m *web.Route) {
|
||||
reqSignIn := verifyAuthWithOptions(&common.VerifyOptions{SignInRequired: true})
|
||||
|
@ -283,7 +285,7 @@ func registerRoutes(m *web.Route) {
|
|||
// TODO: rename them to "optSignIn", which means that the "sign-in" could be optional, depends on the VerifyOptions (RequireSignInView)
|
||||
ignSignIn := verifyAuthWithOptions(&common.VerifyOptions{SignInRequired: setting.Service.RequireSignInView})
|
||||
ignExploreSignIn := verifyAuthWithOptions(&common.VerifyOptions{SignInRequired: setting.Service.RequireSignInView || setting.Service.Explore.RequireSigninView})
|
||||
ignSignInAndCsrf := verifyAuthWithOptions(&common.VerifyOptions{DisableCSRF: true})
|
||||
|
||||
validation.AddBindingRules()
|
||||
|
||||
linkAccountEnabled := func(ctx *context.Context) {
|
||||
|
@ -1512,19 +1514,7 @@ func registerRoutes(m *web.Route) {
|
|||
})
|
||||
}, ignSignInAndCsrf, lfsServerEnabled)
|
||||
|
||||
m.Group("", func() {
|
||||
m.PostOptions("/git-upload-pack", repo.ServiceUploadPack)
|
||||
m.PostOptions("/git-receive-pack", repo.ServiceReceivePack)
|
||||
m.GetOptions("/info/refs", repo.GetInfoRefs)
|
||||
m.GetOptions("/HEAD", repo.GetTextFile("HEAD"))
|
||||
m.GetOptions("/objects/info/alternates", repo.GetTextFile("objects/info/alternates"))
|
||||
m.GetOptions("/objects/info/http-alternates", repo.GetTextFile("objects/info/http-alternates"))
|
||||
m.GetOptions("/objects/info/packs", repo.GetInfoPacks)
|
||||
m.GetOptions("/objects/info/{file:[^/]*}", repo.GetTextFile(""))
|
||||
m.GetOptions("/objects/{head:[0-9a-f]{2}}/{hash:[0-9a-f]{38}}", repo.GetLooseObject)
|
||||
m.GetOptions("/objects/pack/pack-{file:[0-9a-f]{40}}.pack", repo.GetPackFile)
|
||||
m.GetOptions("/objects/pack/pack-{file:[0-9a-f]{40}}.idx", repo.GetIdxFile)
|
||||
}, ignSignInAndCsrf, repo.HTTPGitEnabledHandler, repo.CorsHandler(), context_service.UserAssignmentWeb())
|
||||
gitHTTPRouters(m)
|
||||
})
|
||||
})
|
||||
// ***** END: Repository *****
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"code.gitea.io/gitea/models/db"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/services/auth/source/oauth2"
|
||||
"code.gitea.io/gitea/services/auth/source/smtp"
|
||||
|
||||
|
@ -85,7 +86,9 @@ func UserSignIn(ctx context.Context, username, password string) (*user_model.Use
|
|||
}
|
||||
}
|
||||
|
||||
sources, err := auth.AllActiveSources(ctx)
|
||||
sources, err := auth.FindSources(ctx, auth.FindSourcesOptions{
|
||||
IsActive: util.OptionalBoolTrue,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/gorilla/sessions"
|
||||
|
@ -63,7 +64,13 @@ func ResetOAuth2(ctx context.Context) error {
|
|||
|
||||
// initOAuth2Sources is used to load and register all active OAuth2 providers
|
||||
func initOAuth2Sources(ctx context.Context) error {
|
||||
authSources, _ := auth.GetActiveOAuth2ProviderSources(ctx)
|
||||
authSources, err := auth.FindSources(ctx, auth.FindSourcesOptions{
|
||||
IsActive: util.OptionalBoolTrue,
|
||||
LoginType: auth.OAuth2,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, source := range authSources {
|
||||
oauth2Source, ok := source.Cfg.(*Source)
|
||||
if !ok {
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
"code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/markbates/goth"
|
||||
)
|
||||
|
@ -80,10 +81,10 @@ func RegisterGothProvider(provider GothProvider) {
|
|||
gothProviders[provider.Name()] = provider
|
||||
}
|
||||
|
||||
// GetOAuth2Providers returns the map of unconfigured OAuth2 providers
|
||||
// GetSupportedOAuth2Providers returns the map of unconfigured OAuth2 providers
|
||||
// key is used as technical name (like in the callbackURL)
|
||||
// values to display
|
||||
func GetOAuth2Providers() []Provider {
|
||||
func GetSupportedOAuth2Providers() []Provider {
|
||||
providers := make([]Provider, 0, len(gothProviders))
|
||||
|
||||
for _, provider := range gothProviders {
|
||||
|
@ -95,33 +96,39 @@ func GetOAuth2Providers() []Provider {
|
|||
return providers
|
||||
}
|
||||
|
||||
// GetActiveOAuth2Providers returns the map of configured active OAuth2 providers
|
||||
// key is used as technical name (like in the callbackURL)
|
||||
// values to display
|
||||
func GetActiveOAuth2Providers(ctx context.Context) ([]string, map[string]Provider, error) {
|
||||
// Maybe also separate used and unused providers so we can force the registration of only 1 active provider for each type
|
||||
func CreateProviderFromSource(source *auth.Source) (Provider, error) {
|
||||
oauth2Cfg, ok := source.Cfg.(*Source)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid OAuth2 source config: %v", oauth2Cfg)
|
||||
}
|
||||
gothProv := gothProviders[oauth2Cfg.Provider]
|
||||
return &AuthSourceProvider{GothProvider: gothProv, sourceName: source.Name, iconURL: oauth2Cfg.IconURL}, nil
|
||||
}
|
||||
|
||||
authSources, err := auth.GetActiveOAuth2ProviderSources(ctx)
|
||||
// GetOAuth2Providers returns the list of configured OAuth2 providers
|
||||
func GetOAuth2Providers(ctx context.Context, isActive util.OptionalBool) ([]Provider, error) {
|
||||
authSources, err := auth.FindSources(ctx, auth.FindSourcesOptions{
|
||||
IsActive: isActive,
|
||||
LoginType: auth.OAuth2,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var orderedKeys []string
|
||||
providers := make(map[string]Provider)
|
||||
providers := make([]Provider, 0, len(authSources))
|
||||
for _, source := range authSources {
|
||||
oauth2Cfg, ok := source.Cfg.(*Source)
|
||||
if !ok {
|
||||
log.Error("Invalid OAuth2 source config: %v", oauth2Cfg)
|
||||
continue
|
||||
provider, err := CreateProviderFromSource(source)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gothProv := gothProviders[oauth2Cfg.Provider]
|
||||
providers[source.Name] = &AuthSourceProvider{GothProvider: gothProv, sourceName: source.Name, iconURL: oauth2Cfg.IconURL}
|
||||
orderedKeys = append(orderedKeys, source.Name)
|
||||
providers = append(providers, provider)
|
||||
}
|
||||
|
||||
sort.Strings(orderedKeys)
|
||||
sort.Slice(providers, func(i, j int) bool {
|
||||
return providers[i].Name() < providers[j].Name()
|
||||
})
|
||||
|
||||
return orderedKeys, providers, nil
|
||||
return providers, nil
|
||||
}
|
||||
|
||||
// RegisterProviderWithGothic register a OAuth2 provider in goth lib
|
||||
|
|
|
@ -130,7 +130,10 @@ func (s *SSPI) Verify(req *http.Request, w http.ResponseWriter, store DataStore,
|
|||
|
||||
// getConfig retrieves the SSPI configuration from login sources
|
||||
func (s *SSPI) getConfig(ctx context.Context) (*sspi.Source, error) {
|
||||
sources, err := auth.ActiveSources(ctx, auth.SSPI)
|
||||
sources, err := auth.FindSources(ctx, auth.FindSourcesOptions{
|
||||
IsActive: util.OptionalBoolTrue,
|
||||
LoginType: auth.SSPI,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ import (
|
|||
func SyncExternalUsers(ctx context.Context, updateExisting bool) error {
|
||||
log.Trace("Doing: SyncExternalUsers")
|
||||
|
||||
ls, err := auth.Sources(ctx)
|
||||
ls, err := auth.FindSources(ctx, auth.FindSourcesOptions{})
|
||||
if err != nil {
|
||||
log.Error("SyncExternalUsers: %v", err)
|
||||
return err
|
||||
|
|
|
@ -35,6 +35,7 @@ func ToPackage(ctx context.Context, pd *packages.PackageDescriptor, doer *user_m
|
|||
Name: pd.Package.Name,
|
||||
Version: pd.Version.Version,
|
||||
CreatedAt: pd.Version.CreatedUnix.AsTime(),
|
||||
HTMLURL: pd.FullWebLink(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,6 @@ import (
|
|||
"code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/markup/markdown"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/templates"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/translation"
|
||||
incoming_payload "code.gitea.io/gitea/services/mailer/incoming/payload"
|
||||
|
@ -68,15 +67,12 @@ func SendTestMail(email string) error {
|
|||
func sendUserMail(language string, u *user_model.User, tpl base.TplName, code, subject, info string) {
|
||||
locale := translation.NewLocale(language)
|
||||
data := map[string]any{
|
||||
"locale": locale,
|
||||
"DisplayName": u.DisplayName(),
|
||||
"ActiveCodeLives": timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, locale),
|
||||
"ResetPwdCodeLives": timeutil.MinutesToFriendly(setting.Service.ResetPwdCodeLives, locale),
|
||||
"Code": code,
|
||||
"Language": locale.Language(),
|
||||
// helper
|
||||
"locale": locale,
|
||||
"Str2html": templates.Str2html,
|
||||
"DotEscape": templates.DotEscape,
|
||||
}
|
||||
|
||||
var content bytes.Buffer
|
||||
|
@ -119,15 +115,12 @@ func SendActivateEmailMail(u *user_model.User, email *user_model.EmailAddress) {
|
|||
}
|
||||
locale := translation.NewLocale(u.Language)
|
||||
data := map[string]any{
|
||||
"locale": locale,
|
||||
"DisplayName": u.DisplayName(),
|
||||
"ActiveCodeLives": timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, locale),
|
||||
"Code": u.GenerateEmailActivateCode(email.Email),
|
||||
"Email": email.Email,
|
||||
"Language": locale.Language(),
|
||||
// helper
|
||||
"locale": locale,
|
||||
"Str2html": templates.Str2html,
|
||||
"DotEscape": templates.DotEscape,
|
||||
}
|
||||
|
||||
var content bytes.Buffer
|
||||
|
@ -152,13 +145,10 @@ func SendRegisterNotifyMail(u *user_model.User) {
|
|||
locale := translation.NewLocale(u.Language)
|
||||
|
||||
data := map[string]any{
|
||||
"locale": locale,
|
||||
"DisplayName": u.DisplayName(),
|
||||
"Username": u.Name,
|
||||
"Language": locale.Language(),
|
||||
// helper
|
||||
"locale": locale,
|
||||
"Str2html": templates.Str2html,
|
||||
"DotEscape": templates.DotEscape,
|
||||
}
|
||||
|
||||
var content bytes.Buffer
|
||||
|
@ -185,14 +175,11 @@ func SendCollaboratorMail(u, doer *user_model.User, repo *repo_model.Repository)
|
|||
|
||||
subject := locale.Tr("mail.repo.collaborator.added.subject", doer.DisplayName(), repoName)
|
||||
data := map[string]any{
|
||||
"locale": locale,
|
||||
"Subject": subject,
|
||||
"RepoName": repoName,
|
||||
"Link": repo.HTMLURL(),
|
||||
"Language": locale.Language(),
|
||||
// helper
|
||||
"locale": locale,
|
||||
"Str2html": templates.Str2html,
|
||||
"DotEscape": templates.DotEscape,
|
||||
}
|
||||
|
||||
var content bytes.Buffer
|
||||
|
@ -259,6 +246,7 @@ func composeIssueCommentMessages(ctx *mailCommentContext, lang string, recipient
|
|||
locale := translation.NewLocale(lang)
|
||||
|
||||
mailMeta := map[string]any{
|
||||
"locale": locale,
|
||||
"FallbackSubject": fallback,
|
||||
"Body": body,
|
||||
"Link": link,
|
||||
|
@ -275,10 +263,6 @@ func composeIssueCommentMessages(ctx *mailCommentContext, lang string, recipient
|
|||
"ReviewComments": reviewComments,
|
||||
"Language": locale.Language(),
|
||||
"CanReply": setting.IncomingEmail.Enabled && commentType != issues_model.CommentTypePullRequestPush,
|
||||
// helper
|
||||
"locale": locale,
|
||||
"Str2html": templates.Str2html,
|
||||
"DotEscape": templates.DotEscape,
|
||||
}
|
||||
|
||||
var mailSubject bytes.Buffer
|
||||
|
@ -469,7 +453,7 @@ func SendIssueAssignedMail(ctx context.Context, issue *issues_model.Issue, doer
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
SendAsyncs(msgs)
|
||||
SendAsync(msgs...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -162,7 +162,7 @@ func mailIssueCommentBatch(ctx *mailCommentContext, users []*user_model.User, vi
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
SendAsyncs(msgs)
|
||||
SendAsync(msgs...)
|
||||
receivers = receivers[:i]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@ import (
|
|||
"code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/markup/markdown"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/templates"
|
||||
"code.gitea.io/gitea/modules/translation"
|
||||
)
|
||||
|
||||
|
@ -69,13 +68,10 @@ func mailNewRelease(ctx context.Context, lang string, tos []string, rel *repo_mo
|
|||
|
||||
subject := locale.Tr("mail.release.new.subject", rel.TagName, rel.Repo.FullName())
|
||||
mailMeta := map[string]any{
|
||||
"locale": locale,
|
||||
"Release": rel,
|
||||
"Subject": subject,
|
||||
"Language": locale.Language(),
|
||||
// helper
|
||||
"locale": locale,
|
||||
"Str2html": templates.Str2html,
|
||||
"DotEscape": templates.DotEscape,
|
||||
}
|
||||
|
||||
var mailBody bytes.Buffer
|
||||
|
@ -95,5 +91,5 @@ func mailNewRelease(ctx context.Context, lang string, tos []string, rel *repo_mo
|
|||
msgs = append(msgs, msg)
|
||||
}
|
||||
|
||||
SendAsyncs(msgs)
|
||||
SendAsync(msgs...)
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@ import (
|
|||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/templates"
|
||||
"code.gitea.io/gitea/modules/translation"
|
||||
)
|
||||
|
||||
|
@ -65,6 +64,7 @@ func sendRepoTransferNotifyMailPerLang(lang string, newOwner, doer *user_model.U
|
|||
}
|
||||
|
||||
data := map[string]any{
|
||||
"locale": locale,
|
||||
"Doer": doer,
|
||||
"User": repo.Owner,
|
||||
"Repo": repo.FullName(),
|
||||
|
@ -72,10 +72,6 @@ func sendRepoTransferNotifyMailPerLang(lang string, newOwner, doer *user_model.U
|
|||
"Subject": subject,
|
||||
"Language": locale.Language(),
|
||||
"Destination": destination,
|
||||
// helper
|
||||
"locale": locale,
|
||||
"Str2html": templates.Str2html,
|
||||
"DotEscape": templates.DotEscape,
|
||||
}
|
||||
|
||||
if err := bodyTemplates.ExecuteTemplate(&content, string(mailRepoTransferNotify), data); err != nil {
|
||||
|
|
|
@ -14,7 +14,6 @@ import (
|
|||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/templates"
|
||||
"code.gitea.io/gitea/modules/translation"
|
||||
)
|
||||
|
||||
|
@ -53,16 +52,13 @@ func MailTeamInvite(ctx context.Context, inviter *user_model.User, team *org_mod
|
|||
|
||||
subject := locale.Tr("mail.team_invite.subject", inviter.DisplayName(), org.DisplayName())
|
||||
mailMeta := map[string]any{
|
||||
"locale": locale,
|
||||
"Inviter": inviter,
|
||||
"Organization": org,
|
||||
"Team": team,
|
||||
"Invite": invite,
|
||||
"Subject": subject,
|
||||
"InviteURL": inviteURL,
|
||||
// helper
|
||||
"locale": locale,
|
||||
"Str2html": templates.Str2html,
|
||||
"DotEscape": templates.DotEscape,
|
||||
}
|
||||
|
||||
var mailBody bytes.Buffer
|
||||
|
|
|
@ -425,15 +425,12 @@ func NewContext(ctx context.Context) {
|
|||
go graceful.GetManager().RunWithCancel(mailQueue)
|
||||
}
|
||||
|
||||
// SendAsync send mail asynchronously
|
||||
func SendAsync(msg *Message) {
|
||||
SendAsyncs([]*Message{msg})
|
||||
}
|
||||
// SendAsync send emails asynchronously (make it mockable)
|
||||
var SendAsync = sendAsync
|
||||
|
||||
// SendAsyncs send mails asynchronously
|
||||
func SendAsyncs(msgs []*Message) {
|
||||
func sendAsync(msgs ...*Message) {
|
||||
if setting.MailService == nil {
|
||||
log.Error("Mailer: SendAsyncs is being invoked but mail service hasn't been initialized")
|
||||
log.Error("Mailer: SendAsync is being invoked but mail service hasn't been initialized")
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -282,6 +282,8 @@ func (g *GiteaDownloader) convertGiteaRelease(rel *gitea_sdk.Release) *base.Rele
|
|||
httpClient := NewMigrationHTTPClient()
|
||||
|
||||
for _, asset := range rel.Attachments {
|
||||
assetID := asset.ID // Don't optimize this, for closure we need a local variable
|
||||
assetDownloadURL := asset.DownloadURL
|
||||
size := int(asset.Size)
|
||||
dlCount := int(asset.DownloadCount)
|
||||
r.Assets = append(r.Assets, &base.ReleaseAsset{
|
||||
|
@ -292,18 +294,18 @@ func (g *GiteaDownloader) convertGiteaRelease(rel *gitea_sdk.Release) *base.Rele
|
|||
Created: asset.Created,
|
||||
DownloadURL: &asset.DownloadURL,
|
||||
DownloadFunc: func() (io.ReadCloser, error) {
|
||||
asset, _, err := g.client.GetReleaseAttachment(g.repoOwner, g.repoName, rel.ID, asset.ID)
|
||||
asset, _, err := g.client.GetReleaseAttachment(g.repoOwner, g.repoName, rel.ID, assetID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !hasBaseURL(asset.DownloadURL, g.baseURL) {
|
||||
WarnAndNotice("Unexpected AssetURL for assetID[%d] in %s: %s", asset.ID, g, asset.DownloadURL)
|
||||
if !hasBaseURL(assetDownloadURL, g.baseURL) {
|
||||
WarnAndNotice("Unexpected AssetURL for assetID[%d] in %s: %s", assetID, g, assetDownloadURL)
|
||||
return io.NopCloser(strings.NewReader(asset.DownloadURL)), nil
|
||||
}
|
||||
|
||||
// FIXME: for a private download?
|
||||
req, err := http.NewRequest("GET", asset.DownloadURL, nil)
|
||||
req, err := http.NewRequest("GET", assetDownloadURL, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -310,6 +310,7 @@ func (g *GitlabDownloader) convertGitlabRelease(rel *gitlab.Release) *base.Relea
|
|||
httpClient := NewMigrationHTTPClient()
|
||||
|
||||
for k, asset := range rel.Assets.Links {
|
||||
assetID := asset.ID // Don't optimize this, for closure we need a local variable
|
||||
r.Assets = append(r.Assets, &base.ReleaseAsset{
|
||||
ID: int64(asset.ID),
|
||||
Name: asset.Name,
|
||||
|
@ -317,13 +318,13 @@ func (g *GitlabDownloader) convertGitlabRelease(rel *gitlab.Release) *base.Relea
|
|||
Size: &zero,
|
||||
DownloadCount: &zero,
|
||||
DownloadFunc: func() (io.ReadCloser, error) {
|
||||
link, _, err := g.client.ReleaseLinks.GetReleaseLink(g.repoID, rel.TagName, asset.ID, gitlab.WithContext(g.ctx))
|
||||
link, _, err := g.client.ReleaseLinks.GetReleaseLink(g.repoID, rel.TagName, assetID, gitlab.WithContext(g.ctx))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !hasBaseURL(link.URL, g.baseURL) {
|
||||
WarnAndNotice("Unexpected AssetURL for assetID[%d] in %s: %s", asset.ID, g, link.URL)
|
||||
WarnAndNotice("Unexpected AssetURL for assetID[%d] in %s: %s", assetID, g, link.URL)
|
||||
return io.NopCloser(strings.NewReader(link.URL)), nil
|
||||
}
|
||||
|
||||
|
|
|
@ -173,6 +173,12 @@ func (d *DingtalkPayload) Release(p *api.ReleasePayload) (api.Payloader, error)
|
|||
return createDingtalkPayload(text, text, "view release", p.Release.HTMLURL), nil
|
||||
}
|
||||
|
||||
func (d *DingtalkPayload) Package(p *api.PackagePayload) (api.Payloader, error) {
|
||||
text, _ := getPackagePayloadInfo(p, noneLinkFormatter, true)
|
||||
|
||||
return createDingtalkPayload(text, text, "view package", p.Package.HTMLURL), nil
|
||||
}
|
||||
|
||||
func createDingtalkPayload(title, text, singleTitle, singleURL string) *DingtalkPayload {
|
||||
return &DingtalkPayload{
|
||||
MsgType: "actionCard",
|
||||
|
|
|
@ -256,6 +256,12 @@ func (d *DiscordPayload) Release(p *api.ReleasePayload) (api.Payloader, error) {
|
|||
return d.createPayload(p.Sender, text, p.Release.Note, p.Release.HTMLURL, color), nil
|
||||
}
|
||||
|
||||
func (d *DiscordPayload) Package(p *api.PackagePayload) (api.Payloader, error) {
|
||||
text, color := getPackagePayloadInfo(p, noneLinkFormatter, false)
|
||||
|
||||
return d.createPayload(p.Sender, text, "", p.Package.HTMLURL, color), nil
|
||||
}
|
||||
|
||||
// GetDiscordPayload converts a discord webhook into a DiscordPayload
|
||||
func GetDiscordPayload(p api.Payloader, event webhook_module.HookEventType, meta string) (api.Payloader, error) {
|
||||
s := new(DiscordPayload)
|
||||
|
|
|
@ -16,7 +16,7 @@ import (
|
|||
type (
|
||||
// FeishuPayload represents
|
||||
FeishuPayload struct {
|
||||
MsgType string `json:"msg_type"` // text / post / image / share_chat / interactive
|
||||
MsgType string `json:"msg_type"` // text / post / image / share_chat / interactive / file /audio / media
|
||||
Content struct {
|
||||
Text string `json:"text"`
|
||||
} `json:"content"`
|
||||
|
@ -175,6 +175,12 @@ func (f *FeishuPayload) Release(p *api.ReleasePayload) (api.Payloader, error) {
|
|||
return newFeishuTextPayload(text), nil
|
||||
}
|
||||
|
||||
func (f *FeishuPayload) Package(p *api.PackagePayload) (api.Payloader, error) {
|
||||
text, _ := getPackagePayloadInfo(p, noneLinkFormatter, true)
|
||||
|
||||
return newFeishuTextPayload(text), nil
|
||||
}
|
||||
|
||||
// GetFeishuPayload converts a ding talk webhook into a FeishuPayload
|
||||
func GetFeishuPayload(p api.Payloader, event webhook_module.HookEventType, _ string) (api.Payloader, error) {
|
||||
return convertPayloader(new(FeishuPayload), p, event)
|
||||
|
|
|
@ -293,6 +293,24 @@ func getIssueCommentPayloadInfo(p *api.IssueCommentPayload, linkFormatter linkFo
|
|||
return text, issueTitle, color
|
||||
}
|
||||
|
||||
func getPackagePayloadInfo(p *api.PackagePayload, linkFormatter linkFormatter, withSender bool) (text string, color int) {
|
||||
refLink := linkFormatter(p.Package.HTMLURL, p.Package.Name+":"+p.Package.Version)
|
||||
|
||||
switch p.Action {
|
||||
case api.HookPackageCreated:
|
||||
text = fmt.Sprintf("Package created: %s", refLink)
|
||||
color = greenColor
|
||||
case api.HookPackageDeleted:
|
||||
text = fmt.Sprintf("Package deleted: %s", refLink)
|
||||
color = redColor
|
||||
}
|
||||
if withSender {
|
||||
text += fmt.Sprintf(" by %s", linkFormatter(setting.AppURL+url.PathEscape(p.Sender.UserName), p.Sender.UserName))
|
||||
}
|
||||
|
||||
return text, color
|
||||
}
|
||||
|
||||
// ToHook convert models.Webhook to api.Hook
|
||||
// This function is not part of the convert package to prevent an import cycle
|
||||
func ToHook(repoLink string, w *webhook_model.Webhook) (*api.Hook, error) {
|
||||
|
|
|
@ -210,6 +210,21 @@ func (m *MatrixPayload) Repository(p *api.RepositoryPayload) (api.Payloader, err
|
|||
return getMatrixPayload(text, nil, m.MsgType), nil
|
||||
}
|
||||
|
||||
func (m *MatrixPayload) Package(p *api.PackagePayload) (api.Payloader, error) {
|
||||
senderLink := MatrixLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
|
||||
repoLink := MatrixLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
|
||||
var text string
|
||||
|
||||
switch p.Action {
|
||||
case api.HookPackageCreated:
|
||||
text = fmt.Sprintf("[%s] Package published by %s", repoLink, senderLink)
|
||||
case api.HookPackageDeleted:
|
||||
text = fmt.Sprintf("[%s] Package deleted by %s", repoLink, senderLink)
|
||||
}
|
||||
|
||||
return getMatrixPayload(text, nil, m.MsgType), nil
|
||||
}
|
||||
|
||||
// GetMatrixPayload converts a Matrix webhook into a MatrixPayload
|
||||
func GetMatrixPayload(p api.Payloader, event webhook_module.HookEventType, meta string) (api.Payloader, error) {
|
||||
s := new(MatrixPayload)
|
||||
|
|
|
@ -296,6 +296,20 @@ func (m *MSTeamsPayload) Release(p *api.ReleasePayload) (api.Payloader, error) {
|
|||
), nil
|
||||
}
|
||||
|
||||
func (m *MSTeamsPayload) Package(p *api.PackagePayload) (api.Payloader, error) {
|
||||
title, color := getPackagePayloadInfo(p, noneLinkFormatter, false)
|
||||
|
||||
return createMSTeamsPayload(
|
||||
p.Repository,
|
||||
p.Sender,
|
||||
title,
|
||||
"",
|
||||
p.Package.HTMLURL,
|
||||
color,
|
||||
&MSTeamsFact{"Package:", p.Package.Name},
|
||||
), nil
|
||||
}
|
||||
|
||||
// GetMSTeamsPayload converts a MSTeams webhook into a MSTeamsPayload
|
||||
func GetMSTeamsPayload(p api.Payloader, event webhook_module.HookEventType, _ string) (api.Payloader, error) {
|
||||
return convertPayloader(new(MSTeamsPayload), p, event)
|
||||
|
|
|
@ -104,6 +104,10 @@ func (f *PackagistPayload) Release(_ *api.ReleasePayload) (api.Payloader, error)
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func (f *PackagistPayload) Package(_ *api.PackagePayload) (api.Payloader, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// GetPackagistPayload converts a packagist webhook into a PackagistPayload
|
||||
func GetPackagistPayload(p api.Payloader, event webhook_module.HookEventType, meta string) (api.Payloader, error) {
|
||||
s := new(PackagistPayload)
|
||||
|
|
|
@ -22,6 +22,7 @@ type PayloadConvertor interface {
|
|||
Repository(*api.RepositoryPayload) (api.Payloader, error)
|
||||
Release(*api.ReleasePayload) (api.Payloader, error)
|
||||
Wiki(*api.WikiPayload) (api.Payloader, error)
|
||||
Package(*api.PackagePayload) (api.Payloader, error)
|
||||
}
|
||||
|
||||
func convertPayloader(s PayloadConvertor, p api.Payloader, event webhook_module.HookEventType) (api.Payloader, error) {
|
||||
|
@ -53,6 +54,8 @@ func convertPayloader(s PayloadConvertor, p api.Payloader, event webhook_module.
|
|||
return s.Release(p.(*api.ReleasePayload))
|
||||
case webhook_module.HookEventWiki:
|
||||
return s.Wiki(p.(*api.WikiPayload))
|
||||
case webhook_module.HookEventPackage:
|
||||
return s.Package(p.(*api.PackagePayload))
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
|
|
@ -171,6 +171,12 @@ func (s *SlackPayload) Release(p *api.ReleasePayload) (api.Payloader, error) {
|
|||
return s.createPayload(text, nil), nil
|
||||
}
|
||||
|
||||
func (s *SlackPayload) Package(p *api.PackagePayload) (api.Payloader, error) {
|
||||
text, _ := getPackagePayloadInfo(p, SlackLinkFormatter, true)
|
||||
|
||||
return s.createPayload(text, nil), nil
|
||||
}
|
||||
|
||||
// Push implements PayloadConvertor Push method
|
||||
func (s *SlackPayload) Push(p *api.PushPayload) (api.Payloader, error) {
|
||||
// n new commits
|
||||
|
|
|
@ -186,6 +186,12 @@ func (t *TelegramPayload) Release(p *api.ReleasePayload) (api.Payloader, error)
|
|||
return createTelegramPayload(text), nil
|
||||
}
|
||||
|
||||
func (t *TelegramPayload) Package(p *api.PackagePayload) (api.Payloader, error) {
|
||||
text, _ := getPackagePayloadInfo(p, htmlLinkFormatter, true)
|
||||
|
||||
return createTelegramPayload(text), nil
|
||||
}
|
||||
|
||||
// GetTelegramPayload converts a telegram webhook into a TelegramPayload
|
||||
func GetTelegramPayload(p api.Payloader, event webhook_module.HookEventType, _ string) (api.Payloader, error) {
|
||||
return convertPayloader(new(TelegramPayload), p, event)
|
||||
|
|
|
@ -179,6 +179,12 @@ func (f *WechatworkPayload) Release(p *api.ReleasePayload) (api.Payloader, error
|
|||
return newWechatworkMarkdownPayload(text), nil
|
||||
}
|
||||
|
||||
func (f *WechatworkPayload) Package(p *api.PackagePayload) (api.Payloader, error) {
|
||||
text, _ := getPackagePayloadInfo(p, noneLinkFormatter, true)
|
||||
|
||||
return newWechatworkMarkdownPayload(text), nil
|
||||
}
|
||||
|
||||
// GetWechatworkPayload GetWechatworkPayload converts a ding talk webhook into a WechatworkPayload
|
||||
func GetWechatworkPayload(p api.Payloader, event webhook_module.HookEventType, _ string) (api.Payloader, error) {
|
||||
return convertPayloader(new(WechatworkPayload), p, event)
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
{{end}}
|
||||
<div class="ui top attached header clearing segment gt-relative commit-header {{$class}}">
|
||||
<div class="gt-df gt-mb-4 gt-fw">
|
||||
<h3 class="gt-mb-0 gt-f1"><span class="commit-summary" title="{{.Commit.Summary}}">{{RenderCommitMessage $.Context .Commit.Message $.RepoLink ($.Repository.ComposeMetas ctx)}}</span>{{template "repo/commit_statuses" dict "Status" .CommitStatus "Statuses" .CommitStatuses "root" $}}</h3>
|
||||
<h3 class="gt-mb-0 gt-f1"><span class="commit-summary" title="{{.Commit.Summary}}">{{RenderCommitMessage $.Context .Commit.Message $.RepoLink ($.Repository.ComposeMetas ctx)}}</span>{{template "repo/commit_statuses" dict "Status" .CommitStatus "Statuses" .CommitStatuses}}</h3>
|
||||
{{if not $.PageIsWiki}}
|
||||
<div>
|
||||
<a class="ui primary tiny button" href="{{.SourcePath}}">
|
||||
|
|
|
@ -8,15 +8,7 @@
|
|||
{{template "repo/commit_status" .Status}}
|
||||
</span>
|
||||
{{end}}
|
||||
<div class="tippy-target ui relaxed list divided">
|
||||
{{range .Statuses}}
|
||||
<div class="ui item singular-status gt-df">
|
||||
{{template "repo/commit_status" .}}
|
||||
<span class="ui gt-ml-3 gt-f1">{{.Context}} <span class="text grey">{{.Description}}</span></span>
|
||||
{{if .TargetURL}}
|
||||
<a class="gt-ml-3" href="{{.TargetURL}}" target="_blank" rel="noopener noreferrer">{{ctx.Locale.Tr "repo.pulls.status_checks_details"}}</a>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
<div class="tippy-target">
|
||||
{{template "repo/pulls/status" (dict "CommitStatuses" .Statuses "CommitStatus" .Status)}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
|
|
@ -66,7 +66,7 @@
|
|||
{{if IsMultilineCommitMessage .Message}}
|
||||
<button class="ui button js-toggle-commit-body ellipsis-button" aria-expanded="false">...</button>
|
||||
{{end}}
|
||||
{{template "repo/commit_statuses" dict "Status" .Status "Statuses" .Statuses "root" $}}
|
||||
{{template "repo/commit_statuses" dict "Status" .Status "Statuses" .Statuses}}
|
||||
{{if IsMultilineCommitMessage .Message}}
|
||||
<pre class="commit-body gt-hidden">{{RenderCommitBody $.Context .Message $commitRepoLink ($.Repository.ComposeMetas ctx)}}</pre>
|
||||
{{end}}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
{{$commitLink:= printf "%s/commit/%s" $.comment.Issue.PullRequest.BaseRepo.Link (PathEscape .ID.String)}}
|
||||
|
||||
<span class="shabox gt-df gt-ac gt-float-right">
|
||||
{{template "repo/commit_statuses" dict "Status" .Status "Statuses" .Statuses "root" $.root}}
|
||||
{{template "repo/commit_statuses" dict "Status" .Status "Statuses" .Statuses}}
|
||||
{{$class := "ui sha label"}}
|
||||
{{if .Signature}}
|
||||
{{$class = (print $class " isSigned")}}
|
||||
|
|
|
@ -238,8 +238,8 @@
|
|||
"DropzoneParentContainer" ".ui.form"
|
||||
)}}
|
||||
<div class="text right edit buttons">
|
||||
<button class="ui cancel button" tabindex="3">{{ctx.Locale.Tr "repo.issues.cancel"}}</button>
|
||||
<button class="ui primary save button" tabindex="2">{{ctx.Locale.Tr "repo.issues.save"}}</button>
|
||||
<button class="ui cancel button">{{ctx.Locale.Tr "repo.issues.cancel"}}</button>
|
||||
<button class="ui primary save button">{{ctx.Locale.Tr "repo.issues.save"}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -49,6 +49,13 @@
|
|||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{$tasks := .GetTasks}}
|
||||
{{if gt $tasks 0}}
|
||||
<div class="meta gt-my-2">
|
||||
{{svg "octicon-checklist" 16 "gt-mr-2 gt-vm"}}
|
||||
<span class="gt-vm">{{.GetTasksDone}} / {{$tasks}}</span>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
{{if or .Labels .Assignees}}
|
||||
|
|
|
@ -92,7 +92,7 @@
|
|||
<div class="text right">
|
||||
{{if and (or .HasIssuesOrPullsWritePermission .IsIssuePoster) (not .DisableStatusChange)}}
|
||||
{{if .Issue.IsClosed}}
|
||||
<button id="status-button" class="ui primary basic button" tabindex="6" data-status="{{ctx.Locale.Tr "repo.issues.reopen_issue"}}" data-status-and-comment="{{ctx.Locale.Tr "repo.issues.reopen_comment_issue"}}" name="status" value="reopen">
|
||||
<button id="status-button" class="ui primary basic button" data-status="{{ctx.Locale.Tr "repo.issues.reopen_issue"}}" data-status-and-comment="{{ctx.Locale.Tr "repo.issues.reopen_comment_issue"}}" name="status" value="reopen">
|
||||
{{ctx.Locale.Tr "repo.issues.reopen_issue"}}
|
||||
</button>
|
||||
{{else}}
|
||||
|
@ -100,12 +100,12 @@
|
|||
{{if .Issue.IsPull}}
|
||||
{{$closeTranslationKey = "repo.pulls.close"}}
|
||||
{{end}}
|
||||
<button id="status-button" class="ui red basic button" tabindex="6" data-status="{{ctx.Locale.Tr $closeTranslationKey}}" data-status-and-comment="{{ctx.Locale.Tr "repo.issues.close_comment_issue"}}" name="status" value="close">
|
||||
<button id="status-button" class="ui red basic button" data-status="{{ctx.Locale.Tr $closeTranslationKey}}" data-status-and-comment="{{ctx.Locale.Tr "repo.issues.close_comment_issue"}}" name="status" value="close">
|
||||
{{ctx.Locale.Tr $closeTranslationKey}}
|
||||
</button>
|
||||
{{end}}
|
||||
{{end}}
|
||||
<button class="ui primary button" tabindex="5">
|
||||
<button class="ui primary button">
|
||||
{{ctx.Locale.Tr "repo.issues.create_comment"}}
|
||||
</button>
|
||||
</div>
|
||||
|
@ -162,8 +162,8 @@
|
|||
|
||||
<div class="field">
|
||||
<div class="text right edit">
|
||||
<button class="ui basic cancel button" tabindex="3">{{ctx.Locale.Tr "repo.issues.cancel"}}</button>
|
||||
<button class="ui primary save button" tabindex="2">{{ctx.Locale.Tr "repo.issues.save"}}</button>
|
||||
<button class="ui basic cancel button">{{ctx.Locale.Tr "repo.issues.cancel"}}</button>
|
||||
<button class="ui primary save button">{{ctx.Locale.Tr "repo.issues.save"}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -20,7 +20,14 @@
|
|||
{{- else if .Issue.PullRequest.CanAutoMerge}}green
|
||||
{{- else}}red{{end}}">{{svg "octicon-git-merge" 40}}</div>
|
||||
<div class="content">
|
||||
{{template "repo/pulls/status" .}}
|
||||
<div class="ui attached segment fitted">
|
||||
{{template "repo/pulls/status" (dict
|
||||
"CommitStatus" .LatestCommitStatus
|
||||
"CommitStatuses" .LatestCommitStatuses
|
||||
"ShowHideChecks" true
|
||||
"is_context_required" .is_context_required
|
||||
)}}
|
||||
</div>
|
||||
{{$showGeneralMergeForm := false}}
|
||||
<div class="ui attached merge-section segment {{if not $.LatestCommitStatus}}no-header{{end}} flex-items-block">
|
||||
{{if .Issue.PullRequest.HasMerged}}
|
||||
|
@ -263,7 +270,7 @@
|
|||
},
|
||||
{
|
||||
'name': 'manually-merged',
|
||||
'allowed': {{and $prUnit.PullRequestsConfig.AllowManualMerge $.IsRepoAdmin}},
|
||||
'allowed': {{$prUnit.PullRequestsConfig.AllowManualMerge}},
|
||||
'textDoMerge': {{ctx.Locale.Tr "repo.pulls.merge_manually"}},
|
||||
'hideMergeMessageTexts': true,
|
||||
'hideAutoMerge': true,
|
||||
|
@ -349,13 +356,13 @@
|
|||
{{end}}{{/* end if: pull request status */}}
|
||||
|
||||
{{/*
|
||||
Manually Merged is not a well-known feature, it helps repo admins to mark a non-mergeable PR (already merged, conflicted) as merged
|
||||
Manually Merged is not a well-known feature, it is used to mark a non-mergeable PR (already merged, conflicted) as merged
|
||||
To test it:
|
||||
* Enable "Manually Merged" feature in the Repository Settings
|
||||
* Create a pull request, either:
|
||||
* - Merge the pull request branch locally and push the merged commit to Gitea
|
||||
* - Make some conflicts between the base branch and the pull request branch
|
||||
* Then the Manually Merged form will be shown to repo admin users
|
||||
* Then the Manually Merged form will be shown in the merge form
|
||||
*/}}
|
||||
{{if and $.StillCanManualMerge (not $showGeneralMergeForm)}}
|
||||
<div class="divider"></div>
|
||||
|
|
|
@ -1,27 +1,43 @@
|
|||
{{if $.LatestCommitStatus}}
|
||||
{{if not $.Issue.PullRequest.HasMerged}}
|
||||
<div class="ui top attached header">
|
||||
{{if eq .LatestCommitStatus.State "pending"}}
|
||||
{{ctx.Locale.Tr "repo.pulls.status_checking"}}
|
||||
{{else if eq .LatestCommitStatus.State "success"}}
|
||||
{{ctx.Locale.Tr "repo.pulls.status_checks_success"}}
|
||||
{{else if eq .LatestCommitStatus.State "warning"}}
|
||||
{{ctx.Locale.Tr "repo.pulls.status_checks_warning"}}
|
||||
{{else if eq .LatestCommitStatus.State "failure"}}
|
||||
{{ctx.Locale.Tr "repo.pulls.status_checks_failure"}}
|
||||
{{else if eq .LatestCommitStatus.State "error"}}
|
||||
{{ctx.Locale.Tr "repo.pulls.status_checks_error"}}
|
||||
{{else}}
|
||||
{{ctx.Locale.Tr "repo.pulls.status_checking"}}
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
{{/*
|
||||
Template Attributes:
|
||||
* CommitStatus: summary of all commit status state
|
||||
* CommitStatuses: all commit status elements
|
||||
* ShowHideChecks: whether use a button to show/hide the checks
|
||||
* is_context_required: Used in pull request commit status check table
|
||||
*/}}
|
||||
|
||||
{{range $.LatestCommitStatuses}}
|
||||
<div class="ui attached segment pr-status">
|
||||
{{template "repo/commit_status" .}}
|
||||
<div class="status-context">
|
||||
<span>{{.Context}} <span class="text grey">{{.Description}}</span></span>
|
||||
{{if .CommitStatus}}
|
||||
<div class="commit-status-panel">
|
||||
<div class="ui top attached header commit-status-header">
|
||||
{{if eq .CommitStatus.State "pending"}}
|
||||
{{ctx.Locale.Tr "repo.pulls.status_checking"}}
|
||||
{{else if eq .CommitStatus.State "success"}}
|
||||
{{ctx.Locale.Tr "repo.pulls.status_checks_success"}}
|
||||
{{else if eq .CommitStatus.State "warning"}}
|
||||
{{ctx.Locale.Tr "repo.pulls.status_checks_warning"}}
|
||||
{{else if eq .CommitStatus.State "failure"}}
|
||||
{{ctx.Locale.Tr "repo.pulls.status_checks_failure"}}
|
||||
{{else if eq .CommitStatus.State "error"}}
|
||||
{{ctx.Locale.Tr "repo.pulls.status_checks_error"}}
|
||||
{{else}}
|
||||
{{ctx.Locale.Tr "repo.pulls.status_checking"}}
|
||||
{{end}}
|
||||
|
||||
{{if .ShowHideChecks}}
|
||||
<div class="ui right">
|
||||
<button class="commit-status-hide-checks btn interact-fg"
|
||||
data-show-all="{{ctx.Locale.Tr "repo.pulls.status_checks_show_all"}}"
|
||||
data-hide-all="{{ctx.Locale.Tr "repo.pulls.status_checks_hide_all"}}">
|
||||
{{ctx.Locale.Tr "repo.pulls.status_checks_hide_all"}}</button>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
<div class="commit-status-list">
|
||||
{{range .CommitStatuses}}
|
||||
<div class="commit-status-item">
|
||||
{{template "repo/commit_status" .}}
|
||||
<div class="status-context gt-ellipsis">{{.Context}} <span class="text light-2">{{.Description}}</span></div>
|
||||
<div class="ui status-details">
|
||||
{{if $.is_context_required}}
|
||||
{{if (call $.is_context_required .Context)}}<div class="ui label">{{ctx.Locale.Tr "repo.pulls.status_checks_requested"}}</div>{{end}}
|
||||
|
@ -29,6 +45,7 @@
|
|||
<span>{{if .TargetURL}}<a href="{{.TargetURL}}">{{ctx.Locale.Tr "repo.pulls.status_checks_details"}}</a>{{end}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
{{template "repo/shabox_badge" dict "root" $ "verification" .LatestCommitVerification}}
|
||||
{{end}}
|
||||
</a>
|
||||
{{template "repo/commit_statuses" dict "Status" .LatestCommitStatus "Statuses" .LatestCommitStatuses "root" $}}
|
||||
{{template "repo/commit_statuses" dict "Status" .LatestCommitStatus "Statuses" .LatestCommitStatuses}}
|
||||
{{$commitLink:= printf "%s/commit/%s" .RepoLink (PathEscape .LatestCommit.ID.String)}}
|
||||
<span class="grey commit-summary" title="{{.LatestCommit.Summary}}"><span class="message-wrapper">{{RenderCommitMessageLinkSubject $.Context .LatestCommit.Message $.RepoLink $commitLink ($.Repository.ComposeMetas ctx)}}</span>
|
||||
{{if IsMultilineCommitMessage .LatestCommit.Message}}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
<a class="gt-no-underline issue-title" href="{{if .Link}}{{.Link}}{{else}}{{$.Link}}/{{.Index}}{{end}}">{{RenderEmoji $.Context .Title | RenderCodeBlock}}</a>
|
||||
{{if .IsPull}}
|
||||
{{if (index $.CommitStatuses .PullRequest.ID)}}
|
||||
{{template "repo/commit_statuses" dict "Status" (index $.CommitLastStatus .PullRequest.ID) "Statuses" (index $.CommitStatuses .PullRequest.ID) "root" $}}
|
||||
{{template "repo/commit_statuses" dict "Status" (index $.CommitLastStatus .PullRequest.ID) "Statuses" (index $.CommitStatuses .PullRequest.ID)}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
<span class="labels-list gt-ml-2">
|
||||
|
|
|
@ -1,5 +1 @@
|
|||
{{if .FullName}}
|
||||
{{.FullName}} <span>(<a class="text primary" href="{{.HomeLink}}">{{.Name}}</a>)</span>
|
||||
{{else}}
|
||||
<a class="text primary" href="{{.HomeLink}}">{{.Name}}</a>
|
||||
{{end}}
|
||||
<a class="text muted" href="{{.HomeLink}}">{{.Name}}{{if .FullName}} ({{.FullName}}){{end}}</a>
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
{{/* This page should only depend the minimal template functions/variables, to avoid triggering new panics.
|
||||
* base template functions: AppName, AssetUrlPrefix, AssetVersion, AppSubUrl, ThemeName, Str2html
|
||||
* locale
|
||||
* Flash
|
||||
* ErrorMsg
|
||||
* SignedUser (optional)
|
||||
* ctx.Locale
|
||||
* .Flash
|
||||
* .ErrorMsg
|
||||
* .SignedUser (optional)
|
||||
*/}}
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{.locale.Lang}}" data-theme="{{ThemeName .SignedUser}}">
|
||||
<html lang="{{ctx.Locale.Lang}}" data-theme="{{ThemeName .SignedUser}}">
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Internal Server Error - {{AppName}}</title>
|
||||
|
@ -19,8 +19,8 @@
|
|||
<nav class="ui secondary menu gt-border-secondary-bottom">
|
||||
<div class="ui container gt-df">
|
||||
<div class="item gt-f1">
|
||||
<a href="{{AppSubUrl}}/" aria-label="{{.locale.Tr "home"}}">
|
||||
<img width="30" height="30" src="{{AssetUrlPrefix}}/img/logo.svg" alt="{{.locale.Tr "logo"}}" aria-hidden="true">
|
||||
<a href="{{AppSubUrl}}/" aria-label="{{ctx.Locale.Tr "home"}}">
|
||||
<img width="30" height="30" src="{{AssetUrlPrefix}}/img/logo.svg" alt="{{ctx.Locale.Tr "logo"}}" aria-hidden="true">
|
||||
</a>
|
||||
</div>
|
||||
<div class="item">
|
||||
|
@ -37,12 +37,12 @@
|
|||
<div class="divider"></div>
|
||||
<div class="ui container gt-my-5">
|
||||
{{if .ErrorMsg}}
|
||||
<p>{{.locale.Tr "error.occurred"}}:</p>
|
||||
<p>{{ctx.Locale.Tr "error.occurred"}}:</p>
|
||||
<pre class="gt-whitespace-pre-wrap gt-break-all">{{.ErrorMsg}}</pre>
|
||||
{{end}}
|
||||
<div class="center gt-mt-5">
|
||||
{{if or .SignedUser.IsAdmin .ShowFooterVersion}}<p>{{.locale.Tr "admin.config.app_ver"}}: {{AppVer}}</p>{{end}}
|
||||
{{if .SignedUser.IsAdmin}}<p>{{.locale.Tr "error.report_message" | Str2html}}</p>{{end}}
|
||||
{{if or .SignedUser.IsAdmin .ShowFooterVersion}}<p>{{ctx.Locale.Tr "admin.config.app_ver"}}: {{AppVer}}</p>{{end}}
|
||||
{{if .SignedUser.IsAdmin}}<p>{{ctx.Locale.Tr "error.report_message" | Str2html}}</p>{{end}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -21123,6 +21123,10 @@
|
|||
"creator": {
|
||||
"$ref": "#/definitions/User"
|
||||
},
|
||||
"html_url": {
|
||||
"type": "string",
|
||||
"x-go-name": "HTMLURL"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
|
|
|
@ -52,16 +52,15 @@
|
|||
</div>
|
||||
{{end}}
|
||||
|
||||
{{if and .OrderedOAuth2Names .OAuth2Providers}}
|
||||
{{if .OAuth2Providers}}
|
||||
<div class="divider divider-text">
|
||||
{{ctx.Locale.Tr "sign_in_or"}}
|
||||
</div>
|
||||
<div id="oauth2-login-navigator" class="gt-py-2">
|
||||
<div class="gt-df gt-fc gt-jc">
|
||||
<div id="oauth2-login-navigator-inner" class="gt-df gt-fc gt-fw gt-ac gt-gap-3">
|
||||
{{range $key := .OrderedOAuth2Names}}
|
||||
{{$provider := index $.OAuth2Providers $key}}
|
||||
<a class="{{$provider.Name}} ui button gt-df gt-ac gt-jc gt-py-3 oauth-login-link" href="{{AppSubUrl}}/user/oauth2/{{$key}}">
|
||||
{{range $provider := .OAuth2Providers}}
|
||||
<a class="{{$provider.Name}} ui button gt-df gt-ac gt-jc gt-py-3 oauth-login-link" href="{{AppSubUrl}}/user/oauth2/{{$provider.Name}}">
|
||||
{{$provider.IconHTML 28}}
|
||||
{{ctx.Locale.Tr "sign_in_with_provider" $provider.DisplayName}}
|
||||
</a>
|
||||
|
|
|
@ -56,16 +56,15 @@
|
|||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{if and .OrderedOAuth2Names .OAuth2Providers}}
|
||||
{{if .OAuth2Providers}}
|
||||
<div class="divider divider-text">
|
||||
{{ctx.Locale.Tr "sign_in_or"}}
|
||||
</div>
|
||||
<div id="oauth2-login-navigator" class="gt-py-2">
|
||||
<div class="gt-df gt-fc gt-jc">
|
||||
<div id="oauth2-login-navigator-inner" class="gt-df gt-fc gt-fw gt-ac gt-gap-3">
|
||||
{{range $key := .OrderedOAuth2Names}}
|
||||
{{$provider := index $.OAuth2Providers $key}}
|
||||
<a class="{{$provider.Name}} ui button gt-df gt-ac gt-jc gt-py-3 oauth-login-link" href="{{AppSubUrl}}/user/oauth2/{{$key}}">
|
||||
{{range $provider := .OAuth2Providers}}
|
||||
<a class="{{$provider.Name}} ui button gt-df gt-ac gt-jc gt-py-3 oauth-login-link" href="{{AppSubUrl}}/user/oauth2/{{$provider.Name}}">
|
||||
{{$provider.IconHTML 28}}
|
||||
{{ctx.Locale.Tr "sign_in_with_provider" $provider.DisplayName}}
|
||||
</a>
|
||||
|
|
|
@ -27,14 +27,14 @@
|
|||
{{end}}
|
||||
|
||||
{{if .ContextUser.IsOrganization}}
|
||||
{{if .IsOrganizationMember}}
|
||||
<a class="item" href="{{$.OrgLink}}/members">
|
||||
{{if .NumMembers}}
|
||||
<a class="{{if $.PageIsOrgMembers}}active {{end}}item" href="{{$.OrgLink}}/members">
|
||||
{{svg "octicon-person"}} {{ctx.Locale.Tr "org.members"}}
|
||||
{{if .NumMembers}}
|
||||
<div class="ui small label">{{.NumMembers}}</div>
|
||||
{{end}}
|
||||
<div class="ui small label">{{.NumMembers}}</div>
|
||||
</a>
|
||||
<a class="item" href="{{$.OrgLink}}/teams">
|
||||
{{end}}
|
||||
{{if .IsOrganizationMember}}
|
||||
<a class="{{if $.PageIsOrgTeams}}active {{end}}item" href="{{$.OrgLink}}/teams">
|
||||
{{svg "octicon-people"}} {{ctx.Locale.Tr "org.teams"}}
|
||||
{{if .NumTeams}}
|
||||
<div class="ui small label">{{.NumTeams}}</div>
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package integration
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
auth_model "code.gitea.io/gitea/models/auth"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/tests"
|
||||
)
|
||||
|
||||
func TestAPIUserSecrets(t *testing.T) {
|
||||
defer tests.PrepareTestEnv(t)()
|
||||
|
||||
session := loginUser(t, "user1")
|
||||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteUser)
|
||||
|
||||
t.Run("Create", func(t *testing.T) {
|
||||
cases := []struct {
|
||||
Name string
|
||||
ExpectedStatus int
|
||||
}{
|
||||
{
|
||||
Name: "",
|
||||
ExpectedStatus: http.StatusNotFound,
|
||||
},
|
||||
{
|
||||
Name: "-",
|
||||
ExpectedStatus: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
Name: "_",
|
||||
ExpectedStatus: http.StatusCreated,
|
||||
},
|
||||
{
|
||||
Name: "secret",
|
||||
ExpectedStatus: http.StatusCreated,
|
||||
},
|
||||
{
|
||||
Name: "2secret",
|
||||
ExpectedStatus: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
Name: "GITEA_secret",
|
||||
ExpectedStatus: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
Name: "GITHUB_secret",
|
||||
ExpectedStatus: http.StatusBadRequest,
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
req := NewRequestWithJSON(t, "PUT", fmt.Sprintf("/api/v1/user/actions/secrets/%s?token=%s", c.Name, token), api.CreateOrUpdateSecretOption{
|
||||
Data: "data",
|
||||
})
|
||||
MakeRequest(t, req, c.ExpectedStatus)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Update", func(t *testing.T) {
|
||||
name := "update_secret"
|
||||
url := fmt.Sprintf("/api/v1/user/actions/secrets/%s?token=%s", name, token)
|
||||
|
||||
req := NewRequestWithJSON(t, "PUT", url, api.CreateOrUpdateSecretOption{
|
||||
Data: "initial",
|
||||
})
|
||||
MakeRequest(t, req, http.StatusCreated)
|
||||
|
||||
req = NewRequestWithJSON(t, "PUT", url, api.CreateOrUpdateSecretOption{
|
||||
Data: "changed",
|
||||
})
|
||||
MakeRequest(t, req, http.StatusNoContent)
|
||||
})
|
||||
|
||||
t.Run("Delete", func(t *testing.T) {
|
||||
name := "delete_secret"
|
||||
url := fmt.Sprintf("/api/v1/user/actions/secrets/%s?token=%s", name, token)
|
||||
|
||||
req := NewRequestWithJSON(t, "PUT", url, api.CreateOrUpdateSecretOption{
|
||||
Data: "initial",
|
||||
})
|
||||
MakeRequest(t, req, http.StatusCreated)
|
||||
|
||||
req = NewRequest(t, "DELETE", url)
|
||||
MakeRequest(t, req, http.StatusNoContent)
|
||||
|
||||
req = NewRequest(t, "DELETE", url)
|
||||
MakeRequest(t, req, http.StatusNotFound)
|
||||
|
||||
req = NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/user/actions/secrets/000?token=%s", token))
|
||||
MakeRequest(t, req, http.StatusBadRequest)
|
||||
})
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
.divider {
|
||||
margin: 1rem 0;
|
||||
margin: 10px 0;
|
||||
height: 0;
|
||||
font-weight: var(--font-weight-medium);
|
||||
text-transform: uppercase;
|
||||
|
@ -15,7 +15,7 @@
|
|||
.divider.divider-text {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 7px 0;
|
||||
padding: 5px 0;
|
||||
}
|
||||
|
||||
.divider.divider-text::before,
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
}
|
||||
|
||||
[data-tippy-root] {
|
||||
max-width: calc(100vw - 10px);
|
||||
max-width: calc(100vw - 32px);
|
||||
}
|
||||
|
||||
.tippy-box {
|
||||
|
@ -18,37 +18,59 @@
|
|||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.tippy-content {
|
||||
position: relative;
|
||||
padding: 1rem; /* if you need different padding, use different data-theme */
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* tooltip theme for text tooltips */
|
||||
|
||||
.tippy-box[data-theme="tooltip"] {
|
||||
background-color: var(--color-tooltip-bg);
|
||||
color: var(--color-tooltip-text);
|
||||
border: none;
|
||||
}
|
||||
|
||||
.tippy-box[data-theme="tooltip"] .tippy-content {
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
|
||||
.tippy-box[data-theme="tooltip"] .tippy-svg-arrow-inner,
|
||||
.tippy-box[data-theme="tooltip"] .tippy-svg-arrow-outer {
|
||||
fill: var(--color-tooltip-bg);
|
||||
}
|
||||
|
||||
/* menu theme for .ui.menu */
|
||||
|
||||
.tippy-box[data-theme="menu"] {
|
||||
background-color: var(--color-menu);
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.tippy-box[data-theme="form-fetch-error"] {
|
||||
border-color: var(--color-error-border);
|
||||
background-color: var(--color-error-bg);
|
||||
color: var(--color-error-text);
|
||||
}
|
||||
|
||||
.tippy-content {
|
||||
position: relative;
|
||||
padding: 1rem;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.tippy-box[data-theme="tooltip"] .tippy-content {
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
|
||||
.tippy-box[data-theme="menu"] .tippy-content {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.tippy-box[data-theme="menu"] .tippy-svg-arrow-inner {
|
||||
fill: var(--color-menu);
|
||||
}
|
||||
|
||||
/* box-with-header theme to look like .ui.attached.segment. can contain .ui.attached.header */
|
||||
|
||||
.tippy-box[data-theme="box-with-header"] .tippy-content {
|
||||
background: var(--color-box-body);
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.tippy-box[data-theme="box-with-header"][data-placement^="top"] .tippy-svg-arrow-inner {
|
||||
fill: var(--color-box-body);
|
||||
}
|
||||
|
||||
.tippy-box[data-theme="box-with-header"][data-placement^="bottom"] .tippy-svg-arrow-inner {
|
||||
fill: var(--color-box-header);
|
||||
}
|
||||
|
||||
.tippy-box[data-placement^="top"] > .tippy-svg-arrow {
|
||||
bottom: 0;
|
||||
}
|
||||
|
@ -107,12 +129,3 @@
|
|||
.tippy-svg-arrow-inner {
|
||||
fill: var(--color-body);
|
||||
}
|
||||
|
||||
.tippy-box[data-theme="tooltip"] .tippy-svg-arrow-inner,
|
||||
.tippy-box[data-theme="tooltip"] .tippy-svg-arrow-outer {
|
||||
fill: var(--color-tooltip-bg);
|
||||
}
|
||||
|
||||
.tippy-box[data-theme="menu"] .tippy-svg-arrow-inner {
|
||||
fill: var(--color-menu);
|
||||
}
|
||||
|
|
|
@ -3074,43 +3074,49 @@ tbody.commit-list {
|
|||
}
|
||||
}
|
||||
|
||||
.pr-status {
|
||||
padding: 0 !important; /* To clear fomantic's padding on .ui.segment elements */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.commit-status-header {
|
||||
border: none !important; /* reset the default ".ui.attached.header" styles, to use the outer border */
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.pr-status .commit-status {
|
||||
margin: 1em;
|
||||
.commit-status-list {
|
||||
max-height: 195px; /* fit exactly 4 items */
|
||||
overflow-x: hidden;
|
||||
transition: max-height .2s;
|
||||
}
|
||||
|
||||
.commit-status-item {
|
||||
padding: 14px 10px !important;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
border-top: 1px solid var(--color-secondary);
|
||||
}
|
||||
|
||||
.commit-status-item .commit-status {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.pr-status .status-context {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
.commit-status-item .status-context {
|
||||
color: var(--color-text);
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.pr-status .status-context > span {
|
||||
padding: 1em 0;
|
||||
}
|
||||
|
||||
.pr-status .status-details {
|
||||
.commit-status-item .status-details {
|
||||
display: flex;
|
||||
padding-right: 0.5em;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
@media (max-width: 767.98px) {
|
||||
.pr-status .status-details {
|
||||
.commit-status-item .status-details {
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
.pr-status .status-details > span {
|
||||
.commit-status-item .status-details > span {
|
||||
padding-right: 0.5em; /* To match the alignment with the "required" label */
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: flex-start;
|
||||
padding: 1em 0;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.flex-item .flex-item-leading {
|
||||
|
|
|
@ -66,6 +66,7 @@ export function initCommitStatuses() {
|
|||
placement: top ? 'top-start' : 'bottom-start',
|
||||
interactive: true,
|
||||
role: 'dialog',
|
||||
theme: 'box-with-header',
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
export function initRepoPullRequestCommitStatus() {
|
||||
for (const btn of document.querySelectorAll('.commit-status-hide-checks')) {
|
||||
const panel = btn.closest('.commit-status-panel');
|
||||
const list = panel.querySelector('.commit-status-list');
|
||||
btn.addEventListener('click', () => {
|
||||
list.style.maxHeight = list.style.maxHeight ? '' : '0px'; // toggle
|
||||
list.style.overflow = 'hidden'; // hide scrollbar when hiding
|
||||
btn.textContent = btn.getAttribute(list.style.maxHeight ? 'data-show-all' : 'data-hide-all');
|
||||
});
|
||||
list.addEventListener('animationend', () => list.style.overflow = '');
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@ import {initCommentContent, initMarkupContent} from '../markup/content.js';
|
|||
import {initCompReactionSelector} from './comp/ReactionSelector.js';
|
||||
import {initRepoSettingBranches} from './repo-settings.js';
|
||||
import {initRepoPullRequestMergeForm} from './repo-issue-pr-form.js';
|
||||
import {initRepoPullRequestCommitStatus} from './repo-issue-pr-status.js';
|
||||
import {hideElem, showElem} from '../utils/dom.js';
|
||||
import {getComboMarkdownEditor, initComboMarkdownEditor} from './comp/ComboMarkdownEditor.js';
|
||||
import {attachRefIssueContextPopup} from './contextpopup.js';
|
||||
|
@ -546,6 +547,7 @@ export function initRepository() {
|
|||
initCompReactionSelector($(document));
|
||||
|
||||
initRepoPullRequestMergeForm();
|
||||
initRepoPullRequestCommitStatus();
|
||||
}
|
||||
|
||||
// Pull request
|
||||
|
|
|
@ -34,7 +34,7 @@ export function createTippy(target, opts = {}) {
|
|||
},
|
||||
arrow: `<svg width="16" height="7"><path d="m0 7 8-7 8 7Z" class="tippy-svg-arrow-outer"/><path d="m0 8 8-7 8 7Z" class="tippy-svg-arrow-inner"/></svg>`,
|
||||
role: 'menu', // HTML role attribute, only tooltips should use "tooltip"
|
||||
theme: other.role || 'menu', // CSS theme, we support either "tooltip" or "menu"
|
||||
theme: other.role || 'menu', // CSS theme, either "tooltip", "menu" or "box-with-header"
|
||||
plugins: [followCursor],
|
||||
...other,
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue