mirror of
https://github.com/gogs/gogs.git
synced 2026-07-19 13:52:01 +02:00
Compare commits
29 Commits
copilot/su
...
copilot/re
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
074714d61f | ||
|
|
68f049ad15 | ||
|
|
ec5eede78f | ||
|
|
06b85ac5ba | ||
|
|
c74b48bd2d | ||
|
|
e89db8393d | ||
|
|
653c95c842 | ||
|
|
dfc768b968 | ||
|
|
63221dfec3 | ||
|
|
d2ff1fa978 | ||
|
|
748ecbfdf3 | ||
|
|
04d063c65d | ||
|
|
eebe4065c6 | ||
|
|
de9f03d533 | ||
|
|
8d43893af2 | ||
|
|
233f0fb18d | ||
|
|
aba381e7f8 | ||
|
|
1a285ca5ce | ||
|
|
b74ce2f6bc | ||
|
|
5469f017bc | ||
|
|
cddfd5fbb9 | ||
|
|
9bd5123bcf | ||
|
|
589bf5f532 | ||
|
|
9d9175df80 | ||
|
|
e3e107f95d | ||
|
|
36833b135e | ||
|
|
d5954c42eb | ||
|
|
f9478a3f76 | ||
|
|
9bfb9a719b |
@@ -1,7 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: "Dev: Release a minor version"
|
name: "Dev: Release a minor version"
|
||||||
about: ONLY USED BY MAINTAINERS.
|
about: ONLY USED BY MAINTAINERS.
|
||||||
assignees: "unknwon"
|
|
||||||
title: "Release [VERSION]"
|
title: "Release [VERSION]"
|
||||||
labels: 📸 release
|
labels: 📸 release
|
||||||
---
|
---
|
||||||
@@ -14,33 +13,40 @@ On the `main` branch:
|
|||||||
|
|
||||||
- [ ] Close stale issues with the label [status: needs feedback](https://github.com/gogs/gogs/issues?q=is%3Aissue+is%3Aopen+label%3A%22status%3A+needs+feedback%22).
|
- [ ] Close stale issues with the label [status: needs feedback](https://github.com/gogs/gogs/issues?q=is%3Aissue+is%3Aopen+label%3A%22status%3A+needs+feedback%22).
|
||||||
- [ ] [Sync locales from Crowdin](https://github.com/gogs/gogs/blob/main/docs/dev/import_locale.md).
|
- [ ] [Sync locales from Crowdin](https://github.com/gogs/gogs/blob/main/docs/dev/import_locale.md).
|
||||||
- [ ] [Update CHANGELOG](https://github.com/gogs/gogs/commit/f1102a7a7c545ec221d2906f02fa19170d96f96d) to include entries for the current minor release.
|
- [ ] [Update CHANGELOG](https://github.com/gogs/gogs/commit/540134d4436d8da82247dd2cabe9312ca2f5b1f1) to include entries for the current minor release.
|
||||||
- Do not forget adding entries for GHSA patches.
|
- [ ] Cut a new release branch `release/<MAJOR>.<MINOR>`, e.g. `release/0.12`.
|
||||||
- [ ] Cut a new release branch `release/<MAJOR>.<MINOR>`, e.g. `release/0.14`.
|
|
||||||
|
|
||||||
## During release
|
## During release
|
||||||
|
|
||||||
On the release branch:
|
On the release branch:
|
||||||
|
|
||||||
- [ ] [Update the hard-coded version](https://github.com/gogs/gogs/commit/f0e3cd90f8d7695960eeef2e4e54b2e717302f6c) to the current release, e.g. `0.14.0+dev` -> `0.14.0`.
|
- [ ] [Update the hard-coded version](https://github.com/gogs/gogs/commit/f17e7d5a2c36c52a1121d2315f3d75dcd8053b89) to the current release, e.g. `0.12.0+dev` -> `0.12.0`.
|
||||||
- [ ] Wait for GitHub Actions to complete and no failed jobs.
|
- [ ] Wait for GitHub Actions to complete and no failed jobs.
|
||||||
- [ ] Publish new RC releases (e.g. `v0.14.0-rc.1`, `v0.14.0-rc.2`) ⚠️ **on the release branch** ⚠️ and ensure Docker and release workflows both succeed.
|
- [ ] Publish new RC releases (e.g. `v0.12.0-rc.1`, `v0.12.0-rc.2`) to ensure Docker workflow succeeds. **Make sure the tag is created on the release branch**.
|
||||||
- [ ] Pull down the Docker image and [run through application setup](https://github.com/gogs/gogs/blob/main/docker/README.md) to make sure nothing blows up.
|
|
||||||
- [ ] Download one of the release archives and run through application setup to make sure nothing blows up.
|
|
||||||
- [ ] Publish a new [GitHub release](https://github.com/gogs/gogs/releases) ⚠️ **on the release branch** ⚠️ with entries from [CHANGELOG](https://github.com/gogs/gogs/blob/main/CHANGELOG.md) for the current minor release.
|
|
||||||
- [ ] [Wait for new image tags for the current release](https://github.com/gogs/gogs/actions/workflows/docker.yml?query=event%3Arelease) to be created automatically on both [Docker Hub](https://hub.docker.com/r/gogs/gogs/tags) and [GitHub Container registry](https://github.com/gogs/gogs/pkgs/container/gogs).
|
|
||||||
- Pull down the Docker image and [run through application setup](https://github.com/gogs/gogs/blob/main/docker/README.md) to make sure nothing blows up.
|
- Pull down the Docker image and [run through application setup](https://github.com/gogs/gogs/blob/main/docker/README.md) to make sure nothing blows up.
|
||||||
- [ ] Download all release archives and [generate SHA256 checksum](https://github.com/gogs/gogs/blob/main/docs/dev/release/sha256.sh) for all binaries to the file `checksum_sha256.txt`.
|
- [ ] Publish a new [GitHub release](https://github.com/gogs/gogs/releases) with entries from [CHANGELOG](https://github.com/gogs/gogs/blob/main/CHANGELOG.md) for the current minor release. **Make sure the tag is created on the release branch**.
|
||||||
- [ ] Upload all archives and `checksum_sha256.txt` to https://dl.gogs.io.
|
- [ ] [Wait for a new image tag for the current release](https://github.com/gogs/gogs/actions/workflows/docker.yml?query=event%3Arelease) to be created automatically on both [Docker Hub](https://hub.docker.com/r/gogs/gogs/tags) and [GitHub Container registry](https://github.com/gogs/gogs/pkgs/container/gogs).
|
||||||
|
- [ ] [Push a new Docker image tag](https://github.com/gogs/gogs/blob/main/docs/dev/release/release_new_version.md#update-docker-image-tag) as `<MAJOR>.<MINOR>` to both [Docker Hub](https://hub.docker.com/r/gogs/gogs/tags) and [GitHub Container registry](https://github.com/gogs/gogs/pkgs/container/gogs), e.g.:
|
||||||
|
- [ ] [Compile and pack binaries](https://github.com/gogs/gogs/blob/main/docs/dev/release/release_new_version.md#compile-and-pack-binaries) (all prefixed with `gogs_<MAJOR>.<MINOR>.<PATCH>_`, e.g. `gogs_0.12.0_`):
|
||||||
|
- [ ] macOS: `darwin_amd64.zip`, `darwin_arm64.zip`
|
||||||
|
- [ ] Linux: `linux_386.tar.gz`, `linux_386.zip`, `linux_amd64.tar.gz`, `linux_amd64.zip`
|
||||||
|
- [ ] ARM: `linux_armv7.tar.gz`, `linux_armv7.zip`, `linux_armv8.tar.gz`, `linux_armv8.zip`
|
||||||
|
- [ ] Windows: `windows_amd64.zip`, `windows_amd64_mws.zip`
|
||||||
|
- [ ] [Generate SHA256 checksum](https://github.com/gogs/gogs/blob/main/docs/dev/release/sha256.sh) for all binaries to the file `checksum_sha256.txt`.
|
||||||
|
- [ ] Upload all binaries and `checksum_sha256.txt` to:
|
||||||
|
- [ ] GitHub release
|
||||||
|
- [ ] https://dl.gogs.io
|
||||||
|
- [ ] Update content of [Install from binary](https://gogs.io/docs/installation/install_from_binary).
|
||||||
|
|
||||||
## After release
|
## After release
|
||||||
|
|
||||||
On the `main` branch:
|
On the `main` branch:
|
||||||
|
|
||||||
|
- [ ] Publish [GitHub security advisories](https://github.com/gogs/gogs/security) for security patches included in the release.
|
||||||
- [ ] Update the repository mirror on [Gitee](https://gitee.com/unknwon/gogs).
|
- [ ] Update the repository mirror on [Gitee](https://gitee.com/unknwon/gogs).
|
||||||
- [ ] Create a new release announcement in [Discussions](https://github.com/gogs/gogs/discussions/categories/announcements).
|
- [ ] Create a new release announcement in [Discussions](https://github.com/gogs/gogs/discussions/categories/announcements).
|
||||||
- [ ] Send a tweet on the [official Twitter account](https://twitter.com/GogsHQ) for the minor release.
|
- [ ] Send a tweet on the [official Twitter account](https://twitter.com/GogsHQ) for the minor release.
|
||||||
- [ ] Close the milestone for the minor release.
|
- [ ] Publish a new release article on [OSChina](http://my.oschina.net/Obahua/admin/releases).
|
||||||
- [ ] [Bump the hard-coded version](https://github.com/gogs/gogs/commit/a98968436cd5841cf691bb0b80c54c81470d1676) to the new develop version, e.g. `0.14.0+dev` -> `0.15.0+dev`.
|
- [ ] Close the minor milestone.
|
||||||
|
- [ ] [Bump the hard-coded version](https://github.com/gogs/gogs/commit/a98968436cd5841cf691bb0b80c54c81470d1676) to the new develop version, e.g. `0.12.0+dev` -> `0.13.0+dev`.
|
||||||
- [ ] Run `task legacy` to identify deprecated code that is aimed to be removed in current develop version.
|
- [ ] Run `task legacy` to identify deprecated code that is aimed to be removed in current develop version.
|
||||||
- [ ] **After 14 days**, publish [GitHub security advisories](https://github.com/gogs/gogs/security) for security patches included in the release.
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: "Dev: Release a patch version"
|
name: "Dev: Release a patch version"
|
||||||
about: ONLY USED BY MAINTAINERS.
|
about: ONLY USED BY MAINTAINERS.
|
||||||
assignees: "unknwon"
|
|
||||||
title: "Release [VERSION]"
|
title: "Release [VERSION]"
|
||||||
labels: 📸 release
|
labels: 📸 release
|
||||||
---
|
---
|
||||||
@@ -14,7 +13,7 @@ On the release branch:
|
|||||||
|
|
||||||
- [ ] Make sure all commits are cherry-picked from the `main` branch by checking the patch milestone.
|
- [ ] Make sure all commits are cherry-picked from the `main` branch by checking the patch milestone.
|
||||||
- Run `task build` for every cherry-picked commit to make sure there is no compilation error.
|
- Run `task build` for every cherry-picked commit to make sure there is no compilation error.
|
||||||
- [ ] [Update CHANGELOG on the `main` branch](https://github.com/gogs/gogs/commit/f1102a7a7c545ec221d2906f02fa19170d96f96d) to include entries for the current patch release.
|
- [ ] [Update CHANGELOG on the `main` branch](https://github.com/gogs/gogs/commit/e6c5633f580399c8f4dfc07166a63a01c6c70346) to include entries for the current patch release.
|
||||||
|
|
||||||
## During release
|
## During release
|
||||||
|
|
||||||
@@ -22,18 +21,28 @@ On the release branch:
|
|||||||
|
|
||||||
- [ ] [Update the hard-coded version](https://github.com/gogs/gogs/commit/f0e3cd90f8d7695960eeef2e4e54b2e717302f6c) to the current release, e.g. `0.12.0` -> `0.12.1`.
|
- [ ] [Update the hard-coded version](https://github.com/gogs/gogs/commit/f0e3cd90f8d7695960eeef2e4e54b2e717302f6c) to the current release, e.g. `0.12.0` -> `0.12.1`.
|
||||||
- [ ] Wait for GitHub Actions to complete and no failed jobs.
|
- [ ] Wait for GitHub Actions to complete and no failed jobs.
|
||||||
- [ ] Publish new RC releases in [GitHub release](https://github.com/gogs/gogs/releases) (e.g. `v0.12.0-rc.1`, `v0.12.0-rc.2`) ⚠️ **on the release branch** ⚠️ and ensure Docker workflow succeeds.
|
- [ ] Publish new RC releases in [GitHub release](https://github.com/gogs/gogs/releases) (e.g. `v0.12.0-rc.1`, `v0.12.0-rc.2`) to ensure Docker workflow succeeds.
|
||||||
- [ ] Pull down the Docker image and [run through application setup](https://github.com/gogs/gogs/blob/main/docker/README.md) to make sure nothing blows up.
|
- ⚠️ **Make sure the tag is created on the release branch**.
|
||||||
- [ ] Download one of the release archives and run through application setup to make sure nothing blows up.
|
- Pull down the Docker image and [run through application setup](https://github.com/gogs/gogs/blob/main/docker/README.md) to make sure nothing blows up.
|
||||||
- [ ] Publish a new [GitHub release](https://github.com/gogs/gogs/releases) ⚠️ **on the release branch** ⚠️ with entries from [CHANGELOG](https://github.com/gogs/gogs/blob/main/CHANGELOG.md) for the current patch release and all previous releases with same minor version.
|
- [ ] Publish a new [GitHub release](https://github.com/gogs/gogs/releases) with entries from [CHANGELOG](https://github.com/gogs/gogs/blob/main/CHANGELOG.md) for the current patch release and all previous releases with same minor version.
|
||||||
|
- ⚠️ **Make sure the tag is created on the release branch**.
|
||||||
- [ ] Update all previous GitHub releases with same minor version with the warning:
|
- [ ] Update all previous GitHub releases with same minor version with the warning:
|
||||||
```
|
```
|
||||||
**ℹ️ Heads up! There is a new patch release [0.12.1](https://github.com/gogs/gogs/releases/tag/v0.12.1) available, we recommend directly installing or upgrading to that version.**
|
**ℹ️ Heads up! There is a new patch release [0.12.1](https://github.com/gogs/gogs/releases/tag/v0.12.1) available, we recommend directly installing or upgrading to that version.**
|
||||||
```
|
```
|
||||||
- [ ] [Wait for new image tags for the current release](https://github.com/gogs/gogs/actions/workflows/docker.yml?query=event%3Arelease) to be created automatically on both [Docker Hub](https://hub.docker.com/r/gogs/gogs/tags) and [GitHub Container registry](https://github.com/gogs/gogs/pkgs/container/gogs).
|
- [ ] [Wait for a new image tag for the current release](https://github.com/gogs/gogs/actions/workflows/docker.yml?query=event%3Arelease) to be created automatically on both [Docker Hub](https://hub.docker.com/r/gogs/gogs/tags) and [GitHub Container registry](https://github.com/gogs/gogs/pkgs/container/gogs).
|
||||||
- Pull down the Docker image and [run through application setup](https://github.com/gogs/gogs/blob/main/docker/README.md) to make sure nothing blows up.
|
- Pull down the Docker image and [run through application setup](https://github.com/gogs/gogs/blob/main/docker/README.md) to make sure nothing blows up.
|
||||||
- [ ] Download all release archives and [generate SHA256 checksum](https://github.com/gogs/gogs/blob/main/docs/dev/release/sha256.sh) for all binaries to the file `checksum_sha256.txt`.
|
- [ ] [Update Docker image tag](https://www.notion.so/jcunknwon/Cheatsheet-and-playbooks-c3b053da42114411bd27285cd065b2a6?source=copy_link#1654f105c63f80958d96cd72e2f5df69) for the minor release `<MAJOR>.<MINOR>` on both [Docker Hub](https://hub.docker.com/r/gogs/gogs/tags) and [GitHub Container registry](https://github.com/gogs/gogs/pkgs/container/gogs).
|
||||||
- [ ] Upload all archives and `checksum_sha256.txt` to https://dl.gogs.io.
|
- [ ] [Compile and pack binaries](https://www.notion.so/jcunknwon/Cheatsheet-and-playbooks-c3b053da42114411bd27285cd065b2a6?source=copy_link#1654f105c63f803f8bfcc117395d9747) (all prefixed with `gogs_<MAJOR>.<MINOR>.<PATCH>_`, e.g. `gogs_0.12.0_`):
|
||||||
|
- [ ] macOS: `darwin_arm64.zip`, `darwin_amd64.zip`
|
||||||
|
- [ ] Linux: `linux_amd64.tar.gz`, `linux_amd64.zip`
|
||||||
|
- [ ] ARM: `linux_armv8.tar.gz`, `linux_armv8.zip`
|
||||||
|
- [ ] Windows: `windows_amd64.zip`, `windows_amd64_mws.zip`
|
||||||
|
- [ ] [Generate SHA256 checksum](https://www.notion.so/jcunknwon/Cheatsheet-and-playbooks-c3b053da42114411bd27285cd065b2a6?source=copy_link#1654f105c63f80d4a74ad8821a403f52) for all binaries to the file `checksum_sha256.txt`.
|
||||||
|
- [ ] Upload all binaries and `checksum_sha256.txt` to:
|
||||||
|
- [ ] GitHub release
|
||||||
|
- [ ] https://dl.gogs.io
|
||||||
|
- [ ] Update content of [Install from binary](https://gogs.io/docs/installation/install_from_binary).
|
||||||
|
|
||||||
## After release
|
## After release
|
||||||
|
|
||||||
@@ -45,5 +54,5 @@ On the `main` branch:
|
|||||||
```
|
```
|
||||||
- [ ] Create a new release announcement in [Discussions](https://github.com/gogs/gogs/discussions/categories/announcements).
|
- [ ] Create a new release announcement in [Discussions](https://github.com/gogs/gogs/discussions/categories/announcements).
|
||||||
- [ ] Send a tweet on the [official Twitter account](https://twitter.com/GogsHQ) for the patch release.
|
- [ ] Send a tweet on the [official Twitter account](https://twitter.com/GogsHQ) for the patch release.
|
||||||
- [ ] Close the milestone for the patch release.
|
- [ ] Close the patch milestone.
|
||||||
- [ ] **After 14 days**, publish [GitHub security advisories](https://github.com/gogs/gogs/security) for security patches included in the release.
|
- [ ] **After 14 days**, publish [GitHub security advisories](https://github.com/gogs/gogs/security) for security patches included in the release.
|
||||||
|
|||||||
16
.github/workflows/digitalocean_gc.yml
vendored
16
.github/workflows/digitalocean_gc.yml
vendored
@@ -19,8 +19,18 @@ jobs:
|
|||||||
# --include-untagged-manifests: Deletes unreferenced manifests to maximize space
|
# --include-untagged-manifests: Deletes unreferenced manifests to maximize space
|
||||||
doctl registry garbage-collection start --force --include-untagged-manifests
|
doctl registry garbage-collection start --force --include-untagged-manifests
|
||||||
- name: Send email on failure
|
- name: Send email on failure
|
||||||
uses: unknwon/send-email-on-failure@89339a1bc93f4ad1d30f3b7e4911fcba985c9adb # v1
|
uses: dawidd6/action-send-mail@2cea9617b09d79a095af21254fbcb7ae95903dde # v3.12.0
|
||||||
if: ${{ failure() }}
|
if: ${{ failure() }}
|
||||||
with:
|
with:
|
||||||
smtp_username: ${{ secrets.SMTP_USERNAME }}
|
server_address: smtp.mailgun.org
|
||||||
smtp_password: ${{ secrets.SMTP_PASSWORD }}
|
server_port: 465
|
||||||
|
username: ${{ secrets.SMTP_USERNAME }}
|
||||||
|
password: ${{ secrets.SMTP_PASSWORD }}
|
||||||
|
subject: GitHub Actions (${{ github.repository }}) job result
|
||||||
|
to: github-actions-8ce6454@unknwon.io
|
||||||
|
from: GitHub Actions (${{ github.repository }})
|
||||||
|
reply_to: noreply@unknwon.io
|
||||||
|
body: |
|
||||||
|
The job "${{ github.job }}" of ${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }} completed with "${{ job.status }}".
|
||||||
|
|
||||||
|
View the job run at: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||||
|
|||||||
130
.github/workflows/docker.yml
vendored
130
.github/workflows/docker.yml
vendored
@@ -68,11 +68,21 @@ jobs:
|
|||||||
image-ref: gogs/gogs:latest
|
image-ref: gogs/gogs:latest
|
||||||
exit-code: '1'
|
exit-code: '1'
|
||||||
- name: Send email on failure
|
- name: Send email on failure
|
||||||
uses: unknwon/send-email-on-failure@89339a1bc93f4ad1d30f3b7e4911fcba985c9adb # v1
|
uses: dawidd6/action-send-mail@2cea9617b09d79a095af21254fbcb7ae95903dde # v3.12.0
|
||||||
if: ${{ failure() }}
|
if: ${{ failure() }}
|
||||||
with:
|
with:
|
||||||
smtp_username: ${{ secrets.SMTP_USERNAME }}
|
server_address: smtp.mailgun.org
|
||||||
smtp_password: ${{ secrets.SMTP_PASSWORD }}
|
server_port: 465
|
||||||
|
username: ${{ secrets.SMTP_USERNAME }}
|
||||||
|
password: ${{ secrets.SMTP_PASSWORD }}
|
||||||
|
subject: GitHub Actions (${{ github.repository }}) job result
|
||||||
|
to: github-actions-8ce6454@unknwon.io
|
||||||
|
from: GitHub Actions (${{ github.repository }})
|
||||||
|
reply_to: noreply@unknwon.io
|
||||||
|
body: |
|
||||||
|
The job "${{ github.job }}" of ${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }} completed with "${{ job.status }}".
|
||||||
|
|
||||||
|
View the job run at: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||||
|
|
||||||
buildx-next:
|
buildx-next:
|
||||||
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && github.repository == 'gogs/gogs' }}
|
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && github.repository == 'gogs/gogs' }}
|
||||||
@@ -135,11 +145,21 @@ jobs:
|
|||||||
image-ref: gogs/gogs:next-latest
|
image-ref: gogs/gogs:next-latest
|
||||||
exit-code: '1'
|
exit-code: '1'
|
||||||
- name: Send email on failure
|
- name: Send email on failure
|
||||||
uses: unknwon/send-email-on-failure@89339a1bc93f4ad1d30f3b7e4911fcba985c9adb # v1
|
uses: dawidd6/action-send-mail@2cea9617b09d79a095af21254fbcb7ae95903dde # v3.12.0
|
||||||
if: ${{ failure() }}
|
if: ${{ failure() }}
|
||||||
with:
|
with:
|
||||||
smtp_username: ${{ secrets.SMTP_USERNAME }}
|
server_address: smtp.mailgun.org
|
||||||
smtp_password: ${{ secrets.SMTP_PASSWORD }}
|
server_port: 465
|
||||||
|
username: ${{ secrets.SMTP_USERNAME }}
|
||||||
|
password: ${{ secrets.SMTP_PASSWORD }}
|
||||||
|
subject: GitHub Actions (${{ github.repository }}) job result
|
||||||
|
to: github-actions-8ce6454@unknwon.io
|
||||||
|
from: GitHub Actions (${{ github.repository }})
|
||||||
|
reply_to: noreply@unknwon.io
|
||||||
|
body: |
|
||||||
|
The job "${{ github.job }}" of ${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }} completed with "${{ job.status }}".
|
||||||
|
|
||||||
|
View the job run at: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||||
|
|
||||||
deploy-demo:
|
deploy-demo:
|
||||||
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && github.repository == 'gogs/gogs' }}
|
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && github.repository == 'gogs/gogs' }}
|
||||||
@@ -161,11 +181,21 @@ jobs:
|
|||||||
kubectl rollout restart deployment gogs-demo -n gogs
|
kubectl rollout restart deployment gogs-demo -n gogs
|
||||||
kubectl rollout status deployment gogs-demo -n gogs
|
kubectl rollout status deployment gogs-demo -n gogs
|
||||||
- name: Send email on failure
|
- name: Send email on failure
|
||||||
uses: unknwon/send-email-on-failure@89339a1bc93f4ad1d30f3b7e4911fcba985c9adb # v1
|
uses: dawidd6/action-send-mail@2cea9617b09d79a095af21254fbcb7ae95903dde # v3.12.0
|
||||||
if: ${{ failure() }}
|
if: ${{ failure() }}
|
||||||
with:
|
with:
|
||||||
smtp_username: ${{ secrets.SMTP_USERNAME }}
|
server_address: smtp.mailgun.org
|
||||||
smtp_password: ${{ secrets.SMTP_PASSWORD }}
|
server_port: 465
|
||||||
|
username: ${{ secrets.SMTP_USERNAME }}
|
||||||
|
password: ${{ secrets.SMTP_PASSWORD }}
|
||||||
|
subject: GitHub Actions (${{ github.repository }}) job result
|
||||||
|
to: github-actions-8ce6454@unknwon.io
|
||||||
|
from: GitHub Actions (${{ github.repository }})
|
||||||
|
reply_to: noreply@unknwon.io
|
||||||
|
body: |
|
||||||
|
The job "${{ github.job }}" of ${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }} completed with "${{ job.status }}".
|
||||||
|
|
||||||
|
View the job run at: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||||
|
|
||||||
buildx-pull-request:
|
buildx-pull-request:
|
||||||
if: ${{ github.event_name == 'pull_request'}}
|
if: ${{ github.event_name == 'pull_request'}}
|
||||||
@@ -255,25 +285,8 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
steps:
|
steps:
|
||||||
- name: Compute image tags
|
- name: Compute image tag name
|
||||||
run: |
|
run: echo "IMAGE_TAG=$(echo $GITHUB_REF_NAME | cut -c 2-)" >> $GITHUB_ENV
|
||||||
IMAGE_TAG=$(echo $GITHUB_REF_NAME | cut -c 2-)
|
|
||||||
echo "IMAGE_TAG=$IMAGE_TAG" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
TAGS="gogs/gogs:$IMAGE_TAG
|
|
||||||
ghcr.io/gogs/gogs:$IMAGE_TAG"
|
|
||||||
|
|
||||||
# Add minor version tag for stable releases (no prerelease suffix per semver).
|
|
||||||
if [[ ! "$IMAGE_TAG" =~ - ]]; then
|
|
||||||
MINOR_TAG=$(echo "$IMAGE_TAG" | cut -d. -f1,2)
|
|
||||||
TAGS="$TAGS
|
|
||||||
gogs/gogs:$MINOR_TAG
|
|
||||||
ghcr.io/gogs/gogs:$MINOR_TAG"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "TAGS<<EOF" >> $GITHUB_ENV
|
|
||||||
echo "$TAGS" >> $GITHUB_ENV
|
|
||||||
echo "EOF" >> $GITHUB_ENV
|
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
@@ -307,13 +320,25 @@ jobs:
|
|||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ env.TAGS }}
|
tags: |
|
||||||
|
gogs/gogs:${{ env.IMAGE_TAG }}
|
||||||
|
ghcr.io/gogs/gogs:${{ env.IMAGE_TAG }}
|
||||||
- name: Send email on failure
|
- name: Send email on failure
|
||||||
uses: unknwon/send-email-on-failure@89339a1bc93f4ad1d30f3b7e4911fcba985c9adb # v1
|
uses: dawidd6/action-send-mail@2cea9617b09d79a095af21254fbcb7ae95903dde # v3.12.0
|
||||||
if: ${{ failure() }}
|
if: ${{ failure() }}
|
||||||
with:
|
with:
|
||||||
smtp_username: ${{ secrets.SMTP_USERNAME }}
|
server_address: smtp.mailgun.org
|
||||||
smtp_password: ${{ secrets.SMTP_PASSWORD }}
|
server_port: 465
|
||||||
|
username: ${{ secrets.SMTP_USERNAME }}
|
||||||
|
password: ${{ secrets.SMTP_PASSWORD }}
|
||||||
|
subject: GitHub Actions (${{ github.repository }}) job result
|
||||||
|
to: github-actions-8ce6454@unknwon.io
|
||||||
|
from: GitHub Actions (${{ github.repository }})
|
||||||
|
reply_to: noreply@unknwon.io
|
||||||
|
body: |
|
||||||
|
The job "${{ github.job }}" of ${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }} completed with "${{ job.status }}".
|
||||||
|
|
||||||
|
View the job run at: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||||
|
|
||||||
# Updates to the following section needs to be synced to all release branches within their lifecycles.
|
# Updates to the following section needs to be synced to all release branches within their lifecycles.
|
||||||
buildx-next-release:
|
buildx-next-release:
|
||||||
@@ -324,25 +349,8 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
steps:
|
steps:
|
||||||
- name: Compute image tags
|
- name: Compute image tag name
|
||||||
run: |
|
run: echo "IMAGE_TAG=$(echo $GITHUB_REF_NAME | cut -c 2-)" >> $GITHUB_ENV
|
||||||
IMAGE_TAG=$(echo $GITHUB_REF_NAME | cut -c 2-)
|
|
||||||
echo "IMAGE_TAG=$IMAGE_TAG" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
TAGS="gogs/gogs:next-$IMAGE_TAG
|
|
||||||
ghcr.io/gogs/gogs:next-$IMAGE_TAG"
|
|
||||||
|
|
||||||
# Add minor version tag for stable releases (no prerelease suffix per semver).
|
|
||||||
if [[ ! "$IMAGE_TAG" =~ - ]]; then
|
|
||||||
MINOR_TAG=$(echo "$IMAGE_TAG" | cut -d. -f1,2)
|
|
||||||
TAGS="$TAGS
|
|
||||||
gogs/gogs:next-$MINOR_TAG
|
|
||||||
ghcr.io/gogs/gogs:next-$MINOR_TAG"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "TAGS<<EOF" >> $GITHUB_ENV
|
|
||||||
echo "$TAGS" >> $GITHUB_ENV
|
|
||||||
echo "EOF" >> $GITHUB_ENV
|
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
@@ -377,13 +385,25 @@ jobs:
|
|||||||
file: Dockerfile.next
|
file: Dockerfile.next
|
||||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ env.TAGS }}
|
tags: |
|
||||||
|
gogs/gogs:next-${{ env.IMAGE_TAG }}
|
||||||
|
ghcr.io/gogs/gogs:next-${{ env.IMAGE_TAG }}
|
||||||
- name: Send email on failure
|
- name: Send email on failure
|
||||||
uses: unknwon/send-email-on-failure@89339a1bc93f4ad1d30f3b7e4911fcba985c9adb # v1
|
uses: dawidd6/action-send-mail@2cea9617b09d79a095af21254fbcb7ae95903dde # v3.12.0
|
||||||
if: ${{ failure() }}
|
if: ${{ failure() }}
|
||||||
with:
|
with:
|
||||||
smtp_username: ${{ secrets.SMTP_USERNAME }}
|
server_address: smtp.mailgun.org
|
||||||
smtp_password: ${{ secrets.SMTP_PASSWORD }}
|
server_port: 465
|
||||||
|
username: ${{ secrets.SMTP_USERNAME }}
|
||||||
|
password: ${{ secrets.SMTP_PASSWORD }}
|
||||||
|
subject: GitHub Actions (${{ github.repository }}) job result
|
||||||
|
to: github-actions-8ce6454@unknwon.io
|
||||||
|
from: GitHub Actions (${{ github.repository }})
|
||||||
|
reply_to: noreply@unknwon.io
|
||||||
|
body: |
|
||||||
|
The job "${{ github.job }}" of ${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }} completed with "${{ job.status }}".
|
||||||
|
|
||||||
|
View the job run at: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||||
|
|
||||||
digitalocean-gc:
|
digitalocean-gc:
|
||||||
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && github.repository == 'gogs/gogs' }}
|
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && github.repository == 'gogs/gogs' }}
|
||||||
|
|||||||
76
.github/workflows/go.yml
vendored
76
.github/workflows/go.yml
vendored
@@ -72,16 +72,28 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
go-version: ${{ matrix.go-version }}
|
go-version: ${{ matrix.go-version }}
|
||||||
- name: Run tests with coverage
|
- name: Run tests with coverage
|
||||||
run: |
|
run: go test -shuffle=on -v -race -coverprofile=coverage -covermode=atomic ./...
|
||||||
go test -shuffle=on -v -race -coverprofile=coverage -covermode=atomic -json ./... > test-report.json
|
- name: Upload coverage report to Codecov
|
||||||
go install github.com/mfridman/tparse@latest
|
uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1
|
||||||
tparse -all -file=test-report.json
|
with:
|
||||||
|
file: ./coverage
|
||||||
|
flags: unittests
|
||||||
- name: Send email on failure
|
- name: Send email on failure
|
||||||
uses: unknwon/send-email-on-failure@89339a1bc93f4ad1d30f3b7e4911fcba985c9adb # v1
|
uses: dawidd6/action-send-mail@2cea9617b09d79a095af21254fbcb7ae95903dde # v3.12.0
|
||||||
if: ${{ failure() && github.event_name == 'push' && github.ref == 'refs/heads/main' }}
|
if: ${{ failure() && github.event_name == 'push' && github.ref == 'refs/heads/main' }}
|
||||||
with:
|
with:
|
||||||
smtp_username: ${{ secrets.SMTP_USERNAME }}
|
server_address: smtp.mailgun.org
|
||||||
smtp_password: ${{ secrets.SMTP_PASSWORD }}
|
server_port: 465
|
||||||
|
username: ${{ secrets.SMTP_USERNAME }}
|
||||||
|
password: ${{ secrets.SMTP_PASSWORD }}
|
||||||
|
subject: GitHub Actions (${{ github.repository }}) job result
|
||||||
|
to: github-actions-8ce6454@unknwon.io
|
||||||
|
from: GitHub Actions (${{ github.repository }})
|
||||||
|
reply_to: noreply@unknwon.io
|
||||||
|
body: |
|
||||||
|
The job "${{ github.job }}" of ${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }} completed with "${{ job.status }}".
|
||||||
|
|
||||||
|
View the job run at: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||||
|
|
||||||
# Running tests with race detection consumes too much memory on Windows,
|
# Running tests with race detection consumes too much memory on Windows,
|
||||||
# see https://github.com/golang/go/issues/46099 for details.
|
# see https://github.com/golang/go/issues/46099 for details.
|
||||||
@@ -101,12 +113,27 @@ jobs:
|
|||||||
go-version: ${{ matrix.go-version }}
|
go-version: ${{ matrix.go-version }}
|
||||||
- name: Run tests with coverage
|
- name: Run tests with coverage
|
||||||
run: go test -shuffle=on -v -coverprofile=coverage -covermode=atomic ./...
|
run: go test -shuffle=on -v -coverprofile=coverage -covermode=atomic ./...
|
||||||
|
- name: Upload coverage report to Codecov
|
||||||
|
uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1
|
||||||
|
with:
|
||||||
|
file: ./coverage
|
||||||
|
flags: unittests
|
||||||
- name: Send email on failure
|
- name: Send email on failure
|
||||||
uses: unknwon/send-email-on-failure@89339a1bc93f4ad1d30f3b7e4911fcba985c9adb # v1
|
uses: dawidd6/action-send-mail@2cea9617b09d79a095af21254fbcb7ae95903dde # v3.12.0
|
||||||
if: ${{ failure() && github.event_name == 'push' && github.ref == 'refs/heads/main' }}
|
if: ${{ failure() && github.event_name == 'push' && github.ref == 'refs/heads/main' }}
|
||||||
with:
|
with:
|
||||||
smtp_username: ${{ secrets.SMTP_USERNAME }}
|
server_address: smtp.mailgun.org
|
||||||
smtp_password: ${{ secrets.SMTP_PASSWORD }}
|
server_port: 465
|
||||||
|
username: ${{ secrets.SMTP_USERNAME }}
|
||||||
|
password: ${{ secrets.SMTP_PASSWORD }}
|
||||||
|
subject: GitHub Actions (${{ github.repository }}) job result
|
||||||
|
to: github-actions-8ce6454@unknwon.io
|
||||||
|
from: GitHub Actions (${{ github.repository }})
|
||||||
|
reply_to: noreply@unknwon.io
|
||||||
|
body: |
|
||||||
|
The job "${{ github.job }}" of ${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }} completed with "${{ job.status }}".
|
||||||
|
|
||||||
|
View the job run at: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||||
|
|
||||||
postgres:
|
postgres:
|
||||||
name: Postgres
|
name: Postgres
|
||||||
@@ -135,10 +162,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
go-version: ${{ matrix.go-version }}
|
go-version: ${{ matrix.go-version }}
|
||||||
- name: Run tests with coverage
|
- name: Run tests with coverage
|
||||||
run: |
|
run: go test -shuffle=on -v -race -coverprofile=coverage -covermode=atomic ./internal/database/...
|
||||||
go test -shuffle=on -v -race -coverprofile=coverage -covermode=atomic -json ./internal/database/... > test-report.json
|
|
||||||
go install github.com/mfridman/tparse@latest
|
|
||||||
tparse -all -file=test-report.json
|
|
||||||
env:
|
env:
|
||||||
GOGS_DATABASE_TYPE: postgres
|
GOGS_DATABASE_TYPE: postgres
|
||||||
PGPORT: 5432
|
PGPORT: 5432
|
||||||
@@ -164,13 +188,29 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
go-version: ${{ matrix.go-version }}
|
go-version: ${{ matrix.go-version }}
|
||||||
- name: Run tests with coverage
|
- name: Run tests with coverage
|
||||||
run: |
|
run: go test -shuffle=on -v -race -coverprofile=coverage -covermode=atomic ./internal/database/...
|
||||||
go test -shuffle=on -v -race -coverprofile=coverage -covermode=atomic -json ./internal/database/... > test-report.json
|
|
||||||
go install github.com/mfridman/tparse@latest
|
|
||||||
tparse -all -file=test-report.json
|
|
||||||
env:
|
env:
|
||||||
GOGS_DATABASE_TYPE: mysql
|
GOGS_DATABASE_TYPE: mysql
|
||||||
MYSQL_USER: root
|
MYSQL_USER: root
|
||||||
MYSQL_PASSWORD: root
|
MYSQL_PASSWORD: root
|
||||||
MYSQL_HOST: localhost
|
MYSQL_HOST: localhost
|
||||||
MYSQL_PORT: 3306
|
MYSQL_PORT: 3306
|
||||||
|
|
||||||
|
sqlite-go:
|
||||||
|
name: SQLite - Go
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
go-version: [ 1.25.x ]
|
||||||
|
platform: [ ubuntu-latest ]
|
||||||
|
runs-on: ${{ matrix.platform }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
|
- name: Install Go
|
||||||
|
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||||
|
with:
|
||||||
|
go-version: ${{ matrix.go-version }}
|
||||||
|
- name: Run tests with coverage
|
||||||
|
run: go test -shuffle=on -v -race -parallel=1 -coverprofile=coverage -covermode=atomic ./internal/database/...
|
||||||
|
env:
|
||||||
|
GOGS_DATABASE_TYPE: sqlite
|
||||||
|
|||||||
146
.github/workflows/release.yml
vendored
146
.github/workflows/release.yml
vendored
@@ -1,146 +0,0 @@
|
|||||||
name: Release
|
|
||||||
|
|
||||||
on:
|
|
||||||
release:
|
|
||||||
types: [published]
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
pull_request:
|
|
||||||
paths:
|
|
||||||
- '.github/workflows/release.yml'
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
env:
|
|
||||||
GOPROXY: "https://proxy.golang.org"
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
name: Build ${{ matrix.goos }}/${{ matrix.goarch }}${{ matrix.suffix }}
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- {goos: linux, goarch: amd64}
|
|
||||||
- {goos: linux, goarch: arm64}
|
|
||||||
- {goos: linux, goarch: "386"}
|
|
||||||
- {goos: darwin, goarch: amd64}
|
|
||||||
- {goos: darwin, goarch: arm64}
|
|
||||||
- {goos: windows, goarch: amd64}
|
|
||||||
- {goos: windows, goarch: arm64}
|
|
||||||
- {goos: windows, goarch: "386"}
|
|
||||||
- {goos: windows, goarch: amd64, suffix: "_mws", tags: minwinsvc}
|
|
||||||
- {goos: windows, goarch: arm64, suffix: "_mws", tags: minwinsvc}
|
|
||||||
- {goos: windows, goarch: "386", suffix: "_mws", tags: minwinsvc}
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
|
||||||
- name: Setup Go
|
|
||||||
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
|
||||||
with:
|
|
||||||
go-version: 1.25.x
|
|
||||||
- name: Determine version
|
|
||||||
id: version
|
|
||||||
run: |
|
|
||||||
if [ "${{ github.event_name }}" = "release" ]; then
|
|
||||||
echo "version=${{ github.event.release.tag_name }}" | sed 's/version=v/version=/' >> "$GITHUB_OUTPUT"
|
|
||||||
echo "release_tag=${{ github.event.release.tag_name }}" >> "$GITHUB_OUTPUT"
|
|
||||||
elif [ "${{ github.event_name }}" = "push" ] && [ "${{ github.ref }}" = "refs/heads/main" ]; then
|
|
||||||
echo "version=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "release_tag=latest-commit-build" >> "$GITHUB_OUTPUT"
|
|
||||||
else
|
|
||||||
echo "version=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "release_tag=release-archive-testing" >> "$GITHUB_OUTPUT"
|
|
||||||
fi
|
|
||||||
- name: Build binary
|
|
||||||
env:
|
|
||||||
GOOS: ${{ matrix.goos }}
|
|
||||||
GOARCH: ${{ matrix.goarch }}
|
|
||||||
CGO_ENABLED: "0"
|
|
||||||
run: |
|
|
||||||
BINARY_NAME="gogs"
|
|
||||||
if [ "${{ matrix.goos }}" = "windows" ]; then
|
|
||||||
BINARY_NAME="gogs.exe"
|
|
||||||
fi
|
|
||||||
|
|
||||||
TAGS_FLAG=""
|
|
||||||
if [ -n "${{ matrix.tags }}" ]; then
|
|
||||||
TAGS_FLAG="-tags ${{ matrix.tags }}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
go build -v \
|
|
||||||
-ldflags "
|
|
||||||
-X \"gogs.io/gogs/internal/conf.BuildTime=$(date -u '+%Y-%m-%d %I:%M:%S %Z')\"
|
|
||||||
-X \"gogs.io/gogs/internal/conf.BuildCommit=$(git rev-parse HEAD)\"
|
|
||||||
" \
|
|
||||||
$TAGS_FLAG \
|
|
||||||
-trimpath -o "$BINARY_NAME" ./cmd/gogs
|
|
||||||
- name: Prepare archive contents
|
|
||||||
run: |
|
|
||||||
mkdir -p dist/gogs
|
|
||||||
BINARY_NAME="gogs"
|
|
||||||
if [ "${{ matrix.goos }}" = "windows" ]; then
|
|
||||||
BINARY_NAME="gogs.exe"
|
|
||||||
fi
|
|
||||||
cp "$BINARY_NAME" dist/gogs/
|
|
||||||
cp LICENSE README.md README_ZH.md dist/gogs/
|
|
||||||
cp -r scripts dist/gogs/
|
|
||||||
- name: Create archives
|
|
||||||
working-directory: dist
|
|
||||||
run: |
|
|
||||||
VERSION="${{ steps.version.outputs.version }}"
|
|
||||||
ARCHIVE_BASE="gogs_${VERSION}_${{ matrix.goos }}_${{ matrix.goarch }}${{ matrix.suffix }}"
|
|
||||||
|
|
||||||
zip -r "${ARCHIVE_BASE}.zip" gogs
|
|
||||||
|
|
||||||
if [ "${{ matrix.goos }}" = "linux" ]; then
|
|
||||||
tar -czvf "${ARCHIVE_BASE}.tar.gz" gogs
|
|
||||||
fi
|
|
||||||
- name: Upload to release
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ github.token }}
|
|
||||||
run: |
|
|
||||||
RELEASE_TAG="${{ steps.version.outputs.release_tag }}"
|
|
||||||
|
|
||||||
if [ "${{ github.event_name }}" != "release" ]; then
|
|
||||||
git tag -f "$RELEASE_TAG"
|
|
||||||
git push origin "$RELEASE_TAG" --force || true
|
|
||||||
|
|
||||||
RELEASE_TITLE="Release Archive Testing"
|
|
||||||
RELEASE_NOTES="Automated testing release for workflow development."
|
|
||||||
if [ "$RELEASE_TAG" = "latest-commit-build" ]; then
|
|
||||||
RELEASE_TITLE="Latest Commit Build"
|
|
||||||
RELEASE_NOTES="Automated build from the latest commit on main branch. This release is updated automatically with every push to main."
|
|
||||||
fi
|
|
||||||
|
|
||||||
gh release view "$RELEASE_TAG" || gh release create "$RELEASE_TAG" --title "$RELEASE_TITLE" --notes "$RELEASE_NOTES" --prerelease
|
|
||||||
fi
|
|
||||||
|
|
||||||
PATTERN="_${{ matrix.goos }}_${{ matrix.goarch }}${{ matrix.suffix }}\."
|
|
||||||
gh release view "$RELEASE_TAG" --json assets --jq ".assets[].name" | grep "$PATTERN" | while read -r asset; do
|
|
||||||
gh release delete-asset "$RELEASE_TAG" "$asset" --yes || true
|
|
||||||
done
|
|
||||||
|
|
||||||
gh release upload "$RELEASE_TAG" dist/gogs_*.zip --clobber
|
|
||||||
if [ "${{ matrix.goos }}" = "linux" ]; then
|
|
||||||
gh release upload "$RELEASE_TAG" dist/gogs_*.tar.gz --clobber
|
|
||||||
fi
|
|
||||||
|
|
||||||
notify-failure:
|
|
||||||
name: Notify on failure
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: [build]
|
|
||||||
if: ${{ failure() && github.event_name == 'push' && github.ref == 'refs/heads/main' }}
|
|
||||||
steps:
|
|
||||||
- name: Send email on failure
|
|
||||||
uses: unknwon/send-email-on-failure@89339a1bc93f4ad1d30f3b7e4911fcba985c9adb # v1
|
|
||||||
with:
|
|
||||||
smtp_username: ${{ secrets.SMTP_USERNAME }}
|
|
||||||
smtp_password: ${{ secrets.SMTP_PASSWORD }}
|
|
||||||
24
.gitignore
vendored
24
.gitignore
vendored
@@ -1,16 +1,18 @@
|
|||||||
# Build artifacts
|
.DS_Store
|
||||||
.bin/
|
*.db
|
||||||
dist/
|
*.log
|
||||||
|
|
||||||
# Runtime data
|
|
||||||
log/
|
log/
|
||||||
custom/
|
custom/
|
||||||
data/
|
data/
|
||||||
|
|
||||||
# Configuration and application files
|
|
||||||
.idea/
|
.idea/
|
||||||
.task/
|
*.iml
|
||||||
|
public/img/avatar/
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
/gogs
|
||||||
|
profile/
|
||||||
|
*.pem
|
||||||
|
output*
|
||||||
|
/release
|
||||||
|
.task
|
||||||
.envrc
|
.envrc
|
||||||
|
|
||||||
# System junk
|
|
||||||
.DS_Store
|
|
||||||
|
|||||||
14
AGENTS.md
14
AGENTS.md
@@ -1,7 +1,6 @@
|
|||||||
## Core principles
|
## Core principles
|
||||||
|
|
||||||
- Stop telling me "You're right", it just shows how incompetent you are. Do it right on your first try, fact-check and review after changes. If you are not sure, ask for help.
|
- When you see changes made outside your knowledge, use the current version as your new starting point. Do not blindly overwrite those changes or you suck. Even if you have to update the code, always respect the god damn pattern in the surrounding context!
|
||||||
- When you see changes made outside your knowledge, use the current version as your new starting point. Do not blindly overwrite those changes or you suck. Even if you have to update the code, always respect the pattern in the surrounding context!
|
|
||||||
|
|
||||||
## Style and mechanics
|
## Style and mechanics
|
||||||
|
|
||||||
@@ -16,17 +15,6 @@ This applies to all texts, including but not limited to UI, documentation, code
|
|||||||
- Use `github.com/cockroachdb/errors` for error handling.
|
- Use `github.com/cockroachdb/errors` for error handling.
|
||||||
- Use `github.com/stretchr/testify` for assertions in tests. Be mindful about the choice of `require` and `assert`, the former should be used when the test cannot proceed meaningfully after a failed assertion.
|
- Use `github.com/stretchr/testify` for assertions in tests. Be mindful about the choice of `require` and `assert`, the former should be used when the test cannot proceed meaningfully after a failed assertion.
|
||||||
|
|
||||||
## Build instructions
|
|
||||||
|
|
||||||
- Prefer `task` command over vanilla `go` command when available. Use `--force` flag when necessary.
|
|
||||||
- Run `task lint` after every time you finish changing code, and fix all linter errors.
|
|
||||||
|
|
||||||
## Tool-use guidance
|
## Tool-use guidance
|
||||||
|
|
||||||
- Use `gh` CLI to access information on github.com that is not publicly available.
|
- Use `gh` CLI to access information on github.com that is not publicly available.
|
||||||
|
|
||||||
## Source code control
|
|
||||||
|
|
||||||
- When pushing changes to a pull request from a fork, use SSH address and do not add remote.
|
|
||||||
- Never automatically executes commands that touches Git history even if the session does not require approvals, including but not limited to `rebase`, `commit`, `push`, `pull`, `reset`, `amend`. Exceptions are only allowed case-by-case.
|
|
||||||
- Do not amend commits unless being explicitly asked to do so.
|
|
||||||
|
|||||||
16
CHANGELOG.md
16
CHANGELOG.md
@@ -2,38 +2,26 @@
|
|||||||
|
|
||||||
All notable changes to Gogs are documented in this file.
|
All notable changes to Gogs are documented in this file.
|
||||||
|
|
||||||
## 0.15.0+dev (`main`)
|
## 0.14.0+dev (`main`)
|
||||||
|
|
||||||
## 0.14.1
|
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Support comparing tags in addition to branches. [#6141](https://github.com/gogs/gogs/issues/6141)
|
|
||||||
- Show file name in browser tab title when viewing files. [#5896](https://github.com/gogs/gogs/pull/5896)
|
|
||||||
- Support using TLS for Redis session provider using `[session] PROVIDER_CONFIG = ...,tls=true`. [#7860](https://github.com/gogs/gogs/pull/7860)
|
- Support using TLS for Redis session provider using `[session] PROVIDER_CONFIG = ...,tls=true`. [#7860](https://github.com/gogs/gogs/pull/7860)
|
||||||
- Support expanading values in `app.ini` from environment variables, e.g. `[database] PASSWORD = ${DATABASE_PASSWORD}`. [#8057](https://github.com/gogs/gogs/pull/8057)
|
- Support expanading values in `app.ini` from environment variables, e.g. `[database] PASSWORD = ${DATABASE_PASSWORD}`. [#8057](https://github.com/gogs/gogs/pull/8057)
|
||||||
- Support custom logout URL that users get redirected to after sign out using `[auth] CUSTOM_LOGOUT_URL`. [#8089](https://github.com/gogs/gogs/pull/8089)
|
- Support custom logout URL that users get redirected to after sign out using `[auth] CUSTOM_LOGOUT_URL`. [#8089](https://github.com/gogs/gogs/pull/8089)
|
||||||
- Start publishing next-generation, security-focused Docker image via `gogs/gogs:next-latest`, which will become the default image distribution (`gogs/gogs:latest`) starting 0.16.0. While not all container options support have been added in the next-generation image, the use of current legacy Docker image is deprecated, it will be published as `gogs/gogs:legacy-latest` starting 0.16.0, and be completely removed no earlier than 0.17.0. [#8061](https://github.com/gogs/gogs/pull/8061)
|
- Start publishing next-generation, security-focused Docker image via `gogs/gogs:next-latest`, which will become the default image distribution (`gogs/gogs:latest`) starting 0.15.0. While not all container options support have been added in the next-generation image, the use of current legacy Docker image is deprecated, it will be published as `gogs/gogs:legacy-latest` starting 0.15.0, and be completely removed starting 0.16.0. [#8061](https://github.com/gogs/gogs/pull/8061)
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- The required Go version to compile source code changed to 1.25.
|
- The required Go version to compile source code changed to 1.25.
|
||||||
- The build tag `cert` has been removed, and the `gogs cert` subcommand is now always available. [#7883](https://github.com/gogs/gogs/pull/7883)
|
- The build tag `cert` has been removed, and the `gogs cert` subcommand is now always available. [#7883](https://github.com/gogs/gogs/pull/7883)
|
||||||
- Switched to pure-Go SQLite driver, CGO is no longer required to compile Gogs. [#7882](https://github.com/gogs/gogs/issues/7882)
|
|
||||||
- Updated Mermaid JS to 11.9.0. [#8009](https://github.com/gogs/gogs/pull/8009)
|
- Updated Mermaid JS to 11.9.0. [#8009](https://github.com/gogs/gogs/pull/8009)
|
||||||
- Halt the repository creation and leave the directory untouched if the repository root already exists. [#8091](https://github.com/gogs/gogs/pull/8091)
|
- Halt the repository creation and leave the directory untouched if the repository root already exists. [#8091](https://github.com/gogs/gogs/pull/8091)
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- _Security:_ Unauthenticated file upload. [#8128](https://github.com/gogs/gogs/pull/8128) - [GHSA-fc3h-92p8-h36f](https://github.com/gogs/gogs/security/advisories/GHSA-fc3h-92p8-h36f)
|
|
||||||
- _Security:_ Protected branch bypass in web UI. [#8124](https://github.com/gogs/gogs/pull/8124) - [GHSA-2c6v-8r3v-gh6p](https://github.com/gogs/gogs/security/advisories/GHSA-2c6v-8r3v-gh6p)
|
|
||||||
- _Security:_ Authorization bypass allows cross-repository label modification. [#8123](https://github.com/gogs/gogs/pull/8123) - [GHSA-cv22-72px-f4gh](https://github.com/gogs/gogs/security/advisories/GHSA-cv22-72px-f4gh)
|
|
||||||
- _Security:_ Cross-repository comment deletion. [#8119](https://github.com/gogs/gogs/pull/8119) - [GHSA-jj5m-h57j-5gv7](https://github.com/gogs/gogs/security/advisories/GHSA-jj5m-h57j-5gv7)
|
|
||||||
- 500 error on repository watchers and stargazers pages when using MSSQL. [#5482](https://github.com/gogs/gogs/issues/5482)
|
|
||||||
- Submodules using `ssh://` protocol and a port number are not rendered correctly. [#4941](https://github.com/gogs/gogs/issues/4941)
|
- Submodules using `ssh://` protocol and a port number are not rendered correctly. [#4941](https://github.com/gogs/gogs/issues/4941)
|
||||||
- Missing link to user profile on the first commit in commits history page. [#7404](https://github.com/gogs/gogs/issues/7404)
|
- Missing link to user profile on the first commit in commits history page. [#7404](https://github.com/gogs/gogs/issues/7404)
|
||||||
- Unable to delete or display files with special characters in their names. [#7596](https://github.com/gogs/gogs/issues/7596)
|
|
||||||
- Docker healthcheck fails when `HTTP_PROXY` or `HTTPS_PROXY` environment variables are set. [#7529](https://github.com/gogs/gogs/issues/7529)
|
|
||||||
|
|
||||||
## 0.13.4
|
## 0.13.4
|
||||||
|
|
||||||
|
|||||||
@@ -25,20 +25,20 @@ RUN apk --no-cache --no-progress add \
|
|||||||
tzdata \
|
tzdata \
|
||||||
rsync
|
rsync
|
||||||
|
|
||||||
ENV GOGS_CUSTOM=/data/gogs
|
ENV GOGS_CUSTOM /data/gogs
|
||||||
|
|
||||||
# Configure LibC Name Service
|
# Configure LibC Name Service
|
||||||
COPY docker/nsswitch.conf /etc/nsswitch.conf
|
COPY docker/nsswitch.conf /etc/nsswitch.conf
|
||||||
|
|
||||||
WORKDIR /app/gogs
|
WORKDIR /app/gogs
|
||||||
COPY docker ./docker
|
COPY docker ./docker
|
||||||
COPY --from=binarybuilder /gogs.io/gogs/.bin/gogs .
|
COPY --from=binarybuilder /gogs.io/gogs/gogs .
|
||||||
|
|
||||||
RUN ./docker/build/finalize.sh
|
RUN ./docker/build/finalize.sh
|
||||||
|
|
||||||
# Configure Docker Container
|
# Configure Docker Container
|
||||||
VOLUME ["/data", "/backup"]
|
VOLUME ["/data", "/backup"]
|
||||||
EXPOSE 22 3000
|
EXPOSE 22 3000
|
||||||
HEALTHCHECK CMD (curl --noproxy localhost -o /dev/null -sS http://localhost:3000/healthcheck) || exit 1
|
HEALTHCHECK CMD (curl -o /dev/null -sS http://localhost:3000/healthcheck) || exit 1
|
||||||
ENTRYPOINT ["/app/gogs/docker/start.sh"]
|
ENTRYPOINT ["/app/gogs/docker/start.sh"]
|
||||||
CMD ["/usr/bin/s6-svscan", "/app/gogs/docker/s6/"]
|
CMD ["/usr/bin/s6-svscan", "/app/gogs/docker/s6/"]
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ RUN apk --no-cache --no-progress add \
|
|||||||
ENV GOGS_CUSTOM=/data/gogs
|
ENV GOGS_CUSTOM=/data/gogs
|
||||||
|
|
||||||
WORKDIR /app/gogs
|
WORKDIR /app/gogs
|
||||||
COPY --from=binarybuilder /gogs.io/gogs/.bin/gogs .
|
COPY --from=binarybuilder /gogs.io/gogs/gogs .
|
||||||
COPY docker-next/start.sh .
|
COPY docker-next/start.sh .
|
||||||
RUN chmod +x start.sh && \
|
RUN chmod +x start.sh && \
|
||||||
mkdir -p /data && \
|
mkdir -p /data && \
|
||||||
@@ -41,7 +41,7 @@ RUN chmod +x start.sh && \
|
|||||||
# Configure Docker Container
|
# Configure Docker Container
|
||||||
VOLUME ["/data", "/backup"]
|
VOLUME ["/data", "/backup"]
|
||||||
EXPOSE 22 3000
|
EXPOSE 22 3000
|
||||||
HEALTHCHECK CMD (curl --noproxy localhost -o /dev/null -sS http://localhost:3000/healthcheck) || exit 1
|
HEALTHCHECK CMD (curl -o /dev/null -sS http://localhost:3000/healthcheck) || exit 1
|
||||||
|
|
||||||
# Run as non-root user by default for better K8s security context support.
|
# Run as non-root user by default for better K8s security context support.
|
||||||
USER git:git
|
USER git:git
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
## Supported versions
|
## Supported versions
|
||||||
|
|
||||||
Only the latest minor version releases are supported (e.g., 0.14) for patching vulnerabilities. You can find the latest minor version in the [GitHub releases](https://github.com/gogs/gogs/releases) page.
|
Only the latest minor version releases are supported (>= 0.13) for accepting vulnerability reports and patching fixes.
|
||||||
|
|
||||||
Existing vulnerability reports are being tracked in [GitHub Security Advisories](https://github.com/gogs/gogs/security/advisories). Not all accepted GHSA are published.
|
Existing vulnerability reports are being tracked in [GitHub Security Advisories](https://github.com/gogs/gogs/security/advisories).
|
||||||
|
|
||||||
## Vulnerability lifecycle
|
## Vulnerability lifecycle
|
||||||
|
|
||||||
@@ -14,7 +14,6 @@ Existing vulnerability reports are being tracked in [GitHub Security Advisories]
|
|||||||
|
|
||||||
1. Report an advisory for the vulnerability.
|
1. Report an advisory for the vulnerability.
|
||||||
- Please be aware that **only advisories reported in plain English** will be reviewed.
|
- Please be aware that **only advisories reported in plain English** will be reviewed.
|
||||||
- We DO NOT accept vulnerabilities cannot be reproduced on the latest `main` commit.
|
|
||||||
1. Project maintainers review the advisory:
|
1. Project maintainers review the advisory:
|
||||||
- Ask clarifying questions
|
- Ask clarifying questions
|
||||||
- Make sure there was no prior advisory exists for the same vulnerability
|
- Make sure there was no prior advisory exists for the same vulnerability
|
||||||
|
|||||||
25
Taskfile.yml
25
Taskfile.yml
@@ -10,10 +10,8 @@ tasks:
|
|||||||
web:
|
web:
|
||||||
desc: Build the binary and start the web server
|
desc: Build the binary and start the web server
|
||||||
deps: [build]
|
deps: [build]
|
||||||
env:
|
|
||||||
GOGS_WORK_DIR: '{{.ROOT_DIR}}'
|
|
||||||
cmds:
|
cmds:
|
||||||
- .bin/gogs web
|
- ./gogs web
|
||||||
|
|
||||||
build:
|
build:
|
||||||
desc: Build the binary
|
desc: Build the binary
|
||||||
@@ -24,7 +22,7 @@ tasks:
|
|||||||
-X "{{.PKG_PATH}}.BuildCommit={{.BUILD_COMMIT}}"
|
-X "{{.PKG_PATH}}.BuildCommit={{.BUILD_COMMIT}}"
|
||||||
'
|
'
|
||||||
-tags '{{.TAGS}}'
|
-tags '{{.TAGS}}'
|
||||||
-trimpath -o .bin/gogs{{.BINARY_EXT}} ./cmd/gogs
|
-trimpath -o gogs{{.BINARY_EXT}}
|
||||||
vars:
|
vars:
|
||||||
PKG_PATH: gogs.io/gogs/internal/conf
|
PKG_PATH: gogs.io/gogs/internal/conf
|
||||||
BUILD_TIME:
|
BUILD_TIME:
|
||||||
@@ -33,7 +31,7 @@ tasks:
|
|||||||
sh: git rev-parse HEAD
|
sh: git rev-parse HEAD
|
||||||
sources:
|
sources:
|
||||||
- go.mod
|
- go.mod
|
||||||
- cmd/gogs/*.go
|
- gogs.go
|
||||||
- internal/**/*.go
|
- internal/**/*.go
|
||||||
- conf/**/*
|
- conf/**/*
|
||||||
- public/**/*
|
- public/**/*
|
||||||
@@ -61,6 +59,18 @@ tasks:
|
|||||||
cmds:
|
cmds:
|
||||||
- find . -name "*.DS_Store" -type f -delete
|
- find . -name "*.DS_Store" -type f -delete
|
||||||
|
|
||||||
|
release:
|
||||||
|
desc: Build the binary and pack resources to a ZIP file
|
||||||
|
deps: [build]
|
||||||
|
cmds:
|
||||||
|
- rm -rf {{.RELEASE_GOGS}}
|
||||||
|
- mkdir -p {{.RELEASE_GOGS}}
|
||||||
|
- cp -r gogs{{.BINARY_EXT}} LICENSE README.md README_ZH.md scripts {{.RELEASE_GOGS}}
|
||||||
|
- cd {{.RELEASE_ROOT}} && zip -r gogs.zip "gogs"
|
||||||
|
vars:
|
||||||
|
RELEASE_ROOT: release
|
||||||
|
RELEASE_GOGS: release/gogs
|
||||||
|
|
||||||
less:
|
less:
|
||||||
desc: Generate CSS from LESS files
|
desc: Generate CSS from LESS files
|
||||||
cmds:
|
cmds:
|
||||||
@@ -89,8 +99,3 @@ tasks:
|
|||||||
dropdb "$dbname"
|
dropdb "$dbname"
|
||||||
echo "dropped $dbname"
|
echo "dropped $dbname"
|
||||||
done
|
done
|
||||||
|
|
||||||
lint:
|
|
||||||
desc: Run all linters
|
|
||||||
cmds:
|
|
||||||
- golangci-lint run
|
|
||||||
|
|||||||
16
codecov.yml
Normal file
16
codecov.yml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
coverage:
|
||||||
|
range: "60...95"
|
||||||
|
status:
|
||||||
|
project:
|
||||||
|
default:
|
||||||
|
threshold: 1%
|
||||||
|
informational: true
|
||||||
|
patch:
|
||||||
|
default:
|
||||||
|
only_pulls: true
|
||||||
|
informational: true
|
||||||
|
|
||||||
|
comment:
|
||||||
|
layout: 'diff'
|
||||||
|
|
||||||
|
github_checks: false
|
||||||
@@ -494,8 +494,6 @@ branches.stale_branches=Застинали клонове
|
|||||||
branches.all=Всички клонове
|
branches.all=Всички клонове
|
||||||
branches.updated_by=Актуализирани %[1]s от %[2]s
|
branches.updated_by=Актуализирани %[1]s от %[2]s
|
||||||
branches.change_default_branch=Промяна на клон по подразбиране
|
branches.change_default_branch=Промяна на клон по подразбиране
|
||||||
branches.default_deletion_not_allowed=Cannot delete the default branch.
|
|
||||||
branches.protected_deletion_not_allowed=Cannot delete a protected branch.
|
|
||||||
|
|
||||||
editor.new_file=Нов файл
|
editor.new_file=Нов файл
|
||||||
editor.upload_file=Качи файл
|
editor.upload_file=Качи файл
|
||||||
@@ -1276,7 +1274,6 @@ config.email.test_mail_failed=Failed to send test email to '%s': %v
|
|||||||
config.email.test_mail_sent=Test email has been sent to '%s'.
|
config.email.test_mail_sent=Test email has been sent to '%s'.
|
||||||
|
|
||||||
config.auth_config=Authentication configuration
|
config.auth_config=Authentication configuration
|
||||||
config.auth_custom_logout_url=Custom logout URL
|
|
||||||
config.auth.activate_code_lives=Activate code lives
|
config.auth.activate_code_lives=Activate code lives
|
||||||
config.auth.reset_password_code_lives=Reset password code lives
|
config.auth.reset_password_code_lives=Reset password code lives
|
||||||
config.auth.require_email_confirm=Require email confirmation
|
config.auth.require_email_confirm=Require email confirmation
|
||||||
|
|||||||
@@ -494,8 +494,6 @@ branches.stale_branches=Zastaralé větve
|
|||||||
branches.all=Všechny větve
|
branches.all=Všechny větve
|
||||||
branches.updated_by=%[2]s změnil %[1]s
|
branches.updated_by=%[2]s změnil %[1]s
|
||||||
branches.change_default_branch=Změnit výchozí větev
|
branches.change_default_branch=Změnit výchozí větev
|
||||||
branches.default_deletion_not_allowed=Cannot delete the default branch.
|
|
||||||
branches.protected_deletion_not_allowed=Cannot delete a protected branch.
|
|
||||||
|
|
||||||
editor.new_file=Nový soubor
|
editor.new_file=Nový soubor
|
||||||
editor.upload_file=Nahrát soubor
|
editor.upload_file=Nahrát soubor
|
||||||
@@ -1276,7 +1274,6 @@ config.email.test_mail_failed=Failed to send test email to '%s': %v
|
|||||||
config.email.test_mail_sent=Test email has been sent to '%s'.
|
config.email.test_mail_sent=Test email has been sent to '%s'.
|
||||||
|
|
||||||
config.auth_config=Authentication configuration
|
config.auth_config=Authentication configuration
|
||||||
config.auth_custom_logout_url=Custom logout URL
|
|
||||||
config.auth.activate_code_lives=Activate code lives
|
config.auth.activate_code_lives=Activate code lives
|
||||||
config.auth.reset_password_code_lives=Reset password code lives
|
config.auth.reset_password_code_lives=Reset password code lives
|
||||||
config.auth.require_email_confirm=Require email confirmation
|
config.auth.require_email_confirm=Require email confirmation
|
||||||
|
|||||||
@@ -494,8 +494,6 @@ branches.stale_branches=Alte Branches
|
|||||||
branches.all=Alle Branches
|
branches.all=Alle Branches
|
||||||
branches.updated_by=Aktualisiert %[1]s von %[2]s
|
branches.updated_by=Aktualisiert %[1]s von %[2]s
|
||||||
branches.change_default_branch=Ändere Standard-Branch
|
branches.change_default_branch=Ändere Standard-Branch
|
||||||
branches.default_deletion_not_allowed=Cannot delete the default branch.
|
|
||||||
branches.protected_deletion_not_allowed=Cannot delete a protected branch.
|
|
||||||
|
|
||||||
editor.new_file=Neue Datei
|
editor.new_file=Neue Datei
|
||||||
editor.upload_file=Datei hochladen
|
editor.upload_file=Datei hochladen
|
||||||
@@ -1276,7 +1274,6 @@ config.email.test_mail_failed=Fehler beim Senden der Test-E-Mail an '%s': %v
|
|||||||
config.email.test_mail_sent=Test-E-Mail wurde an '%s ' gesendet.
|
config.email.test_mail_sent=Test-E-Mail wurde an '%s ' gesendet.
|
||||||
|
|
||||||
config.auth_config=Authentifizierungskonfiguration
|
config.auth_config=Authentifizierungskonfiguration
|
||||||
config.auth_custom_logout_url=Custom logout URL
|
|
||||||
config.auth.activate_code_lives=Aktivierungscode Lebensdauer
|
config.auth.activate_code_lives=Aktivierungscode Lebensdauer
|
||||||
config.auth.reset_password_code_lives=Gültigkeitsdauer Zurücksetzungs-Code
|
config.auth.reset_password_code_lives=Gültigkeitsdauer Zurücksetzungs-Code
|
||||||
config.auth.require_email_confirm=E-Mail-Bestätigung erforderlich
|
config.auth.require_email_confirm=E-Mail-Bestätigung erforderlich
|
||||||
|
|||||||
@@ -501,8 +501,6 @@ branches.stale_branches=Stale Branches
|
|||||||
branches.all=All Branches
|
branches.all=All Branches
|
||||||
branches.updated_by=Updated %[1]s by %[2]s
|
branches.updated_by=Updated %[1]s by %[2]s
|
||||||
branches.change_default_branch=Change Default Branch
|
branches.change_default_branch=Change Default Branch
|
||||||
branches.default_deletion_not_allowed=Cannot delete the default branch.
|
|
||||||
branches.protected_deletion_not_allowed=Cannot delete a protected branch.
|
|
||||||
|
|
||||||
editor.new_file=New file
|
editor.new_file=New file
|
||||||
editor.upload_file=Upload file
|
editor.upload_file=Upload file
|
||||||
@@ -1348,7 +1346,6 @@ config.email.test_mail_sent=Test email has been sent to '%s'.
|
|||||||
|
|
||||||
config.auth_config=Authentication configuration
|
config.auth_config=Authentication configuration
|
||||||
|
|
||||||
config.auth_custom_logout_url=Custom logout URL
|
|
||||||
config.auth.activate_code_lives=Activate code lives
|
config.auth.activate_code_lives=Activate code lives
|
||||||
|
|
||||||
config.auth.reset_password_code_lives=Reset password code lives
|
config.auth.reset_password_code_lives=Reset password code lives
|
||||||
|
|||||||
@@ -494,8 +494,6 @@ branches.stale_branches = Stale Branches
|
|||||||
branches.all = All Branches
|
branches.all = All Branches
|
||||||
branches.updated_by = Updated %[1]s by %[2]s
|
branches.updated_by = Updated %[1]s by %[2]s
|
||||||
branches.change_default_branch = Change Default Branch
|
branches.change_default_branch = Change Default Branch
|
||||||
branches.default_deletion_not_allowed = Cannot delete the default branch.
|
|
||||||
branches.protected_deletion_not_allowed = Cannot delete a protected branch.
|
|
||||||
|
|
||||||
editor.new_file = New file
|
editor.new_file = New file
|
||||||
editor.upload_file = Upload file
|
editor.upload_file = Upload file
|
||||||
|
|||||||
@@ -494,8 +494,6 @@ branches.stale_branches=Ramas Viejas
|
|||||||
branches.all=Todas las Ramas
|
branches.all=Todas las Ramas
|
||||||
branches.updated_by=%[1]s actualizado por %[2]s
|
branches.updated_by=%[1]s actualizado por %[2]s
|
||||||
branches.change_default_branch=Cambiar la Rama por Defecto
|
branches.change_default_branch=Cambiar la Rama por Defecto
|
||||||
branches.default_deletion_not_allowed=Cannot delete the default branch.
|
|
||||||
branches.protected_deletion_not_allowed=Cannot delete a protected branch.
|
|
||||||
|
|
||||||
editor.new_file=Nuevo archivo
|
editor.new_file=Nuevo archivo
|
||||||
editor.upload_file=Subir archivo
|
editor.upload_file=Subir archivo
|
||||||
@@ -1276,7 +1274,6 @@ config.email.test_mail_failed=Error al enviar correo electrónico de prueba a '%
|
|||||||
config.email.test_mail_sent=Test email has been sent to '%s'.
|
config.email.test_mail_sent=Test email has been sent to '%s'.
|
||||||
|
|
||||||
config.auth_config=Authentication configuration
|
config.auth_config=Authentication configuration
|
||||||
config.auth_custom_logout_url=Custom logout URL
|
|
||||||
config.auth.activate_code_lives=Activate code lives
|
config.auth.activate_code_lives=Activate code lives
|
||||||
config.auth.reset_password_code_lives=Reset password code lives
|
config.auth.reset_password_code_lives=Reset password code lives
|
||||||
config.auth.require_email_confirm=Require email confirmation
|
config.auth.require_email_confirm=Require email confirmation
|
||||||
|
|||||||
@@ -496,8 +496,6 @@ branches.stale_branches=شاخه های قدیمی
|
|||||||
branches.all=همه شاخه
|
branches.all=همه شاخه
|
||||||
branches.updated_by=%[1]s به روزشده توسط %[2]s
|
branches.updated_by=%[1]s به روزشده توسط %[2]s
|
||||||
branches.change_default_branch=تغییر شاخه ی پیش فرض
|
branches.change_default_branch=تغییر شاخه ی پیش فرض
|
||||||
branches.default_deletion_not_allowed=Cannot delete the default branch.
|
|
||||||
branches.protected_deletion_not_allowed=Cannot delete a protected branch.
|
|
||||||
|
|
||||||
editor.new_file=پرونده جدید
|
editor.new_file=پرونده جدید
|
||||||
editor.upload_file=بارگذاری پرونده
|
editor.upload_file=بارگذاری پرونده
|
||||||
@@ -1278,7 +1276,6 @@ config.email.test_mail_failed=Failed to send test email to '%s': %v
|
|||||||
config.email.test_mail_sent=Test email has been sent to '%s'.
|
config.email.test_mail_sent=Test email has been sent to '%s'.
|
||||||
|
|
||||||
config.auth_config=Authentication configuration
|
config.auth_config=Authentication configuration
|
||||||
config.auth_custom_logout_url=Custom logout URL
|
|
||||||
config.auth.activate_code_lives=Activate code lives
|
config.auth.activate_code_lives=Activate code lives
|
||||||
config.auth.reset_password_code_lives=Reset password code lives
|
config.auth.reset_password_code_lives=Reset password code lives
|
||||||
config.auth.require_email_confirm=Require email confirmation
|
config.auth.require_email_confirm=Require email confirmation
|
||||||
|
|||||||
@@ -494,8 +494,6 @@ branches.stale_branches=Passivoituneet haarat
|
|||||||
branches.all=Kaikki haarat
|
branches.all=Kaikki haarat
|
||||||
branches.updated_by=Päivitetty %[1]s %[2]s
|
branches.updated_by=Päivitetty %[1]s %[2]s
|
||||||
branches.change_default_branch=Muuta oletushaaraa
|
branches.change_default_branch=Muuta oletushaaraa
|
||||||
branches.default_deletion_not_allowed=Cannot delete the default branch.
|
|
||||||
branches.protected_deletion_not_allowed=Cannot delete a protected branch.
|
|
||||||
|
|
||||||
editor.new_file=Uusi tiedosto
|
editor.new_file=Uusi tiedosto
|
||||||
editor.upload_file=Liitä tiedosto
|
editor.upload_file=Liitä tiedosto
|
||||||
@@ -1276,7 +1274,6 @@ config.email.test_mail_failed=Testisähköpostin lähettäminen vastaanottajalle
|
|||||||
config.email.test_mail_sent=Testi sähköposti on lähetetty vastaanottajalle '%s'.
|
config.email.test_mail_sent=Testi sähköposti on lähetetty vastaanottajalle '%s'.
|
||||||
|
|
||||||
config.auth_config=Todennuksen asetukset
|
config.auth_config=Todennuksen asetukset
|
||||||
config.auth_custom_logout_url=Custom logout URL
|
|
||||||
config.auth.activate_code_lives=Aktiivinen koodi elämät ennen vanhenemista
|
config.auth.activate_code_lives=Aktiivinen koodi elämät ennen vanhenemista
|
||||||
config.auth.reset_password_code_lives=Nollaa salasana koodi elämät
|
config.auth.reset_password_code_lives=Nollaa salasana koodi elämät
|
||||||
config.auth.require_email_confirm=Vaadi sähköpostivahvistus
|
config.auth.require_email_confirm=Vaadi sähköpostivahvistus
|
||||||
|
|||||||
@@ -494,8 +494,6 @@ branches.stale_branches=Branches stagnantes
|
|||||||
branches.all=Toutes les Branches
|
branches.all=Toutes les Branches
|
||||||
branches.updated_by=Mise à jour %[1]s par %[2]s
|
branches.updated_by=Mise à jour %[1]s par %[2]s
|
||||||
branches.change_default_branch=Changer la Branche par Défaut
|
branches.change_default_branch=Changer la Branche par Défaut
|
||||||
branches.default_deletion_not_allowed=Cannot delete the default branch.
|
|
||||||
branches.protected_deletion_not_allowed=Cannot delete a protected branch.
|
|
||||||
|
|
||||||
editor.new_file=Nouveau fichier
|
editor.new_file=Nouveau fichier
|
||||||
editor.upload_file=Téléverser un fichier
|
editor.upload_file=Téléverser un fichier
|
||||||
@@ -1276,7 +1274,6 @@ config.email.test_mail_failed=Impossible d'envoyer un e-mail de test à '%s' :
|
|||||||
config.email.test_mail_sent=Un e-mail de test à été envoyé à '%s'.
|
config.email.test_mail_sent=Un e-mail de test à été envoyé à '%s'.
|
||||||
|
|
||||||
config.auth_config=Configuration de l'authentification
|
config.auth_config=Configuration de l'authentification
|
||||||
config.auth_custom_logout_url=Custom logout URL
|
|
||||||
config.auth.activate_code_lives=Activer les vies sur les codes
|
config.auth.activate_code_lives=Activer les vies sur les codes
|
||||||
config.auth.reset_password_code_lives=Vies sur les codes de réinitialisation des mots de passes
|
config.auth.reset_password_code_lives=Vies sur les codes de réinitialisation des mots de passes
|
||||||
config.auth.require_email_confirm=Nécessite une confirmation par e-mail
|
config.auth.require_email_confirm=Nécessite une confirmation par e-mail
|
||||||
|
|||||||
@@ -495,8 +495,6 @@ branches.stale_branches=Stale Branches
|
|||||||
branches.all=All Branches
|
branches.all=All Branches
|
||||||
branches.updated_by=Updated %[1]s by %[2]s
|
branches.updated_by=Updated %[1]s by %[2]s
|
||||||
branches.change_default_branch=Change Default Branch
|
branches.change_default_branch=Change Default Branch
|
||||||
branches.default_deletion_not_allowed=Cannot delete the default branch.
|
|
||||||
branches.protected_deletion_not_allowed=Cannot delete a protected branch.
|
|
||||||
|
|
||||||
editor.new_file=Novo arquivo
|
editor.new_file=Novo arquivo
|
||||||
editor.upload_file=Subir arquivo
|
editor.upload_file=Subir arquivo
|
||||||
@@ -1277,7 +1275,6 @@ config.email.test_mail_failed=Failed to send test email to '%s': %v
|
|||||||
config.email.test_mail_sent=Test email has been sent to '%s'.
|
config.email.test_mail_sent=Test email has been sent to '%s'.
|
||||||
|
|
||||||
config.auth_config=Authentication configuration
|
config.auth_config=Authentication configuration
|
||||||
config.auth_custom_logout_url=Custom logout URL
|
|
||||||
config.auth.activate_code_lives=Activate code lives
|
config.auth.activate_code_lives=Activate code lives
|
||||||
config.auth.reset_password_code_lives=Reset password code lives
|
config.auth.reset_password_code_lives=Reset password code lives
|
||||||
config.auth.require_email_confirm=Require email confirmation
|
config.auth.require_email_confirm=Require email confirmation
|
||||||
|
|||||||
@@ -494,8 +494,6 @@ branches.stale_branches=Elavult ágak
|
|||||||
branches.all=Minden ág
|
branches.all=Minden ág
|
||||||
branches.updated_by=Frissítve ekkor: %[1]s %[2]s által
|
branches.updated_by=Frissítve ekkor: %[1]s %[2]s által
|
||||||
branches.change_default_branch=Alapértelmezett ág megváltoztatása
|
branches.change_default_branch=Alapértelmezett ág megváltoztatása
|
||||||
branches.default_deletion_not_allowed=Cannot delete the default branch.
|
|
||||||
branches.protected_deletion_not_allowed=Cannot delete a protected branch.
|
|
||||||
|
|
||||||
editor.new_file=Új fájl
|
editor.new_file=Új fájl
|
||||||
editor.upload_file=Fájl feltöltése
|
editor.upload_file=Fájl feltöltése
|
||||||
@@ -1276,7 +1274,6 @@ config.email.test_mail_failed=Nem sikerült kiküldeni a teszt e-mailt '%s'-nek:
|
|||||||
config.email.test_mail_sent=Teszt e-mail kiküldve '%s'-nek.
|
config.email.test_mail_sent=Teszt e-mail kiküldve '%s'-nek.
|
||||||
|
|
||||||
config.auth_config=Hitelesítési beállítások
|
config.auth_config=Hitelesítési beállítások
|
||||||
config.auth_custom_logout_url=Custom logout URL
|
|
||||||
config.auth.activate_code_lives=Activate code lives
|
config.auth.activate_code_lives=Activate code lives
|
||||||
config.auth.reset_password_code_lives=Jelszó visszaállítási kód élettartama
|
config.auth.reset_password_code_lives=Jelszó visszaállítási kód élettartama
|
||||||
config.auth.require_email_confirm=E-mail megerősítés szükségessé tétele
|
config.auth.require_email_confirm=E-mail megerősítés szükségessé tétele
|
||||||
|
|||||||
@@ -494,8 +494,6 @@ branches.stale_branches=Cabang Basi
|
|||||||
branches.all=Semua Cabang
|
branches.all=Semua Cabang
|
||||||
branches.updated_by=Diperbarui %[1]s oleh %[2]s
|
branches.updated_by=Diperbarui %[1]s oleh %[2]s
|
||||||
branches.change_default_branch=Ubah Cabang Default
|
branches.change_default_branch=Ubah Cabang Default
|
||||||
branches.default_deletion_not_allowed=Cannot delete the default branch.
|
|
||||||
branches.protected_deletion_not_allowed=Cannot delete a protected branch.
|
|
||||||
|
|
||||||
editor.new_file=Berkas baru
|
editor.new_file=Berkas baru
|
||||||
editor.upload_file=Unggah Berkas
|
editor.upload_file=Unggah Berkas
|
||||||
@@ -1276,7 +1274,6 @@ config.email.test_mail_failed=Gagal mengirim surel uji ke '%s': %v
|
|||||||
config.email.test_mail_sent=Surel uji telah dikirim ke '%s'.
|
config.email.test_mail_sent=Surel uji telah dikirim ke '%s'.
|
||||||
|
|
||||||
config.auth_config=Konfigurasi otentikasi
|
config.auth_config=Konfigurasi otentikasi
|
||||||
config.auth_custom_logout_url=Custom logout URL
|
|
||||||
config.auth.activate_code_lives=Activate code lives
|
config.auth.activate_code_lives=Activate code lives
|
||||||
config.auth.reset_password_code_lives=Reset password code lives
|
config.auth.reset_password_code_lives=Reset password code lives
|
||||||
config.auth.require_email_confirm=Perlu konfirmasi surel
|
config.auth.require_email_confirm=Perlu konfirmasi surel
|
||||||
|
|||||||
@@ -494,8 +494,6 @@ branches.stale_branches=Stale Branches
|
|||||||
branches.all=Tutti i rami (branch)
|
branches.all=Tutti i rami (branch)
|
||||||
branches.updated_by=Updated %[1]s by %[2]s
|
branches.updated_by=Updated %[1]s by %[2]s
|
||||||
branches.change_default_branch=Cambia branch di default
|
branches.change_default_branch=Cambia branch di default
|
||||||
branches.default_deletion_not_allowed=Cannot delete the default branch.
|
|
||||||
branches.protected_deletion_not_allowed=Cannot delete a protected branch.
|
|
||||||
|
|
||||||
editor.new_file=Nuovo file
|
editor.new_file=Nuovo file
|
||||||
editor.upload_file=Carica File
|
editor.upload_file=Carica File
|
||||||
@@ -1276,7 +1274,6 @@ config.email.test_mail_failed=Failed to send test email to '%s': %v
|
|||||||
config.email.test_mail_sent=Test email has been sent to '%s'.
|
config.email.test_mail_sent=Test email has been sent to '%s'.
|
||||||
|
|
||||||
config.auth_config=Authentication configuration
|
config.auth_config=Authentication configuration
|
||||||
config.auth_custom_logout_url=Custom logout URL
|
|
||||||
config.auth.activate_code_lives=Activate code lives
|
config.auth.activate_code_lives=Activate code lives
|
||||||
config.auth.reset_password_code_lives=Reset password code lives
|
config.auth.reset_password_code_lives=Reset password code lives
|
||||||
config.auth.require_email_confirm=Require email confirmation
|
config.auth.require_email_confirm=Require email confirmation
|
||||||
|
|||||||
@@ -494,8 +494,6 @@ branches.stale_branches=古いブランチ
|
|||||||
branches.all=すべてのブランチ
|
branches.all=すべてのブランチ
|
||||||
branches.updated_by=%[1]s が %[2]s によって更新されました
|
branches.updated_by=%[1]s が %[2]s によって更新されました
|
||||||
branches.change_default_branch=デフォルトブランチの変更
|
branches.change_default_branch=デフォルトブランチの変更
|
||||||
branches.default_deletion_not_allowed=Cannot delete the default branch.
|
|
||||||
branches.protected_deletion_not_allowed=Cannot delete a protected branch.
|
|
||||||
|
|
||||||
editor.new_file=新規ファイル
|
editor.new_file=新規ファイル
|
||||||
editor.upload_file=ファイルをアップロード
|
editor.upload_file=ファイルをアップロード
|
||||||
@@ -1276,7 +1274,6 @@ config.email.test_mail_failed=Failed to send test email to '%s': %v
|
|||||||
config.email.test_mail_sent=Test email has been sent to '%s'.
|
config.email.test_mail_sent=Test email has been sent to '%s'.
|
||||||
|
|
||||||
config.auth_config=Authentication configuration
|
config.auth_config=Authentication configuration
|
||||||
config.auth_custom_logout_url=Custom logout URL
|
|
||||||
config.auth.activate_code_lives=Activate code lives
|
config.auth.activate_code_lives=Activate code lives
|
||||||
config.auth.reset_password_code_lives=Reset password code lives
|
config.auth.reset_password_code_lives=Reset password code lives
|
||||||
config.auth.require_email_confirm=Require email confirmation
|
config.auth.require_email_confirm=Require email confirmation
|
||||||
|
|||||||
@@ -495,8 +495,6 @@ branches.stale_branches=오래된 브랜치
|
|||||||
branches.all=모든 브랜치
|
branches.all=모든 브랜치
|
||||||
branches.updated_by=%[2]s이 %[1]s를 업데이트
|
branches.updated_by=%[2]s이 %[1]s를 업데이트
|
||||||
branches.change_default_branch=기본 브랜치 변경
|
branches.change_default_branch=기본 브랜치 변경
|
||||||
branches.default_deletion_not_allowed=Cannot delete the default branch.
|
|
||||||
branches.protected_deletion_not_allowed=Cannot delete a protected branch.
|
|
||||||
|
|
||||||
editor.new_file=파일 생성
|
editor.new_file=파일 생성
|
||||||
editor.upload_file=파일 업로드
|
editor.upload_file=파일 업로드
|
||||||
@@ -1278,7 +1276,6 @@ config.email.test_mail_failed=Failed to send test email to '%s': %v
|
|||||||
config.email.test_mail_sent='%s'로 테스트 이메일을 보냈습니다.
|
config.email.test_mail_sent='%s'로 테스트 이메일을 보냈습니다.
|
||||||
|
|
||||||
config.auth_config=Authentication configuration
|
config.auth_config=Authentication configuration
|
||||||
config.auth_custom_logout_url=Custom logout URL
|
|
||||||
config.auth.activate_code_lives=Activate code lives
|
config.auth.activate_code_lives=Activate code lives
|
||||||
config.auth.reset_password_code_lives=Reset password code lives
|
config.auth.reset_password_code_lives=Reset password code lives
|
||||||
config.auth.require_email_confirm=이메일 인증 필요
|
config.auth.require_email_confirm=이메일 인증 필요
|
||||||
|
|||||||
@@ -494,8 +494,6 @@ branches.stale_branches=Pamests atzars
|
|||||||
branches.all=Visi atzari
|
branches.all=Visi atzari
|
||||||
branches.updated_by=%[2]s atjaunoja %[1]s
|
branches.updated_by=%[2]s atjaunoja %[1]s
|
||||||
branches.change_default_branch=Mainīt noklusēto atzaru
|
branches.change_default_branch=Mainīt noklusēto atzaru
|
||||||
branches.default_deletion_not_allowed=Cannot delete the default branch.
|
|
||||||
branches.protected_deletion_not_allowed=Cannot delete a protected branch.
|
|
||||||
|
|
||||||
editor.new_file=Jauns fails
|
editor.new_file=Jauns fails
|
||||||
editor.upload_file=Augšupielādēt failu
|
editor.upload_file=Augšupielādēt failu
|
||||||
@@ -1276,7 +1274,6 @@ config.email.test_mail_failed=Failed to send test email to '%s': %v
|
|||||||
config.email.test_mail_sent=Test email has been sent to '%s'.
|
config.email.test_mail_sent=Test email has been sent to '%s'.
|
||||||
|
|
||||||
config.auth_config=Authentication configuration
|
config.auth_config=Authentication configuration
|
||||||
config.auth_custom_logout_url=Custom logout URL
|
|
||||||
config.auth.activate_code_lives=Activate code lives
|
config.auth.activate_code_lives=Activate code lives
|
||||||
config.auth.reset_password_code_lives=Reset password code lives
|
config.auth.reset_password_code_lives=Reset password code lives
|
||||||
config.auth.require_email_confirm=Require email confirmation
|
config.auth.require_email_confirm=Require email confirmation
|
||||||
|
|||||||
@@ -494,8 +494,6 @@ branches.stale_branches=Хуучирсан салаанууд
|
|||||||
branches.all=Бүх салаанууд
|
branches.all=Бүх салаанууд
|
||||||
branches.updated_by=Шинэчлэгдсэн %[1]s by %[2]s
|
branches.updated_by=Шинэчлэгдсэн %[1]s by %[2]s
|
||||||
branches.change_default_branch=Анхны салаа өөрчлөх
|
branches.change_default_branch=Анхны салаа өөрчлөх
|
||||||
branches.default_deletion_not_allowed=Cannot delete the default branch.
|
|
||||||
branches.protected_deletion_not_allowed=Cannot delete a protected branch.
|
|
||||||
|
|
||||||
editor.new_file=Шинэ файл
|
editor.new_file=Шинэ файл
|
||||||
editor.upload_file=Файл хуулах
|
editor.upload_file=Файл хуулах
|
||||||
@@ -1276,7 +1274,6 @@ config.email.test_mail_failed=Туршилтын имэйлийг '%s': %v рү
|
|||||||
config.email.test_mail_sent=Туршилтын имэйл '%s' рүү илгээгдлээ.
|
config.email.test_mail_sent=Туршилтын имэйл '%s' рүү илгээгдлээ.
|
||||||
|
|
||||||
config.auth_config=Authentication тохиргоо
|
config.auth_config=Authentication тохиргоо
|
||||||
config.auth_custom_logout_url=Custom logout URL
|
|
||||||
config.auth.activate_code_lives=Кодын ашиглалтыг идэвхжүүлэх
|
config.auth.activate_code_lives=Кодын ашиглалтыг идэвхжүүлэх
|
||||||
config.auth.reset_password_code_lives=Нууц үгийн кодыг шинэчлэх
|
config.auth.reset_password_code_lives=Нууц үгийн кодыг шинэчлэх
|
||||||
config.auth.require_email_confirm=Имэйлээр баталгаажуулахыг шаардана
|
config.auth.require_email_confirm=Имэйлээр баталгаажуулахыг шаардана
|
||||||
|
|||||||
@@ -494,8 +494,6 @@ branches.stale_branches=Stale Branches
|
|||||||
branches.all=All Branches
|
branches.all=All Branches
|
||||||
branches.updated_by=Updated %[1]s by %[2]s
|
branches.updated_by=Updated %[1]s by %[2]s
|
||||||
branches.change_default_branch=Change Default Branch
|
branches.change_default_branch=Change Default Branch
|
||||||
branches.default_deletion_not_allowed=Cannot delete the default branch.
|
|
||||||
branches.protected_deletion_not_allowed=Cannot delete a protected branch.
|
|
||||||
|
|
||||||
editor.new_file=Nieuw bestand
|
editor.new_file=Nieuw bestand
|
||||||
editor.upload_file=Bestand uploaden
|
editor.upload_file=Bestand uploaden
|
||||||
@@ -1276,7 +1274,6 @@ config.email.test_mail_failed=Failed to send test email to '%s': %v
|
|||||||
config.email.test_mail_sent=Test email has been sent to '%s'.
|
config.email.test_mail_sent=Test email has been sent to '%s'.
|
||||||
|
|
||||||
config.auth_config=Authentication configuration
|
config.auth_config=Authentication configuration
|
||||||
config.auth_custom_logout_url=Custom logout URL
|
|
||||||
config.auth.activate_code_lives=Activate code lives
|
config.auth.activate_code_lives=Activate code lives
|
||||||
config.auth.reset_password_code_lives=Reset password code lives
|
config.auth.reset_password_code_lives=Reset password code lives
|
||||||
config.auth.require_email_confirm=Require email confirmation
|
config.auth.require_email_confirm=Require email confirmation
|
||||||
|
|||||||
@@ -494,8 +494,6 @@ branches.stale_branches=Stare gałęzie
|
|||||||
branches.all=Wszystkie gałęzie
|
branches.all=Wszystkie gałęzie
|
||||||
branches.updated_by=Zaktualizowano %[1]s przez %[2]s
|
branches.updated_by=Zaktualizowano %[1]s przez %[2]s
|
||||||
branches.change_default_branch=Zmiana domyślnej gałęzi
|
branches.change_default_branch=Zmiana domyślnej gałęzi
|
||||||
branches.default_deletion_not_allowed=Cannot delete the default branch.
|
|
||||||
branches.protected_deletion_not_allowed=Cannot delete a protected branch.
|
|
||||||
|
|
||||||
editor.new_file=Nowy plik
|
editor.new_file=Nowy plik
|
||||||
editor.upload_file=Załaduj plik
|
editor.upload_file=Załaduj plik
|
||||||
@@ -1276,7 +1274,6 @@ config.email.test_mail_failed=Nie udało się wysłać wiadomości testowej do '
|
|||||||
config.email.test_mail_sent=Wiadomość testowa została wysłana do '%s'.
|
config.email.test_mail_sent=Wiadomość testowa została wysłana do '%s'.
|
||||||
|
|
||||||
config.auth_config=Konfiguracja uwierzytelniania
|
config.auth_config=Konfiguracja uwierzytelniania
|
||||||
config.auth_custom_logout_url=Custom logout URL
|
|
||||||
config.auth.activate_code_lives=Activate code lives
|
config.auth.activate_code_lives=Activate code lives
|
||||||
config.auth.reset_password_code_lives=Reset password code lives
|
config.auth.reset_password_code_lives=Reset password code lives
|
||||||
config.auth.require_email_confirm=Wymagaj potwierdzenia adresu e-mail
|
config.auth.require_email_confirm=Wymagaj potwierdzenia adresu e-mail
|
||||||
|
|||||||
@@ -494,8 +494,6 @@ branches.stale_branches=Branches obsoletos
|
|||||||
branches.all=Todos os branches
|
branches.all=Todos os branches
|
||||||
branches.updated_by=Atualizado %[1]s por %[2]s
|
branches.updated_by=Atualizado %[1]s por %[2]s
|
||||||
branches.change_default_branch=Modificar branch padrão
|
branches.change_default_branch=Modificar branch padrão
|
||||||
branches.default_deletion_not_allowed=Cannot delete the default branch.
|
|
||||||
branches.protected_deletion_not_allowed=Cannot delete a protected branch.
|
|
||||||
|
|
||||||
editor.new_file=Novo arquivo
|
editor.new_file=Novo arquivo
|
||||||
editor.upload_file=Enviar arquivo
|
editor.upload_file=Enviar arquivo
|
||||||
@@ -1276,7 +1274,6 @@ config.email.test_mail_failed=Failed to send test email to '%s': %v
|
|||||||
config.email.test_mail_sent=Test email has been sent to '%s'.
|
config.email.test_mail_sent=Test email has been sent to '%s'.
|
||||||
|
|
||||||
config.auth_config=Authentication configuration
|
config.auth_config=Authentication configuration
|
||||||
config.auth_custom_logout_url=Custom logout URL
|
|
||||||
config.auth.activate_code_lives=Activate code lives
|
config.auth.activate_code_lives=Activate code lives
|
||||||
config.auth.reset_password_code_lives=Reset password code lives
|
config.auth.reset_password_code_lives=Reset password code lives
|
||||||
config.auth.require_email_confirm=Require email confirmation
|
config.auth.require_email_confirm=Require email confirmation
|
||||||
|
|||||||
@@ -446,7 +446,7 @@ migrate.clone_address_desc=Isto pode ser um URL de HTTP/HTTPS/GIT.
|
|||||||
migrate.clone_address_desc_import_local=Você também pode migrar um repositório pelo caminho do servidor local.
|
migrate.clone_address_desc_import_local=Você também pode migrar um repositório pelo caminho do servidor local.
|
||||||
migrate.permission_denied=Não está autorizado a importar repositórios locais.
|
migrate.permission_denied=Não está autorizado a importar repositórios locais.
|
||||||
migrate.invalid_local_path=Caminho local inválido, o caminho não existe ou não é um directório.
|
migrate.invalid_local_path=Caminho local inválido, o caminho não existe ou não é um directório.
|
||||||
migrate.clone_address_resolved_to_blocked_local_address=Clonar endereço resolvido para um endereço de rede local implicitamente bloqueado.
|
migrate.clone_address_resolved_to_blocked_local_address=Clone address resolved to a local network address that is implicitly blocked.
|
||||||
migrate.failed=Migração falhada: %v
|
migrate.failed=Migração falhada: %v
|
||||||
|
|
||||||
mirror_from=mirror de
|
mirror_from=mirror de
|
||||||
@@ -494,8 +494,6 @@ branches.stale_branches=Ramificações Obsoletas
|
|||||||
branches.all=Todas as Ramificações
|
branches.all=Todas as Ramificações
|
||||||
branches.updated_by=Atualizado %[1]s por %[2]s
|
branches.updated_by=Atualizado %[1]s por %[2]s
|
||||||
branches.change_default_branch=Mudar Ramificação Predefinida
|
branches.change_default_branch=Mudar Ramificação Predefinida
|
||||||
branches.default_deletion_not_allowed=Cannot delete the default branch.
|
|
||||||
branches.protected_deletion_not_allowed=Cannot delete a protected branch.
|
|
||||||
|
|
||||||
editor.new_file=Novo Ficheiro
|
editor.new_file=Novo Ficheiro
|
||||||
editor.upload_file=Enviar ficheiro
|
editor.upload_file=Enviar ficheiro
|
||||||
@@ -1204,120 +1202,119 @@ config.ssh.port=Porta exposta
|
|||||||
config.ssh.root_path=Caminho para a raíz
|
config.ssh.root_path=Caminho para a raíz
|
||||||
config.ssh.keygen_path=Localização do gerador de chaves criptográficas
|
config.ssh.keygen_path=Localização do gerador de chaves criptográficas
|
||||||
config.ssh.key_test_path=Localização do teste das chaves criptográficas
|
config.ssh.key_test_path=Localização do teste das chaves criptográficas
|
||||||
config.ssh.minimum_key_size_check=Verificação de tamanho mínimo da chave
|
config.ssh.minimum_key_size_check=Minimum key size check
|
||||||
config.ssh.minimum_key_sizes=Tamanhos mínimos de chaves
|
config.ssh.minimum_key_sizes=Minimum key sizes
|
||||||
config.ssh.rewrite_authorized_keys_at_start=Rewrite "authorized_keys" at start
|
config.ssh.rewrite_authorized_keys_at_start=Rewrite "authorized_keys" at start
|
||||||
config.ssh.start_builtin_server=Iniciar servidor embutido
|
config.ssh.start_builtin_server=Start builtin server
|
||||||
config.ssh.listen_host=Servidor
|
config.ssh.listen_host=Servidor
|
||||||
config.ssh.listen_port=Porta do servidor
|
config.ssh.listen_port=Porta do servidor
|
||||||
config.ssh.server_ciphers=Cifras do servidor
|
config.ssh.server_ciphers=Cifras do servidor
|
||||||
config.ssh.server_macs=MACs do servidor
|
config.ssh.server_macs=Server MACs
|
||||||
config.ssh.server_algorithms=Algoritmos do servidor
|
config.ssh.server_algorithms=Server algorithms
|
||||||
|
|
||||||
config.repo_config=Configuração de repositório
|
config.repo_config=Configuração de repositório
|
||||||
config.repo.root_path=Localização base
|
config.repo.root_path=Localização base
|
||||||
config.repo.script_type=Tipo de script
|
config.repo.script_type=Tipo de script
|
||||||
config.repo.ansi_chatset=ANSI charset
|
config.repo.ansi_chatset=ANSI charset
|
||||||
config.repo.force_private=Forçar privado
|
config.repo.force_private=Force private
|
||||||
config.repo.max_creation_limit=Limite máximo de criação
|
config.repo.max_creation_limit=Max creation limit
|
||||||
config.repo.preferred_licenses=Licenças preferidas
|
config.repo.preferred_licenses=Preferred licenses
|
||||||
config.repo.disable_http_git=Desativar Git HTTP
|
config.repo.disable_http_git=Disable HTTP Git
|
||||||
config.repo.enable_local_path_migration=Ativar a migração de caminho local
|
config.repo.enable_local_path_migration=Enable local path migration
|
||||||
config.repo.enable_raw_file_render_mode=Ativar o modo de renderização do ficheiro bruto
|
config.repo.enable_raw_file_render_mode=Enable raw file render mode
|
||||||
config.repo.commits_fetch_concurrency=Commits fetch concurrency
|
config.repo.commits_fetch_concurrency=Commits fetch concurrency
|
||||||
config.repo.editor.line_wrap_extensions=Extensões de quebra automática de linha do editor
|
config.repo.editor.line_wrap_extensions=Editor line wrap extensions
|
||||||
config.repo.editor.previewable_file_modes=Editor previewable file modes
|
config.repo.editor.previewable_file_modes=Editor previewable file modes
|
||||||
config.repo.upload.enabled=Envio ativado
|
config.repo.upload.enabled=Upload enabled
|
||||||
config.repo.upload.temp_path=Caminho temporário para envios
|
config.repo.upload.temp_path=Upload temporary path
|
||||||
config.repo.upload.allowed_types=Tipos de envios permitidos
|
config.repo.upload.allowed_types=Upload allowed types
|
||||||
config.repo.upload.file_max_size=Tamanho limite de ficheiros enviados
|
config.repo.upload.file_max_size=Upload file size limit
|
||||||
config.repo.upload.max_files=Quantidade limite de ficheiros enviados
|
config.repo.upload.max_files=Upload files limit
|
||||||
|
|
||||||
config.db_config=Configuração da base de dados
|
config.db_config=Configuração da base de dados
|
||||||
config.db.type=Tipo
|
config.db.type=Type
|
||||||
config.db.host=Anfitrião
|
config.db.host=Host
|
||||||
config.db.name=Nome
|
config.db.name=Name
|
||||||
config.db.schema=Esquema
|
config.db.schema=Schema
|
||||||
config.db.schema_helper=(apenas para "postgres")
|
config.db.schema_helper=(for "postgres" only)
|
||||||
config.db.user=Utilizador
|
config.db.user=User
|
||||||
config.db.ssl_mode=Modo SSL
|
config.db.ssl_mode=SSL mode
|
||||||
config.db.ssl_mode_helper=(apenas para "postgres")
|
config.db.ssl_mode_helper=(for "postgres" only)
|
||||||
config.db.path=Caminho
|
config.db.path=Path
|
||||||
config.db.path_helper=(apenas para "sqlite3")
|
config.db.path_helper=(for "sqlite3"only)
|
||||||
config.db.max_open_conns=Máximo de conexões abertas
|
config.db.max_open_conns=Maximum open connections
|
||||||
config.db.max_idle_conns=Máximo de conexões ociosas
|
config.db.max_idle_conns=Maximum idle connections
|
||||||
|
|
||||||
config.security_config=Configuração da segurança
|
config.security_config=Security configuration
|
||||||
config.security.login_remember_days=Dias lembrados de login
|
config.security.login_remember_days=Login remember days
|
||||||
config.security.cookie_remember_name=Lembrar do cookie
|
config.security.cookie_remember_name=Remember cookie
|
||||||
config.security.cookie_username=Cookie do nome do utilizador
|
config.security.cookie_username=Username cookie
|
||||||
config.security.cookie_secure=Ativar cookie seguro
|
config.security.cookie_secure=Enable secure cookie
|
||||||
config.security.reverse_proxy_auth_user=Cabeçalho de autenticação de proxy reverso
|
config.security.reverse_proxy_auth_user=Reverse proxy authentication header
|
||||||
config.security.enable_login_status_cookie=Enable login status cookie
|
config.security.enable_login_status_cookie=Enable login status cookie
|
||||||
config.security.login_status_cookie_name=Login status cookie
|
config.security.login_status_cookie_name=Login status cookie
|
||||||
config.security.local_network_allowlist=Local network allowlist
|
config.security.local_network_allowlist=Local network allowlist
|
||||||
|
|
||||||
config.email_config=Configuração de E-mail
|
config.email_config=Email configuration
|
||||||
config.email.enabled=Ativado
|
config.email.enabled=Enabled
|
||||||
config.email.subject_prefix=Prefixo do assunto
|
config.email.subject_prefix=Subject prefix
|
||||||
config.email.host=Anfitrião
|
config.email.host=Host
|
||||||
config.email.from=De
|
config.email.from=From
|
||||||
config.email.user=Utilizador
|
config.email.user=User
|
||||||
config.email.disable_helo=Desativar HELO
|
config.email.disable_helo=Disable HELO
|
||||||
config.email.helo_hostname=Nome de anfitrião HELO
|
config.email.helo_hostname=HELO hostname
|
||||||
config.email.skip_verify=Skip certificate verify
|
config.email.skip_verify=Skip certificate verify
|
||||||
config.email.use_certificate=Usar certificado personalizado
|
config.email.use_certificate=Use custom certificate
|
||||||
config.email.cert_file=Ficheiro de certificado criptográfico
|
config.email.cert_file=Certificate file
|
||||||
config.email.key_file=Ficheiro da chave criptográfica
|
config.email.key_file=Key file
|
||||||
config.email.use_plain_text=Usar texto simples
|
config.email.use_plain_text=Use plain text
|
||||||
config.email.add_plain_text_alt=Adicionar alternativa de texto simples
|
config.email.add_plain_text_alt=Add plain text alternative
|
||||||
config.email.send_test_mail=Enviar e-mail de teste
|
config.email.send_test_mail=Send test email
|
||||||
config.email.test_mail_failed=Falhou o envio do e-mail de teste para '%s': %v
|
config.email.test_mail_failed=Failed to send test email to '%s': %v
|
||||||
config.email.test_mail_sent=O e-mail de teste foi enviado para '%s'.
|
config.email.test_mail_sent=Test email has been sent to '%s'.
|
||||||
|
|
||||||
config.auth_config=Configuração da autenticação
|
config.auth_config=Authentication configuration
|
||||||
config.auth_custom_logout_url=Custom logout URL
|
|
||||||
config.auth.activate_code_lives=Activate code lives
|
config.auth.activate_code_lives=Activate code lives
|
||||||
config.auth.reset_password_code_lives=Reset password code lives
|
config.auth.reset_password_code_lives=Reset password code lives
|
||||||
config.auth.require_email_confirm=Exigir confirmação por e-mail
|
config.auth.require_email_confirm=Require email confirmation
|
||||||
config.auth.require_sign_in_view=Exigir login para ver
|
config.auth.require_sign_in_view=Require sign in view
|
||||||
config.auth.disable_registration=Desativar registo
|
config.auth.disable_registration=Disable registration
|
||||||
config.auth.enable_registration_captcha=Ativar captcha para registar
|
config.auth.enable_registration_captcha=Enable registration captcha
|
||||||
config.auth.enable_reverse_proxy_authentication=Ativar autenticação do proxy reverso
|
config.auth.enable_reverse_proxy_authentication=Enable reverse proxy authentication
|
||||||
config.auth.enable_reverse_proxy_auto_registration=Ativar o registo automático do proxy reverso
|
config.auth.enable_reverse_proxy_auto_registration=Enable reverse proxy auto registration
|
||||||
config.auth.reverse_proxy_authentication_header=Cabeçalho de autenticação de proxy reverso
|
config.auth.reverse_proxy_authentication_header=Reverse proxy authentication header
|
||||||
|
|
||||||
config.user_config=Configuração do utilizador
|
config.user_config=User configuration
|
||||||
config.user.enable_email_notify=Ativar a notificação por e-mail
|
config.user.enable_email_notify=Enable email notification
|
||||||
|
|
||||||
config.session_config=Configuração de sessão
|
config.session_config=Configuração de sessão
|
||||||
config.session.provider=Provedor
|
config.session.provider=Provider
|
||||||
config.session.provider_config=Configuração do provedor
|
config.session.provider_config=Provider config
|
||||||
config.session.cookie_name=Cookie
|
config.session.cookie_name=Cookie
|
||||||
config.session.https_only=Somente HTTPS
|
config.session.https_only=HTTPS only
|
||||||
config.session.gc_interval=GC interval
|
config.session.gc_interval=GC interval
|
||||||
config.session.max_life_time=Max life time
|
config.session.max_life_time=Max life time
|
||||||
config.session.csrf_cookie_name=CSRF cookie
|
config.session.csrf_cookie_name=CSRF cookie
|
||||||
|
|
||||||
config.cache_config=Configuração de cache
|
config.cache_config=Configuração de cache
|
||||||
config.cache.adapter=Adaptador
|
config.cache.adapter=Adapter
|
||||||
config.cache.interval=Intervalo de GC
|
config.cache.interval=GC interval
|
||||||
config.cache.host=Anfitrião
|
config.cache.host=Host
|
||||||
|
|
||||||
config.http_config=Configuração HTTP
|
config.http_config=Configuração HTTP
|
||||||
config.http.access_control_allow_origin=Access control allow origin
|
config.http.access_control_allow_origin=Access control allow origin
|
||||||
|
|
||||||
config.attachment_config=Configuração de anexos
|
config.attachment_config=Attachment configuration
|
||||||
config.attachment.enabled=Ativado
|
config.attachment.enabled=Enabled
|
||||||
config.attachment.path=Caminho
|
config.attachment.path=Path
|
||||||
config.attachment.allowed_types=Tipos permitidos
|
config.attachment.allowed_types=Allowed types
|
||||||
config.attachment.max_size=Limite de tamanho
|
config.attachment.max_size=Size limit
|
||||||
config.attachment.max_files=Limite de ficheiros
|
config.attachment.max_files=Files limit
|
||||||
|
|
||||||
config.release_config=Release configuration
|
config.release_config=Release configuration
|
||||||
config.release.attachment.enabled=Attachment enabled
|
config.release.attachment.enabled=Attachment enabled
|
||||||
config.release.attachment.allowed_types=Attachment allowed types
|
config.release.attachment.allowed_types=Attachment allowed types
|
||||||
config.release.attachment.max_size=Tamanho máximo dos anexos
|
config.release.attachment.max_size=Attachment size limit
|
||||||
config.release.attachment.max_files=Attachment files limit
|
config.release.attachment.max_files=Attachment files limit
|
||||||
|
|
||||||
config.picture_config=Configuração de imagem
|
config.picture_config=Configuração de imagem
|
||||||
@@ -1328,10 +1325,10 @@ config.picture.disable_gravatar=Disable Gravatar
|
|||||||
config.picture.enable_federated_avatar=Enable federated avatars
|
config.picture.enable_federated_avatar=Enable federated avatars
|
||||||
|
|
||||||
config.mirror_config=Mirror configuration
|
config.mirror_config=Mirror configuration
|
||||||
config.mirror.default_interval=Intervalo predefinido
|
config.mirror.default_interval=Default interval
|
||||||
|
|
||||||
config.webhook_config=Configuração de WebHook
|
config.webhook_config=Configuração de WebHook
|
||||||
config.webhook.types=Tipos
|
config.webhook.types=Types
|
||||||
config.webhook.deliver_timeout=Deliver timeout
|
config.webhook.deliver_timeout=Deliver timeout
|
||||||
config.webhook.skip_tls_verify=Skip TLS verify
|
config.webhook.skip_tls_verify=Skip TLS verify
|
||||||
|
|
||||||
|
|||||||
@@ -494,8 +494,6 @@ branches.stale_branches=Ramuri vechi
|
|||||||
branches.all=Toate ramurile
|
branches.all=Toate ramurile
|
||||||
branches.updated_by=Actualizat %[1]s prin %[2]s
|
branches.updated_by=Actualizat %[1]s prin %[2]s
|
||||||
branches.change_default_branch=Schimbarea ramurii implicite
|
branches.change_default_branch=Schimbarea ramurii implicite
|
||||||
branches.default_deletion_not_allowed=Cannot delete the default branch.
|
|
||||||
branches.protected_deletion_not_allowed=Cannot delete a protected branch.
|
|
||||||
|
|
||||||
editor.new_file=Fișier nou
|
editor.new_file=Fișier nou
|
||||||
editor.upload_file=Încărcați fișier
|
editor.upload_file=Încărcați fișier
|
||||||
@@ -1276,7 +1274,6 @@ config.email.test_mail_failed=Nu a reușit să trimită un e-mail de test către
|
|||||||
config.email.test_mail_sent=E-mailul de test a fost trimis la '%s'.
|
config.email.test_mail_sent=E-mailul de test a fost trimis la '%s'.
|
||||||
|
|
||||||
config.auth_config=Configurația de autentificare
|
config.auth_config=Configurația de autentificare
|
||||||
config.auth_custom_logout_url=Custom logout URL
|
|
||||||
config.auth.activate_code_lives=Activați viețile de cod
|
config.auth.activate_code_lives=Activați viețile de cod
|
||||||
config.auth.reset_password_code_lives=Resetează viețile codului parolei
|
config.auth.reset_password_code_lives=Resetează viețile codului parolei
|
||||||
config.auth.require_email_confirm=Solicită confirmarea prin e-mail
|
config.auth.require_email_confirm=Solicită confirmarea prin e-mail
|
||||||
|
|||||||
@@ -494,8 +494,6 @@ branches.stale_branches=Устаревшие ветки
|
|||||||
branches.all=Все ветки
|
branches.all=Все ветки
|
||||||
branches.updated_by=Обновлено %[1]s пользователем %[2]s
|
branches.updated_by=Обновлено %[1]s пользователем %[2]s
|
||||||
branches.change_default_branch=Change Default Branch
|
branches.change_default_branch=Change Default Branch
|
||||||
branches.default_deletion_not_allowed=Cannot delete the default branch.
|
|
||||||
branches.protected_deletion_not_allowed=Cannot delete a protected branch.
|
|
||||||
|
|
||||||
editor.new_file=Новый файл
|
editor.new_file=Новый файл
|
||||||
editor.upload_file=Загрузить файл
|
editor.upload_file=Загрузить файл
|
||||||
@@ -1276,7 +1274,6 @@ config.email.test_mail_failed=Не удалось отправить тесто
|
|||||||
config.email.test_mail_sent=Тестовое письмо было отправлено на %s
|
config.email.test_mail_sent=Тестовое письмо было отправлено на %s
|
||||||
|
|
||||||
config.auth_config=Конфигурация аутентификации
|
config.auth_config=Конфигурация аутентификации
|
||||||
config.auth_custom_logout_url=Custom logout URL
|
|
||||||
config.auth.activate_code_lives=Activate code lives
|
config.auth.activate_code_lives=Activate code lives
|
||||||
config.auth.reset_password_code_lives=Срок действия кода сброса пароля
|
config.auth.reset_password_code_lives=Срок действия кода сброса пароля
|
||||||
config.auth.require_email_confirm=Требовать подтверждение по электронной почте
|
config.auth.require_email_confirm=Требовать подтверждение по электронной почте
|
||||||
|
|||||||
@@ -494,8 +494,6 @@ branches.stale_branches=Zastaralé vetvy
|
|||||||
branches.all=Všetky vetvy
|
branches.all=Všetky vetvy
|
||||||
branches.updated_by=%[2]s zmenil %[1]s
|
branches.updated_by=%[2]s zmenil %[1]s
|
||||||
branches.change_default_branch=Zmeniť základnú vetvu
|
branches.change_default_branch=Zmeniť základnú vetvu
|
||||||
branches.default_deletion_not_allowed=Cannot delete the default branch.
|
|
||||||
branches.protected_deletion_not_allowed=Cannot delete a protected branch.
|
|
||||||
|
|
||||||
editor.new_file=Nový súbor
|
editor.new_file=Nový súbor
|
||||||
editor.upload_file=Nahrať súbor
|
editor.upload_file=Nahrať súbor
|
||||||
@@ -1276,7 +1274,6 @@ config.email.test_mail_failed=Failed to send test email to '%s': %v
|
|||||||
config.email.test_mail_sent=Test email has been sent to '%s'.
|
config.email.test_mail_sent=Test email has been sent to '%s'.
|
||||||
|
|
||||||
config.auth_config=Authentication configuration
|
config.auth_config=Authentication configuration
|
||||||
config.auth_custom_logout_url=Custom logout URL
|
|
||||||
config.auth.activate_code_lives=Activate code lives
|
config.auth.activate_code_lives=Activate code lives
|
||||||
config.auth.reset_password_code_lives=Reset password code lives
|
config.auth.reset_password_code_lives=Reset password code lives
|
||||||
config.auth.require_email_confirm=Require email confirmation
|
config.auth.require_email_confirm=Require email confirmation
|
||||||
|
|||||||
@@ -494,8 +494,6 @@ branches.stale_branches=Застареле гране
|
|||||||
branches.all=Све гране
|
branches.all=Све гране
|
||||||
branches.updated_by=Ажуриран %[1]s од %[2]s
|
branches.updated_by=Ажуриран %[1]s од %[2]s
|
||||||
branches.change_default_branch=Промените подразумевану грану
|
branches.change_default_branch=Промените подразумевану грану
|
||||||
branches.default_deletion_not_allowed=Cannot delete the default branch.
|
|
||||||
branches.protected_deletion_not_allowed=Cannot delete a protected branch.
|
|
||||||
|
|
||||||
editor.new_file=Нова датотека
|
editor.new_file=Нова датотека
|
||||||
editor.upload_file=Отпреми датотеку
|
editor.upload_file=Отпреми датотеку
|
||||||
@@ -1276,7 +1274,6 @@ config.email.test_mail_failed=Failed to send test email to '%s': %v
|
|||||||
config.email.test_mail_sent=Test email has been sent to '%s'.
|
config.email.test_mail_sent=Test email has been sent to '%s'.
|
||||||
|
|
||||||
config.auth_config=Authentication configuration
|
config.auth_config=Authentication configuration
|
||||||
config.auth_custom_logout_url=Custom logout URL
|
|
||||||
config.auth.activate_code_lives=Activate code lives
|
config.auth.activate_code_lives=Activate code lives
|
||||||
config.auth.reset_password_code_lives=Reset password code lives
|
config.auth.reset_password_code_lives=Reset password code lives
|
||||||
config.auth.require_email_confirm=Require email confirmation
|
config.auth.require_email_confirm=Require email confirmation
|
||||||
|
|||||||
@@ -494,8 +494,6 @@ branches.stale_branches=Djärva brancher
|
|||||||
branches.all=Alla brancher
|
branches.all=Alla brancher
|
||||||
branches.updated_by=Uppdaterade %[1]s med %[2]s
|
branches.updated_by=Uppdaterade %[1]s med %[2]s
|
||||||
branches.change_default_branch=Ändra standard branch
|
branches.change_default_branch=Ändra standard branch
|
||||||
branches.default_deletion_not_allowed=Cannot delete the default branch.
|
|
||||||
branches.protected_deletion_not_allowed=Cannot delete a protected branch.
|
|
||||||
|
|
||||||
editor.new_file=Ny fil
|
editor.new_file=Ny fil
|
||||||
editor.upload_file=Ladda upp fil
|
editor.upload_file=Ladda upp fil
|
||||||
@@ -1276,7 +1274,6 @@ config.email.test_mail_failed=Failed to send test email to '%s': %v
|
|||||||
config.email.test_mail_sent=Test email has been sent to '%s'.
|
config.email.test_mail_sent=Test email has been sent to '%s'.
|
||||||
|
|
||||||
config.auth_config=Authentication configuration
|
config.auth_config=Authentication configuration
|
||||||
config.auth_custom_logout_url=Custom logout URL
|
|
||||||
config.auth.activate_code_lives=Activate code lives
|
config.auth.activate_code_lives=Activate code lives
|
||||||
config.auth.reset_password_code_lives=Reset password code lives
|
config.auth.reset_password_code_lives=Reset password code lives
|
||||||
config.auth.require_email_confirm=Require email confirmation
|
config.auth.require_email_confirm=Require email confirmation
|
||||||
|
|||||||
@@ -494,8 +494,6 @@ branches.stale_branches=Eskimiş Bölümler
|
|||||||
branches.all=Bütün Bölümler
|
branches.all=Bütün Bölümler
|
||||||
branches.updated_by=%[2]s tarafından %[1]s güncellendi
|
branches.updated_by=%[2]s tarafından %[1]s güncellendi
|
||||||
branches.change_default_branch=Varsayılan Bölümü Değiştir
|
branches.change_default_branch=Varsayılan Bölümü Değiştir
|
||||||
branches.default_deletion_not_allowed=Cannot delete the default branch.
|
|
||||||
branches.protected_deletion_not_allowed=Cannot delete a protected branch.
|
|
||||||
|
|
||||||
editor.new_file=Yeni dosya
|
editor.new_file=Yeni dosya
|
||||||
editor.upload_file=Dosyayı yükle
|
editor.upload_file=Dosyayı yükle
|
||||||
@@ -1276,7 +1274,6 @@ config.email.test_mail_failed=Failed to send test email to '%s': %v
|
|||||||
config.email.test_mail_sent=Test email has been sent to '%s'.
|
config.email.test_mail_sent=Test email has been sent to '%s'.
|
||||||
|
|
||||||
config.auth_config=Authentication configuration
|
config.auth_config=Authentication configuration
|
||||||
config.auth_custom_logout_url=Custom logout URL
|
|
||||||
config.auth.activate_code_lives=Activate code lives
|
config.auth.activate_code_lives=Activate code lives
|
||||||
config.auth.reset_password_code_lives=Reset password code lives
|
config.auth.reset_password_code_lives=Reset password code lives
|
||||||
config.auth.require_email_confirm=Require email confirmation
|
config.auth.require_email_confirm=Require email confirmation
|
||||||
|
|||||||
@@ -494,8 +494,6 @@ branches.stale_branches=Застарілі гілки
|
|||||||
branches.all=Усі гілки
|
branches.all=Усі гілки
|
||||||
branches.updated_by=Оновлено %[1]s з %[2]s
|
branches.updated_by=Оновлено %[1]s з %[2]s
|
||||||
branches.change_default_branch=Гілку за замовчуванням змінено
|
branches.change_default_branch=Гілку за замовчуванням змінено
|
||||||
branches.default_deletion_not_allowed=Cannot delete the default branch.
|
|
||||||
branches.protected_deletion_not_allowed=Cannot delete a protected branch.
|
|
||||||
|
|
||||||
editor.new_file=Новий файл
|
editor.new_file=Новий файл
|
||||||
editor.upload_file=Завантажити файл
|
editor.upload_file=Завантажити файл
|
||||||
@@ -1276,7 +1274,6 @@ config.email.test_mail_failed=Помилка відправлення пробн
|
|||||||
config.email.test_mail_sent=Пробного листа було відправлено до '%s'.
|
config.email.test_mail_sent=Пробного листа було відправлено до '%s'.
|
||||||
|
|
||||||
config.auth_config=Налаштування аутентифікації
|
config.auth_config=Налаштування аутентифікації
|
||||||
config.auth_custom_logout_url=Custom logout URL
|
|
||||||
config.auth.activate_code_lives=Активувати код підтвердження
|
config.auth.activate_code_lives=Активувати код підтвердження
|
||||||
config.auth.reset_password_code_lives=Термін придатності кода при скиданні пароля
|
config.auth.reset_password_code_lives=Термін придатності кода при скиданні пароля
|
||||||
config.auth.require_email_confirm=Вимагає підтвердження електронною поштою
|
config.auth.require_email_confirm=Вимагає підтвердження електронною поштою
|
||||||
|
|||||||
@@ -494,8 +494,6 @@ branches.stale_branches=Các nhánh cũ
|
|||||||
branches.all=Tất cả các nhánh
|
branches.all=Tất cả các nhánh
|
||||||
branches.updated_by=Updated %[1]s by %[2]s
|
branches.updated_by=Updated %[1]s by %[2]s
|
||||||
branches.change_default_branch=Thay đổi nhánh mặc định
|
branches.change_default_branch=Thay đổi nhánh mặc định
|
||||||
branches.default_deletion_not_allowed=Cannot delete the default branch.
|
|
||||||
branches.protected_deletion_not_allowed=Cannot delete a protected branch.
|
|
||||||
|
|
||||||
editor.new_file=Tập tin mới
|
editor.new_file=Tập tin mới
|
||||||
editor.upload_file=Tải tập tin lên
|
editor.upload_file=Tải tập tin lên
|
||||||
@@ -1276,7 +1274,6 @@ config.email.test_mail_failed=Gửi email kiểm tra đến '%s':%v thất bại
|
|||||||
config.email.test_mail_sent=Email kiểm tra đã được gửi đến '%s'.
|
config.email.test_mail_sent=Email kiểm tra đã được gửi đến '%s'.
|
||||||
|
|
||||||
config.auth_config=Cấu hình xác thực
|
config.auth_config=Cấu hình xác thực
|
||||||
config.auth_custom_logout_url=Custom logout URL
|
|
||||||
config.auth.activate_code_lives=Activate code lives
|
config.auth.activate_code_lives=Activate code lives
|
||||||
config.auth.reset_password_code_lives=Reset password code lives
|
config.auth.reset_password_code_lives=Reset password code lives
|
||||||
config.auth.require_email_confirm=Yêu cầu xác nhận email
|
config.auth.require_email_confirm=Yêu cầu xác nhận email
|
||||||
|
|||||||
@@ -494,8 +494,6 @@ branches.stale_branches=陈旧分支
|
|||||||
branches.all=所有分支
|
branches.all=所有分支
|
||||||
branches.updated_by=由 %[2]s 更新于 %[1]s
|
branches.updated_by=由 %[2]s 更新于 %[1]s
|
||||||
branches.change_default_branch=更改默认分支
|
branches.change_default_branch=更改默认分支
|
||||||
branches.default_deletion_not_allowed=Cannot delete the default branch.
|
|
||||||
branches.protected_deletion_not_allowed=Cannot delete a protected branch.
|
|
||||||
|
|
||||||
editor.new_file=新的文件
|
editor.new_file=新的文件
|
||||||
editor.upload_file=上传文件
|
editor.upload_file=上传文件
|
||||||
@@ -1277,7 +1275,6 @@ config.email.test_mail_failed=发送测试邮件至 '%s' 时失败:%v
|
|||||||
config.email.test_mail_sent=测试邮件已经发送至 '%s'。
|
config.email.test_mail_sent=测试邮件已经发送至 '%s'。
|
||||||
|
|
||||||
config.auth_config=认证配置
|
config.auth_config=认证配置
|
||||||
config.auth_custom_logout_url=Custom logout URL
|
|
||||||
config.auth.activate_code_lives=激活用户链接有效期
|
config.auth.activate_code_lives=激活用户链接有效期
|
||||||
config.auth.reset_password_code_lives=重置密码链接有效期
|
config.auth.reset_password_code_lives=重置密码链接有效期
|
||||||
config.auth.require_email_confirm=注册邮件确认
|
config.auth.require_email_confirm=注册邮件确认
|
||||||
|
|||||||
@@ -494,8 +494,6 @@ branches.stale_branches=Stale Branches
|
|||||||
branches.all=All Branches
|
branches.all=All Branches
|
||||||
branches.updated_by=Updated %[1]s by %[2]s
|
branches.updated_by=Updated %[1]s by %[2]s
|
||||||
branches.change_default_branch=Change Default Branch
|
branches.change_default_branch=Change Default Branch
|
||||||
branches.default_deletion_not_allowed=Cannot delete the default branch.
|
|
||||||
branches.protected_deletion_not_allowed=Cannot delete a protected branch.
|
|
||||||
|
|
||||||
editor.new_file=New file
|
editor.new_file=New file
|
||||||
editor.upload_file=Upload file
|
editor.upload_file=Upload file
|
||||||
@@ -1276,7 +1274,6 @@ config.email.test_mail_failed=Failed to send test email to '%s': %v
|
|||||||
config.email.test_mail_sent=Test email has been sent to '%s'.
|
config.email.test_mail_sent=Test email has been sent to '%s'.
|
||||||
|
|
||||||
config.auth_config=Authentication configuration
|
config.auth_config=Authentication configuration
|
||||||
config.auth_custom_logout_url=Custom logout URL
|
|
||||||
config.auth.activate_code_lives=Activate code lives
|
config.auth.activate_code_lives=Activate code lives
|
||||||
config.auth.reset_password_code_lives=Reset password code lives
|
config.auth.reset_password_code_lives=Reset password code lives
|
||||||
config.auth.require_email_confirm=Require email confirmation
|
config.auth.require_email_confirm=Require email confirmation
|
||||||
|
|||||||
@@ -494,8 +494,6 @@ branches.stale_branches=陳舊分支
|
|||||||
branches.all=所有分支
|
branches.all=所有分支
|
||||||
branches.updated_by=%[2]s 更新了 %[1]s
|
branches.updated_by=%[2]s 更新了 %[1]s
|
||||||
branches.change_default_branch=變更預設分支
|
branches.change_default_branch=變更預設分支
|
||||||
branches.default_deletion_not_allowed=Cannot delete the default branch.
|
|
||||||
branches.protected_deletion_not_allowed=Cannot delete a protected branch.
|
|
||||||
|
|
||||||
editor.new_file=開新檔案
|
editor.new_file=開新檔案
|
||||||
editor.upload_file=上傳檔案
|
editor.upload_file=上傳檔案
|
||||||
@@ -1276,7 +1274,6 @@ config.email.test_mail_failed=發送測試郵件至 '%s' 時失敗:%v
|
|||||||
config.email.test_mail_sent=測試電子郵件已發送到 '%s'。
|
config.email.test_mail_sent=測試電子郵件已發送到 '%s'。
|
||||||
|
|
||||||
config.auth_config=Authentication configuration
|
config.auth_config=Authentication configuration
|
||||||
config.auth_custom_logout_url=Custom logout URL
|
|
||||||
config.auth.activate_code_lives=Activate code lives
|
config.auth.activate_code_lives=Activate code lives
|
||||||
config.auth.reset_password_code_lives=Reset password code lives
|
config.auth.reset_password_code_lives=Reset password code lives
|
||||||
config.auth.require_email_confirm=Require email confirmation
|
config.auth.require_email_confirm=Require email confirmation
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Docker for Gogs (Next Generation)
|
# Docker for Gogs (Next Generation)
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> This is the next-generation, security-focused Docker image. This will become the default image distribution (`gogs/gogs:latest`) starting 0.16.0.
|
> This is the next-generation, security-focused Docker image. This will become the default image distribution (`gogs/gogs:latest`) starting 0.15.0.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,10 @@
|
|||||||
# Docker for Gogs
|
# Docker for Gogs
|
||||||
|
|
||||||
> [!WARNING]
|
> [!WARNING]
|
||||||
> This is now the legacy Docker image that lacks modern security best practices. It will be published as `gogs/gogs:legacy-latest` starting 0.16.0, and be completely removed no earlier than 0.17.0.
|
> This is now the legacy Docker image that lacks modern security best practices. It will be published as `gogs/gogs:legacy-latest` starting 0.15.0, and be completely removed starting 0.16.0.
|
||||||
>
|
>
|
||||||
> To use the next-generation, security-focused Docker image, see [docker-next/README.md](../docker-next/README.md).
|
> To use the next-generation, security-focused Docker image, see [docker-next/README.md](../docker-next/README.md).
|
||||||
|
|
||||||
> [!IMPORTANT]
|
|
||||||
> Image versions:
|
|
||||||
> - Every released version has its own tag , e.g., `gogs/gogs:0.13.4`, and a tag points to the latest patch of the minor version, e.g., `gogs/gogs:0.13`.
|
|
||||||
> - The `latest` tag is the image version built from the latest `main` branch.
|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Visit [Docker Hub](https://hub.docker.com/u/gogs) or [GitHub Container registry](https://github.com/gogs/gogs/pkgs/container/gogs) to see all available images and tags.
|
Visit [Docker Hub](https://hub.docker.com/u/gogs) or [GitHub Container registry](https://github.com/gogs/gogs/pkgs/container/gogs) to see all available images and tags.
|
||||||
|
|||||||
@@ -1,131 +0,0 @@
|
|||||||
# Table "access"
|
|
||||||
|
|
||||||
```
|
|
||||||
Field | Column | PostgreSQL | MySQL | SQLite3
|
|
||||||
--------+---------+-----------------+-----------------------+-----------------------
|
|
||||||
ID | id | BIGSERIAL | BIGINT AUTO_INCREMENT | INTEGER AUTOINCREMENT
|
|
||||||
UserID | user_id | BIGINT NOT NULL | BIGINT NOT NULL | INTEGER NOT NULL
|
|
||||||
RepoID | repo_id | BIGINT NOT NULL | BIGINT NOT NULL | INTEGER NOT NULL
|
|
||||||
Mode | mode | BIGINT NOT NULL | BIGINT NOT NULL | INTEGER NOT NULL
|
|
||||||
|
|
||||||
Primary keys: id
|
|
||||||
Indexes:
|
|
||||||
"access_user_repo_unique" UNIQUE (user_id, repo_id)
|
|
||||||
```
|
|
||||||
|
|
||||||
# Table "access_token"
|
|
||||||
|
|
||||||
```
|
|
||||||
Field | Column | PostgreSQL | MySQL | SQLite3
|
|
||||||
-------------+--------------+-----------------------------+-----------------------------+-----------------------------
|
|
||||||
ID | id | BIGSERIAL | BIGINT AUTO_INCREMENT | INTEGER AUTOINCREMENT
|
|
||||||
UserID | uid | BIGINT | BIGINT | INTEGER
|
|
||||||
Name | name | TEXT | LONGTEXT | TEXT
|
|
||||||
Sha1 | sha1 | VARCHAR(40) UNIQUE | VARCHAR(40) UNIQUE | VARCHAR(40) UNIQUE
|
|
||||||
SHA256 | sha256 | VARCHAR(64) NOT NULL UNIQUE | VARCHAR(64) NOT NULL UNIQUE | VARCHAR(64) NOT NULL UNIQUE
|
|
||||||
CreatedUnix | created_unix | BIGINT | BIGINT | INTEGER
|
|
||||||
UpdatedUnix | updated_unix | BIGINT | BIGINT | INTEGER
|
|
||||||
|
|
||||||
Primary keys: id
|
|
||||||
Indexes:
|
|
||||||
"idx_access_token_user_id" (uid)
|
|
||||||
```
|
|
||||||
|
|
||||||
# Table "action"
|
|
||||||
|
|
||||||
```
|
|
||||||
Field | Column | PostgreSQL | MySQL | SQLite3
|
|
||||||
--------------+----------------+--------------------------------+--------------------------------+--------------------------------
|
|
||||||
ID | id | BIGSERIAL | BIGINT AUTO_INCREMENT | INTEGER AUTOINCREMENT
|
|
||||||
UserID | user_id | BIGINT | BIGINT | INTEGER
|
|
||||||
OpType | op_type | BIGINT | BIGINT | INTEGER
|
|
||||||
ActUserID | act_user_id | BIGINT | BIGINT | INTEGER
|
|
||||||
ActUserName | act_user_name | TEXT | LONGTEXT | TEXT
|
|
||||||
RepoID | repo_id | BIGINT | BIGINT | INTEGER
|
|
||||||
RepoUserName | repo_user_name | TEXT | LONGTEXT | TEXT
|
|
||||||
RepoName | repo_name | TEXT | LONGTEXT | TEXT
|
|
||||||
RefName | ref_name | TEXT | LONGTEXT | TEXT
|
|
||||||
IsPrivate | is_private | BOOLEAN NOT NULL DEFAULT FALSE | BOOLEAN NOT NULL DEFAULT FALSE | NUMERIC NOT NULL DEFAULT FALSE
|
|
||||||
Content | content | TEXT | LONGTEXT | TEXT
|
|
||||||
CreatedUnix | created_unix | BIGINT | BIGINT | INTEGER
|
|
||||||
|
|
||||||
Primary keys: id
|
|
||||||
Indexes:
|
|
||||||
"idx_action_repo_id" (repo_id)
|
|
||||||
"idx_action_user_id" (user_id)
|
|
||||||
```
|
|
||||||
|
|
||||||
# Table "email_address"
|
|
||||||
|
|
||||||
```
|
|
||||||
Field | Column | PostgreSQL | MySQL | SQLite3
|
|
||||||
-------------+--------------+--------------------------------+--------------------------------+--------------------------------
|
|
||||||
ID | id | BIGSERIAL | BIGINT AUTO_INCREMENT | INTEGER AUTOINCREMENT
|
|
||||||
UserID | uid | BIGINT NOT NULL | BIGINT NOT NULL | INTEGER NOT NULL
|
|
||||||
Email | email | VARCHAR(254) NOT NULL | VARCHAR(254) NOT NULL | TEXT NOT NULL
|
|
||||||
IsActivated | is_activated | BOOLEAN NOT NULL DEFAULT FALSE | BOOLEAN NOT NULL DEFAULT FALSE | NUMERIC NOT NULL DEFAULT FALSE
|
|
||||||
|
|
||||||
Primary keys: id
|
|
||||||
Indexes:
|
|
||||||
"email_address_user_email_unique" UNIQUE (uid, email)
|
|
||||||
"idx_email_address_user_id" (uid)
|
|
||||||
```
|
|
||||||
|
|
||||||
# Table "follow"
|
|
||||||
|
|
||||||
```
|
|
||||||
Field | Column | PostgreSQL | MySQL | SQLite3
|
|
||||||
----------+-----------+-----------------+-----------------------+-----------------------
|
|
||||||
ID | id | BIGSERIAL | BIGINT AUTO_INCREMENT | INTEGER AUTOINCREMENT
|
|
||||||
UserID | user_id | BIGINT NOT NULL | BIGINT NOT NULL | INTEGER NOT NULL
|
|
||||||
FollowID | follow_id | BIGINT NOT NULL | BIGINT NOT NULL | INTEGER NOT NULL
|
|
||||||
|
|
||||||
Primary keys: id
|
|
||||||
Indexes:
|
|
||||||
"follow_user_follow_unique" UNIQUE (user_id, follow_id)
|
|
||||||
```
|
|
||||||
|
|
||||||
# Table "lfs_object"
|
|
||||||
|
|
||||||
```
|
|
||||||
Field | Column | PostgreSQL | MySQL | SQLite3
|
|
||||||
-----------+------------+----------------------+----------------------+-------------------
|
|
||||||
RepoID | repo_id | BIGINT | BIGINT | INTEGER
|
|
||||||
OID | oid | TEXT | VARCHAR(191) | TEXT
|
|
||||||
Size | size | BIGINT NOT NULL | BIGINT NOT NULL | INTEGER NOT NULL
|
|
||||||
Storage | storage | TEXT NOT NULL | LONGTEXT NOT NULL | TEXT NOT NULL
|
|
||||||
CreatedAt | created_at | TIMESTAMPTZ NOT NULL | DATETIME(3) NOT NULL | DATETIME NOT NULL
|
|
||||||
|
|
||||||
Primary keys: repo_id, oid
|
|
||||||
```
|
|
||||||
|
|
||||||
# Table "login_source"
|
|
||||||
|
|
||||||
```
|
|
||||||
Field | Column | PostgreSQL | MySQL | SQLite3
|
|
||||||
-------------+--------------+------------------+-----------------------+-----------------------
|
|
||||||
ID | id | BIGSERIAL | BIGINT AUTO_INCREMENT | INTEGER AUTOINCREMENT
|
|
||||||
Type | type | BIGINT | BIGINT | INTEGER
|
|
||||||
Name | name | TEXT UNIQUE | VARCHAR(191) UNIQUE | TEXT UNIQUE
|
|
||||||
IsActived | is_actived | BOOLEAN NOT NULL | BOOLEAN NOT NULL | NUMERIC NOT NULL
|
|
||||||
IsDefault | is_default | BOOLEAN | BOOLEAN | NUMERIC
|
|
||||||
Config | cfg | TEXT | TEXT | TEXT
|
|
||||||
CreatedUnix | created_unix | BIGINT | BIGINT | INTEGER
|
|
||||||
UpdatedUnix | updated_unix | BIGINT | BIGINT | INTEGER
|
|
||||||
|
|
||||||
Primary keys: id
|
|
||||||
```
|
|
||||||
|
|
||||||
# Table "notice"
|
|
||||||
|
|
||||||
```
|
|
||||||
Field | Column | PostgreSQL | MySQL | SQLite3
|
|
||||||
-------------+--------------+------------+-----------------------+-----------------------
|
|
||||||
ID | id | BIGSERIAL | BIGINT AUTO_INCREMENT | INTEGER AUTOINCREMENT
|
|
||||||
Type | type | BIGINT | BIGINT | INTEGER
|
|
||||||
Description | description | TEXT | TEXT | TEXT
|
|
||||||
CreatedUnix | created_unix | BIGINT | BIGINT | INTEGER
|
|
||||||
|
|
||||||
Primary keys: id
|
|
||||||
```
|
|
||||||
|
|
||||||
131
docs/dev/database_schema.md
Normal file
131
docs/dev/database_schema.md
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
# Table "access"
|
||||||
|
|
||||||
|
```
|
||||||
|
FIELD | COLUMN | POSTGRESQL | MYSQL | SQLITE3
|
||||||
|
---------+---------+-----------------+-----------------------+-------------------
|
||||||
|
ID | id | BIGSERIAL | BIGINT AUTO_INCREMENT | INTEGER
|
||||||
|
UserID | user_id | BIGINT NOT NULL | BIGINT NOT NULL | INTEGER NOT NULL
|
||||||
|
RepoID | repo_id | BIGINT NOT NULL | BIGINT NOT NULL | INTEGER NOT NULL
|
||||||
|
Mode | mode | BIGINT NOT NULL | BIGINT NOT NULL | INTEGER NOT NULL
|
||||||
|
|
||||||
|
Primary keys: id
|
||||||
|
Indexes:
|
||||||
|
"access_user_repo_unique" UNIQUE (user_id, repo_id)
|
||||||
|
```
|
||||||
|
|
||||||
|
# Table "access_token"
|
||||||
|
|
||||||
|
```
|
||||||
|
FIELD | COLUMN | POSTGRESQL | MYSQL | SQLITE3
|
||||||
|
--------------+--------------+-----------------------------+-----------------------------+------------------------------
|
||||||
|
ID | id | BIGSERIAL | BIGINT AUTO_INCREMENT | INTEGER
|
||||||
|
UserID | uid | BIGINT | BIGINT | INTEGER
|
||||||
|
Name | name | TEXT | LONGTEXT | TEXT
|
||||||
|
Sha1 | sha1 | VARCHAR(40) UNIQUE | VARCHAR(40) UNIQUE | VARCHAR(40) UNIQUE
|
||||||
|
SHA256 | sha256 | VARCHAR(64) NOT NULL UNIQUE | VARCHAR(64) NOT NULL UNIQUE | VARCHAR(64) NOT NULL UNIQUE
|
||||||
|
CreatedUnix | created_unix | BIGINT | BIGINT | INTEGER
|
||||||
|
UpdatedUnix | updated_unix | BIGINT | BIGINT | INTEGER
|
||||||
|
|
||||||
|
Primary keys: id
|
||||||
|
Indexes:
|
||||||
|
"idx_access_token_user_id" (uid)
|
||||||
|
```
|
||||||
|
|
||||||
|
# Table "action"
|
||||||
|
|
||||||
|
```
|
||||||
|
FIELD | COLUMN | POSTGRESQL | MYSQL | SQLITE3
|
||||||
|
---------------+----------------+--------------------------------+--------------------------------+---------------------------------
|
||||||
|
ID | id | BIGSERIAL | BIGINT AUTO_INCREMENT | INTEGER
|
||||||
|
UserID | user_id | BIGINT | BIGINT | INTEGER
|
||||||
|
OpType | op_type | BIGINT | BIGINT | INTEGER
|
||||||
|
ActUserID | act_user_id | BIGINT | BIGINT | INTEGER
|
||||||
|
ActUserName | act_user_name | TEXT | LONGTEXT | TEXT
|
||||||
|
RepoID | repo_id | BIGINT | BIGINT | INTEGER
|
||||||
|
RepoUserName | repo_user_name | TEXT | LONGTEXT | TEXT
|
||||||
|
RepoName | repo_name | TEXT | LONGTEXT | TEXT
|
||||||
|
RefName | ref_name | TEXT | LONGTEXT | TEXT
|
||||||
|
IsPrivate | is_private | BOOLEAN NOT NULL DEFAULT FALSE | BOOLEAN NOT NULL DEFAULT FALSE | NUMERIC NOT NULL DEFAULT FALSE
|
||||||
|
Content | content | TEXT | LONGTEXT | TEXT
|
||||||
|
CreatedUnix | created_unix | BIGINT | BIGINT | INTEGER
|
||||||
|
|
||||||
|
Primary keys: id
|
||||||
|
Indexes:
|
||||||
|
"idx_action_repo_id" (repo_id)
|
||||||
|
"idx_action_user_id" (user_id)
|
||||||
|
```
|
||||||
|
|
||||||
|
# Table "email_address"
|
||||||
|
|
||||||
|
```
|
||||||
|
FIELD | COLUMN | POSTGRESQL | MYSQL | SQLITE3
|
||||||
|
--------------+--------------+--------------------------------+--------------------------------+---------------------------------
|
||||||
|
ID | id | BIGSERIAL | BIGINT AUTO_INCREMENT | INTEGER
|
||||||
|
UserID | uid | BIGINT NOT NULL | BIGINT NOT NULL | INTEGER NOT NULL
|
||||||
|
Email | email | VARCHAR(254) NOT NULL | VARCHAR(254) NOT NULL | TEXT NOT NULL
|
||||||
|
IsActivated | is_activated | BOOLEAN NOT NULL DEFAULT FALSE | BOOLEAN NOT NULL DEFAULT FALSE | NUMERIC NOT NULL DEFAULT FALSE
|
||||||
|
|
||||||
|
Primary keys: id
|
||||||
|
Indexes:
|
||||||
|
"email_address_user_email_unique" UNIQUE (uid, email)
|
||||||
|
"idx_email_address_user_id" (uid)
|
||||||
|
```
|
||||||
|
|
||||||
|
# Table "follow"
|
||||||
|
|
||||||
|
```
|
||||||
|
FIELD | COLUMN | POSTGRESQL | MYSQL | SQLITE3
|
||||||
|
-----------+-----------+-----------------+-----------------------+-------------------
|
||||||
|
ID | id | BIGSERIAL | BIGINT AUTO_INCREMENT | INTEGER
|
||||||
|
UserID | user_id | BIGINT NOT NULL | BIGINT NOT NULL | INTEGER NOT NULL
|
||||||
|
FollowID | follow_id | BIGINT NOT NULL | BIGINT NOT NULL | INTEGER NOT NULL
|
||||||
|
|
||||||
|
Primary keys: id
|
||||||
|
Indexes:
|
||||||
|
"follow_user_follow_unique" UNIQUE (user_id, follow_id)
|
||||||
|
```
|
||||||
|
|
||||||
|
# Table "lfs_object"
|
||||||
|
|
||||||
|
```
|
||||||
|
FIELD | COLUMN | POSTGRESQL | MYSQL | SQLITE3
|
||||||
|
------------+------------+----------------------+----------------------+--------------------
|
||||||
|
RepoID | repo_id | BIGINT | BIGINT | INTEGER
|
||||||
|
OID | oid | TEXT | VARCHAR(191) | TEXT
|
||||||
|
Size | size | BIGINT NOT NULL | BIGINT NOT NULL | INTEGER NOT NULL
|
||||||
|
Storage | storage | TEXT NOT NULL | LONGTEXT NOT NULL | TEXT NOT NULL
|
||||||
|
CreatedAt | created_at | TIMESTAMPTZ NOT NULL | DATETIME(3) NOT NULL | DATETIME NOT NULL
|
||||||
|
|
||||||
|
Primary keys: repo_id, oid
|
||||||
|
```
|
||||||
|
|
||||||
|
# Table "login_source"
|
||||||
|
|
||||||
|
```
|
||||||
|
FIELD | COLUMN | POSTGRESQL | MYSQL | SQLITE3
|
||||||
|
--------------+--------------+------------------+-----------------------+-------------------
|
||||||
|
ID | id | BIGSERIAL | BIGINT AUTO_INCREMENT | INTEGER
|
||||||
|
Type | type | BIGINT | BIGINT | INTEGER
|
||||||
|
Name | name | TEXT UNIQUE | VARCHAR(191) UNIQUE | TEXT UNIQUE
|
||||||
|
IsActived | is_actived | BOOLEAN NOT NULL | BOOLEAN NOT NULL | NUMERIC NOT NULL
|
||||||
|
IsDefault | is_default | BOOLEAN | BOOLEAN | NUMERIC
|
||||||
|
Config | cfg | TEXT | TEXT | TEXT
|
||||||
|
CreatedUnix | created_unix | BIGINT | BIGINT | INTEGER
|
||||||
|
UpdatedUnix | updated_unix | BIGINT | BIGINT | INTEGER
|
||||||
|
|
||||||
|
Primary keys: id
|
||||||
|
```
|
||||||
|
|
||||||
|
# Table "notice"
|
||||||
|
|
||||||
|
```
|
||||||
|
FIELD | COLUMN | POSTGRESQL | MYSQL | SQLITE3
|
||||||
|
--------------+--------------+------------+-----------------------+----------
|
||||||
|
ID | id | BIGSERIAL | BIGINT AUTO_INCREMENT | INTEGER
|
||||||
|
Type | type | BIGINT | BIGINT | INTEGER
|
||||||
|
Description | description | TEXT | TEXT | TEXT
|
||||||
|
CreatedUnix | created_unix | BIGINT | BIGINT | INTEGER
|
||||||
|
|
||||||
|
Primary keys: id
|
||||||
|
```
|
||||||
|
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
1. Run the `import` subcommand:
|
1. Run the `import` subcommand:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ ./.bin/gogs import locale --source <path to the unzipped directory> --target ./conf/locale
|
$ ./gogs import locale --source <path to the unzipped directory> --target ./conf/locale
|
||||||
Locale files has been successfully imported!
|
Locale files has been successfully imported!
|
||||||
```
|
```
|
||||||
|
|
||||||
58
go.mod
58
go.mod
@@ -6,11 +6,8 @@ require (
|
|||||||
github.com/Masterminds/semver/v3 v3.4.0
|
github.com/Masterminds/semver/v3 v3.4.0
|
||||||
github.com/cockroachdb/errors v1.12.0
|
github.com/cockroachdb/errors v1.12.0
|
||||||
github.com/derision-test/go-mockgen/v2 v2.1.1
|
github.com/derision-test/go-mockgen/v2 v2.1.1
|
||||||
github.com/editorconfig/editorconfig-core-go/v2 v2.6.4
|
github.com/editorconfig/editorconfig-core-go/v2 v2.6.3
|
||||||
github.com/fergusstrange/embedded-postgres v1.33.0
|
github.com/go-ldap/ldap/v3 v3.4.11
|
||||||
github.com/glebarez/go-sqlite v1.21.2
|
|
||||||
github.com/glebarez/sqlite v1.11.0
|
|
||||||
github.com/go-ldap/ldap/v3 v3.4.12
|
|
||||||
github.com/go-macaron/binding v1.2.0
|
github.com/go-macaron/binding v1.2.0
|
||||||
github.com/go-macaron/cache v0.0.0-20190810181446-10f7c57e2196
|
github.com/go-macaron/cache v0.0.0-20190810181446-10f7c57e2196
|
||||||
github.com/go-macaron/captcha v0.2.0
|
github.com/go-macaron/captcha v0.2.0
|
||||||
@@ -21,19 +18,19 @@ require (
|
|||||||
github.com/go-macaron/toolbox v0.0.0-20190813233741-94defb8383c6
|
github.com/go-macaron/toolbox v0.0.0-20190813233741-94defb8383c6
|
||||||
github.com/gogs/chardet v0.0.0-20150115103509-2404f7772561
|
github.com/gogs/chardet v0.0.0-20150115103509-2404f7772561
|
||||||
github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14
|
github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14
|
||||||
github.com/gogs/git-module v1.8.6
|
github.com/gogs/git-module v1.8.4
|
||||||
github.com/gogs/go-gogs-client v0.0.0-20200128182646-c69cb7680fd4
|
github.com/gogs/go-gogs-client v0.0.0-20200128182646-c69cb7680fd4
|
||||||
github.com/gogs/go-libravatar v0.0.0-20191106065024-33a75213d0a0
|
github.com/gogs/go-libravatar v0.0.0-20191106065024-33a75213d0a0
|
||||||
github.com/gogs/minwinsvc v0.0.0-20170301035411-95be6356811a
|
github.com/gogs/minwinsvc v0.0.0-20170301035411-95be6356811a
|
||||||
github.com/google/go-github v17.0.0+incompatible
|
github.com/google/go-github v17.0.0+incompatible
|
||||||
github.com/inbucket/html2text v1.0.0
|
|
||||||
github.com/issue9/identicon v1.2.1
|
github.com/issue9/identicon v1.2.1
|
||||||
|
github.com/jaytaylor/html2text v0.0.0-20190408195923-01ec452cbe43
|
||||||
github.com/json-iterator/go v1.1.12
|
github.com/json-iterator/go v1.1.12
|
||||||
github.com/microcosm-cc/bluemonday v1.0.27
|
github.com/microcosm-cc/bluemonday v1.0.27
|
||||||
github.com/msteinert/pam v1.2.0
|
github.com/msteinert/pam v1.2.0
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
|
||||||
github.com/niklasfasching/go-org v1.9.1
|
github.com/niklasfasching/go-org v1.9.1
|
||||||
github.com/olekukonko/tablewriter v1.1.3
|
github.com/olekukonko/tablewriter v0.0.5
|
||||||
github.com/pquerna/otp v1.5.0
|
github.com/pquerna/otp v1.5.0
|
||||||
github.com/prometheus/client_golang v1.23.0
|
github.com/prometheus/client_golang v1.23.0
|
||||||
github.com/russross/blackfriday v1.6.0
|
github.com/russross/blackfriday v1.6.0
|
||||||
@@ -46,51 +43,47 @@ require (
|
|||||||
github.com/unknwon/i18n v0.0.0-20190805065654-5c6446a380b6
|
github.com/unknwon/i18n v0.0.0-20190805065654-5c6446a380b6
|
||||||
github.com/unknwon/paginater v0.0.0-20170405233947-45e5d631308e
|
github.com/unknwon/paginater v0.0.0-20170405233947-45e5d631308e
|
||||||
github.com/urfave/cli v1.22.17
|
github.com/urfave/cli v1.22.17
|
||||||
golang.org/x/crypto v0.47.0
|
golang.org/x/crypto v0.45.0
|
||||||
golang.org/x/net v0.48.0
|
golang.org/x/net v0.47.0
|
||||||
golang.org/x/text v0.33.0
|
golang.org/x/text v0.31.0
|
||||||
gopkg.in/DATA-DOG/go-sqlmock.v2 v2.0.0-20180914054222-c19298f520d0
|
gopkg.in/DATA-DOG/go-sqlmock.v2 v2.0.0-20180914054222-c19298f520d0
|
||||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
||||||
gopkg.in/ini.v1 v1.67.0
|
gopkg.in/ini.v1 v1.67.0
|
||||||
gopkg.in/macaron.v1 v1.5.1
|
gopkg.in/macaron.v1 v1.5.0
|
||||||
gorm.io/driver/mysql v1.5.2
|
gorm.io/driver/mysql v1.5.2
|
||||||
gorm.io/driver/postgres v1.6.0
|
gorm.io/driver/postgres v1.6.0
|
||||||
|
gorm.io/driver/sqlite v1.4.2
|
||||||
gorm.io/driver/sqlserver v1.4.1
|
gorm.io/driver/sqlserver v1.4.1
|
||||||
gorm.io/gorm v1.25.12
|
gorm.io/gorm v1.25.12
|
||||||
|
modernc.org/sqlite v1.38.2
|
||||||
unknwon.dev/clog/v2 v2.2.0
|
unknwon.dev/clog/v2 v2.2.0
|
||||||
xorm.io/builder v0.3.6
|
|
||||||
xorm.io/core v0.7.2
|
|
||||||
xorm.io/xorm v0.8.0
|
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
bitbucket.org/creachadair/shell v0.0.7 // indirect
|
bitbucket.org/creachadair/shell v0.0.7 // indirect
|
||||||
|
filippo.io/edwards25519 v1.1.0 // indirect
|
||||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
|
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
|
||||||
github.com/aymerick/douceur v0.2.0 // indirect
|
github.com/aymerick/douceur v0.2.0 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
|
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
|
||||||
github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668 // indirect
|
github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
github.com/clipperhouse/displaywidth v0.6.2 // indirect
|
|
||||||
github.com/clipperhouse/stringish v0.1.1 // indirect
|
|
||||||
github.com/clipperhouse/uax29/v2 v2.3.0 // indirect
|
|
||||||
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
|
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
|
||||||
github.com/cockroachdb/redact v1.1.5 // indirect
|
github.com/cockroachdb/redact v1.1.5 // indirect
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
|
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/denisenkom/go-mssqldb v0.12.0 // indirect
|
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
github.com/djherbis/buffer v1.2.0 // indirect
|
github.com/djherbis/buffer v1.2.0 // indirect
|
||||||
github.com/djherbis/nio/v3 v3.0.1 // indirect
|
github.com/djherbis/nio/v3 v3.0.1 // indirect
|
||||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
github.com/fatih/color v1.18.0 // indirect
|
github.com/fatih/color v1.13.0 // indirect
|
||||||
github.com/getsentry/sentry-go v0.27.0 // indirect
|
github.com/getsentry/sentry-go v0.27.0 // indirect
|
||||||
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect
|
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect
|
||||||
github.com/go-logr/logr v1.2.3 // indirect
|
github.com/go-logr/logr v1.2.3 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/go-macaron/inject v0.0.0-20200308113650-138e5925c53b // indirect
|
github.com/go-macaron/inject v0.0.0-20200308113650-138e5925c53b // indirect
|
||||||
github.com/go-redis/redis/v8 v8.11.5 // indirect
|
github.com/go-redis/redis/v8 v8.11.5 // indirect
|
||||||
github.com/go-sql-driver/mysql v1.7.0 // indirect
|
github.com/go-sql-driver/mysql v1.8.1 // indirect
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
|
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
|
||||||
github.com/golang-sql/sqlexp v0.1.0 // indirect
|
github.com/golang-sql/sqlexp v0.1.0 // indirect
|
||||||
@@ -108,38 +101,34 @@ require (
|
|||||||
github.com/klauspost/compress v1.18.0 // indirect
|
github.com/klauspost/compress v1.18.0 // indirect
|
||||||
github.com/kr/pretty v0.3.1 // indirect
|
github.com/kr/pretty v0.3.1 // indirect
|
||||||
github.com/kr/text v0.2.0 // indirect
|
github.com/kr/text v0.2.0 // indirect
|
||||||
github.com/lib/pq v1.10.9 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.19 // indirect
|
github.com/mattn/go-runewidth v0.0.14 // indirect
|
||||||
github.com/mattn/go-sqlite3 v1.14.24 // indirect
|
github.com/mattn/go-sqlite3 v1.14.32 // indirect
|
||||||
github.com/mcuadros/go-version v0.0.0-20190830083331-035f6764e8d2 // indirect
|
github.com/mcuadros/go-version v0.0.0-20190830083331-035f6764e8d2 // indirect
|
||||||
github.com/microsoft/go-mssqldb v0.17.0 // indirect
|
github.com/microsoft/go-mssqldb v1.8.2 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||||
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect
|
|
||||||
github.com/olekukonko/errors v1.1.0 // indirect
|
|
||||||
github.com/olekukonko/ll v0.1.4-0.20260115111900-9e59c2286df0 // indirect
|
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/prometheus/client_model v0.6.2 // indirect
|
github.com/prometheus/client_model v0.6.2 // indirect
|
||||||
github.com/prometheus/common v0.65.0 // indirect
|
github.com/prometheus/common v0.65.0 // indirect
|
||||||
github.com/prometheus/procfs v0.16.1 // indirect
|
github.com/prometheus/procfs v0.16.1 // indirect
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
|
github.com/rivo/uniseg v0.2.0 // indirect
|
||||||
github.com/rogpeppe/go-internal v1.10.0 // indirect
|
github.com/rogpeppe/go-internal v1.10.0 // indirect
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca // indirect
|
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca // indirect
|
||||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
|
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
|
||||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
|
|
||||||
go.bobheadxi.dev/streamline v1.2.1 // indirect
|
go.bobheadxi.dev/streamline v1.2.1 // indirect
|
||||||
go.opentelemetry.io/otel v1.11.0 // indirect
|
go.opentelemetry.io/otel v1.11.0 // indirect
|
||||||
go.opentelemetry.io/otel/trace v1.11.0 // indirect
|
go.opentelemetry.io/otel/trace v1.11.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect
|
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect
|
||||||
golang.org/x/mod v0.31.0 // indirect
|
golang.org/x/mod v0.29.0 // indirect
|
||||||
golang.org/x/sync v0.19.0 // indirect
|
golang.org/x/sync v0.18.0 // indirect
|
||||||
golang.org/x/sys v0.40.0 // indirect
|
golang.org/x/sys v0.38.0 // indirect
|
||||||
google.golang.org/protobuf v1.36.6 // indirect
|
google.golang.org/protobuf v1.36.6 // indirect
|
||||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||||
gopkg.in/bufio.v1 v1.0.0-20140618132640-567b2bfa514e // indirect
|
gopkg.in/bufio.v1 v1.0.0-20140618132640-567b2bfa514e // indirect
|
||||||
@@ -148,8 +137,7 @@ require (
|
|||||||
modernc.org/libc v1.66.3 // indirect
|
modernc.org/libc v1.66.3 // indirect
|
||||||
modernc.org/mathutil v1.7.1 // indirect
|
modernc.org/mathutil v1.7.1 // indirect
|
||||||
modernc.org/memory v1.11.0 // indirect
|
modernc.org/memory v1.11.0 // indirect
|
||||||
modernc.org/sqlite v1.39.0 // indirect
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// +heroku goVersion go1.25
|
// +heroku goVersion go1.25
|
||||||
// +heroku install ./cmd/gogs
|
// +heroku install ./
|
||||||
|
|||||||
262
go.sum
262
go.sum
@@ -1,33 +1,35 @@
|
|||||||
bitbucket.org/creachadair/shell v0.0.7 h1:Z96pB6DkSb7F3Y3BBnJeOZH2gazyMTWlvecSD4vDqfk=
|
bitbucket.org/creachadair/shell v0.0.7 h1:Z96pB6DkSb7F3Y3BBnJeOZH2gazyMTWlvecSD4vDqfk=
|
||||||
bitbucket.org/creachadair/shell v0.0.7/go.mod h1:oqtXSSvSYr4624lnnabXHaBsYW6RD80caLi2b3hJk0U=
|
bitbucket.org/creachadair/shell v0.0.7/go.mod h1:oqtXSSvSYr4624lnnabXHaBsYW6RD80caLi2b3hJk0U=
|
||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||||
cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw=
|
|
||||||
gitea.com/lunny/log v0.0.0-20190322053110-01b5df579c4e/go.mod h1:uJEsN4LQpeGYRCjuPXPZBClU7N5pWzGuyF4uqLpE/e0=
|
gitea.com/lunny/log v0.0.0-20190322053110-01b5df579c4e/go.mod h1:uJEsN4LQpeGYRCjuPXPZBClU7N5pWzGuyF4uqLpE/e0=
|
||||||
gitea.com/lunny/nodb v0.0.0-20200923032308-3238c4655727/go.mod h1:h0OwsgcpJLSYtHcM5+Xciw9OEeuxi6ty4HDiO8C7aIY=
|
gitea.com/lunny/nodb v0.0.0-20200923032308-3238c4655727/go.mod h1:h0OwsgcpJLSYtHcM5+Xciw9OEeuxi6ty4HDiO8C7aIY=
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw=
|
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.0.0/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U=
|
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.0.0/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U=
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0=
|
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 h1:E+OJmp2tPvt1W+amx48v1eqbjDYsgN+RzP4q16yV5eM=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1/go.mod h1:a6xsAQUZg+VsS3TJ05SRp524Hs4pZ/AeFSr5ENf0Yjo=
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.0.0/go.mod h1:+6sju8gk8FRmSajX3Oz4G5Gm7P+mbqE9FVaXXFYTkCM=
|
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.0.0/go.mod h1:+6sju8gk8FRmSajX3Oz4G5Gm7P+mbqE9FVaXXFYTkCM=
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8=
|
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0 h1:U2rTu3Ef+7w9FHKIAXM6ZyqF3UOWJZ12zIm8zECAFfg=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg=
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
|
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0 h1:jBQA3cKT4L2rWMpgE7Yt3Hwh2aUj8KXjIGLxjHeYNNo=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0/go.mod h1:4OG6tQ9EOP/MT0NMjDlRzWoVFxfu9rN9B2X+tlSVktg=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.1 h1:MyVTgWR8qd/Jw1Le0NZebGBUCLbtak3bJ3z1OlqZBpw=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.1/go.mod h1:GpPjLhVR9dnUoJMyHWSPy71xY9/lcmpzIPZXmF0FCVY=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0 h1:D3occbWoio4EBLkbkevetNMAVX197GkzbUMtqjGWn80=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0/go.mod h1:bTSOgj05NGRuHHhQwAdPnYr9TOdNmKlZTgGLL6nyAdI=
|
||||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
|
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
|
||||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||||
github.com/AzureAD/microsoft-authentication-library-for-go v0.4.0/go.mod h1:Vt9sXTKwMyGcOxSmLDMnGPgqsUg7m8pe215qMLrDXw4=
|
github.com/AzureAD/microsoft-authentication-library-for-go v0.4.0/go.mod h1:Vt9sXTKwMyGcOxSmLDMnGPgqsUg7m8pe215qMLrDXw4=
|
||||||
|
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU=
|
||||||
|
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||||
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
|
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
|
||||||
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI=
|
||||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
|
||||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
|
||||||
github.com/alexbrainman/sspi v0.0.0-20250919150558-7d374ff0d59e h1:4dAU9FXIyQktpoUAgOJK3OTFc/xug0PCXYCqU0FgDKI=
|
|
||||||
github.com/alexbrainman/sspi v0.0.0-20250919150558-7d374ff0d59e/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
|
|
||||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
|
||||||
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
||||||
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
||||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=
|
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=
|
||||||
@@ -40,13 +42,6 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
|
|||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
|
||||||
github.com/clipperhouse/displaywidth v0.6.2 h1:ZDpTkFfpHOKte4RG5O/BOyf3ysnvFswpyYrV7z2uAKo=
|
|
||||||
github.com/clipperhouse/displaywidth v0.6.2/go.mod h1:R+kHuzaYWFkTm7xoMmK1lFydbci4X2CicfbGstSGg0o=
|
|
||||||
github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs=
|
|
||||||
github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
|
|
||||||
github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4=
|
|
||||||
github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
|
|
||||||
github.com/cockroachdb/errors v1.12.0 h1:d7oCs6vuIMUQRVbi6jWWWEJZahLCfJpnJSVobd1/sUo=
|
github.com/cockroachdb/errors v1.12.0 h1:d7oCs6vuIMUQRVbi6jWWWEJZahLCfJpnJSVobd1/sUo=
|
||||||
github.com/cockroachdb/errors v1.12.0/go.mod h1:SvzfYNNBshAVbZ8wzNc/UPK3w1vf0dKDUP41ucAIf7g=
|
github.com/cockroachdb/errors v1.12.0/go.mod h1:SvzfYNNBshAVbZ8wzNc/UPK3w1vf0dKDUP41ucAIf7g=
|
||||||
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE=
|
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE=
|
||||||
@@ -66,9 +61,6 @@ github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGii
|
|||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM=
|
|
||||||
github.com/denisenkom/go-mssqldb v0.12.0 h1:VtrkII767ttSPNRfFekePK3sctr+joXgO58stqQbtUA=
|
|
||||||
github.com/denisenkom/go-mssqldb v0.12.0/go.mod h1:iiK0YP1ZeepvmBQk/QpLEhhTNJgfzrpArPY/aFvc9yU=
|
|
||||||
github.com/derision-test/go-mockgen/v2 v2.1.1 h1:MXG9rzyvsrDBfa1a1GatvHjCrbmEug3hVt0rSOXipCw=
|
github.com/derision-test/go-mockgen/v2 v2.1.1 h1:MXG9rzyvsrDBfa1a1GatvHjCrbmEug3hVt0rSOXipCw=
|
||||||
github.com/derision-test/go-mockgen/v2 v2.1.1/go.mod h1:cDK2Y9IF5roTJgugWV23IvlOJsllhDN5zxRDN+g4cZo=
|
github.com/derision-test/go-mockgen/v2 v2.1.1/go.mod h1:cDK2Y9IF5roTJgugWV23IvlOJsllhDN5zxRDN+g4cZo=
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||||
@@ -82,17 +74,12 @@ github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/
|
|||||||
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
|
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
|
||||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
github.com/editorconfig/editorconfig-core-go/v2 v2.6.3 h1:XVUp6qW3BIkmM3/1EkrHpa6bL56APOynfXcZEmIgOhs=
|
||||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
github.com/editorconfig/editorconfig-core-go/v2 v2.6.3/go.mod h1:ThHVc+hqbUsmE1wmK/MASpQEhCleWu1JDJDNhUOMy0c=
|
||||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
|
||||||
github.com/editorconfig/editorconfig-core-go/v2 v2.6.4 h1:CHwUbBVVyKWRX9kt5A/OtwhYUJB32DrFp9xzmjR6cac=
|
|
||||||
github.com/editorconfig/editorconfig-core-go/v2 v2.6.4/go.mod h1:JWRVKHdVW+dkv6F8p+xGCa6a+TyMrqsFbFkSs/aQkrQ=
|
|
||||||
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
||||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||||
github.com/fergusstrange/embedded-postgres v1.33.0 h1:ka8vmRpm4IDsES7NPXQ/NThAp1fc/f+crcXYjCW7wK0=
|
|
||||||
github.com/fergusstrange/embedded-postgres v1.33.0/go.mod h1:w0YvnCgf19o6tskInrOOACtnqfVlOvluz3hlNLY7tRk=
|
|
||||||
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
|
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
|
||||||
github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
|
github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
@@ -100,18 +87,12 @@ github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWo
|
|||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps=
|
github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps=
|
||||||
github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
|
github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
|
||||||
github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo=
|
|
||||||
github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k=
|
|
||||||
github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
|
|
||||||
github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
|
|
||||||
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo=
|
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo=
|
||||||
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||||
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
|
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
|
||||||
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
||||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
github.com/go-ldap/ldap/v3 v3.4.11 h1:4k0Yxweg+a3OyBLjdYn5OKglv18JNvfDykSoI8bW0gU=
|
||||||
github.com/go-ldap/ldap/v3 v3.4.12 h1:1b81mv7MagXZ7+1r7cLTWmyuTqVqdwbtJSjC0DAp9s4=
|
github.com/go-ldap/ldap/v3 v3.4.11/go.mod h1:bY7t0FLK8OAVpp/vV6sSlpz3EQDGcQwc8pF0ujLgKvM=
|
||||||
github.com/go-ldap/ldap/v3 v3.4.12/go.mod h1:+SPAGcTtOfmGsCb3h1RFiq4xpp4N636G75OEace8lNo=
|
|
||||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
|
||||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
|
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
|
||||||
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
@@ -140,22 +121,18 @@ github.com/go-macaron/toolbox v0.0.0-20190813233741-94defb8383c6/go.mod h1:YFNJ/
|
|||||||
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
|
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
|
||||||
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
|
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
|
||||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||||
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
|
|
||||||
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||||
|
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||||
github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:9wScpmSP5A3Bk8V3XHWUcJmYTh+ZnlHVyc+A4oZYS3Y=
|
|
||||||
github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:56xuuqnHyryaerycW3BfssRdxQstACi0Epw/yC5E2xM=
|
|
||||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
|
||||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
|
||||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||||
github.com/gogs/chardet v0.0.0-20150115103509-2404f7772561 h1:aBzukfDxQlCTVS0NBUjI5YA3iVeaZ9Tb5PxNrrIP1xs=
|
github.com/gogs/chardet v0.0.0-20150115103509-2404f7772561 h1:aBzukfDxQlCTVS0NBUjI5YA3iVeaZ9Tb5PxNrrIP1xs=
|
||||||
github.com/gogs/chardet v0.0.0-20150115103509-2404f7772561/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14=
|
github.com/gogs/chardet v0.0.0-20150115103509-2404f7772561/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14=
|
||||||
github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14 h1:yXtpJr/LV6PFu4nTLgfjQdcMdzjbqqXMEnHfq0Or6p8=
|
github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14 h1:yXtpJr/LV6PFu4nTLgfjQdcMdzjbqqXMEnHfq0Or6p8=
|
||||||
github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14/go.mod h1:jPoNZLWDAqA5N3G5amEoiNbhVrmM+ZQEcnQvNQ2KaZk=
|
github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14/go.mod h1:jPoNZLWDAqA5N3G5amEoiNbhVrmM+ZQEcnQvNQ2KaZk=
|
||||||
github.com/gogs/git-module v1.8.6 h1:4Io9vWZYQyIjdIPxfKgeYZXnDKNgydc6OZTxII5xCH4=
|
github.com/gogs/git-module v1.8.4 h1:oSt8sOL4NWOGrSo/CwbS+C4YXtk76QvxyPofem/ViTU=
|
||||||
github.com/gogs/git-module v1.8.6/go.mod h1:IiMSJqi8XH62Kjqjt5Rw8IawSo+DHfM2dDjkSzWLjhs=
|
github.com/gogs/git-module v1.8.4/go.mod h1:bQY0aoMK5Q5+NKgy4jXe3K1GFW+GnsSk0SJK0jh6yD0=
|
||||||
github.com/gogs/go-gogs-client v0.0.0-20200128182646-c69cb7680fd4 h1:C7NryI/RQhsIWwC2bHN601P1wJKeuQ6U/UCOYTn3Cic=
|
github.com/gogs/go-gogs-client v0.0.0-20200128182646-c69cb7680fd4 h1:C7NryI/RQhsIWwC2bHN601P1wJKeuQ6U/UCOYTn3Cic=
|
||||||
github.com/gogs/go-gogs-client v0.0.0-20200128182646-c69cb7680fd4/go.mod h1:fR6z1Ie6rtF7kl/vBYMfgD5/G5B1blui7z426/sj2DU=
|
github.com/gogs/go-gogs-client v0.0.0-20200128182646-c69cb7680fd4/go.mod h1:fR6z1Ie6rtF7kl/vBYMfgD5/G5B1blui7z426/sj2DU=
|
||||||
github.com/gogs/go-libravatar v0.0.0-20191106065024-33a75213d0a0 h1:K02vod+sn3M1OOkdqi2tPxN2+xESK4qyITVQ3JkGEv4=
|
github.com/gogs/go-libravatar v0.0.0-20191106065024-33a75213d0a0 h1:K02vod+sn3M1OOkdqi2tPxN2+xESK4qyITVQ3JkGEv4=
|
||||||
@@ -163,17 +140,16 @@ github.com/gogs/go-libravatar v0.0.0-20191106065024-33a75213d0a0/go.mod h1:Zas3B
|
|||||||
github.com/gogs/minwinsvc v0.0.0-20170301035411-95be6356811a h1:8DZwxETOVWIinYxDK+i6L+rMb7eGATGaakD6ZucfHVk=
|
github.com/gogs/minwinsvc v0.0.0-20170301035411-95be6356811a h1:8DZwxETOVWIinYxDK+i6L+rMb7eGATGaakD6ZucfHVk=
|
||||||
github.com/gogs/minwinsvc v0.0.0-20170301035411-95be6356811a/go.mod h1:TUIZ+29jodWQ8Gk6Pvtg4E09aMsc3C/VLZiVYfUhWQU=
|
github.com/gogs/minwinsvc v0.0.0-20170301035411-95be6356811a/go.mod h1:TUIZ+29jodWQ8Gk6Pvtg4E09aMsc3C/VLZiVYfUhWQU=
|
||||||
github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||||
|
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
||||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||||
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
|
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
|
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
|
||||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||||
github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188/go.mod h1:vXjM/+wXQnTPR4KqTKDgJukSZ6amVRtWMPEjE6sQoK8=
|
|
||||||
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
|
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
|
||||||
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
|
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
|
||||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
@@ -187,8 +163,6 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS
|
|||||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
@@ -201,8 +175,6 @@ github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+u
|
|||||||
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
||||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
|
||||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
|
||||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
|
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
|
||||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
||||||
@@ -210,19 +182,14 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
|
|||||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
|
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
|
|
||||||
github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=
|
|
||||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
|
||||||
github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
|
github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
|
||||||
github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=
|
github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=
|
||||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
|
||||||
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
|
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
|
||||||
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
|
||||||
github.com/hexops/autogold v1.3.1 h1:YgxF9OHWbEIUjhDbpnLhgVsjUDsiHDTyDfy2lrfdlzo=
|
github.com/hexops/autogold v1.3.1 h1:YgxF9OHWbEIUjhDbpnLhgVsjUDsiHDTyDfy2lrfdlzo=
|
||||||
github.com/hexops/autogold v1.3.1/go.mod h1:sQO+mQUCVfxOKPht+ipDSkJ2SCJ7BNJVHZexsXqWMx4=
|
github.com/hexops/autogold v1.3.1/go.mod h1:sQO+mQUCVfxOKPht+ipDSkJ2SCJ7BNJVHZexsXqWMx4=
|
||||||
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
|
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
|
||||||
@@ -231,8 +198,6 @@ github.com/hexops/valast v1.4.3 h1:oBoGERMJh6UZdRc6cduE1CTPK+VAdXA59Y1HFgu3sm0=
|
|||||||
github.com/hexops/valast v1.4.3/go.mod h1:Iqx2kLj3Jn47wuXpj3wX40xn6F93QNFBHuiKBerkTGA=
|
github.com/hexops/valast v1.4.3/go.mod h1:Iqx2kLj3Jn47wuXpj3wX40xn6F93QNFBHuiKBerkTGA=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
github.com/inbucket/html2text v1.0.0 h1:N5kza++4uBBDJ2Z3KUnTRyPNoBcW+YfOgNiNmNB+sgs=
|
|
||||||
github.com/inbucket/html2text v1.0.0/go.mod h1:5TrhXQKGU+LXurODaSm55Y9eXoPBRnYiOz4x2XfUoJU=
|
|
||||||
github.com/issue9/assert/v2 v2.0.0 h1:vN7fr70g5ND6zM39tPZk/E4WCyjGMqApmFbujSTmEo0=
|
github.com/issue9/assert/v2 v2.0.0 h1:vN7fr70g5ND6zM39tPZk/E4WCyjGMqApmFbujSTmEo0=
|
||||||
github.com/issue9/assert/v2 v2.0.0/go.mod h1:rKr1eVGzXUhAo2af1thiKAhIA8uiSK9Wyn7mcZ4BzAg=
|
github.com/issue9/assert/v2 v2.0.0/go.mod h1:rKr1eVGzXUhAo2af1thiKAhIA8uiSK9Wyn7mcZ4BzAg=
|
||||||
github.com/issue9/identicon v1.2.1 h1:9RUq3DcmDJvfXAYZWJDaq/Bi45oS/Fr79W0CazbXNaY=
|
github.com/issue9/identicon v1.2.1 h1:9RUq3DcmDJvfXAYZWJDaq/Bi45oS/Fr79W0CazbXNaY=
|
||||||
@@ -249,6 +214,8 @@ github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY=
|
|||||||
github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw=
|
github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw=
|
||||||
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
||||||
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||||
|
github.com/jaytaylor/html2text v0.0.0-20190408195923-01ec452cbe43 h1:jTkyeF7NZ5oIr0ESmcrpiDgAfoidCBF4F5kJhjtaRwE=
|
||||||
|
github.com/jaytaylor/html2text v0.0.0-20190408195923-01ec452cbe43/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
|
||||||
github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8=
|
github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8=
|
||||||
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
|
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
|
||||||
github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo=
|
github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo=
|
||||||
@@ -268,17 +235,13 @@ github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
|||||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
|
||||||
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
|
||||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
|
||||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
@@ -288,32 +251,36 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
|||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
|
||||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
|
||||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
|
||||||
github.com/lunny/log v0.0.0-20160921050905-7887c61bf0de/go.mod h1:3q8WtuPQsoRbatJuy3nvq/hRSvuBJrHHr+ybPPiNvHQ=
|
github.com/lunny/log v0.0.0-20160921050905-7887c61bf0de/go.mod h1:3q8WtuPQsoRbatJuy3nvq/hRSvuBJrHHr+ybPPiNvHQ=
|
||||||
github.com/lunny/nodb v0.0.0-20160621015157-fc1ef06ad4af/go.mod h1:Cqz6pqow14VObJ7peltM+2n3PWOz7yTrfUuGbVFkzN0=
|
github.com/lunny/nodb v0.0.0-20160621015157-fc1ef06ad4af/go.mod h1:Cqz6pqow14VObJ7peltM+2n3PWOz7yTrfUuGbVFkzN0=
|
||||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||||
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
|
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||||
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
|
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
|
||||||
|
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||||
github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||||
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
|
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||||
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||||
|
github.com/mcuadros/go-version v0.0.0-20190308113854-92cdf37c5b75/go.mod h1:76rfSfYPWj01Z85hUf/ituArm797mNKcvINh1OlsZKo=
|
||||||
github.com/mcuadros/go-version v0.0.0-20190830083331-035f6764e8d2 h1:YocNLcTBdEdvY3iDK6jfWXvEaM5OCKkjxPKoJRdB3Gg=
|
github.com/mcuadros/go-version v0.0.0-20190830083331-035f6764e8d2 h1:YocNLcTBdEdvY3iDK6jfWXvEaM5OCKkjxPKoJRdB3Gg=
|
||||||
github.com/mcuadros/go-version v0.0.0-20190830083331-035f6764e8d2/go.mod h1:76rfSfYPWj01Z85hUf/ituArm797mNKcvINh1OlsZKo=
|
github.com/mcuadros/go-version v0.0.0-20190830083331-035f6764e8d2/go.mod h1:76rfSfYPWj01Z85hUf/ituArm797mNKcvINh1OlsZKo=
|
||||||
github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
|
github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
|
||||||
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
|
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
|
||||||
github.com/microsoft/go-mssqldb v0.17.0 h1:Fto83dMZPnYv1Zwx5vHHxpNraeEaUlQ/hhHLgZiaenE=
|
|
||||||
github.com/microsoft/go-mssqldb v0.17.0/go.mod h1:OkoNGhGEs8EZqchVTtochlXruEhEOaO4S0d2sB5aeGQ=
|
github.com/microsoft/go-mssqldb v0.17.0/go.mod h1:OkoNGhGEs8EZqchVTtochlXruEhEOaO4S0d2sB5aeGQ=
|
||||||
|
github.com/microsoft/go-mssqldb v1.8.2 h1:236sewazvC8FvG6Dr3bszrVhMkAl4KYImryLkRMCd0I=
|
||||||
|
github.com/microsoft/go-mssqldb v1.8.2/go.mod h1:vp38dT33FGfVotRiTmDo3bFyaHq+p3LektQrjTULowo=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
@@ -325,7 +292,6 @@ github.com/msteinert/pam v1.2.0 h1:mYfjlvN2KYs2Pb9G6nb/1f/nPfAttT/Jee5Sq9r3bGE=
|
|||||||
github.com/msteinert/pam v1.2.0/go.mod h1:d2n0DCUK8rGecChV3JzvmsDjOY4R7AYbsNxAT+ftQl0=
|
github.com/msteinert/pam v1.2.0/go.mod h1:d2n0DCUK8rGecChV3JzvmsDjOY4R7AYbsNxAT+ftQl0=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
|
||||||
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||||
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||||
@@ -337,14 +303,8 @@ github.com/niklasfasching/go-org v1.9.1/go.mod h1:ZAGFFkWvUQcpazmi/8nHqwvARpr1xp
|
|||||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||||
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 h1:zrbMGy9YXpIeTnGj4EljqMiZsIcE09mmF8XsD5AYOJc=
|
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||||
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6/go.mod h1:rEKTHC9roVVicUIfZK7DYrdIoM0EOr8mK1Hj5s3JjH0=
|
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||||
github.com/olekukonko/errors v1.1.0 h1:RNuGIh15QdDenh+hNvKrJkmxxjV4hcS50Db478Ou5sM=
|
|
||||||
github.com/olekukonko/errors v1.1.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
|
|
||||||
github.com/olekukonko/ll v0.1.4-0.20260115111900-9e59c2286df0 h1:jrYnow5+hy3WRDCBypUFvVKNSPPCdqgSXIE9eJDD8LM=
|
|
||||||
github.com/olekukonko/ll v0.1.4-0.20260115111900-9e59c2286df0/go.mod h1:b52bVQRRPObe+yyBl0TxNfhesL0nedD4Cht0/zx55Ew=
|
|
||||||
github.com/olekukonko/tablewriter v1.1.3 h1:VSHhghXxrP0JHl+0NnKid7WoEmd9/urKRJLysb70nnA=
|
|
||||||
github.com/olekukonko/tablewriter v1.1.3/go.mod h1:9VU0knjhmMkXjnMKrZ3+L2JhhtsQ/L38BbL3CRNE8tM=
|
|
||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
@@ -361,17 +321,15 @@ github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAl
|
|||||||
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
|
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
|
||||||
github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw=
|
github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw=
|
||||||
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
|
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
|
||||||
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
|
||||||
github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=
|
github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=
|
||||||
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
|
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
|
||||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
|
||||||
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
|
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
|
||||||
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
|
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
|
||||||
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
|
|
||||||
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ=
|
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ=
|
||||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
|
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
|
||||||
|
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
||||||
|
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
||||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
@@ -379,24 +337,18 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
|||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/pquerna/otp v1.5.0 h1:NMMR+WrmaqXU4EzdGJEE1aUUI0AMRzsp96fFFWNPwxs=
|
github.com/pquerna/otp v1.5.0 h1:NMMR+WrmaqXU4EzdGJEE1aUUI0AMRzsp96fFFWNPwxs=
|
||||||
github.com/pquerna/otp v1.5.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
|
github.com/pquerna/otp v1.5.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
|
||||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
|
||||||
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
|
|
||||||
github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc=
|
github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc=
|
||||||
github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE=
|
github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE=
|
||||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
|
||||||
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
|
||||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||||
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
|
||||||
github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE=
|
github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE=
|
||||||
github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
|
github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
|
||||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
|
||||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
|
||||||
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
|
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
|
||||||
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
|
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
|
||||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||||
|
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||||
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||||
@@ -414,27 +366,22 @@ github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726/go.mod h1:3yhqj7WBBf
|
|||||||
github.com/siddontang/go-snappy v0.0.0-20140704025258-d8f7bb82a96d/go.mod h1:vq0tzqLRu6TS7Id0wMo2N5QzJoKedVeovOpHjnykSzY=
|
github.com/siddontang/go-snappy v0.0.0-20140704025258-d8f7bb82a96d/go.mod h1:vq0tzqLRu6TS7Id0wMo2N5QzJoKedVeovOpHjnykSzY=
|
||||||
github.com/siddontang/ledisdb v0.0.0-20190202134119-8ceb77e66a92/go.mod h1:mF1DpOSOUiJRMR+FDqaqu3EBqrybQtrDDszLUZ6oxPg=
|
github.com/siddontang/ledisdb v0.0.0-20190202134119-8ceb77e66a92/go.mod h1:mF1DpOSOUiJRMR+FDqaqu3EBqrybQtrDDszLUZ6oxPg=
|
||||||
github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA=
|
github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA=
|
||||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
|
||||||
github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY=
|
|
||||||
github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec=
|
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
|
github.com/smartystreets/assertions v1.0.1 h1:voD4ITNjPL5jjBfgR/r8fPIIBrliWrWHeiJApdr3r4w=
|
||||||
github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
|
github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
|
||||||
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
|
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
|
||||||
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||||
|
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||||
github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY=
|
|
||||||
github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60=
|
|
||||||
github.com/sourcegraph/run v0.12.0 h1:3A8w5e8HIYPfafHekvmdmmh42RHKGVhmiTZAPJclg7I=
|
github.com/sourcegraph/run v0.12.0 h1:3A8w5e8HIYPfafHekvmdmmh42RHKGVhmiTZAPJclg7I=
|
||||||
github.com/sourcegraph/run v0.12.0/go.mod h1:PwaP936BTnAJC1cqR5rSbG5kOs/EWStTK3lqvMX5GUA=
|
github.com/sourcegraph/run v0.12.0/go.mod h1:PwaP936BTnAJC1cqR5rSbG5kOs/EWStTK3lqvMX5GUA=
|
||||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo=
|
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo=
|
||||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM=
|
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
@@ -458,15 +405,10 @@ github.com/unknwon/paginater v0.0.0-20170405233947-45e5d631308e h1:Qf3QQl/zmEbWD
|
|||||||
github.com/unknwon/paginater v0.0.0-20170405233947-45e5d631308e/go.mod h1:TBwoao3Q4Eb/cp+dHbXDfRTrZSsj/k7kLr2j1oWRWC0=
|
github.com/unknwon/paginater v0.0.0-20170405233947-45e5d631308e/go.mod h1:TBwoao3Q4Eb/cp+dHbXDfRTrZSsj/k7kLr2j1oWRWC0=
|
||||||
github.com/urfave/cli v1.22.17 h1:SYzXoiPfQjHBbkYxbew5prZHS1TOLT3ierW8SYLqtVQ=
|
github.com/urfave/cli v1.22.17 h1:SYzXoiPfQjHBbkYxbew5prZHS1TOLT3ierW8SYLqtVQ=
|
||||||
github.com/urfave/cli v1.22.17/go.mod h1:b0ht0aqgH/6pBYzzxURyrM4xXNgsoT/n2ZzwQiEhNVo=
|
github.com/urfave/cli v1.22.17/go.mod h1:b0ht0aqgH/6pBYzzxURyrM4xXNgsoT/n2ZzwQiEhNVo=
|
||||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
|
|
||||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
|
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
|
|
||||||
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
|
|
||||||
go.bobheadxi.dev/streamline v1.2.1 h1:IqKSA1TbeuDqCzYNAwtlh8sqf3tsQus8XgJdkCWFT8c=
|
go.bobheadxi.dev/streamline v1.2.1 h1:IqKSA1TbeuDqCzYNAwtlh8sqf3tsQus8XgJdkCWFT8c=
|
||||||
go.bobheadxi.dev/streamline v1.2.1/go.mod h1:yJsVXOSBFLgAKvsnf6WmIzmB2A65nWqkR/sRNxJPa74=
|
go.bobheadxi.dev/streamline v1.2.1/go.mod h1:yJsVXOSBFLgAKvsnf6WmIzmB2A65nWqkR/sRNxJPa74=
|
||||||
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
|
||||||
go.opentelemetry.io/otel v1.11.0 h1:kfToEGMDq6TrVrJ9Vht84Y8y9enykSZzDDZglV0kIEk=
|
go.opentelemetry.io/otel v1.11.0 h1:kfToEGMDq6TrVrJ9Vht84Y8y9enykSZzDDZglV0kIEk=
|
||||||
go.opentelemetry.io/otel v1.11.0/go.mod h1:H2KtuEphyMvlhZ+F7tg9GRhAOe60moNx61Ex+WmiKkk=
|
go.opentelemetry.io/otel v1.11.0/go.mod h1:H2KtuEphyMvlhZ+F7tg9GRhAOe60moNx61Ex+WmiKkk=
|
||||||
go.opentelemetry.io/otel/sdk v1.11.0 h1:ZnKIL9V9Ztaq+ME43IUi/eo22mNsb6a7tGfzaOWB5fo=
|
go.opentelemetry.io/otel/sdk v1.11.0 h1:ZnKIL9V9Ztaq+ME43IUi/eo22mNsb6a7tGfzaOWB5fo=
|
||||||
@@ -475,9 +417,7 @@ go.opentelemetry.io/otel/trace v1.11.0 h1:20U/Vj42SX+mASlXLmSGBg6jpI1jQtv682lZtT
|
|||||||
go.opentelemetry.io/otel/trace v1.11.0/go.mod h1:nyYjis9jy0gytE9LXGU+/m1sHTKbRY0fX0hulNNDP1U=
|
go.opentelemetry.io/otel/trace v1.11.0/go.mod h1:nyYjis9jy0gytE9LXGU+/m1sHTKbRY0fX0hulNNDP1U=
|
||||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
|
||||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
@@ -486,25 +426,15 @@ golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPh
|
|||||||
golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||||
golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
|
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||||
golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
|
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
|
||||||
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o=
|
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o=
|
||||||
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8=
|
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
|
||||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=
|
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
|
||||||
golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
|
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
@@ -515,27 +445,18 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/
|
|||||||
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||||
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||||
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||||
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@@ -547,6 +468,8 @@ golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@@ -555,30 +478,26 @@ golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220224120231-95c6836cb0e7/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220224120231-95c6836cb0e7/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||||
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
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-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-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY=
|
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
|
||||||
golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww=
|
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
|
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||||
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
|
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
|
||||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
|
||||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
|
||||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
golang.org/x/tools v0.0.0-20190802220118-1d1727260058/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
golang.org/x/tools v0.0.0-20190802220118-1d1727260058/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||||
@@ -586,23 +505,14 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
|
|||||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA=
|
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
|
||||||
golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc=
|
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
|
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
|
||||||
google.golang.org/appengine v1.6.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
|
||||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
|
||||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
|
||||||
google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
|
||||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
@@ -615,7 +525,6 @@ google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9x
|
|||||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||||
gopkg.in/DATA-DOG/go-sqlmock.v2 v2.0.0-20180914054222-c19298f520d0 h1:/21c4hNFgj8A1D54vgJZwQlywp64/RUBHzlPdpy5h4s=
|
gopkg.in/DATA-DOG/go-sqlmock.v2 v2.0.0-20180914054222-c19298f520d0 h1:/21c4hNFgj8A1D54vgJZwQlywp64/RUBHzlPdpy5h4s=
|
||||||
gopkg.in/DATA-DOG/go-sqlmock.v2 v2.0.0-20180914054222-c19298f520d0/go.mod h1:0uueny64T996pN6bez2N3S8HWyPcpyfTPma8Wc1Awx4=
|
gopkg.in/DATA-DOG/go-sqlmock.v2 v2.0.0-20180914054222-c19298f520d0/go.mod h1:0uueny64T996pN6bez2N3S8HWyPcpyfTPma8Wc1Awx4=
|
||||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
|
||||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
|
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
|
||||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
|
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
|
||||||
gopkg.in/bufio.v1 v1.0.0-20140618132640-567b2bfa514e h1:wGA78yza6bu/mWcc4QfBuIEHEtc06xdiU0X8sY36yUU=
|
gopkg.in/bufio.v1 v1.0.0-20140618132640-567b2bfa514e h1:wGA78yza6bu/mWcc4QfBuIEHEtc06xdiU0X8sY36yUU=
|
||||||
@@ -635,8 +544,8 @@ gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
|||||||
gopkg.in/macaron.v1 v1.3.4/go.mod h1:/RoHTdC8ALpyJ3+QR36mKjwnT1F1dyYtsGM9Ate6ZFI=
|
gopkg.in/macaron.v1 v1.3.4/go.mod h1:/RoHTdC8ALpyJ3+QR36mKjwnT1F1dyYtsGM9Ate6ZFI=
|
||||||
gopkg.in/macaron.v1 v1.3.5/go.mod h1:uMZCFccv9yr5TipIalVOyAyZQuOH3OkmXvgcWwhJuP4=
|
gopkg.in/macaron.v1 v1.3.5/go.mod h1:uMZCFccv9yr5TipIalVOyAyZQuOH3OkmXvgcWwhJuP4=
|
||||||
gopkg.in/macaron.v1 v1.4.0/go.mod h1:uMZCFccv9yr5TipIalVOyAyZQuOH3OkmXvgcWwhJuP4=
|
gopkg.in/macaron.v1 v1.4.0/go.mod h1:uMZCFccv9yr5TipIalVOyAyZQuOH3OkmXvgcWwhJuP4=
|
||||||
gopkg.in/macaron.v1 v1.5.1 h1:0ytdXYcf6//a8bzedl1fVXzPeIXblEqoNPntWAo9YLU=
|
gopkg.in/macaron.v1 v1.5.0 h1:/dXJaeQagWLjVjCrKH8dgSSU7yG4qTv6rBKpqhYaCyc=
|
||||||
gopkg.in/macaron.v1 v1.5.1/go.mod h1:AiquOw8YeZJC8sUe11vIO6NeA1/TKSlzQXuJ7Tc4cCQ=
|
gopkg.in/macaron.v1 v1.5.0/go.mod h1:sAYUd2r8Q+jLnCN4/ZmdAYHzQn67agV5sAqKFQgrRrw=
|
||||||
gopkg.in/redis.v2 v2.3.2 h1:GPVIIB/JnL1wvfULefy3qXmPu1nfNu2d0yA09FHgwfs=
|
gopkg.in/redis.v2 v2.3.2 h1:GPVIIB/JnL1wvfULefy3qXmPu1nfNu2d0yA09FHgwfs=
|
||||||
gopkg.in/redis.v2 v2.3.2/go.mod h1:4wl9PJ/CqzeHk3LVq1hNLHH8krm3+AXEgut4jVc++LU=
|
gopkg.in/redis.v2 v2.3.2/go.mod h1:4wl9PJ/CqzeHk3LVq1hNLHH8krm3+AXEgut4jVc++LU=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||||
@@ -657,15 +566,14 @@ gorm.io/driver/mysql v1.5.2 h1:QC2HRskSE75wBuOxe0+iCkyJZ+RqpudsQtqkp+IMuXs=
|
|||||||
gorm.io/driver/mysql v1.5.2/go.mod h1:pQLhh1Ut/WUAySdTHwBpBv6+JKcj+ua4ZFx1QQTBzb8=
|
gorm.io/driver/mysql v1.5.2/go.mod h1:pQLhh1Ut/WUAySdTHwBpBv6+JKcj+ua4ZFx1QQTBzb8=
|
||||||
gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4=
|
gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4=
|
||||||
gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo=
|
gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo=
|
||||||
|
gorm.io/driver/sqlite v1.4.2 h1:F6vYJcmR4Cnh0ErLyoY8JSfabBGyR0epIGuhgHJuNws=
|
||||||
|
gorm.io/driver/sqlite v1.4.2/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI=
|
||||||
gorm.io/driver/sqlserver v1.4.1 h1:t4r4r6Jam5E6ejqP7N82qAJIJAht27EGT41HyPfXRw0=
|
gorm.io/driver/sqlserver v1.4.1 h1:t4r4r6Jam5E6ejqP7N82qAJIJAht27EGT41HyPfXRw0=
|
||||||
gorm.io/driver/sqlserver v1.4.1/go.mod h1:DJ4P+MeZbc5rvY58PnmN1Lnyvb5gw5NPzGshHDnJLig=
|
gorm.io/driver/sqlserver v1.4.1/go.mod h1:DJ4P+MeZbc5rvY58PnmN1Lnyvb5gw5NPzGshHDnJLig=
|
||||||
gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
|
gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
|
||||||
gorm.io/gorm v1.25.2-0.20230530020048-26663ab9bf55/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
gorm.io/gorm v1.25.2-0.20230530020048-26663ab9bf55/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
||||||
gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
|
gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
|
||||||
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
|
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
|
||||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
||||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
||||||
modernc.org/cc/v4 v4.26.2 h1:991HMkLjJzYBIfha6ECZdjrIYz2/1ayr+FL8GN+CNzM=
|
modernc.org/cc/v4 v4.26.2 h1:991HMkLjJzYBIfha6ECZdjrIYz2/1ayr+FL8GN+CNzM=
|
||||||
modernc.org/cc/v4 v4.26.2/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
modernc.org/cc/v4 v4.26.2/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||||
modernc.org/ccgo/v4 v4.28.0 h1:rjznn6WWehKq7dG4JtLRKxb52Ecv8OUGah8+Z/SfpNU=
|
modernc.org/ccgo/v4 v4.28.0 h1:rjznn6WWehKq7dG4JtLRKxb52Ecv8OUGah8+Z/SfpNU=
|
||||||
@@ -686,8 +594,8 @@ modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
|
|||||||
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
||||||
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
||||||
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
||||||
modernc.org/sqlite v1.39.0 h1:6bwu9Ooim0yVYA7IZn9demiQk/Ejp0BtTjBWFLymSeY=
|
modernc.org/sqlite v1.38.2 h1:Aclu7+tgjgcQVShZqim41Bbw9Cho0y/7WzYptXqkEek=
|
||||||
modernc.org/sqlite v1.39.0/go.mod h1:cPTJYSlgg3Sfg046yBShXENNtPrWrDX8bsbAQBzgQ5E=
|
modernc.org/sqlite v1.38.2/go.mod h1:cPTJYSlgg3Sfg046yBShXENNtPrWrDX8bsbAQBzgQ5E=
|
||||||
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
||||||
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
||||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||||
@@ -696,9 +604,3 @@ mvdan.cc/gofumpt v0.4.0 h1:JVf4NN1mIpHogBj7ABpgOyZc65/UUOkKQFkoURsz4MM=
|
|||||||
mvdan.cc/gofumpt v0.4.0/go.mod h1:PljLOHDeZqgS8opHRKLzp2It2VBuSdteAgqUfzMTxlQ=
|
mvdan.cc/gofumpt v0.4.0/go.mod h1:PljLOHDeZqgS8opHRKLzp2It2VBuSdteAgqUfzMTxlQ=
|
||||||
unknwon.dev/clog/v2 v2.2.0 h1:jkPdsxux0MC04BT/9NHbT75z4prK92SH10VBNmIpVCc=
|
unknwon.dev/clog/v2 v2.2.0 h1:jkPdsxux0MC04BT/9NHbT75z4prK92SH10VBNmIpVCc=
|
||||||
unknwon.dev/clog/v2 v2.2.0/go.mod h1:zvUlyibDHI4mykYdWyWje2G9nF/nBzfDOqRo2my4mWc=
|
unknwon.dev/clog/v2 v2.2.0/go.mod h1:zvUlyibDHI4mykYdWyWje2G9nF/nBzfDOqRo2my4mWc=
|
||||||
xorm.io/builder v0.3.6 h1:ha28mQ2M+TFx96Hxo+iq6tQgnkC9IZkM6D8w9sKHHF8=
|
|
||||||
xorm.io/builder v0.3.6/go.mod h1:LEFAPISnRzG+zxaxj2vPicRwz67BdhFreKg8yv8/TgU=
|
|
||||||
xorm.io/core v0.7.2 h1:mEO22A2Z7a3fPaZMk6gKL/jMD80iiyNwRrX5HOv3XLw=
|
|
||||||
xorm.io/core v0.7.2/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM=
|
|
||||||
xorm.io/xorm v0.8.0 h1:iALxgJrX8O00f8Jk22GbZwPmxJNgssV5Mv4uc2HL9PM=
|
|
||||||
xorm.io/xorm v0.8.0/go.mod h1:ZkJLEYLoVyg7amJK/5r779bHyzs2AU8f8VMiP6BM7uY=
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//go:build go1.18
|
||||||
|
|
||||||
// Gogs is a painless self-hosted Git Service.
|
// Gogs is a painless self-hosted Git Service.
|
||||||
package main
|
package main
|
||||||
|
|
||||||
@@ -7,11 +9,12 @@ import (
|
|||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
log "unknwon.dev/clog/v2"
|
log "unknwon.dev/clog/v2"
|
||||||
|
|
||||||
|
"gogs.io/gogs/internal/cmd"
|
||||||
"gogs.io/gogs/internal/conf"
|
"gogs.io/gogs/internal/conf"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
conf.App.Version = "0.15.0+dev"
|
conf.App.Version = "0.14.0+dev"
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -20,14 +23,14 @@ func main() {
|
|||||||
app.Usage = "A painless self-hosted Git service"
|
app.Usage = "A painless self-hosted Git service"
|
||||||
app.Version = conf.App.Version
|
app.Version = conf.App.Version
|
||||||
app.Commands = []cli.Command{
|
app.Commands = []cli.Command{
|
||||||
webCommand,
|
cmd.Web,
|
||||||
servCommand,
|
cmd.Serv,
|
||||||
hookCommand,
|
cmd.Hook,
|
||||||
certCommand,
|
cmd.Cert,
|
||||||
adminCommand,
|
cmd.Admin,
|
||||||
importCommand,
|
cmd.Import,
|
||||||
backupCommand,
|
cmd.Backup,
|
||||||
restoreCommand,
|
cmd.Restore,
|
||||||
}
|
}
|
||||||
if err := app.Run(os.Args); err != nil {
|
if err := app.Run(os.Args); err != nil {
|
||||||
log.Fatal("Failed to start application: %v", err)
|
log.Fatal("Failed to start application: %v", err)
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -14,7 +14,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
adminCommand = cli.Command{
|
Admin = cli.Command{
|
||||||
Name: "admin",
|
Name: "admin",
|
||||||
Usage: "Perform admin operations on command line",
|
Usage: "Perform admin operations on command line",
|
||||||
Description: `Allow using internal logic of Gogs without hacking into the source code
|
Description: `Allow using internal logic of Gogs without hacking into the source code
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -20,7 +20,7 @@ import (
|
|||||||
"gogs.io/gogs/internal/osutil"
|
"gogs.io/gogs/internal/osutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
var backupCommand = cli.Command{
|
var Backup = cli.Command{
|
||||||
Name: "backup",
|
Name: "backup",
|
||||||
Usage: "Backup files and database",
|
Usage: "Backup files and database",
|
||||||
Description: `Backup dumps and compresses all related files and database into zip file,
|
Description: `Backup dumps and compresses all related files and database into zip file,
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package main
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
@@ -22,7 +22,7 @@ import (
|
|||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
var certCommand = cli.Command{
|
var Cert = cli.Command{
|
||||||
Name: "cert",
|
Name: "cert",
|
||||||
Usage: "Generate self-signed certificate",
|
Usage: "Generate self-signed certificate",
|
||||||
Description: `Generate a self-signed X.509 certificate for a TLS server.
|
Description: `Generate a self-signed X.509 certificate for a TLS server.
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
@@ -21,6 +21,7 @@ func boolFlag(name, usage string) cli.BoolFlag {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//nolint:deadcode,unused
|
||||||
func intFlag(name string, value int, usage string) cli.IntFlag {
|
func intFlag(name string, value int, usage string) cli.IntFlag {
|
||||||
return cli.IntFlag{
|
return cli.IntFlag{
|
||||||
Name: name,
|
Name: name,
|
||||||
@@ -29,6 +30,7 @@ func intFlag(name string, value int, usage string) cli.IntFlag {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//nolint:deadcode,unused
|
||||||
func durationFlag(name string, value time.Duration, usage string) cli.DurationFlag {
|
func durationFlag(name string, value time.Duration, usage string) cli.DurationFlag {
|
||||||
return cli.DurationFlag{
|
return cli.DurationFlag{
|
||||||
Name: name,
|
Name: name,
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
@@ -24,7 +24,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
hookCommand = cli.Command{
|
Hook = cli.Command{
|
||||||
Name: "hook",
|
Name: "hook",
|
||||||
Usage: "Delegate commands to corresponding Git hooks",
|
Usage: "Delegate commands to corresponding Git hooks",
|
||||||
Description: "All sub-commands should only be called by Git",
|
Description: "All sub-commands should only be called by Git",
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
@@ -16,7 +16,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
importCommand = cli.Command{
|
Import = cli.Command{
|
||||||
Name: "import",
|
Name: "import",
|
||||||
Usage: "Import portable data as local Gogs data",
|
Usage: "Import portable data as local Gogs data",
|
||||||
Description: `Allow user import data from other Gogs installations to local instance
|
Description: `Allow user import data from other Gogs installations to local instance
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -18,7 +18,7 @@ import (
|
|||||||
"gogs.io/gogs/internal/semverutil"
|
"gogs.io/gogs/internal/semverutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
var restoreCommand = cli.Command{
|
var Restore = cli.Command{
|
||||||
Name: "restore",
|
Name: "restore",
|
||||||
Usage: "Restore files and database from backup",
|
Usage: "Restore files and database from backup",
|
||||||
Description: `Restore imports all related files and database from a backup archive.
|
Description: `Restore imports all related files and database from a backup archive.
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -21,7 +21,7 @@ const (
|
|||||||
accessDeniedMessage = "Repository does not exist or you do not have access"
|
accessDeniedMessage = "Repository does not exist or you do not have access"
|
||||||
)
|
)
|
||||||
|
|
||||||
var servCommand = cli.Command{
|
var Serv = cli.Command{
|
||||||
Name: "serv",
|
Name: "serv",
|
||||||
Usage: "This command should only be called by SSH shell",
|
Usage: "This command should only be called by SSH shell",
|
||||||
Description: `Serv provide access auth for repositories`,
|
Description: `Serv provide access auth for repositories`,
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
@@ -30,7 +30,6 @@ import (
|
|||||||
"gogs.io/gogs/internal/conf"
|
"gogs.io/gogs/internal/conf"
|
||||||
"gogs.io/gogs/internal/context"
|
"gogs.io/gogs/internal/context"
|
||||||
"gogs.io/gogs/internal/database"
|
"gogs.io/gogs/internal/database"
|
||||||
"gogs.io/gogs/internal/embeddedpg"
|
|
||||||
"gogs.io/gogs/internal/form"
|
"gogs.io/gogs/internal/form"
|
||||||
"gogs.io/gogs/internal/osutil"
|
"gogs.io/gogs/internal/osutil"
|
||||||
"gogs.io/gogs/internal/route"
|
"gogs.io/gogs/internal/route"
|
||||||
@@ -46,7 +45,7 @@ import (
|
|||||||
"gogs.io/gogs/templates"
|
"gogs.io/gogs/templates"
|
||||||
)
|
)
|
||||||
|
|
||||||
var webCommand = cli.Command{
|
var Web = cli.Command{
|
||||||
Name: "web",
|
Name: "web",
|
||||||
Usage: "Start web server",
|
Usage: "Start web server",
|
||||||
Description: `Gogs web server is the only thing you need to run,
|
Description: `Gogs web server is the only thing you need to run,
|
||||||
@@ -55,7 +54,6 @@ and it takes care of all the other things for you`,
|
|||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
stringFlag("port, p", "3000", "Temporary port number to prevent conflict"),
|
stringFlag("port, p", "3000", "Temporary port number to prevent conflict"),
|
||||||
stringFlag("config, c", "", "Custom configuration file path"),
|
stringFlag("config, c", "", "Custom configuration file path"),
|
||||||
boolFlag("embedded-postgres", "Use embedded PostgreSQL database"),
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,29 +160,7 @@ func newMacaron() *macaron.Macaron {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runWeb(c *cli.Context) error {
|
func runWeb(c *cli.Context) error {
|
||||||
// Initialize configuration first to get WorkDir
|
err := route.GlobalInit(c.String("config"))
|
||||||
err := conf.Init(c.String("config"))
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Failed to initialize configuration: %v", err)
|
|
||||||
}
|
|
||||||
conf.InitLogging(false)
|
|
||||||
|
|
||||||
var localPg *embeddedpg.LocalPostgres
|
|
||||||
|
|
||||||
if c.Bool("embedded-postgres") {
|
|
||||||
localPg = embeddedpg.Initialize(conf.WorkDir())
|
|
||||||
if err := localPg.Launch(); err != nil {
|
|
||||||
log.Fatal("Failed to launch embedded postgres: %v", err)
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if err := localPg.Shutdown(); err != nil {
|
|
||||||
log.Error("Failed to shutdown embedded postgres: %v", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
localPg.ConfigureGlobalDatabase()
|
|
||||||
}
|
|
||||||
|
|
||||||
err = route.GlobalInit(c.String("config"))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Failed to initialize application: %v", err)
|
log.Fatal("Failed to initialize application: %v", err)
|
||||||
}
|
}
|
||||||
@@ -353,12 +329,9 @@ func runWeb(c *cli.Context) error {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, ignSignIn)
|
|
||||||
|
|
||||||
m.Group("", func() {
|
|
||||||
m.Post("/issues/attachments", repo.UploadIssueAttachment)
|
m.Post("/issues/attachments", repo.UploadIssueAttachment)
|
||||||
m.Post("/releases/attachments", repo.UploadReleaseAttachment)
|
m.Post("/releases/attachments", repo.UploadReleaseAttachment)
|
||||||
}, reqSignIn)
|
}, ignSignIn)
|
||||||
|
|
||||||
m.Group("/:username", func() {
|
m.Group("/:username", func() {
|
||||||
m.Post("/action/:action", user.Action)
|
m.Post("/action/:action", user.Action)
|
||||||
@@ -503,7 +476,7 @@ func runWeb(c *cli.Context) error {
|
|||||||
m.Get("/milestones", repo.Milestones)
|
m.Get("/milestones", repo.Milestones)
|
||||||
}, ignSignIn, context.RepoAssignment(true))
|
}, ignSignIn, context.RepoAssignment(true))
|
||||||
m.Group("/:username/:reponame", func() {
|
m.Group("/:username/:reponame", func() {
|
||||||
// FIXME: should use different URLs but mostly same logic for comments of issue and pull request.
|
// FIXME: should use different URLs but mostly same logic for comments of issue and pull reuqest.
|
||||||
// So they can apply their own enable/disable logic on routers.
|
// So they can apply their own enable/disable logic on routers.
|
||||||
m.Group("/issues", func() {
|
m.Group("/issues", func() {
|
||||||
m.Combo("/new", repo.MustEnableIssues).Get(context.RepoRef(), repo.NewIssue).
|
m.Combo("/new", repo.MustEnableIssues).Get(context.RepoRef(), repo.NewIssue).
|
||||||
@@ -528,7 +501,7 @@ func runWeb(c *cli.Context) error {
|
|||||||
}, ignSignIn, context.RepoAssignment(false, true))
|
}, ignSignIn, context.RepoAssignment(false, true))
|
||||||
|
|
||||||
m.Group("/:username/:reponame", func() {
|
m.Group("/:username/:reponame", func() {
|
||||||
// FIXME: should use different URLs but mostly same logic for comments of issue and pull request.
|
// FIXME: should use different URLs but mostly same logic for comments of issue and pull reuqest.
|
||||||
// So they can apply their own enable/disable logic on routers.
|
// So they can apply their own enable/disable logic on routers.
|
||||||
m.Group("/issues", func() {
|
m.Group("/issues", func() {
|
||||||
m.Group("/:index", func() {
|
m.Group("/:index", func() {
|
||||||
@@ -70,7 +70,7 @@ func Init(customConf string) error {
|
|||||||
if err = File.Append(customConf); err != nil {
|
if err = File.Append(customConf); err != nil {
|
||||||
return errors.Wrapf(err, "append %q", customConf)
|
return errors.Wrapf(err, "append %q", customConf)
|
||||||
}
|
}
|
||||||
} else if !HookMode {
|
} else {
|
||||||
log.Warn("Custom config %q not found. Ignore this warning if you're running for the first time", customConf)
|
log.Warn("Custom config %q not found. Ignore this warning if you're running for the first time", customConf)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,11 +142,9 @@ func Init(customConf string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if IsWindowsRuntime() || semverutil.Compare(sshVersion, "<", "5.1") {
|
if IsWindowsRuntime() || semverutil.Compare(sshVersion, "<", "5.1") {
|
||||||
if !HookMode {
|
log.Warn(`SSH minimum key size check is forced to be disabled because server is not eligible:
|
||||||
log.Warn(`SSH minimum key size check is forced to be disabled because server is not eligible:
|
|
||||||
1. Windows server
|
1. Windows server
|
||||||
2. OpenSSH version is lower than 5.1`)
|
2. OpenSSH version is lower than 5.1`)
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
SSH.MinimumKeySizes = map[string]int{}
|
SSH.MinimumKeySizes = map[string]int{}
|
||||||
for _, key := range File.Section("ssh.minimum_key_sizes").Keys() {
|
for _, key := range File.Section("ssh.minimum_key_sizes").Keys() {
|
||||||
@@ -348,11 +346,6 @@ func Init(customConf string) error {
|
|||||||
LFS.ObjectsPath = ensureAbs(LFS.ObjectsPath)
|
LFS.ObjectsPath = ensureAbs(LFS.ObjectsPath)
|
||||||
|
|
||||||
handleDeprecated()
|
handleDeprecated()
|
||||||
if !HookMode {
|
|
||||||
for _, warning := range checkInvalidOptions(File) {
|
|
||||||
log.Warn("%s", warning)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = File.Section("cache").MapTo(&Cache); err != nil {
|
if err = File.Section("cache").MapTo(&Cache); err != nil {
|
||||||
return errors.Wrap(err, "mapping [cache] section")
|
return errors.Wrap(err, "mapping [cache] section")
|
||||||
|
|||||||
@@ -1,15 +1,11 @@
|
|||||||
package conf
|
package conf
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gogs/go-libravatar"
|
"github.com/gogs/go-libravatar"
|
||||||
"gopkg.in/ini.v1"
|
|
||||||
|
|
||||||
"gogs.io/gogs/conf"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ℹ️ README: This file contains static values that should only be set at initialization time.
|
// ℹ️ README: This file contains static values that should only be set at initialization time.
|
||||||
@@ -231,7 +227,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type AppOpts struct {
|
type AppOpts struct {
|
||||||
// ⚠️ WARNING: Should only be set by the main package (i.e. "cmd/gogs/main.go").
|
// ⚠️ WARNING: Should only be set by the main package (i.e. "gogs.go").
|
||||||
Version string `ini:"-"`
|
Version string `ini:"-"`
|
||||||
|
|
||||||
BrandName string
|
BrandName string
|
||||||
@@ -434,75 +430,10 @@ func handleDeprecated() {
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkInvalidOptions checks invalid (renamed/deleted) configuration sections
|
|
||||||
// and options and returns a list of warnings.
|
|
||||||
func checkInvalidOptions(config *ini.File) (warnings []string) {
|
|
||||||
renamedSections := map[string]string{
|
|
||||||
"mailer": "email",
|
|
||||||
"service": "auth",
|
|
||||||
}
|
|
||||||
for oldSection, newSection := range renamedSections {
|
|
||||||
if len(config.Section(oldSection).KeyStrings()) > 0 {
|
|
||||||
warnings = append(warnings, fmt.Sprintf("section [%s] is invalid, use [%s] instead", oldSection, newSection))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type optionPath struct {
|
|
||||||
section string
|
|
||||||
option string
|
|
||||||
}
|
|
||||||
renamedOptionPaths := map[optionPath]optionPath{
|
|
||||||
// Example:
|
|
||||||
// {"security", "REVERSE_PROXY_AUTHENTICATION_USER"}: {"auth", "REVERSE_PROXY_AUTHENTICATION_HEADER"},
|
|
||||||
}
|
|
||||||
for oldPath, newPath := range renamedOptionPaths {
|
|
||||||
if config.Section(oldPath.section).HasKey(oldPath.option) {
|
|
||||||
warnings = append(
|
|
||||||
warnings,
|
|
||||||
fmt.Sprintf("option [%s] %s is invalid, use [%s] %s instead",
|
|
||||||
oldPath.section, oldPath.option,
|
|
||||||
newPath.section, newPath.option,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check options that don't exist anymore.
|
|
||||||
defaultConfigData, err := conf.Files.ReadFile("app.ini")
|
|
||||||
if err != nil {
|
|
||||||
// Warning is best-effort, OK to skip on error.
|
|
||||||
return warnings
|
|
||||||
}
|
|
||||||
defaultConfig, err := ini.LoadSources(
|
|
||||||
ini.LoadOptions{IgnoreInlineComment: true},
|
|
||||||
defaultConfigData,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
// Warning is best-effort, OK to skip on error.
|
|
||||||
return warnings
|
|
||||||
}
|
|
||||||
for _, section := range config.Sections() {
|
|
||||||
// Skip sections already warned about.
|
|
||||||
if _, ok := renamedSections[section.Name()]; ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for _, option := range section.Keys() {
|
|
||||||
if _, ok := renamedOptionPaths[optionPath{section.Name(), option.Name()}]; ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !defaultConfig.Section(section.Name()).HasKey(option.Name()) {
|
|
||||||
warnings = append(warnings, fmt.Sprintf("option [%s] %s is invalid", section.Name(), option.Name()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return warnings
|
|
||||||
}
|
|
||||||
|
|
||||||
// HookMode indicates whether program starts as Git server-side hook callback.
|
// HookMode indicates whether program starts as Git server-side hook callback.
|
||||||
// All operations should be done synchronously to prevent program exits before finishing.
|
// All operations should be done synchronously to prevent program exits before finishing.
|
||||||
//
|
//
|
||||||
// ⚠️ WARNING: Should only be set by "cmd/gogs/serv.go".
|
// ⚠️ WARNING: Should only be set by "internal/cmd/serv.go".
|
||||||
var HookMode bool
|
var HookMode bool
|
||||||
|
|
||||||
// Indicates which database backend is currently being used.
|
// Indicates which database backend is currently being used.
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
package conf
|
package conf
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sort"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"gopkg.in/ini.v1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_i18n_DateLang(t *testing.T) {
|
func Test_i18n_DateLang(t *testing.T) {
|
||||||
@@ -31,48 +29,3 @@ func Test_i18n_DateLang(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCheckInvalidOptions(t *testing.T) {
|
|
||||||
cfg := ini.Empty()
|
|
||||||
_, _ = cfg.Section("mailer").NewKey("ENABLED", "true")
|
|
||||||
_, _ = cfg.Section("service").NewKey("START_TYPE", "true")
|
|
||||||
_, _ = cfg.Section("security").NewKey("REVERSE_PROXY_AUTHENTICATION_USER", "true")
|
|
||||||
_, _ = cfg.Section("auth").NewKey("ACTIVE_CODE_LIVE_MINUTES", "10")
|
|
||||||
_, _ = cfg.Section("auth").NewKey("RESET_PASSWD_CODE_LIVE_MINUTES", "10")
|
|
||||||
_, _ = cfg.Section("auth").NewKey("ENABLE_CAPTCHA", "true")
|
|
||||||
_, _ = cfg.Section("auth").NewKey("ENABLE_NOTIFY_MAIL", "true")
|
|
||||||
_, _ = cfg.Section("auth").NewKey("REGISTER_EMAIL_CONFIRM", "true")
|
|
||||||
_, _ = cfg.Section("session").NewKey("GC_INTERVAL_TIME", "10")
|
|
||||||
_, _ = cfg.Section("session").NewKey("SESSION_LIFE_TIME", "10")
|
|
||||||
_, _ = cfg.Section("server").NewKey("ROOT_URL", "true")
|
|
||||||
_, _ = cfg.Section("server").NewKey("LANDING_PAGE", "true")
|
|
||||||
_, _ = cfg.Section("database").NewKey("DB_TYPE", "true")
|
|
||||||
_, _ = cfg.Section("database").NewKey("PASSWD", "true")
|
|
||||||
_, _ = cfg.Section("other").NewKey("SHOW_FOOTER_BRANDING", "true")
|
|
||||||
_, _ = cfg.Section("other").NewKey("SHOW_FOOTER_TEMPLATE_LOAD_TIME", "true")
|
|
||||||
_, _ = cfg.Section("email").NewKey("ENABLED", "true")
|
|
||||||
_, _ = cfg.Section("server").NewKey("NONEXISTENT_OPTION", "true")
|
|
||||||
|
|
||||||
wantWarnings := []string{
|
|
||||||
"option [auth] ACTIVE_CODE_LIVE_MINUTES is invalid",
|
|
||||||
"option [auth] ENABLE_CAPTCHA is invalid",
|
|
||||||
"option [auth] ENABLE_NOTIFY_MAIL is invalid",
|
|
||||||
"option [auth] REGISTER_EMAIL_CONFIRM is invalid",
|
|
||||||
"option [auth] RESET_PASSWD_CODE_LIVE_MINUTES is invalid",
|
|
||||||
"option [database] DB_TYPE is invalid",
|
|
||||||
"option [database] PASSWD is invalid",
|
|
||||||
"option [security] REVERSE_PROXY_AUTHENTICATION_USER is invalid",
|
|
||||||
"option [session] GC_INTERVAL_TIME is invalid",
|
|
||||||
"option [session] SESSION_LIFE_TIME is invalid",
|
|
||||||
"section [mailer] is invalid, use [email] instead",
|
|
||||||
"section [service] is invalid, use [auth] instead",
|
|
||||||
"option [server] ROOT_URL is invalid",
|
|
||||||
"option [server] LANDING_PAGE is invalid",
|
|
||||||
"option [server] NONEXISTENT_OPTION is invalid",
|
|
||||||
}
|
|
||||||
|
|
||||||
gotWarnings := checkInvalidOptions(cfg)
|
|
||||||
sort.Strings(wantWarnings)
|
|
||||||
sort.Strings(gotWarnings)
|
|
||||||
assert.Equal(t, wantWarnings, gotWarnings)
|
|
||||||
}
|
|
||||||
|
|||||||
5
internal/conf/testdata/custom.ini
vendored
5
internal/conf/testdata/custom.ini
vendored
@@ -28,8 +28,9 @@ PASSWORD = 87654321
|
|||||||
ACTIVATE_CODE_LIVES = 10
|
ACTIVATE_CODE_LIVES = 10
|
||||||
RESET_PASSWORD_CODE_LIVES = 10
|
RESET_PASSWORD_CODE_LIVES = 10
|
||||||
REQUIRE_EMAIL_CONFIRMATION = true
|
REQUIRE_EMAIL_CONFIRMATION = true
|
||||||
ENABLE_REGISTRATION_CAPTCHA = true
|
ENABLE_CAPTCHA = true
|
||||||
REVERSE_PROXY_AUTHENTICATION_HEADER = X-FORWARDED-FOR
|
ENABLE_NOTIFY_MAIL = true
|
||||||
|
REVERSE_PROXY_AUTHENTICATION_HEADER=X-FORWARDED-FOR
|
||||||
|
|
||||||
[user]
|
[user]
|
||||||
ENABLE_EMAIL_NOTIFICATION = true
|
ENABLE_EMAIL_NOTIFICATION = true
|
||||||
|
|||||||
@@ -147,13 +147,13 @@ func (c *Context) RedirectSubpath(location string, status ...int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RenderWithErr used for page has form validation but need to prompt error to users.
|
// RenderWithErr used for page has form validation but need to prompt error to users.
|
||||||
func (c *Context) RenderWithErr(msg string, status int, tpl string, f any) {
|
func (c *Context) RenderWithErr(msg, tpl string, f any) {
|
||||||
if f != nil {
|
if f != nil {
|
||||||
form.Assign(f, c.Data)
|
form.Assign(f, c.Data)
|
||||||
}
|
}
|
||||||
c.Flash.ErrorMsg = msg
|
c.Flash.ErrorMsg = msg
|
||||||
c.Data["Flash"] = c.Flash
|
c.Data["Flash"] = c.Flash
|
||||||
c.HTML(status, tpl)
|
c.HTML(http.StatusOK, tpl)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NotFound renders the 404 page.
|
// NotFound renders the 404 page.
|
||||||
|
|||||||
@@ -142,11 +142,6 @@ func actionsCommitRepo(t *testing.T, ctx context.Context, s *ActionsStore) {
|
|||||||
now := time.Unix(1588568886, 0).UTC()
|
now := time.Unix(1588568886, 0).UTC()
|
||||||
|
|
||||||
conf.SetMockSSH(t, conf.SSHOpts{})
|
conf.SetMockSSH(t, conf.SSHOpts{})
|
||||||
conf.SetMockUI(t, conf.UIOpts{
|
|
||||||
User: conf.UIUserOpts{
|
|
||||||
NewsFeedPagingNum: 20,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("new commit", func(t *testing.T) {
|
t.Run("new commit", func(t *testing.T) {
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
@@ -437,12 +432,6 @@ func actionsListByUser(t *testing.T, ctx context.Context, s *ActionsStore) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func actionsMergePullRequest(t *testing.T, ctx context.Context, s *ActionsStore) {
|
func actionsMergePullRequest(t *testing.T, ctx context.Context, s *ActionsStore) {
|
||||||
conf.SetMockUI(t, conf.UIOpts{
|
|
||||||
User: conf.UIUserOpts{
|
|
||||||
NewsFeedPagingNum: 20,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
alice, err := newUsersStore(s.db).Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
|
alice, err := newUsersStore(s.db).Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
repo, err := newReposStore(s.db).Create(ctx,
|
repo, err := newReposStore(s.db).Create(ctx,
|
||||||
@@ -488,12 +477,6 @@ func actionsMergePullRequest(t *testing.T, ctx context.Context, s *ActionsStore)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func actionsMirrorSyncCreate(t *testing.T, ctx context.Context, s *ActionsStore) {
|
func actionsMirrorSyncCreate(t *testing.T, ctx context.Context, s *ActionsStore) {
|
||||||
conf.SetMockUI(t, conf.UIOpts{
|
|
||||||
User: conf.UIUserOpts{
|
|
||||||
NewsFeedPagingNum: 20,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
alice, err := newUsersStore(s.db).Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
|
alice, err := newUsersStore(s.db).Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
repo, err := newReposStore(s.db).Create(ctx,
|
repo, err := newReposStore(s.db).Create(ctx,
|
||||||
@@ -535,12 +518,6 @@ func actionsMirrorSyncCreate(t *testing.T, ctx context.Context, s *ActionsStore)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func actionsMirrorSyncDelete(t *testing.T, ctx context.Context, s *ActionsStore) {
|
func actionsMirrorSyncDelete(t *testing.T, ctx context.Context, s *ActionsStore) {
|
||||||
conf.SetMockUI(t, conf.UIOpts{
|
|
||||||
User: conf.UIUserOpts{
|
|
||||||
NewsFeedPagingNum: 20,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
alice, err := newUsersStore(s.db).Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
|
alice, err := newUsersStore(s.db).Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
repo, err := newReposStore(s.db).Create(ctx,
|
repo, err := newReposStore(s.db).Create(ctx,
|
||||||
@@ -582,12 +559,6 @@ func actionsMirrorSyncDelete(t *testing.T, ctx context.Context, s *ActionsStore)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func actionsMirrorSyncPush(t *testing.T, ctx context.Context, s *ActionsStore) {
|
func actionsMirrorSyncPush(t *testing.T, ctx context.Context, s *ActionsStore) {
|
||||||
conf.SetMockUI(t, conf.UIOpts{
|
|
||||||
User: conf.UIUserOpts{
|
|
||||||
NewsFeedPagingNum: 20,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
alice, err := newUsersStore(s.db).Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
|
alice, err := newUsersStore(s.db).Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
repo, err := newReposStore(s.db).Create(ctx,
|
repo, err := newReposStore(s.db).Create(ctx,
|
||||||
@@ -653,12 +624,6 @@ func actionsMirrorSyncPush(t *testing.T, ctx context.Context, s *ActionsStore) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func actionsNewRepo(t *testing.T, ctx context.Context, s *ActionsStore) {
|
func actionsNewRepo(t *testing.T, ctx context.Context, s *ActionsStore) {
|
||||||
conf.SetMockUI(t, conf.UIOpts{
|
|
||||||
User: conf.UIUserOpts{
|
|
||||||
NewsFeedPagingNum: 20,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
alice, err := newUsersStore(s.db).Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
|
alice, err := newUsersStore(s.db).Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
repo, err := newReposStore(s.db).Create(ctx,
|
repo, err := newReposStore(s.db).Create(ctx,
|
||||||
@@ -738,11 +703,6 @@ func actionsPushTag(t *testing.T, ctx context.Context, s *ActionsStore) {
|
|||||||
// to the mock server because this function holds a lock.
|
// to the mock server because this function holds a lock.
|
||||||
conf.SetMockServer(t, conf.ServerOpts{})
|
conf.SetMockServer(t, conf.ServerOpts{})
|
||||||
conf.SetMockSSH(t, conf.SSHOpts{})
|
conf.SetMockSSH(t, conf.SSHOpts{})
|
||||||
conf.SetMockUI(t, conf.UIOpts{
|
|
||||||
User: conf.UIUserOpts{
|
|
||||||
NewsFeedPagingNum: 20,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
alice, err := newUsersStore(s.db).Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
|
alice, err := newUsersStore(s.db).Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -836,12 +796,6 @@ func actionsPushTag(t *testing.T, ctx context.Context, s *ActionsStore) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func actionsRenameRepo(t *testing.T, ctx context.Context, s *ActionsStore) {
|
func actionsRenameRepo(t *testing.T, ctx context.Context, s *ActionsStore) {
|
||||||
conf.SetMockUI(t, conf.UIOpts{
|
|
||||||
User: conf.UIUserOpts{
|
|
||||||
NewsFeedPagingNum: 20,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
alice, err := newUsersStore(s.db).Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
|
alice, err := newUsersStore(s.db).Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
repo, err := newReposStore(s.db).Create(ctx,
|
repo, err := newReposStore(s.db).Create(ctx,
|
||||||
@@ -879,12 +833,6 @@ func actionsRenameRepo(t *testing.T, ctx context.Context, s *ActionsStore) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func actionsTransferRepo(t *testing.T, ctx context.Context, s *ActionsStore) {
|
func actionsTransferRepo(t *testing.T, ctx context.Context, s *ActionsStore) {
|
||||||
conf.SetMockUI(t, conf.UIOpts{
|
|
||||||
User: conf.UIUserOpts{
|
|
||||||
NewsFeedPagingNum: 20,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
alice, err := newUsersStore(s.db).Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
|
alice, err := newUsersStore(s.db).Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
bob, err := newUsersStore(s.db).Create(ctx, "bob", "bob@example.com", CreateUserOptions{})
|
bob, err := newUsersStore(s.db).Create(ctx, "bob", "bob@example.com", CreateUserOptions{})
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
|
|
||||||
"github.com/cockroachdb/errors"
|
"github.com/cockroachdb/errors"
|
||||||
gouuid "github.com/satori/go.uuid"
|
gouuid "github.com/satori/go.uuid"
|
||||||
"xorm.io/xorm"
|
"gorm.io/gorm"
|
||||||
|
|
||||||
"gogs.io/gogs/internal/conf"
|
"gogs.io/gogs/internal/conf"
|
||||||
"gogs.io/gogs/internal/errutil"
|
"gogs.io/gogs/internal/errutil"
|
||||||
@@ -19,25 +19,26 @@ import (
|
|||||||
// Attachment represent a attachment of issue/comment/release.
|
// Attachment represent a attachment of issue/comment/release.
|
||||||
type Attachment struct {
|
type Attachment struct {
|
||||||
ID int64
|
ID int64
|
||||||
UUID string `xorm:"uuid UNIQUE"`
|
UUID string `gorm:"column:uuid;type:varchar(191);uniqueIndex"`
|
||||||
IssueID int64 `xorm:"INDEX"`
|
IssueID int64 `gorm:"index"`
|
||||||
CommentID int64
|
CommentID int64
|
||||||
ReleaseID int64 `xorm:"INDEX"`
|
ReleaseID int64 `gorm:"index"`
|
||||||
Name string
|
Name string
|
||||||
|
|
||||||
Created time.Time `xorm:"-" json:"-" gorm:"-"`
|
Created time.Time `gorm:"-" json:"-"`
|
||||||
CreatedUnix int64
|
CreatedUnix int64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Attachment) BeforeInsert() {
|
func (a *Attachment) BeforeCreate(tx *gorm.DB) error {
|
||||||
a.CreatedUnix = time.Now().Unix()
|
if a.CreatedUnix == 0 {
|
||||||
|
a.CreatedUnix = tx.NowFunc().Unix()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Attachment) AfterSet(colName string, _ xorm.Cell) {
|
func (a *Attachment) AfterFind(tx *gorm.DB) error {
|
||||||
switch colName {
|
a.Created = time.Unix(a.CreatedUnix, 0).Local()
|
||||||
case "created_unix":
|
return nil
|
||||||
a.Created = time.Unix(a.CreatedUnix, 0).Local()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AttachmentLocalPath returns where attachment is stored in local file system based on given UUID.
|
// AttachmentLocalPath returns where attachment is stored in local file system based on given UUID.
|
||||||
@@ -74,7 +75,7 @@ func NewAttachment(name string, buf []byte, file multipart.File) (_ *Attachment,
|
|||||||
return nil, errors.Newf("copy: %v", err)
|
return nil, errors.Newf("copy: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := x.Insert(attach); err != nil {
|
if err := db.Create(attach).Error; err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,60 +101,60 @@ func (ErrAttachmentNotExist) NotFound() bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAttachmentByUUID(e Engine, uuid string) (*Attachment, error) {
|
func getAttachmentByUUID(e *gorm.DB, uuid string) (*Attachment, error) {
|
||||||
attach := &Attachment{UUID: uuid}
|
attach := &Attachment{}
|
||||||
has, err := e.Get(attach)
|
err := e.Where("uuid = ?", uuid).First(attach).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil, ErrAttachmentNotExist{args: map[string]any{"uuid": uuid}}
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if !has {
|
|
||||||
return nil, ErrAttachmentNotExist{args: map[string]any{"uuid": uuid}}
|
|
||||||
}
|
}
|
||||||
return attach, nil
|
return attach, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAttachmentsByUUIDs(e Engine, uuids []string) ([]*Attachment, error) {
|
func getAttachmentsByUUIDs(e *gorm.DB, uuids []string) ([]*Attachment, error) {
|
||||||
if len(uuids) == 0 {
|
if len(uuids) == 0 {
|
||||||
return []*Attachment{}, nil
|
return []*Attachment{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Silently drop invalid uuids.
|
|
||||||
attachments := make([]*Attachment, 0, len(uuids))
|
attachments := make([]*Attachment, 0, len(uuids))
|
||||||
return attachments, e.In("uuid", uuids).Find(&attachments)
|
return attachments, e.Where("uuid IN ?", uuids).Find(&attachments).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAttachmentByUUID returns attachment by given UUID.
|
// GetAttachmentByUUID returns attachment by given UUID.
|
||||||
func GetAttachmentByUUID(uuid string) (*Attachment, error) {
|
func GetAttachmentByUUID(uuid string) (*Attachment, error) {
|
||||||
return getAttachmentByUUID(x, uuid)
|
return getAttachmentByUUID(db, uuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAttachmentsByIssueID(e Engine, issueID int64) ([]*Attachment, error) {
|
func getAttachmentsByIssueID(e *gorm.DB, issueID int64) ([]*Attachment, error) {
|
||||||
attachments := make([]*Attachment, 0, 5)
|
attachments := make([]*Attachment, 0, 5)
|
||||||
return attachments, e.Where("issue_id = ? AND comment_id = 0", issueID).Find(&attachments)
|
return attachments, e.Where("issue_id = ? AND comment_id = 0", issueID).Find(&attachments).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAttachmentsByIssueID returns all attachments of an issue.
|
// GetAttachmentsByIssueID returns all attachments of an issue.
|
||||||
func GetAttachmentsByIssueID(issueID int64) ([]*Attachment, error) {
|
func GetAttachmentsByIssueID(issueID int64) ([]*Attachment, error) {
|
||||||
return getAttachmentsByIssueID(x, issueID)
|
return getAttachmentsByIssueID(db, issueID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAttachmentsByCommentID(e Engine, commentID int64) ([]*Attachment, error) {
|
func getAttachmentsByCommentID(e *gorm.DB, commentID int64) ([]*Attachment, error) {
|
||||||
attachments := make([]*Attachment, 0, 5)
|
attachments := make([]*Attachment, 0, 5)
|
||||||
return attachments, e.Where("comment_id=?", commentID).Find(&attachments)
|
return attachments, e.Where("comment_id = ?", commentID).Find(&attachments).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAttachmentsByCommentID returns all attachments of a comment.
|
// GetAttachmentsByCommentID returns all attachments of a comment.
|
||||||
func GetAttachmentsByCommentID(commentID int64) ([]*Attachment, error) {
|
func GetAttachmentsByCommentID(commentID int64) ([]*Attachment, error) {
|
||||||
return getAttachmentsByCommentID(x, commentID)
|
return getAttachmentsByCommentID(db, commentID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAttachmentsByReleaseID(e Engine, releaseID int64) ([]*Attachment, error) {
|
func getAttachmentsByReleaseID(e *gorm.DB, releaseID int64) ([]*Attachment, error) {
|
||||||
attachments := make([]*Attachment, 0, 10)
|
attachments := make([]*Attachment, 0, 10)
|
||||||
return attachments, e.Where("release_id = ?", releaseID).Find(&attachments)
|
return attachments, e.Where("release_id = ?", releaseID).Find(&attachments).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAttachmentsByReleaseID returns all attachments of a release.
|
// GetAttachmentsByReleaseID returns all attachments of a release.
|
||||||
func GetAttachmentsByReleaseID(releaseID int64) ([]*Attachment, error) {
|
func GetAttachmentsByReleaseID(releaseID int64) ([]*Attachment, error) {
|
||||||
return getAttachmentsByReleaseID(x, releaseID)
|
return getAttachmentsByReleaseID(db, releaseID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteAttachment deletes the given attachment and optionally the associated file.
|
// DeleteAttachment deletes the given attachment and optionally the associated file.
|
||||||
@@ -171,7 +172,7 @@ func DeleteAttachments(attachments []*Attachment, remove bool) (int, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := x.Delete(a); err != nil {
|
if err := db.Delete(a).Error; err != nil {
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,8 +17,6 @@ import (
|
|||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
"gorm.io/gorm/schema"
|
"gorm.io/gorm/schema"
|
||||||
log "unknwon.dev/clog/v2"
|
log "unknwon.dev/clog/v2"
|
||||||
"xorm.io/core"
|
|
||||||
"xorm.io/xorm"
|
|
||||||
|
|
||||||
"gogs.io/gogs/internal/conf"
|
"gogs.io/gogs/internal/conf"
|
||||||
"gogs.io/gogs/internal/osutil"
|
"gogs.io/gogs/internal/osutil"
|
||||||
@@ -122,19 +120,19 @@ func dumpLegacyTables(ctx context.Context, dirPath string, verbose bool) error {
|
|||||||
log.Trace("Dumping table %q...", tableName)
|
log.Trace("Dumping table %q...", tableName)
|
||||||
}
|
}
|
||||||
|
|
||||||
tableFile := filepath.Join(dirPath, tableName+".json")
|
err := func() error {
|
||||||
f, err := os.Create(tableFile)
|
tableFile := filepath.Join(dirPath, tableName+".json")
|
||||||
if err != nil {
|
f, err := os.Create(tableFile)
|
||||||
return errors.Newf("create JSON file: %v", err)
|
if err != nil {
|
||||||
}
|
return errors.Wrap(err, "create JSON file")
|
||||||
|
}
|
||||||
|
defer func() { _ = f.Close() }()
|
||||||
|
|
||||||
if err = x.Context(ctx).Asc("id").Iterate(table, func(idx int, bean any) (err error) {
|
return dumpTable(ctx, db, table, f)
|
||||||
return jsoniter.NewEncoder(f).Encode(bean)
|
}()
|
||||||
}); err != nil {
|
if err != nil {
|
||||||
_ = f.Close()
|
return errors.Wrapf(err, "dump table %q", tableName)
|
||||||
return errors.Newf("dump table '%s': %v", tableName, err)
|
|
||||||
}
|
}
|
||||||
_ = f.Close()
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -229,8 +227,6 @@ func importTable(ctx context.Context, db *gorm.DB, table any, r io.Reader) error
|
|||||||
}
|
}
|
||||||
|
|
||||||
func importLegacyTables(ctx context.Context, dirPath string, verbose bool) error {
|
func importLegacyTables(ctx context.Context, dirPath string, verbose bool) error {
|
||||||
snakeMapper := core.SnakeMapper{}
|
|
||||||
|
|
||||||
skipInsertProcessors := map[string]bool{
|
skipInsertProcessors := map[string]bool{
|
||||||
"mirror": true,
|
"mirror": true,
|
||||||
"milestone": true,
|
"milestone": true,
|
||||||
@@ -255,9 +251,9 @@ func importLegacyTables(ctx context.Context, dirPath string, verbose bool) error
|
|||||||
log.Trace("Importing table %q...", tableName)
|
log.Trace("Importing table %q...", tableName)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := x.DropTables(table); err != nil {
|
if err := db.WithContext(ctx).Migrator().DropTable(table); err != nil {
|
||||||
return errors.Newf("drop table %q: %v", tableName, err)
|
return errors.Newf("drop table %q: %v", tableName, err)
|
||||||
} else if err = x.Sync2(table); err != nil {
|
} else if err = db.WithContext(ctx).Migrator().AutoMigrate(table); err != nil {
|
||||||
return errors.Newf("sync table %q: %v", tableName, err)
|
return errors.Newf("sync table %q: %v", tableName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,16 +261,28 @@ func importLegacyTables(ctx context.Context, dirPath string, verbose bool) error
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Newf("open JSON file: %v", err)
|
return errors.Newf("open JSON file: %v", err)
|
||||||
}
|
}
|
||||||
rawTableName := x.TableName(table)
|
|
||||||
_, isInsertProcessor := table.(xorm.BeforeInsertProcessor)
|
s, err := schema.Parse(table, &sync.Map{}, db.NamingStrategy)
|
||||||
|
if err != nil {
|
||||||
|
_ = f.Close()
|
||||||
|
return errors.Wrap(err, "parse schema")
|
||||||
|
}
|
||||||
|
rawTableName := s.Table
|
||||||
|
|
||||||
|
_, isInsertProcessor := table.(interface{ BeforeCreate(*gorm.DB) error })
|
||||||
scanner := bufio.NewScanner(f)
|
scanner := bufio.NewScanner(f)
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
if err = jsoniter.Unmarshal(scanner.Bytes(), table); err != nil {
|
// PostgreSQL does not like the null characters (U+0000)
|
||||||
|
cleaned := bytes.ReplaceAll(scanner.Bytes(), []byte("\\u0000"), []byte(""))
|
||||||
|
|
||||||
|
if err = jsoniter.Unmarshal(cleaned, table); err != nil {
|
||||||
|
_ = f.Close()
|
||||||
return errors.Newf("unmarshal to struct: %v", err)
|
return errors.Newf("unmarshal to struct: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = x.Insert(table); err != nil {
|
if err = db.WithContext(ctx).Create(table).Error; err != nil {
|
||||||
return errors.Newf("insert strcut: %v", err)
|
_ = f.Close()
|
||||||
|
return errors.Newf("insert struct: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var meta struct {
|
var meta struct {
|
||||||
@@ -283,30 +291,30 @@ func importLegacyTables(ctx context.Context, dirPath string, verbose bool) error
|
|||||||
DeadlineUnix int64
|
DeadlineUnix int64
|
||||||
ClosedDateUnix int64
|
ClosedDateUnix int64
|
||||||
}
|
}
|
||||||
if err = jsoniter.Unmarshal(scanner.Bytes(), &meta); err != nil {
|
if err = jsoniter.Unmarshal(cleaned, &meta); err != nil {
|
||||||
log.Error("Failed to unmarshal to map: %v", err)
|
log.Error("Failed to unmarshal to map: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset created_unix back to the date save in archive because Insert method updates its value
|
// Reset created_unix back to the date saved in archive because Create method updates its value
|
||||||
if isInsertProcessor && !skipInsertProcessors[rawTableName] {
|
if isInsertProcessor && !skipInsertProcessors[rawTableName] {
|
||||||
if _, err = x.Exec("UPDATE `"+rawTableName+"` SET created_unix=? WHERE id=?", meta.CreatedUnix, meta.ID); err != nil {
|
if err = db.WithContext(ctx).Exec("UPDATE `"+rawTableName+"` SET created_unix=? WHERE id=?", meta.CreatedUnix, meta.ID).Error; err != nil {
|
||||||
log.Error("Failed to reset '%s.created_unix': %v", rawTableName, err)
|
log.Error("Failed to reset '%s.created_unix': %v", rawTableName, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch rawTableName {
|
switch rawTableName {
|
||||||
case "milestone":
|
case "milestone":
|
||||||
if _, err = x.Exec("UPDATE `"+rawTableName+"` SET deadline_unix=?, closed_date_unix=? WHERE id=?", meta.DeadlineUnix, meta.ClosedDateUnix, meta.ID); err != nil {
|
if err = db.WithContext(ctx).Exec("UPDATE `"+rawTableName+"` SET deadline_unix=?, closed_date_unix=? WHERE id=?", meta.DeadlineUnix, meta.ClosedDateUnix, meta.ID).Error; err != nil {
|
||||||
log.Error("Failed to reset 'milestone.deadline_unix', 'milestone.closed_date_unix': %v", err)
|
log.Error("Failed to reset 'milestone.deadline_unix', 'milestone.closed_date_unix': %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_ = f.Close()
|
||||||
|
|
||||||
// PostgreSQL needs manually reset table sequence for auto increment keys
|
// PostgreSQL needs manually reset table sequence for auto increment keys
|
||||||
if conf.UsePostgreSQL {
|
if conf.UsePostgreSQL {
|
||||||
rawTableName := snakeMapper.Obj2Table(tableName)
|
|
||||||
seqName := rawTableName + "_id_seq"
|
seqName := rawTableName + "_id_seq"
|
||||||
if _, err = x.Exec(fmt.Sprintf(`SELECT setval('%s', COALESCE((SELECT MAX(id)+1 FROM "%s"), 1), false);`, seqName, rawTableName)); err != nil {
|
if err = db.WithContext(ctx).Exec(fmt.Sprintf(`SELECT setval('%s', COALESCE((SELECT MAX(id)+1 FROM "%s"), 1), false);`, seqName, rawTableName)).Error; err != nil {
|
||||||
return errors.Newf("reset table %q' sequence: %v", rawTableName, err)
|
return errors.Newf("reset table %q' sequence: %v", rawTableName, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,11 +7,10 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/cockroachdb/errors"
|
"github.com/cockroachdb/errors"
|
||||||
"github.com/unknwon/com"
|
|
||||||
log "unknwon.dev/clog/v2"
|
|
||||||
"xorm.io/xorm"
|
|
||||||
|
|
||||||
api "github.com/gogs/go-gogs-client"
|
api "github.com/gogs/go-gogs-client"
|
||||||
|
"github.com/unknwon/com"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
log "unknwon.dev/clog/v2"
|
||||||
|
|
||||||
"gogs.io/gogs/internal/errutil"
|
"gogs.io/gogs/internal/errutil"
|
||||||
"gogs.io/gogs/internal/markup"
|
"gogs.io/gogs/internal/markup"
|
||||||
@@ -50,47 +49,50 @@ type Comment struct {
|
|||||||
ID int64
|
ID int64
|
||||||
Type CommentType
|
Type CommentType
|
||||||
PosterID int64
|
PosterID int64
|
||||||
Poster *User `xorm:"-" json:"-" gorm:"-"`
|
Poster *User `gorm:"-" json:"-"`
|
||||||
IssueID int64 `xorm:"INDEX"`
|
IssueID int64 `gorm:"index"`
|
||||||
Issue *Issue `xorm:"-" json:"-" gorm:"-"`
|
Issue *Issue `gorm:"-" json:"-"`
|
||||||
CommitID int64
|
CommitID int64
|
||||||
Line int64
|
Line int64
|
||||||
Content string `xorm:"TEXT"`
|
Content string `gorm:"type:text"`
|
||||||
RenderedContent string `xorm:"-" json:"-" gorm:"-"`
|
RenderedContent string `gorm:"-" json:"-"`
|
||||||
|
|
||||||
Created time.Time `xorm:"-" json:"-" gorm:"-"`
|
Created time.Time `gorm:"-" json:"-"`
|
||||||
CreatedUnix int64
|
CreatedUnix int64
|
||||||
Updated time.Time `xorm:"-" json:"-" gorm:"-"`
|
Updated time.Time `gorm:"-" json:"-"`
|
||||||
UpdatedUnix int64
|
UpdatedUnix int64
|
||||||
|
|
||||||
// Reference issue in commit message
|
// Reference issue in commit message
|
||||||
CommitSHA string `xorm:"VARCHAR(40)"`
|
CommitSHA string `gorm:"type:varchar(40)"`
|
||||||
|
|
||||||
Attachments []*Attachment `xorm:"-" json:"-" gorm:"-"`
|
Attachments []*Attachment `gorm:"-" json:"-"`
|
||||||
|
|
||||||
// For view issue page.
|
// For view issue page.
|
||||||
ShowTag CommentTag `xorm:"-" json:"-" gorm:"-"`
|
ShowTag CommentTag `gorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Comment) BeforeInsert() {
|
func (c *Comment) BeforeCreate(tx *gorm.DB) error {
|
||||||
c.CreatedUnix = time.Now().Unix()
|
if c.CreatedUnix == 0 {
|
||||||
c.UpdatedUnix = c.CreatedUnix
|
c.CreatedUnix = tx.NowFunc().Unix()
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Comment) BeforeUpdate() {
|
|
||||||
c.UpdatedUnix = time.Now().Unix()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Comment) AfterSet(colName string, _ xorm.Cell) {
|
|
||||||
switch colName {
|
|
||||||
case "created_unix":
|
|
||||||
c.Created = time.Unix(c.CreatedUnix, 0).Local()
|
|
||||||
case "updated_unix":
|
|
||||||
c.Updated = time.Unix(c.UpdatedUnix, 0).Local()
|
|
||||||
}
|
}
|
||||||
|
if c.UpdatedUnix == 0 {
|
||||||
|
c.UpdatedUnix = c.CreatedUnix
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Comment) loadAttributes(e Engine) (err error) {
|
func (c *Comment) BeforeUpdate(tx *gorm.DB) error {
|
||||||
|
c.UpdatedUnix = tx.NowFunc().Unix()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Comment) AfterFind(tx *gorm.DB) error {
|
||||||
|
c.Created = time.Unix(c.CreatedUnix, 0).Local()
|
||||||
|
c.Updated = time.Unix(c.UpdatedUnix, 0).Local()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Comment) loadAttributes(tx *gorm.DB) (err error) {
|
||||||
if c.Poster == nil {
|
if c.Poster == nil {
|
||||||
c.Poster, err = Handle.Users().GetByID(context.TODO(), c.PosterID)
|
c.Poster, err = Handle.Users().GetByID(context.TODO(), c.PosterID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -104,12 +106,12 @@ func (c *Comment) loadAttributes(e Engine) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if c.Issue == nil {
|
if c.Issue == nil {
|
||||||
c.Issue, err = getRawIssueByID(e, c.IssueID)
|
c.Issue, err = getRawIssueByID(tx, c.IssueID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Newf("getIssueByID [%d]: %v", c.IssueID, err)
|
return errors.Newf("getIssueByID [%d]: %v", c.IssueID, err)
|
||||||
}
|
}
|
||||||
if c.Issue.Repo == nil {
|
if c.Issue.Repo == nil {
|
||||||
c.Issue.Repo, err = getRepositoryByID(e, c.Issue.RepoID)
|
c.Issue.Repo, err = getRepositoryByID(tx, c.Issue.RepoID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Newf("getRepositoryByID [%d]: %v", c.Issue.RepoID, err)
|
return errors.Newf("getRepositoryByID [%d]: %v", c.Issue.RepoID, err)
|
||||||
}
|
}
|
||||||
@@ -117,7 +119,7 @@ func (c *Comment) loadAttributes(e Engine) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if c.Attachments == nil {
|
if c.Attachments == nil {
|
||||||
c.Attachments, err = getAttachmentsByCommentID(e, c.ID)
|
c.Attachments, err = getAttachmentsByCommentID(tx, c.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Newf("getAttachmentsByCommentID [%d]: %v", c.ID, err)
|
return errors.Newf("getAttachmentsByCommentID [%d]: %v", c.ID, err)
|
||||||
}
|
}
|
||||||
@@ -127,7 +129,7 @@ func (c *Comment) loadAttributes(e Engine) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Comment) LoadAttributes() error {
|
func (c *Comment) LoadAttributes() error {
|
||||||
return c.loadAttributes(x)
|
return c.loadAttributes(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Comment) HTMLURL() string {
|
func (c *Comment) HTMLURL() string {
|
||||||
@@ -163,9 +165,9 @@ func (c *Comment) EventTag() string {
|
|||||||
|
|
||||||
// mailParticipants sends new comment emails to repository watchers
|
// mailParticipants sends new comment emails to repository watchers
|
||||||
// and mentioned people.
|
// and mentioned people.
|
||||||
func (c *Comment) mailParticipants(e Engine, opType ActionType, issue *Issue) (err error) {
|
func (c *Comment) mailParticipants(tx *gorm.DB, opType ActionType, issue *Issue) (err error) {
|
||||||
mentions := markup.FindAllMentions(c.Content)
|
mentions := markup.FindAllMentions(c.Content)
|
||||||
if err = updateIssueMentions(e, c.IssueID, mentions); err != nil {
|
if err = updateIssueMentions(tx, c.IssueID, mentions); err != nil {
|
||||||
return errors.Newf("UpdateIssueMentions [%d]: %v", c.IssueID, err)
|
return errors.Newf("UpdateIssueMentions [%d]: %v", c.IssueID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,7 +186,7 @@ func (c *Comment) mailParticipants(e Engine, opType ActionType, issue *Issue) (e
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err error) {
|
func createComment(tx *gorm.DB, opts *CreateCommentOptions) (_ *Comment, err error) {
|
||||||
comment := &Comment{
|
comment := &Comment{
|
||||||
Type: opts.Type,
|
Type: opts.Type,
|
||||||
PosterID: opts.Doer.ID,
|
PosterID: opts.Doer.ID,
|
||||||
@@ -195,7 +197,7 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err
|
|||||||
Line: opts.LineNum,
|
Line: opts.LineNum,
|
||||||
Content: opts.Content,
|
Content: opts.Content,
|
||||||
}
|
}
|
||||||
if _, err = e.Insert(comment); err != nil {
|
if err = tx.Create(comment).Error; err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,14 +218,14 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err
|
|||||||
case CommentTypeComment:
|
case CommentTypeComment:
|
||||||
act.OpType = ActionCommentIssue
|
act.OpType = ActionCommentIssue
|
||||||
|
|
||||||
if _, err = e.Exec("UPDATE `issue` SET num_comments=num_comments+1 WHERE id=?", opts.Issue.ID); err != nil {
|
if err = tx.Exec("UPDATE `issue` SET num_comments=num_comments+1 WHERE id=?", opts.Issue.ID).Error; err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check attachments
|
// Check attachments
|
||||||
attachments := make([]*Attachment, 0, len(opts.Attachments))
|
attachments := make([]*Attachment, 0, len(opts.Attachments))
|
||||||
for _, uuid := range opts.Attachments {
|
for _, uuid := range opts.Attachments {
|
||||||
attach, err := getAttachmentByUUID(e, uuid)
|
attach, err := getAttachmentByUUID(tx, uuid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if IsErrAttachmentNotExist(err) {
|
if IsErrAttachmentNotExist(err) {
|
||||||
continue
|
continue
|
||||||
@@ -236,8 +238,10 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err
|
|||||||
for i := range attachments {
|
for i := range attachments {
|
||||||
attachments[i].IssueID = opts.Issue.ID
|
attachments[i].IssueID = opts.Issue.ID
|
||||||
attachments[i].CommentID = comment.ID
|
attachments[i].CommentID = comment.ID
|
||||||
// No assign value could be 0, so ignore AllCols().
|
if err = tx.Model(attachments[i]).Where("id = ?", attachments[i].ID).Updates(map[string]any{
|
||||||
if _, err = e.ID(attachments[i].ID).Update(attachments[i]); err != nil {
|
"issue_id": attachments[i].IssueID,
|
||||||
|
"comment_id": attachments[i].CommentID,
|
||||||
|
}).Error; err != nil {
|
||||||
return nil, errors.Newf("update attachment [%d]: %v", attachments[i].ID, err)
|
return nil, errors.Newf("update attachment [%d]: %v", attachments[i].ID, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -249,9 +253,9 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err
|
|||||||
}
|
}
|
||||||
|
|
||||||
if opts.Issue.IsPull {
|
if opts.Issue.IsPull {
|
||||||
_, err = e.Exec("UPDATE `repository` SET num_closed_pulls=num_closed_pulls-1 WHERE id=?", opts.Repo.ID)
|
err = tx.Exec("UPDATE `repository` SET num_closed_pulls=num_closed_pulls-1 WHERE id=?", opts.Repo.ID).Error
|
||||||
} else {
|
} else {
|
||||||
_, err = e.Exec("UPDATE `repository` SET num_closed_issues=num_closed_issues-1 WHERE id=?", opts.Repo.ID)
|
err = tx.Exec("UPDATE `repository` SET num_closed_issues=num_closed_issues-1 WHERE id=?", opts.Repo.ID).Error
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -264,38 +268,38 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err
|
|||||||
}
|
}
|
||||||
|
|
||||||
if opts.Issue.IsPull {
|
if opts.Issue.IsPull {
|
||||||
_, err = e.Exec("UPDATE `repository` SET num_closed_pulls=num_closed_pulls+1 WHERE id=?", opts.Repo.ID)
|
err = tx.Exec("UPDATE `repository` SET num_closed_pulls=num_closed_pulls+1 WHERE id=?", opts.Repo.ID).Error
|
||||||
} else {
|
} else {
|
||||||
_, err = e.Exec("UPDATE `repository` SET num_closed_issues=num_closed_issues+1 WHERE id=?", opts.Repo.ID)
|
err = tx.Exec("UPDATE `repository` SET num_closed_issues=num_closed_issues+1 WHERE id=?", opts.Repo.ID).Error
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = e.Exec("UPDATE `issue` SET updated_unix = ? WHERE id = ?", time.Now().Unix(), opts.Issue.ID); err != nil {
|
if err = tx.Exec("UPDATE `issue` SET updated_unix = ? WHERE id = ?", tx.NowFunc().Unix(), opts.Issue.ID).Error; err != nil {
|
||||||
return nil, errors.Newf("update issue 'updated_unix': %v", err)
|
return nil, errors.Newf("update issue 'updated_unix': %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify watchers for whatever action comes in, ignore if no action type.
|
// Notify watchers for whatever action comes in, ignore if no action type.
|
||||||
if act.OpType > 0 {
|
if act.OpType > 0 {
|
||||||
if err = notifyWatchers(e, act); err != nil {
|
if err = notifyWatchers(tx, act); err != nil {
|
||||||
log.Error("notifyWatchers: %v", err)
|
log.Error("notifyWatchers: %v", err)
|
||||||
}
|
}
|
||||||
if err = comment.mailParticipants(e, act.OpType, opts.Issue); err != nil {
|
if err = comment.mailParticipants(tx, act.OpType, opts.Issue); err != nil {
|
||||||
log.Error("MailParticipants: %v", err)
|
log.Error("MailParticipants: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return comment, comment.loadAttributes(e)
|
return comment, comment.loadAttributes(tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func createStatusComment(e *xorm.Session, doer *User, repo *Repository, issue *Issue) (*Comment, error) {
|
func createStatusComment(tx *gorm.DB, doer *User, repo *Repository, issue *Issue) (*Comment, error) {
|
||||||
cmtType := CommentTypeClose
|
cmtType := CommentTypeClose
|
||||||
if !issue.IsClosed {
|
if !issue.IsClosed {
|
||||||
cmtType = CommentTypeReopen
|
cmtType = CommentTypeReopen
|
||||||
}
|
}
|
||||||
return createComment(e, &CreateCommentOptions{
|
return createComment(tx, &CreateCommentOptions{
|
||||||
Type: cmtType,
|
Type: cmtType,
|
||||||
Doer: doer,
|
Doer: doer,
|
||||||
Repo: repo,
|
Repo: repo,
|
||||||
@@ -318,18 +322,12 @@ type CreateCommentOptions struct {
|
|||||||
|
|
||||||
// CreateComment creates comment of issue or commit.
|
// CreateComment creates comment of issue or commit.
|
||||||
func CreateComment(opts *CreateCommentOptions) (comment *Comment, err error) {
|
func CreateComment(opts *CreateCommentOptions) (comment *Comment, err error) {
|
||||||
sess := x.NewSession()
|
err = db.Transaction(func(tx *gorm.DB) error {
|
||||||
defer sess.Close()
|
var err error
|
||||||
if err = sess.Begin(); err != nil {
|
comment, err = createComment(tx, opts)
|
||||||
return nil, err
|
return err
|
||||||
}
|
})
|
||||||
|
return comment, err
|
||||||
comment, err = createComment(sess, opts)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return comment, sess.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateIssueComment creates a plain issue comment.
|
// CreateIssueComment creates a plain issue comment.
|
||||||
@@ -367,14 +365,12 @@ func CreateRefComment(doer *User, repo *Repository, issue *Issue, content, commi
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if same reference from same commit has already existed.
|
// Check if same reference from same commit has already existed.
|
||||||
has, err := x.Get(&Comment{
|
var count int64
|
||||||
Type: CommentTypeCommitRef,
|
err := db.Model(new(Comment)).Where("type = ? AND issue_id = ? AND commit_sha = ?",
|
||||||
IssueID: issue.ID,
|
CommentTypeCommitRef, issue.ID, commitSHA).Count(&count).Error
|
||||||
CommitSHA: commitSHA,
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Newf("check reference comment: %v", err)
|
return errors.Newf("check reference comment: %v", err)
|
||||||
} else if has {
|
} else if count > 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -411,19 +407,20 @@ func (ErrCommentNotExist) NotFound() bool {
|
|||||||
// GetCommentByID returns the comment by given ID.
|
// GetCommentByID returns the comment by given ID.
|
||||||
func GetCommentByID(id int64) (*Comment, error) {
|
func GetCommentByID(id int64) (*Comment, error) {
|
||||||
c := new(Comment)
|
c := new(Comment)
|
||||||
has, err := x.Id(id).Get(c)
|
err := db.Where("id = ?", id).First(c).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil, ErrCommentNotExist{args: map[string]any{"commentID": id}}
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if !has {
|
|
||||||
return nil, ErrCommentNotExist{args: map[string]any{"commentID": id}}
|
|
||||||
}
|
}
|
||||||
return c, c.LoadAttributes()
|
return c, c.LoadAttributes()
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: use CommentList to improve performance.
|
// FIXME: use CommentList to improve performance.
|
||||||
func loadCommentsAttributes(e Engine, comments []*Comment) (err error) {
|
func loadCommentsAttributes(tx *gorm.DB, comments []*Comment) (err error) {
|
||||||
for i := range comments {
|
for i := range comments {
|
||||||
if err = comments[i].loadAttributes(e); err != nil {
|
if err = comments[i].loadAttributes(tx); err != nil {
|
||||||
return errors.Newf("loadAttributes [%d]: %v", comments[i].ID, err)
|
return errors.Newf("loadAttributes [%d]: %v", comments[i].ID, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -431,53 +428,55 @@ func loadCommentsAttributes(e Engine, comments []*Comment) (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCommentsByIssueIDSince(e Engine, issueID, since int64) ([]*Comment, error) {
|
func getCommentsByIssueIDSince(tx *gorm.DB, issueID, since int64) ([]*Comment, error) {
|
||||||
comments := make([]*Comment, 0, 10)
|
comments := make([]*Comment, 0, 10)
|
||||||
sess := e.Where("issue_id = ?", issueID).Asc("created_unix")
|
query := tx.Where("issue_id = ?", issueID).Order("created_unix ASC")
|
||||||
if since > 0 {
|
if since > 0 {
|
||||||
sess.And("updated_unix >= ?", since)
|
query = query.Where("updated_unix >= ?", since)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := sess.Find(&comments); err != nil {
|
if err := query.Find(&comments).Error; err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return comments, loadCommentsAttributes(e, comments)
|
return comments, loadCommentsAttributes(tx, comments)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCommentsByRepoIDSince(e Engine, repoID, since int64) ([]*Comment, error) {
|
func getCommentsByRepoIDSince(tx *gorm.DB, repoID, since int64) ([]*Comment, error) {
|
||||||
comments := make([]*Comment, 0, 10)
|
comments := make([]*Comment, 0, 10)
|
||||||
sess := e.Where("issue.repo_id = ?", repoID).Join("INNER", "issue", "issue.id = comment.issue_id").Asc("comment.created_unix")
|
query := tx.Joins("INNER JOIN issue ON issue.id = comment.issue_id").
|
||||||
|
Where("issue.repo_id = ?", repoID).
|
||||||
|
Order("comment.created_unix ASC")
|
||||||
if since > 0 {
|
if since > 0 {
|
||||||
sess.And("comment.updated_unix >= ?", since)
|
query = query.Where("comment.updated_unix >= ?", since)
|
||||||
}
|
}
|
||||||
if err := sess.Find(&comments); err != nil {
|
if err := query.Find(&comments).Error; err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return comments, loadCommentsAttributes(e, comments)
|
return comments, loadCommentsAttributes(tx, comments)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCommentsByIssueID(e Engine, issueID int64) ([]*Comment, error) {
|
func getCommentsByIssueID(tx *gorm.DB, issueID int64) ([]*Comment, error) {
|
||||||
return getCommentsByIssueIDSince(e, issueID, -1)
|
return getCommentsByIssueIDSince(tx, issueID, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCommentsByIssueID returns all comments of an issue.
|
// GetCommentsByIssueID returns all comments of an issue.
|
||||||
func GetCommentsByIssueID(issueID int64) ([]*Comment, error) {
|
func GetCommentsByIssueID(issueID int64) ([]*Comment, error) {
|
||||||
return getCommentsByIssueID(x, issueID)
|
return getCommentsByIssueID(db, issueID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCommentsByIssueIDSince returns a list of comments of an issue since a given time point.
|
// GetCommentsByIssueIDSince returns a list of comments of an issue since a given time point.
|
||||||
func GetCommentsByIssueIDSince(issueID, since int64) ([]*Comment, error) {
|
func GetCommentsByIssueIDSince(issueID, since int64) ([]*Comment, error) {
|
||||||
return getCommentsByIssueIDSince(x, issueID, since)
|
return getCommentsByIssueIDSince(db, issueID, since)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCommentsByRepoIDSince returns a list of comments for all issues in a repo since a given time point.
|
// GetCommentsByRepoIDSince returns a list of comments for all issues in a repo since a given time point.
|
||||||
func GetCommentsByRepoIDSince(repoID, since int64) ([]*Comment, error) {
|
func GetCommentsByRepoIDSince(repoID, since int64) ([]*Comment, error) {
|
||||||
return getCommentsByRepoIDSince(x, repoID, since)
|
return getCommentsByRepoIDSince(db, repoID, since)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateComment updates information of comment.
|
// UpdateComment updates information of comment.
|
||||||
func UpdateComment(doer *User, c *Comment, oldContent string) (err error) {
|
func UpdateComment(doer *User, c *Comment, oldContent string) (err error) {
|
||||||
if _, err = x.Id(c.ID).AllCols().Update(c); err != nil {
|
if err = db.Model(c).Where("id = ?", c.ID).Updates(c).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -511,24 +510,21 @@ func DeleteCommentByID(doer *User, id int64) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
sess := x.NewSession()
|
err = db.Transaction(func(tx *gorm.DB) error {
|
||||||
defer sess.Close()
|
if err := tx.Where("id = ?", comment.ID).Delete(new(Comment)).Error; err != nil {
|
||||||
if err = sess.Begin(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = sess.ID(comment.ID).Delete(new(Comment)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if comment.Type == CommentTypeComment {
|
|
||||||
if _, err = sess.Exec("UPDATE `issue` SET num_comments = num_comments - 1 WHERE id = ?", comment.IssueID); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if err = sess.Commit(); err != nil {
|
if comment.Type == CommentTypeComment {
|
||||||
return errors.Newf("commit: %v", err)
|
if err := tx.Exec("UPDATE `issue` SET num_comments = num_comments - 1 WHERE id = ?", comment.IssueID).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return errors.Newf("transaction: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = DeleteAttachmentsByComment(comment.ID, true)
|
_, err = DeleteAttachmentsByComment(comment.ID, true)
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -6,14 +6,12 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"xorm.io/xorm"
|
|
||||||
|
|
||||||
"github.com/cockroachdb/errors"
|
"github.com/cockroachdb/errors"
|
||||||
api "github.com/gogs/go-gogs-client"
|
api "github.com/gogs/go-gogs-client"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
|
||||||
"gogs.io/gogs/internal/errutil"
|
"gogs.io/gogs/internal/errutil"
|
||||||
"gogs.io/gogs/internal/lazyregexp"
|
"gogs.io/gogs/internal/lazyregexp"
|
||||||
"gogs.io/gogs/internal/tool"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var labelColorPattern = lazyregexp.New("#([a-fA-F0-9]{6})")
|
var labelColorPattern = lazyregexp.New("#([a-fA-F0-9]{6})")
|
||||||
@@ -53,13 +51,13 @@ func GetLabelTemplateFile(name string) ([][2]string, error) {
|
|||||||
// Label represents a label of repository for issues.
|
// Label represents a label of repository for issues.
|
||||||
type Label struct {
|
type Label struct {
|
||||||
ID int64
|
ID int64
|
||||||
RepoID int64 `xorm:"INDEX"`
|
RepoID int64 `gorm:"index"`
|
||||||
Name string
|
Name string
|
||||||
Color string `xorm:"VARCHAR(7)"`
|
Color string `gorm:"type:varchar(7)"`
|
||||||
NumIssues int
|
NumIssues int
|
||||||
NumClosedIssues int
|
NumClosedIssues int
|
||||||
NumOpenIssues int `xorm:"-" json:"-" gorm:"-"`
|
NumOpenIssues int `gorm:"-" json:"-"`
|
||||||
IsChecked bool `xorm:"-" json:"-" gorm:"-"`
|
IsChecked bool `gorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Label) APIFormat() *api.Label {
|
func (l *Label) APIFormat() *api.Label {
|
||||||
@@ -97,8 +95,7 @@ func (l *Label) ForegroundColor() template.CSS {
|
|||||||
|
|
||||||
// NewLabels creates new label(s) for a repository.
|
// NewLabels creates new label(s) for a repository.
|
||||||
func NewLabels(labels ...*Label) error {
|
func NewLabels(labels ...*Label) error {
|
||||||
_, err := x.Insert(labels)
|
return db.Create(labels).Error
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ errutil.NotFound = (*ErrLabelNotExist)(nil)
|
var _ errutil.NotFound = (*ErrLabelNotExist)(nil)
|
||||||
@@ -123,20 +120,22 @@ func (ErrLabelNotExist) NotFound() bool {
|
|||||||
// getLabelOfRepoByName returns a label by Name in given repository.
|
// getLabelOfRepoByName returns a label by Name in given repository.
|
||||||
// If pass repoID as 0, then ORM will ignore limitation of repository
|
// If pass repoID as 0, then ORM will ignore limitation of repository
|
||||||
// and can return arbitrary label with any valid ID.
|
// and can return arbitrary label with any valid ID.
|
||||||
func getLabelOfRepoByName(e Engine, repoID int64, labelName string) (*Label, error) {
|
func getLabelOfRepoByName(tx *gorm.DB, repoID int64, labelName string) (*Label, error) {
|
||||||
if len(labelName) <= 0 {
|
if len(labelName) <= 0 {
|
||||||
return nil, ErrLabelNotExist{args: map[string]any{"repoID": repoID}}
|
return nil, ErrLabelNotExist{args: map[string]any{"repoID": repoID}}
|
||||||
}
|
}
|
||||||
|
|
||||||
l := &Label{
|
l := &Label{}
|
||||||
Name: labelName,
|
query := tx.Where("name = ?", labelName)
|
||||||
RepoID: repoID,
|
if repoID > 0 {
|
||||||
|
query = query.Where("repo_id = ?", repoID)
|
||||||
}
|
}
|
||||||
has, err := e.Get(l)
|
err := query.First(l).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil, ErrLabelNotExist{args: map[string]any{"repoID": repoID}}
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if !has {
|
|
||||||
return nil, ErrLabelNotExist{args: map[string]any{"repoID": repoID}}
|
|
||||||
}
|
}
|
||||||
return l, nil
|
return l, nil
|
||||||
}
|
}
|
||||||
@@ -144,54 +143,56 @@ func getLabelOfRepoByName(e Engine, repoID int64, labelName string) (*Label, err
|
|||||||
// getLabelInRepoByID returns a label by ID in given repository.
|
// getLabelInRepoByID returns a label by ID in given repository.
|
||||||
// If pass repoID as 0, then ORM will ignore limitation of repository
|
// If pass repoID as 0, then ORM will ignore limitation of repository
|
||||||
// and can return arbitrary label with any valid ID.
|
// and can return arbitrary label with any valid ID.
|
||||||
func getLabelOfRepoByID(e Engine, repoID, labelID int64) (*Label, error) {
|
func getLabelOfRepoByID(tx *gorm.DB, repoID, labelID int64) (*Label, error) {
|
||||||
if labelID <= 0 {
|
if labelID <= 0 {
|
||||||
return nil, ErrLabelNotExist{args: map[string]any{"repoID": repoID, "labelID": labelID}}
|
return nil, ErrLabelNotExist{args: map[string]any{"repoID": repoID, "labelID": labelID}}
|
||||||
}
|
}
|
||||||
|
|
||||||
l := &Label{
|
l := &Label{}
|
||||||
ID: labelID,
|
query := tx.Where("id = ?", labelID)
|
||||||
RepoID: repoID,
|
if repoID > 0 {
|
||||||
|
query = query.Where("repo_id = ?", repoID)
|
||||||
}
|
}
|
||||||
has, err := e.Get(l)
|
err := query.First(l).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil, ErrLabelNotExist{args: map[string]any{"repoID": repoID, "labelID": labelID}}
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if !has {
|
|
||||||
return nil, ErrLabelNotExist{args: map[string]any{"repoID": repoID, "labelID": labelID}}
|
|
||||||
}
|
}
|
||||||
return l, nil
|
return l, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLabelByID returns a label by given ID.
|
// GetLabelByID returns a label by given ID.
|
||||||
func GetLabelByID(id int64) (*Label, error) {
|
func GetLabelByID(id int64) (*Label, error) {
|
||||||
return getLabelOfRepoByID(x, 0, id)
|
return getLabelOfRepoByID(db, 0, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLabelOfRepoByID returns a label by ID in given repository.
|
// GetLabelOfRepoByID returns a label by ID in given repository.
|
||||||
func GetLabelOfRepoByID(repoID, labelID int64) (*Label, error) {
|
func GetLabelOfRepoByID(repoID, labelID int64) (*Label, error) {
|
||||||
return getLabelOfRepoByID(x, repoID, labelID)
|
return getLabelOfRepoByID(db, repoID, labelID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLabelOfRepoByName returns a label by name in given repository.
|
// GetLabelOfRepoByName returns a label by name in given repository.
|
||||||
func GetLabelOfRepoByName(repoID int64, labelName string) (*Label, error) {
|
func GetLabelOfRepoByName(repoID int64, labelName string) (*Label, error) {
|
||||||
return getLabelOfRepoByName(x, repoID, labelName)
|
return getLabelOfRepoByName(db, repoID, labelName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLabelsInRepoByIDs returns a list of labels by IDs in given repository,
|
// GetLabelsInRepoByIDs returns a list of labels by IDs in given repository,
|
||||||
// it silently ignores label IDs that are not belong to the repository.
|
// it silently ignores label IDs that are not belong to the repository.
|
||||||
func GetLabelsInRepoByIDs(repoID int64, labelIDs []int64) ([]*Label, error) {
|
func GetLabelsInRepoByIDs(repoID int64, labelIDs []int64) ([]*Label, error) {
|
||||||
labels := make([]*Label, 0, len(labelIDs))
|
labels := make([]*Label, 0, len(labelIDs))
|
||||||
return labels, x.Where("repo_id = ?", repoID).In("id", tool.Int64sToStrings(labelIDs)).Asc("name").Find(&labels)
|
return labels, db.Where("repo_id = ? AND id IN ?", repoID, labelIDs).Order("name ASC").Find(&labels).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLabelsByRepoID returns all labels that belong to given repository by ID.
|
// GetLabelsByRepoID returns all labels that belong to given repository by ID.
|
||||||
func GetLabelsByRepoID(repoID int64) ([]*Label, error) {
|
func GetLabelsByRepoID(repoID int64) ([]*Label, error) {
|
||||||
labels := make([]*Label, 0, 10)
|
labels := make([]*Label, 0, 10)
|
||||||
return labels, x.Where("repo_id = ?", repoID).Asc("name").Find(&labels)
|
return labels, db.Where("repo_id = ?", repoID).Order("name ASC").Find(&labels).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func getLabelsByIssueID(e Engine, issueID int64) ([]*Label, error) {
|
func getLabelsByIssueID(tx *gorm.DB, issueID int64) ([]*Label, error) {
|
||||||
issueLabels, err := getIssueLabels(e, issueID)
|
issueLabels, err := getIssueLabels(tx, issueID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Newf("getIssueLabels: %v", err)
|
return nil, errors.Newf("getIssueLabels: %v", err)
|
||||||
} else if len(issueLabels) == 0 {
|
} else if len(issueLabels) == 0 {
|
||||||
@@ -204,22 +205,21 @@ func getLabelsByIssueID(e Engine, issueID int64) ([]*Label, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
labels := make([]*Label, 0, len(labelIDs))
|
labels := make([]*Label, 0, len(labelIDs))
|
||||||
return labels, e.Where("id > 0").In("id", tool.Int64sToStrings(labelIDs)).Asc("name").Find(&labels)
|
return labels, tx.Where("id > 0 AND id IN ?", labelIDs).Order("name ASC").Find(&labels).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLabelsByIssueID returns all labels that belong to given issue by ID.
|
// GetLabelsByIssueID returns all labels that belong to given issue by ID.
|
||||||
func GetLabelsByIssueID(issueID int64) ([]*Label, error) {
|
func GetLabelsByIssueID(issueID int64) ([]*Label, error) {
|
||||||
return getLabelsByIssueID(x, issueID)
|
return getLabelsByIssueID(db, issueID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateLabel(e Engine, l *Label) error {
|
func updateLabel(tx *gorm.DB, l *Label) error {
|
||||||
_, err := e.ID(l.ID).AllCols().Update(l)
|
return tx.Model(l).Where("id = ?", l.ID).Updates(l).Error
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateLabel updates label information.
|
// UpdateLabel updates label information.
|
||||||
func UpdateLabel(l *Label) error {
|
func UpdateLabel(l *Label) error {
|
||||||
return updateLabel(x, l)
|
return updateLabel(db, l)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteLabel delete a label of given repository.
|
// DeleteLabel delete a label of given repository.
|
||||||
@@ -232,19 +232,15 @@ func DeleteLabel(repoID, labelID int64) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
sess := x.NewSession()
|
return db.Transaction(func(tx *gorm.DB) error {
|
||||||
defer sess.Close()
|
if err := tx.Where("id = ?", labelID).Delete(new(Label)).Error; err != nil {
|
||||||
if err = sess.Begin(); err != nil {
|
return err
|
||||||
return err
|
}
|
||||||
}
|
if err := tx.Where("label_id = ?", labelID).Delete(new(IssueLabel)).Error; err != nil {
|
||||||
|
return err
|
||||||
if _, err = sess.ID(labelID).Delete(new(Label)); err != nil {
|
}
|
||||||
return err
|
return nil
|
||||||
} else if _, err = sess.Where("label_id = ?", labelID).Delete(new(IssueLabel)); err != nil {
|
})
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return sess.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// .___ .____ ___. .__
|
// .___ .____ ___. .__
|
||||||
@@ -257,25 +253,26 @@ func DeleteLabel(repoID, labelID int64) error {
|
|||||||
// IssueLabel represents an issue-lable relation.
|
// IssueLabel represents an issue-lable relation.
|
||||||
type IssueLabel struct {
|
type IssueLabel struct {
|
||||||
ID int64
|
ID int64
|
||||||
IssueID int64 `xorm:"UNIQUE(s)"`
|
IssueID int64 `gorm:"uniqueIndex:issue_label_unique"`
|
||||||
LabelID int64 `xorm:"UNIQUE(s)"`
|
LabelID int64 `gorm:"uniqueIndex:issue_label_unique"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func hasIssueLabel(e Engine, issueID, labelID int64) bool {
|
func hasIssueLabel(tx *gorm.DB, issueID, labelID int64) bool {
|
||||||
has, _ := e.Where("issue_id = ? AND label_id = ?", issueID, labelID).Get(new(IssueLabel))
|
var count int64
|
||||||
return has
|
tx.Model(new(IssueLabel)).Where("issue_id = ? AND label_id = ?", issueID, labelID).Count(&count)
|
||||||
|
return count > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasIssueLabel returns true if issue has been labeled.
|
// HasIssueLabel returns true if issue has been labeled.
|
||||||
func HasIssueLabel(issueID, labelID int64) bool {
|
func HasIssueLabel(issueID, labelID int64) bool {
|
||||||
return hasIssueLabel(x, issueID, labelID)
|
return hasIssueLabel(db, issueID, labelID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newIssueLabel(e *xorm.Session, issue *Issue, label *Label) (err error) {
|
func newIssueLabel(tx *gorm.DB, issue *Issue, label *Label) (err error) {
|
||||||
if _, err = e.Insert(&IssueLabel{
|
if err = tx.Create(&IssueLabel{
|
||||||
IssueID: issue.ID,
|
IssueID: issue.ID,
|
||||||
LabelID: label.ID,
|
LabelID: label.ID,
|
||||||
}); err != nil {
|
}).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,7 +281,7 @@ func newIssueLabel(e *xorm.Session, issue *Issue, label *Label) (err error) {
|
|||||||
label.NumClosedIssues++
|
label.NumClosedIssues++
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = updateLabel(e, label); err != nil {
|
if err = updateLabel(tx, label); err != nil {
|
||||||
return errors.Newf("updateLabel: %v", err)
|
return errors.Newf("updateLabel: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,26 +295,18 @@ func NewIssueLabel(issue *Issue, label *Label) (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
sess := x.NewSession()
|
return db.Transaction(func(tx *gorm.DB) error {
|
||||||
defer sess.Close()
|
return newIssueLabel(tx, issue, label)
|
||||||
if err = sess.Begin(); err != nil {
|
})
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = newIssueLabel(sess, issue, label); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return sess.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newIssueLabels(e *xorm.Session, issue *Issue, labels []*Label) (err error) {
|
func newIssueLabels(tx *gorm.DB, issue *Issue, labels []*Label) (err error) {
|
||||||
for i := range labels {
|
for i := range labels {
|
||||||
if hasIssueLabel(e, issue.ID, labels[i].ID) {
|
if hasIssueLabel(tx, issue.ID, labels[i].ID) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = newIssueLabel(e, issue, labels[i]); err != nil {
|
if err = newIssueLabel(tx, issue, labels[i]); err != nil {
|
||||||
return errors.Newf("newIssueLabel: %v", err)
|
return errors.Newf("newIssueLabel: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -327,34 +316,23 @@ func newIssueLabels(e *xorm.Session, issue *Issue, labels []*Label) (err error)
|
|||||||
|
|
||||||
// NewIssueLabels creates a list of issue-label relations.
|
// NewIssueLabels creates a list of issue-label relations.
|
||||||
func NewIssueLabels(issue *Issue, labels []*Label) (err error) {
|
func NewIssueLabels(issue *Issue, labels []*Label) (err error) {
|
||||||
sess := x.NewSession()
|
return db.Transaction(func(tx *gorm.DB) error {
|
||||||
defer sess.Close()
|
return newIssueLabels(tx, issue, labels)
|
||||||
if err = sess.Begin(); err != nil {
|
})
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = newIssueLabels(sess, issue, labels); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return sess.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getIssueLabels(e Engine, issueID int64) ([]*IssueLabel, error) {
|
func getIssueLabels(tx *gorm.DB, issueID int64) ([]*IssueLabel, error) {
|
||||||
issueLabels := make([]*IssueLabel, 0, 10)
|
issueLabels := make([]*IssueLabel, 0, 10)
|
||||||
return issueLabels, e.Where("issue_id=?", issueID).Asc("label_id").Find(&issueLabels)
|
return issueLabels, tx.Where("issue_id = ?", issueID).Order("label_id ASC").Find(&issueLabels).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetIssueLabels returns all issue-label relations of given issue by ID.
|
// GetIssueLabels returns all issue-label relations of given issue by ID.
|
||||||
func GetIssueLabels(issueID int64) ([]*IssueLabel, error) {
|
func GetIssueLabels(issueID int64) ([]*IssueLabel, error) {
|
||||||
return getIssueLabels(x, issueID)
|
return getIssueLabels(db, issueID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteIssueLabel(e *xorm.Session, issue *Issue, label *Label) (err error) {
|
func deleteIssueLabel(tx *gorm.DB, issue *Issue, label *Label) (err error) {
|
||||||
if _, err = e.Delete(&IssueLabel{
|
if err = tx.Where("issue_id = ? AND label_id = ?", issue.ID, label.ID).Delete(&IssueLabel{}).Error; err != nil {
|
||||||
IssueID: issue.ID,
|
|
||||||
LabelID: label.ID,
|
|
||||||
}); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -362,7 +340,7 @@ func deleteIssueLabel(e *xorm.Session, issue *Issue, label *Label) (err error) {
|
|||||||
if issue.IsClosed {
|
if issue.IsClosed {
|
||||||
label.NumClosedIssues--
|
label.NumClosedIssues--
|
||||||
}
|
}
|
||||||
if err = updateLabel(e, label); err != nil {
|
if err = updateLabel(tx, label); err != nil {
|
||||||
return errors.Newf("updateLabel: %v", err)
|
return errors.Newf("updateLabel: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -377,15 +355,7 @@ func deleteIssueLabel(e *xorm.Session, issue *Issue, label *Label) (err error) {
|
|||||||
|
|
||||||
// DeleteIssueLabel deletes issue-label relation.
|
// DeleteIssueLabel deletes issue-label relation.
|
||||||
func DeleteIssueLabel(issue *Issue, label *Label) (err error) {
|
func DeleteIssueLabel(issue *Issue, label *Label) (err error) {
|
||||||
sess := x.NewSession()
|
return db.Transaction(func(tx *gorm.DB) error {
|
||||||
defer sess.Close()
|
return deleteIssueLabel(tx, issue, label)
|
||||||
if err = sess.Begin(); err != nil {
|
})
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = deleteIssueLabel(sess, issue, label); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return sess.Commit()
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ func mailIssueCommentToParticipants(issue *Issue, doer *User, mentions []string)
|
|||||||
// and mentioned people.
|
// and mentioned people.
|
||||||
func (issue *Issue) MailParticipants() (err error) {
|
func (issue *Issue) MailParticipants() (err error) {
|
||||||
mentions := markup.FindAllMentions(issue.Content)
|
mentions := markup.FindAllMentions(issue.Content)
|
||||||
if err = updateIssueMentions(x, issue.ID, mentions); err != nil {
|
if err = updateIssueMentions(db, issue.ID, mentions); err != nil {
|
||||||
return errors.Newf("UpdateIssueMentions [%d]: %v", issue.ID, err)
|
return errors.Newf("UpdateIssueMentions [%d]: %v", issue.ID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
"gorm.io/gorm/logger"
|
"gorm.io/gorm/logger"
|
||||||
|
_ "modernc.org/sqlite"
|
||||||
log "unknwon.dev/clog/v2"
|
log "unknwon.dev/clog/v2"
|
||||||
|
|
||||||
"gogs.io/gogs/internal/conf"
|
"gogs.io/gogs/internal/conf"
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"gorm.io/gorm/logger"
|
"gorm.io/gorm/logger"
|
||||||
|
_ "modernc.org/sqlite"
|
||||||
log "unknwon.dev/clog/v2"
|
log "unknwon.dev/clog/v2"
|
||||||
|
|
||||||
"gogs.io/gogs/internal/testutil"
|
"gogs.io/gogs/internal/testutil"
|
||||||
|
|||||||
@@ -4,11 +4,10 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
log "unknwon.dev/clog/v2"
|
|
||||||
"xorm.io/xorm"
|
|
||||||
|
|
||||||
"github.com/cockroachdb/errors"
|
"github.com/cockroachdb/errors"
|
||||||
api "github.com/gogs/go-gogs-client"
|
api "github.com/gogs/go-gogs-client"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
log "unknwon.dev/clog/v2"
|
||||||
|
|
||||||
"gogs.io/gogs/internal/conf"
|
"gogs.io/gogs/internal/conf"
|
||||||
"gogs.io/gogs/internal/errutil"
|
"gogs.io/gogs/internal/errutil"
|
||||||
@@ -17,29 +16,30 @@ import (
|
|||||||
// Milestone represents a milestone of repository.
|
// Milestone represents a milestone of repository.
|
||||||
type Milestone struct {
|
type Milestone struct {
|
||||||
ID int64
|
ID int64
|
||||||
RepoID int64 `xorm:"INDEX"`
|
RepoID int64 `gorm:"index"`
|
||||||
Name string
|
Name string
|
||||||
Content string `xorm:"TEXT"`
|
Content string `gorm:"type:text"`
|
||||||
RenderedContent string `xorm:"-" json:"-" gorm:"-"`
|
RenderedContent string `gorm:"-" json:"-"`
|
||||||
IsClosed bool
|
IsClosed bool
|
||||||
NumIssues int
|
NumIssues int
|
||||||
NumClosedIssues int
|
NumClosedIssues int
|
||||||
NumOpenIssues int `xorm:"-" json:"-" gorm:"-"`
|
NumOpenIssues int `gorm:"-" json:"-"`
|
||||||
Completeness int // Percentage(1-100).
|
Completeness int // Percentage(1-100).
|
||||||
IsOverDue bool `xorm:"-" json:"-" gorm:"-"`
|
IsOverDue bool `gorm:"-" json:"-"`
|
||||||
|
|
||||||
DeadlineString string `xorm:"-" json:"-" gorm:"-"`
|
DeadlineString string `gorm:"-" json:"-"`
|
||||||
Deadline time.Time `xorm:"-" json:"-" gorm:"-"`
|
Deadline time.Time `gorm:"-" json:"-"`
|
||||||
DeadlineUnix int64
|
DeadlineUnix int64
|
||||||
ClosedDate time.Time `xorm:"-" json:"-" gorm:"-"`
|
ClosedDate time.Time `gorm:"-" json:"-"`
|
||||||
ClosedDateUnix int64
|
ClosedDateUnix int64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Milestone) BeforeInsert() {
|
func (m *Milestone) BeforeCreate(tx *gorm.DB) error {
|
||||||
m.DeadlineUnix = m.Deadline.Unix()
|
m.DeadlineUnix = m.Deadline.Unix()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Milestone) BeforeUpdate() {
|
func (m *Milestone) BeforeUpdate(tx *gorm.DB) error {
|
||||||
if m.NumIssues > 0 {
|
if m.NumIssues > 0 {
|
||||||
m.Completeness = m.NumClosedIssues * 100 / m.NumIssues
|
m.Completeness = m.NumClosedIssues * 100 / m.NumIssues
|
||||||
} else {
|
} else {
|
||||||
@@ -48,27 +48,22 @@ func (m *Milestone) BeforeUpdate() {
|
|||||||
|
|
||||||
m.DeadlineUnix = m.Deadline.Unix()
|
m.DeadlineUnix = m.Deadline.Unix()
|
||||||
m.ClosedDateUnix = m.ClosedDate.Unix()
|
m.ClosedDateUnix = m.ClosedDate.Unix()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Milestone) AfterSet(colName string, _ xorm.Cell) {
|
func (m *Milestone) AfterFind(tx *gorm.DB) error {
|
||||||
switch colName {
|
m.NumOpenIssues = m.NumIssues - m.NumClosedIssues
|
||||||
case "num_closed_issues":
|
|
||||||
m.NumOpenIssues = m.NumIssues - m.NumClosedIssues
|
|
||||||
|
|
||||||
case "deadline_unix":
|
|
||||||
m.Deadline = time.Unix(m.DeadlineUnix, 0).Local()
|
|
||||||
if m.Deadline.Year() == 9999 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
|
m.Deadline = time.Unix(m.DeadlineUnix, 0).Local()
|
||||||
|
if m.Deadline.Year() != 9999 {
|
||||||
m.DeadlineString = m.Deadline.Format("2006-01-02")
|
m.DeadlineString = m.Deadline.Format("2006-01-02")
|
||||||
if time.Now().Local().After(m.Deadline) {
|
if time.Now().Local().After(m.Deadline) {
|
||||||
m.IsOverDue = true
|
m.IsOverDue = true
|
||||||
}
|
}
|
||||||
|
|
||||||
case "closed_date_unix":
|
|
||||||
m.ClosedDate = time.Unix(m.ClosedDateUnix, 0).Local()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m.ClosedDate = time.Unix(m.ClosedDateUnix, 0).Local()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// State returns string representation of milestone status.
|
// State returns string representation of milestone status.
|
||||||
@@ -102,30 +97,24 @@ func (m *Milestone) APIFormat() *api.Milestone {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *Milestone) CountIssues(isClosed, includePulls bool) int64 {
|
func (m *Milestone) CountIssues(isClosed, includePulls bool) int64 {
|
||||||
sess := x.Where("milestone_id = ?", m.ID).And("is_closed = ?", isClosed)
|
query := db.Model(new(Issue)).Where("milestone_id = ? AND is_closed = ?", m.ID, isClosed)
|
||||||
if !includePulls {
|
if !includePulls {
|
||||||
sess.And("is_pull = ?", false)
|
query = query.Where("is_pull = ?", false)
|
||||||
}
|
}
|
||||||
count, _ := sess.Count(new(Issue))
|
var count int64
|
||||||
|
query.Count(&count)
|
||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMilestone creates new milestone of repository.
|
// NewMilestone creates new milestone of repository.
|
||||||
func NewMilestone(m *Milestone) (err error) {
|
func NewMilestone(m *Milestone) (err error) {
|
||||||
sess := x.NewSession()
|
return db.Transaction(func(tx *gorm.DB) error {
|
||||||
defer sess.Close()
|
if err := tx.Create(m).Error; err != nil {
|
||||||
if err = sess.Begin(); err != nil {
|
return err
|
||||||
return err
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = sess.Insert(m); err != nil {
|
return tx.Exec("UPDATE `repository` SET num_milestones = num_milestones + 1 WHERE id = ?", m.RepoID).Error
|
||||||
return err
|
})
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = sess.Exec("UPDATE `repository` SET num_milestones = num_milestones + 1 WHERE id = ?", m.RepoID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return sess.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ errutil.NotFound = (*ErrMilestoneNotExist)(nil)
|
var _ errutil.NotFound = (*ErrMilestoneNotExist)(nil)
|
||||||
@@ -147,74 +136,73 @@ func (ErrMilestoneNotExist) NotFound() bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func getMilestoneByRepoID(e Engine, repoID, id int64) (*Milestone, error) {
|
func getMilestoneByRepoID(e *gorm.DB, repoID, id int64) (*Milestone, error) {
|
||||||
m := &Milestone{
|
m := &Milestone{}
|
||||||
ID: id,
|
err := e.Where("id = ? AND repo_id = ?", id, repoID).First(m).Error
|
||||||
RepoID: repoID,
|
|
||||||
}
|
|
||||||
has, err := e.Get(m)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil, ErrMilestoneNotExist{args: map[string]any{"repoID": repoID, "milestoneID": id}}
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if !has {
|
|
||||||
return nil, ErrMilestoneNotExist{args: map[string]any{"repoID": repoID, "milestoneID": id}}
|
|
||||||
}
|
}
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetWebhookByRepoID returns the milestone in a repository.
|
// GetWebhookByRepoID returns the milestone in a repository.
|
||||||
func GetMilestoneByRepoID(repoID, id int64) (*Milestone, error) {
|
func GetMilestoneByRepoID(repoID, id int64) (*Milestone, error) {
|
||||||
return getMilestoneByRepoID(x, repoID, id)
|
return getMilestoneByRepoID(db, repoID, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMilestonesByRepoID returns all milestones of a repository.
|
// GetMilestonesByRepoID returns all milestones of a repository.
|
||||||
func GetMilestonesByRepoID(repoID int64) ([]*Milestone, error) {
|
func GetMilestonesByRepoID(repoID int64) ([]*Milestone, error) {
|
||||||
miles := make([]*Milestone, 0, 10)
|
miles := make([]*Milestone, 0, 10)
|
||||||
return miles, x.Where("repo_id = ?", repoID).Find(&miles)
|
return miles, db.Where("repo_id = ?", repoID).Find(&miles).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMilestones returns a list of milestones of given repository and status.
|
// GetMilestones returns a list of milestones of given repository and status.
|
||||||
func GetMilestones(repoID int64, page int, isClosed bool) ([]*Milestone, error) {
|
func GetMilestones(repoID int64, page int, isClosed bool) ([]*Milestone, error) {
|
||||||
miles := make([]*Milestone, 0, conf.UI.IssuePagingNum)
|
miles := make([]*Milestone, 0, conf.UI.IssuePagingNum)
|
||||||
sess := x.Where("repo_id = ? AND is_closed = ?", repoID, isClosed)
|
query := db.Where("repo_id = ? AND is_closed = ?", repoID, isClosed)
|
||||||
if page > 0 {
|
if page > 0 {
|
||||||
sess = sess.Limit(conf.UI.IssuePagingNum, (page-1)*conf.UI.IssuePagingNum)
|
query = query.Limit(conf.UI.IssuePagingNum).Offset((page - 1) * conf.UI.IssuePagingNum)
|
||||||
}
|
}
|
||||||
return miles, sess.Find(&miles)
|
return miles, query.Find(&miles).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateMilestone(e Engine, m *Milestone) error {
|
func updateMilestone(e *gorm.DB, m *Milestone) error {
|
||||||
_, err := e.ID(m.ID).AllCols().Update(m)
|
return e.Model(m).Where("id = ?", m.ID).Updates(m).Error
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateMilestone updates information of given milestone.
|
// UpdateMilestone updates information of given milestone.
|
||||||
func UpdateMilestone(m *Milestone) error {
|
func UpdateMilestone(m *Milestone) error {
|
||||||
return updateMilestone(x, m)
|
return updateMilestone(db, m)
|
||||||
}
|
}
|
||||||
|
|
||||||
func countRepoMilestones(e Engine, repoID int64) int64 {
|
func countRepoMilestones(e *gorm.DB, repoID int64) int64 {
|
||||||
count, _ := e.Where("repo_id=?", repoID).Count(new(Milestone))
|
var count int64
|
||||||
|
e.Model(new(Milestone)).Where("repo_id = ?", repoID).Count(&count)
|
||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
// CountRepoMilestones returns number of milestones in given repository.
|
// CountRepoMilestones returns number of milestones in given repository.
|
||||||
func CountRepoMilestones(repoID int64) int64 {
|
func CountRepoMilestones(repoID int64) int64 {
|
||||||
return countRepoMilestones(x, repoID)
|
return countRepoMilestones(db, repoID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func countRepoClosedMilestones(e Engine, repoID int64) int64 {
|
func countRepoClosedMilestones(e *gorm.DB, repoID int64) int64 {
|
||||||
closed, _ := e.Where("repo_id=? AND is_closed=?", repoID, true).Count(new(Milestone))
|
var count int64
|
||||||
return closed
|
e.Model(new(Milestone)).Where("repo_id = ? AND is_closed = ?", repoID, true).Count(&count)
|
||||||
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
// CountRepoClosedMilestones returns number of closed milestones in given repository.
|
// CountRepoClosedMilestones returns number of closed milestones in given repository.
|
||||||
func CountRepoClosedMilestones(repoID int64) int64 {
|
func CountRepoClosedMilestones(repoID int64) int64 {
|
||||||
return countRepoClosedMilestones(x, repoID)
|
return countRepoClosedMilestones(db, repoID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MilestoneStats returns number of open and closed milestones of given repository.
|
// MilestoneStats returns number of open and closed milestones of given repository.
|
||||||
func MilestoneStats(repoID int64) (open, closed int64) {
|
func MilestoneStats(repoID int64) (open, closed int64) {
|
||||||
open, _ = x.Where("repo_id=? AND is_closed=?", repoID, false).Count(new(Milestone))
|
db.Model(new(Milestone)).Where("repo_id = ? AND is_closed = ?", repoID, false).Count(&open)
|
||||||
return open, CountRepoClosedMilestones(repoID)
|
return open, CountRepoClosedMilestones(repoID)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,26 +215,19 @@ func ChangeMilestoneStatus(m *Milestone, isClosed bool) (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
sess := x.NewSession()
|
return db.Transaction(func(tx *gorm.DB) error {
|
||||||
defer sess.Close()
|
m.IsClosed = isClosed
|
||||||
if err = sess.Begin(); err != nil {
|
if err := updateMilestone(tx, m); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
m.IsClosed = isClosed
|
repo.NumMilestones = int(countRepoMilestones(tx, repo.ID))
|
||||||
if err = updateMilestone(sess, m); err != nil {
|
repo.NumClosedMilestones = int(countRepoClosedMilestones(tx, repo.ID))
|
||||||
return err
|
return tx.Model(repo).Where("id = ?", repo.ID).Updates(repo).Error
|
||||||
}
|
})
|
||||||
|
|
||||||
repo.NumMilestones = int(countRepoMilestones(sess, repo.ID))
|
|
||||||
repo.NumClosedMilestones = int(countRepoClosedMilestones(sess, repo.ID))
|
|
||||||
if _, err = sess.ID(repo.ID).AllCols().Update(repo); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return sess.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func changeMilestoneIssueStats(e *xorm.Session, issue *Issue) error {
|
func changeMilestoneIssueStats(e *gorm.DB, issue *Issue) error {
|
||||||
if issue.MilestoneID == 0 {
|
if issue.MilestoneID == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -270,20 +251,12 @@ func changeMilestoneIssueStats(e *xorm.Session, issue *Issue) error {
|
|||||||
// ChangeMilestoneIssueStats updates the open/closed issues counter and progress
|
// ChangeMilestoneIssueStats updates the open/closed issues counter and progress
|
||||||
// for the milestone associated with the given issue.
|
// for the milestone associated with the given issue.
|
||||||
func ChangeMilestoneIssueStats(issue *Issue) (err error) {
|
func ChangeMilestoneIssueStats(issue *Issue) (err error) {
|
||||||
sess := x.NewSession()
|
return db.Transaction(func(tx *gorm.DB) error {
|
||||||
defer sess.Close()
|
return changeMilestoneIssueStats(tx, issue)
|
||||||
if err = sess.Begin(); err != nil {
|
})
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = changeMilestoneIssueStats(sess, issue); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return sess.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func changeMilestoneAssign(e *xorm.Session, issue *Issue, oldMilestoneID int64) error {
|
func changeMilestoneAssign(e *gorm.DB, issue *Issue, oldMilestoneID int64) error {
|
||||||
if oldMilestoneID > 0 {
|
if oldMilestoneID > 0 {
|
||||||
m, err := getMilestoneByRepoID(e, issue.RepoID, oldMilestoneID)
|
m, err := getMilestoneByRepoID(e, issue.RepoID, oldMilestoneID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -297,7 +270,9 @@ func changeMilestoneAssign(e *xorm.Session, issue *Issue, oldMilestoneID int64)
|
|||||||
|
|
||||||
if err = updateMilestone(e, m); err != nil {
|
if err = updateMilestone(e, m); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if _, err = e.Exec("UPDATE `issue_user` SET milestone_id = 0 WHERE issue_id = ?", issue.ID); err != nil {
|
}
|
||||||
|
|
||||||
|
if err = e.Exec("UPDATE `issue_user` SET milestone_id = 0 WHERE issue_id = ?", issue.ID).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -317,7 +292,9 @@ func changeMilestoneAssign(e *xorm.Session, issue *Issue, oldMilestoneID int64)
|
|||||||
|
|
||||||
if err = updateMilestone(e, m); err != nil {
|
if err = updateMilestone(e, m); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if _, err = e.Exec("UPDATE `issue_user` SET milestone_id = ? WHERE issue_id = ?", m.ID, issue.ID); err != nil {
|
}
|
||||||
|
|
||||||
|
if err = e.Exec("UPDATE `issue_user` SET milestone_id = ? WHERE issue_id = ?", m.ID, issue.ID).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -329,18 +306,11 @@ func changeMilestoneAssign(e *xorm.Session, issue *Issue, oldMilestoneID int64)
|
|||||||
|
|
||||||
// ChangeMilestoneAssign changes assignment of milestone for issue.
|
// ChangeMilestoneAssign changes assignment of milestone for issue.
|
||||||
func ChangeMilestoneAssign(doer *User, issue *Issue, oldMilestoneID int64) (err error) {
|
func ChangeMilestoneAssign(doer *User, issue *Issue, oldMilestoneID int64) (err error) {
|
||||||
sess := x.NewSession()
|
err = db.Transaction(func(tx *gorm.DB) error {
|
||||||
defer sess.Close()
|
return changeMilestoneAssign(tx, issue, oldMilestoneID)
|
||||||
if err = sess.Begin(); err != nil {
|
})
|
||||||
return err
|
if err != nil {
|
||||||
}
|
return errors.Newf("transaction: %v", err)
|
||||||
|
|
||||||
if err = changeMilestoneAssign(sess, issue, oldMilestoneID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = sess.Commit(); err != nil {
|
|
||||||
return errors.Newf("commit: %v", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var hookAction api.HookIssueAction
|
var hookAction api.HookIssueAction
|
||||||
@@ -394,26 +364,21 @@ func DeleteMilestoneOfRepoByID(repoID, id int64) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
sess := x.NewSession()
|
return db.Transaction(func(tx *gorm.DB) error {
|
||||||
defer sess.Close()
|
if err := tx.Where("id = ?", m.ID).Delete(new(Milestone)).Error; err != nil {
|
||||||
if err = sess.Begin(); err != nil {
|
return err
|
||||||
return err
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = sess.ID(m.ID).Delete(new(Milestone)); err != nil {
|
repo.NumMilestones = int(countRepoMilestones(tx, repo.ID))
|
||||||
return err
|
repo.NumClosedMilestones = int(countRepoClosedMilestones(tx, repo.ID))
|
||||||
}
|
if err := tx.Model(repo).Where("id = ?", repo.ID).Updates(repo).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
repo.NumMilestones = int(countRepoMilestones(sess, repo.ID))
|
if err := tx.Exec("UPDATE `issue` SET milestone_id = 0 WHERE milestone_id = ?", m.ID).Error; err != nil {
|
||||||
repo.NumClosedMilestones = int(countRepoClosedMilestones(sess, repo.ID))
|
return err
|
||||||
if _, err = sess.ID(repo.ID).AllCols().Update(repo); err != nil {
|
}
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = sess.Exec("UPDATE `issue` SET milestone_id = 0 WHERE milestone_id = ?", m.ID); err != nil {
|
return tx.Exec("UPDATE `issue_user` SET milestone_id = 0 WHERE milestone_id = ?", m.ID).Error
|
||||||
return err
|
})
|
||||||
} else if _, err = sess.Exec("UPDATE `issue_user` SET milestone_id = 0 WHERE milestone_id = ?", m.ID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return sess.Commit()
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,12 +8,11 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/cockroachdb/errors"
|
"github.com/cockroachdb/errors"
|
||||||
|
"github.com/gogs/git-module"
|
||||||
"github.com/unknwon/com"
|
"github.com/unknwon/com"
|
||||||
"gopkg.in/ini.v1"
|
"gopkg.in/ini.v1"
|
||||||
|
"gorm.io/gorm"
|
||||||
log "unknwon.dev/clog/v2"
|
log "unknwon.dev/clog/v2"
|
||||||
"xorm.io/xorm"
|
|
||||||
|
|
||||||
"github.com/gogs/git-module"
|
|
||||||
|
|
||||||
"gogs.io/gogs/internal/conf"
|
"gogs.io/gogs/internal/conf"
|
||||||
"gogs.io/gogs/internal/process"
|
"gogs.io/gogs/internal/process"
|
||||||
@@ -41,41 +40,41 @@ func (err MirrorNotExist) Error() string {
|
|||||||
type Mirror struct {
|
type Mirror struct {
|
||||||
ID int64
|
ID int64
|
||||||
RepoID int64
|
RepoID int64
|
||||||
Repo *Repository `xorm:"-" json:"-" gorm:"-"`
|
Repo *Repository `gorm:"-" json:"-"`
|
||||||
Interval int // Hour.
|
Interval int // Hour.
|
||||||
EnablePrune bool `xorm:"NOT NULL DEFAULT true"`
|
EnablePrune bool `gorm:"not null;default:true"`
|
||||||
|
|
||||||
// Last and next sync time of Git data from upstream
|
// Last and next sync time of Git data from upstream
|
||||||
LastSync time.Time `xorm:"-" json:"-" gorm:"-"`
|
LastSync time.Time `gorm:"-" json:"-"`
|
||||||
LastSyncUnix int64 `xorm:"updated_unix"`
|
LastSyncUnix int64 `gorm:"column:updated_unix"`
|
||||||
NextSync time.Time `xorm:"-" json:"-" gorm:"-"`
|
NextSync time.Time `gorm:"-" json:"-"`
|
||||||
NextSyncUnix int64 `xorm:"next_update_unix"`
|
NextSyncUnix int64 `gorm:"column:next_update_unix"`
|
||||||
|
|
||||||
address string `xorm:"-"`
|
address string `gorm:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Mirror) BeforeInsert() {
|
func (m *Mirror) BeforeCreate(tx *gorm.DB) error {
|
||||||
m.NextSyncUnix = m.NextSync.Unix()
|
m.NextSyncUnix = m.NextSync.Unix()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Mirror) BeforeUpdate() {
|
func (m *Mirror) BeforeUpdate(tx *gorm.DB) error {
|
||||||
m.LastSyncUnix = m.LastSync.Unix()
|
m.LastSyncUnix = m.LastSync.Unix()
|
||||||
m.NextSyncUnix = m.NextSync.Unix()
|
m.NextSyncUnix = m.NextSync.Unix()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Mirror) AfterSet(colName string, _ xorm.Cell) {
|
func (m *Mirror) AfterFind(tx *gorm.DB) error {
|
||||||
var err error
|
if m.RepoID > 0 {
|
||||||
switch colName {
|
var err error
|
||||||
case "repo_id":
|
|
||||||
m.Repo, err = GetRepositoryByID(m.RepoID)
|
m.Repo, err = GetRepositoryByID(m.RepoID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("GetRepositoryByID [%d]: %v", m.ID, err)
|
log.Error("GetRepositoryByID [%d]: %v", m.ID, err)
|
||||||
}
|
}
|
||||||
case "updated_unix":
|
|
||||||
m.LastSync = time.Unix(m.LastSyncUnix, 0).Local()
|
|
||||||
case "next_update_unix":
|
|
||||||
m.NextSync = time.Unix(m.NextSyncUnix, 0).Local()
|
|
||||||
}
|
}
|
||||||
|
m.LastSync = time.Unix(m.LastSyncUnix, 0).Local()
|
||||||
|
m.NextSync = time.Unix(m.NextSyncUnix, 0).Local()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ScheduleNextSync calculates and sets next sync time based on repository mirror setting.
|
// ScheduleNextSync calculates and sets next sync time based on repository mirror setting.
|
||||||
@@ -277,34 +276,33 @@ func (m *Mirror) runSync() ([]*mirrorSyncResult, bool) {
|
|||||||
return parseRemoteUpdateOutput(output), true
|
return parseRemoteUpdateOutput(output), true
|
||||||
}
|
}
|
||||||
|
|
||||||
func getMirrorByRepoID(e Engine, repoID int64) (*Mirror, error) {
|
func getMirrorByRepoID(e *gorm.DB, repoID int64) (*Mirror, error) {
|
||||||
m := &Mirror{RepoID: repoID}
|
m := &Mirror{}
|
||||||
has, err := e.Get(m)
|
err := e.Where("repo_id = ?", repoID).First(m).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil, MirrorNotExist{RepoID: repoID}
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if !has {
|
|
||||||
return nil, MirrorNotExist{RepoID: repoID}
|
|
||||||
}
|
}
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMirrorByRepoID returns mirror information of a repository.
|
// GetMirrorByRepoID returns mirror information of a repository.
|
||||||
func GetMirrorByRepoID(repoID int64) (*Mirror, error) {
|
func GetMirrorByRepoID(repoID int64) (*Mirror, error) {
|
||||||
return getMirrorByRepoID(x, repoID)
|
return getMirrorByRepoID(db, repoID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateMirror(e Engine, m *Mirror) error {
|
func updateMirror(e *gorm.DB, m *Mirror) error {
|
||||||
_, err := e.ID(m.ID).AllCols().Update(m)
|
return e.Model(m).Where("id = ?", m.ID).Updates(m).Error
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func UpdateMirror(m *Mirror) error {
|
func UpdateMirror(m *Mirror) error {
|
||||||
return updateMirror(x, m)
|
return updateMirror(db, m)
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeleteMirrorByRepoID(repoID int64) error {
|
func DeleteMirrorByRepoID(repoID int64) error {
|
||||||
_, err := x.Delete(&Mirror{RepoID: repoID})
|
return db.Where("repo_id = ?", repoID).Delete(&Mirror{}).Error
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MirrorUpdate checks and updates mirror repositories.
|
// MirrorUpdate checks and updates mirror repositories.
|
||||||
@@ -317,17 +315,18 @@ func MirrorUpdate() {
|
|||||||
|
|
||||||
log.Trace("Doing: MirrorUpdate")
|
log.Trace("Doing: MirrorUpdate")
|
||||||
|
|
||||||
if err := x.Where("next_update_unix<=?", time.Now().Unix()).Iterate(new(Mirror), func(idx int, bean any) error {
|
var mirrors []*Mirror
|
||||||
m := bean.(*Mirror)
|
if err := db.Where("next_update_unix <= ?", time.Now().Unix()).Find(&mirrors).Error; err != nil {
|
||||||
|
log.Error("MirrorUpdate: find mirrors: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, m := range mirrors {
|
||||||
if m.Repo == nil {
|
if m.Repo == nil {
|
||||||
log.Error("Disconnected mirror repository found: %d", m.ID)
|
log.Error("Disconnected mirror repository found: %d", m.ID)
|
||||||
return nil
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
MirrorQueue.Add(m.RepoID)
|
MirrorQueue.Add(m.RepoID)
|
||||||
return nil
|
|
||||||
}); err != nil {
|
|
||||||
log.Error("MirrorUpdate: %v", err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -454,7 +453,7 @@ func SyncMirrors() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = x.Exec("UPDATE mirror SET updated_unix = ? WHERE repo_id = ?", time.Now().Unix(), m.RepoID); err != nil {
|
if err = db.Exec("UPDATE mirror SET updated_unix = ? WHERE repo_id = ?", time.Now().Unix(), m.RepoID).Error; err != nil {
|
||||||
log.Error("Update 'mirror.updated_unix' [%d]: %v", m.RepoID, err)
|
log.Error("Update 'mirror.updated_unix' [%d]: %v", m.RepoID, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -469,7 +468,7 @@ func SyncMirrors() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = x.Exec("UPDATE repository SET updated_unix = ? WHERE id = ?", latestCommitTime.Unix(), m.RepoID); err != nil {
|
if err = db.Exec("UPDATE repository SET updated_unix = ? WHERE id = ?", latestCommitTime.Unix(), m.RepoID).Error; err != nil {
|
||||||
log.Error("Update 'repository.updated_unix' [%d]: %v", m.RepoID, err)
|
log.Error("Update 'repository.updated_unix' [%d]: %v", m.RepoID, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package database
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
@@ -11,44 +10,23 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/cockroachdb/errors"
|
"github.com/cockroachdb/errors"
|
||||||
"github.com/glebarez/go-sqlite"
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
"gorm.io/gorm/logger"
|
"gorm.io/gorm/logger"
|
||||||
|
"gorm.io/gorm/schema"
|
||||||
log "unknwon.dev/clog/v2"
|
log "unknwon.dev/clog/v2"
|
||||||
"xorm.io/core"
|
|
||||||
"xorm.io/xorm"
|
|
||||||
|
|
||||||
"gogs.io/gogs/internal/conf"
|
"gogs.io/gogs/internal/conf"
|
||||||
"gogs.io/gogs/internal/database/migrations"
|
"gogs.io/gogs/internal/database/migrations"
|
||||||
"gogs.io/gogs/internal/dbutil"
|
"gogs.io/gogs/internal/dbutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Engine represents a XORM engine or session.
|
|
||||||
type Engine interface {
|
|
||||||
Delete(any) (int64, error)
|
|
||||||
Exec(...any) (sql.Result, error)
|
|
||||||
Find(any, ...any) error
|
|
||||||
Get(any) (bool, error)
|
|
||||||
ID(any) *xorm.Session
|
|
||||||
In(string, ...any) *xorm.Session
|
|
||||||
Insert(...any) (int64, error)
|
|
||||||
InsertOne(any) (int64, error)
|
|
||||||
Iterate(any, xorm.IterFunc) error
|
|
||||||
Sql(string, ...any) *xorm.Session
|
|
||||||
Table(any) *xorm.Session
|
|
||||||
Where(any, ...any) *xorm.Session
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
x *xorm.Engine
|
db *gorm.DB
|
||||||
legacyTables []any
|
legacyTables []any
|
||||||
HasEngine bool
|
HasEngine bool
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
// Register the pure-Go SQLite driver as "sqlite3" for XORM compatibility.
|
|
||||||
sql.Register("sqlite3", &sqlite.Driver{})
|
|
||||||
|
|
||||||
legacyTables = append(legacyTables,
|
legacyTables = append(legacyTables,
|
||||||
new(User), new(PublicKey), new(TwoFactor), new(TwoFactorRecoveryCode),
|
new(User), new(PublicKey), new(TwoFactor), new(TwoFactorRecoveryCode),
|
||||||
new(Repository), new(DeployKey), new(Collaboration), new(Upload),
|
new(Repository), new(DeployKey), new(Collaboration), new(Upload),
|
||||||
@@ -59,93 +37,90 @@ func init() {
|
|||||||
new(ProtectBranch), new(ProtectBranchWhitelist),
|
new(ProtectBranch), new(ProtectBranchWhitelist),
|
||||||
new(Team), new(OrgUser), new(TeamUser), new(TeamRepo),
|
new(Team), new(OrgUser), new(TeamUser), new(TeamRepo),
|
||||||
)
|
)
|
||||||
|
|
||||||
gonicNames := []string{"SSL"}
|
|
||||||
for _, name := range gonicNames {
|
|
||||||
core.LintGonicMapper[name] = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getEngine() (*xorm.Engine, error) {
|
func getGormDB(gormLogger logger.Writer) (*gorm.DB, error) {
|
||||||
Param := "?"
|
if conf.Database.Type == "sqlite3" {
|
||||||
if strings.Contains(conf.Database.Name, Param) {
|
|
||||||
Param = "&"
|
|
||||||
}
|
|
||||||
|
|
||||||
driver := conf.Database.Type
|
|
||||||
connStr := ""
|
|
||||||
switch conf.Database.Type {
|
|
||||||
case "mysql":
|
|
||||||
conf.UseMySQL = true
|
|
||||||
if conf.Database.Host[0] == '/' { // looks like a unix socket
|
|
||||||
connStr = fmt.Sprintf("%s:%s@unix(%s)/%s%scharset=utf8mb4&parseTime=true",
|
|
||||||
conf.Database.User, conf.Database.Password, conf.Database.Host, conf.Database.Name, Param)
|
|
||||||
} else {
|
|
||||||
connStr = fmt.Sprintf("%s:%s@tcp(%s)/%s%scharset=utf8mb4&parseTime=true",
|
|
||||||
conf.Database.User, conf.Database.Password, conf.Database.Host, conf.Database.Name, Param)
|
|
||||||
}
|
|
||||||
engineParams := map[string]string{"rowFormat": "DYNAMIC"}
|
|
||||||
return xorm.NewEngineWithParams(conf.Database.Type, connStr, engineParams)
|
|
||||||
|
|
||||||
case "postgres":
|
|
||||||
conf.UsePostgreSQL = true
|
|
||||||
host, port := dbutil.ParsePostgreSQLHostPort(conf.Database.Host)
|
|
||||||
connStr = fmt.Sprintf("user='%s' password='%s' host='%s' port='%s' dbname='%s' sslmode='%s' search_path='%s'",
|
|
||||||
conf.Database.User, conf.Database.Password, host, port, conf.Database.Name, conf.Database.SSLMode, conf.Database.Schema)
|
|
||||||
driver = "pgx"
|
|
||||||
|
|
||||||
case "mssql":
|
|
||||||
conf.UseMSSQL = true
|
|
||||||
host, port := dbutil.ParseMSSQLHostPort(conf.Database.Host)
|
|
||||||
connStr = fmt.Sprintf("server=%s; port=%s; database=%s; user id=%s; password=%s;", host, port, conf.Database.Name, conf.Database.User, conf.Database.Password)
|
|
||||||
|
|
||||||
case "sqlite3":
|
|
||||||
if err := os.MkdirAll(path.Dir(conf.Database.Path), os.ModePerm); err != nil {
|
if err := os.MkdirAll(path.Dir(conf.Database.Path), os.ModePerm); err != nil {
|
||||||
return nil, errors.Newf("create directories: %v", err)
|
return nil, errors.Newf("create directories: %v", err)
|
||||||
}
|
}
|
||||||
conf.UseSQLite3 = true
|
|
||||||
connStr = "file:" + conf.Database.Path + "?cache=shared&mode=rwc"
|
|
||||||
|
|
||||||
default:
|
|
||||||
return nil, errors.Newf("unknown database type: %s", conf.Database.Type)
|
|
||||||
}
|
}
|
||||||
return xorm.NewEngine(driver, connStr)
|
|
||||||
|
level := logger.Info
|
||||||
|
if conf.IsProdMode() {
|
||||||
|
level = logger.Warn
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Default = logger.New(gormLogger, logger.Config{
|
||||||
|
SlowThreshold: 100 * time.Millisecond,
|
||||||
|
LogLevel: level,
|
||||||
|
})
|
||||||
|
|
||||||
|
gormDB, err := dbutil.OpenDB(
|
||||||
|
conf.Database,
|
||||||
|
&gorm.Config{
|
||||||
|
SkipDefaultTransaction: true,
|
||||||
|
NamingStrategy: schema.NamingStrategy{
|
||||||
|
SingularTable: true,
|
||||||
|
},
|
||||||
|
NowFunc: func() time.Time {
|
||||||
|
return time.Now().UTC().Truncate(time.Microsecond)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "open database")
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlDB, err := gormDB.DB()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "get underlying *sql.DB")
|
||||||
|
}
|
||||||
|
sqlDB.SetMaxOpenConns(conf.Database.MaxOpenConns)
|
||||||
|
sqlDB.SetMaxIdleConns(conf.Database.MaxIdleConns)
|
||||||
|
sqlDB.SetConnMaxLifetime(time.Minute)
|
||||||
|
|
||||||
|
switch conf.Database.Type {
|
||||||
|
case "postgres":
|
||||||
|
conf.UsePostgreSQL = true
|
||||||
|
case "mysql":
|
||||||
|
conf.UseMySQL = true
|
||||||
|
gormDB = gormDB.Set("gorm:table_options", "ENGINE=InnoDB").Session(&gorm.Session{})
|
||||||
|
case "sqlite3":
|
||||||
|
conf.UseSQLite3 = true
|
||||||
|
case "mssql":
|
||||||
|
conf.UseMSSQL = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return gormDB, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTestEngine() error {
|
func NewTestEngine() error {
|
||||||
x, err := getEngine()
|
var err error
|
||||||
|
db, err = getGormDB(&dbutil.Logger{Writer: os.Stdout})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Newf("connect to database: %v", err)
|
return errors.Newf("connect to database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if conf.UsePostgreSQL {
|
for _, table := range legacyTables {
|
||||||
x.SetSchema(conf.Database.Schema)
|
if db.Migrator().HasTable(table) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err = db.Migrator().AutoMigrate(table); err != nil {
|
||||||
|
return errors.Wrap(err, "auto migrate")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
x.SetMapper(core.GonicMapper{})
|
|
||||||
return x.StoreEngine("InnoDB").Sync2(legacyTables...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetEngine() (*gorm.DB, error) {
|
func SetEngine() (*gorm.DB, error) {
|
||||||
var err error
|
|
||||||
x, err = getEngine()
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Newf("connect to database: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if conf.UsePostgreSQL {
|
|
||||||
x.SetSchema(conf.Database.Schema)
|
|
||||||
}
|
|
||||||
|
|
||||||
x.SetMapper(core.GonicMapper{})
|
|
||||||
|
|
||||||
var logPath string
|
var logPath string
|
||||||
if conf.HookMode {
|
if conf.HookMode {
|
||||||
logPath = filepath.Join(conf.Log.RootPath, "hooks", "xorm.log")
|
logPath = filepath.Join(conf.Log.RootPath, "hooks", "gorm.log")
|
||||||
} else {
|
} else {
|
||||||
logPath = filepath.Join(conf.Log.RootPath, "xorm.log")
|
logPath = filepath.Join(conf.Log.RootPath, "gorm.log")
|
||||||
}
|
}
|
||||||
sec := conf.File.Section("log.xorm")
|
sec := conf.File.Section("log.gorm")
|
||||||
fileWriter, err := log.NewFileWriter(logPath,
|
fileWriter, err := log.NewFileWriter(logPath,
|
||||||
log.FileRotationConfig{
|
log.FileRotationConfig{
|
||||||
Rotate: sec.Key("ROTATE").MustBool(true),
|
Rotate: sec.Key("ROTATE").MustBool(true),
|
||||||
@@ -155,20 +130,9 @@ func SetEngine() (*gorm.DB, error) {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Newf("create 'xorm.log': %v", err)
|
return nil, errors.Newf("create 'gorm.log': %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
x.SetMaxOpenConns(conf.Database.MaxOpenConns)
|
|
||||||
x.SetMaxIdleConns(conf.Database.MaxIdleConns)
|
|
||||||
x.SetConnMaxLifetime(time.Second)
|
|
||||||
|
|
||||||
if conf.IsProdMode() {
|
|
||||||
x.SetLogger(xorm.NewSimpleLogger3(fileWriter, xorm.DEFAULT_LOG_PREFIX, xorm.DEFAULT_LOG_FLAG, core.LOG_ERR))
|
|
||||||
} else {
|
|
||||||
x.SetLogger(xorm.NewSimpleLogger(fileWriter))
|
|
||||||
}
|
|
||||||
x.ShowSQL(true)
|
|
||||||
|
|
||||||
var gormLogger logger.Writer
|
var gormLogger logger.Writer
|
||||||
if conf.HookMode {
|
if conf.HookMode {
|
||||||
gormLogger = &dbutil.Logger{Writer: fileWriter}
|
gormLogger = &dbutil.Logger{Writer: fileWriter}
|
||||||
@@ -178,23 +142,37 @@ func SetEngine() (*gorm.DB, error) {
|
|||||||
return nil, errors.Wrap(err, "new log writer")
|
return nil, errors.Wrap(err, "new log writer")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
db, err = getGormDB(gormLogger)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return NewConnection(gormLogger)
|
return NewConnection(gormLogger)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewEngine() error {
|
func NewEngine() error {
|
||||||
db, err := SetEngine()
|
gormDB, err := SetEngine()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = migrations.Migrate(db); err != nil {
|
if err = migrations.Migrate(gormDB); err != nil {
|
||||||
return errors.Newf("migrate: %v", err)
|
return errors.Newf("migrate: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = x.StoreEngine("InnoDB").Sync2(legacyTables...); err != nil {
|
for _, table := range legacyTables {
|
||||||
return errors.Wrap(err, "sync tables")
|
if gormDB.Migrator().HasTable(table) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
name := strings.TrimPrefix(fmt.Sprintf("%T", table), "*database.")
|
||||||
|
if err = gormDB.Migrator().AutoMigrate(table); err != nil {
|
||||||
|
return errors.Wrapf(err, "auto migrate %q", name)
|
||||||
|
}
|
||||||
|
log.Trace("Auto migrated %q", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HasEngine = true
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,33 +190,54 @@ type Statistic struct {
|
|||||||
func GetStatistic(ctx context.Context) (stats Statistic) {
|
func GetStatistic(ctx context.Context) (stats Statistic) {
|
||||||
stats.Counter.User = Handle.Users().Count(ctx)
|
stats.Counter.User = Handle.Users().Count(ctx)
|
||||||
stats.Counter.Org = CountOrganizations()
|
stats.Counter.Org = CountOrganizations()
|
||||||
stats.Counter.PublicKey, _ = x.Count(new(PublicKey))
|
var count int64
|
||||||
|
db.Model(new(PublicKey)).Count(&count)
|
||||||
|
stats.Counter.PublicKey = count
|
||||||
stats.Counter.Repo = CountRepositories(true)
|
stats.Counter.Repo = CountRepositories(true)
|
||||||
stats.Counter.Watch, _ = x.Count(new(Watch))
|
db.Model(new(Watch)).Count(&count)
|
||||||
stats.Counter.Star, _ = x.Count(new(Star))
|
stats.Counter.Watch = count
|
||||||
stats.Counter.Action, _ = x.Count(new(Action))
|
db.Model(new(Star)).Count(&count)
|
||||||
stats.Counter.Access, _ = x.Count(new(Access))
|
stats.Counter.Star = count
|
||||||
stats.Counter.Issue, _ = x.Count(new(Issue))
|
db.Model(new(Action)).Count(&count)
|
||||||
stats.Counter.Comment, _ = x.Count(new(Comment))
|
stats.Counter.Action = count
|
||||||
|
db.Model(new(Access)).Count(&count)
|
||||||
|
stats.Counter.Access = count
|
||||||
|
db.Model(new(Issue)).Count(&count)
|
||||||
|
stats.Counter.Issue = count
|
||||||
|
db.Model(new(Comment)).Count(&count)
|
||||||
|
stats.Counter.Comment = count
|
||||||
stats.Counter.Oauth = 0
|
stats.Counter.Oauth = 0
|
||||||
stats.Counter.Follow, _ = x.Count(new(Follow))
|
db.Model(new(Follow)).Count(&count)
|
||||||
stats.Counter.Mirror, _ = x.Count(new(Mirror))
|
stats.Counter.Follow = count
|
||||||
stats.Counter.Release, _ = x.Count(new(Release))
|
db.Model(new(Mirror)).Count(&count)
|
||||||
|
stats.Counter.Mirror = count
|
||||||
|
db.Model(new(Release)).Count(&count)
|
||||||
|
stats.Counter.Release = count
|
||||||
stats.Counter.LoginSource = Handle.LoginSources().Count(ctx)
|
stats.Counter.LoginSource = Handle.LoginSources().Count(ctx)
|
||||||
stats.Counter.Webhook, _ = x.Count(new(Webhook))
|
db.Model(new(Webhook)).Count(&count)
|
||||||
stats.Counter.Milestone, _ = x.Count(new(Milestone))
|
stats.Counter.Webhook = count
|
||||||
stats.Counter.Label, _ = x.Count(new(Label))
|
db.Model(new(Milestone)).Count(&count)
|
||||||
stats.Counter.HookTask, _ = x.Count(new(HookTask))
|
stats.Counter.Milestone = count
|
||||||
stats.Counter.Team, _ = x.Count(new(Team))
|
db.Model(new(Label)).Count(&count)
|
||||||
stats.Counter.Attachment, _ = x.Count(new(Attachment))
|
stats.Counter.Label = count
|
||||||
|
db.Model(new(HookTask)).Count(&count)
|
||||||
|
stats.Counter.HookTask = count
|
||||||
|
db.Model(new(Team)).Count(&count)
|
||||||
|
stats.Counter.Team = count
|
||||||
|
db.Model(new(Attachment)).Count(&count)
|
||||||
|
stats.Counter.Attachment = count
|
||||||
return stats
|
return stats
|
||||||
}
|
}
|
||||||
|
|
||||||
func Ping() error {
|
func Ping() error {
|
||||||
if x == nil {
|
if db == nil {
|
||||||
return errors.New("database not available")
|
return errors.New("database not available")
|
||||||
}
|
}
|
||||||
return x.Ping()
|
sqlDB, err := db.DB()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return sqlDB.Ping()
|
||||||
}
|
}
|
||||||
|
|
||||||
// The version table. Should have only one row with id==1
|
// The version table. Should have only one row with id==1
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/cockroachdb/errors"
|
"github.com/cockroachdb/errors"
|
||||||
"xorm.io/builder"
|
"gorm.io/gorm"
|
||||||
"xorm.io/xorm"
|
|
||||||
|
|
||||||
"gogs.io/gogs/internal/errutil"
|
"gogs.io/gogs/internal/errutil"
|
||||||
"gogs.io/gogs/internal/repoutil"
|
"gogs.io/gogs/internal/repoutil"
|
||||||
@@ -26,32 +25,32 @@ func (org *User) IsOrgMember(uid int64) bool {
|
|||||||
return org.IsOrganization() && IsOrganizationMember(org.ID, uid)
|
return org.IsOrganization() && IsOrganizationMember(org.ID, uid)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (org *User) getTeam(e Engine, name string) (*Team, error) {
|
func (org *User) getTeam(tx *gorm.DB, name string) (*Team, error) {
|
||||||
return getTeamOfOrgByName(e, org.ID, name)
|
return getTeamOfOrgByName(tx, org.ID, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTeamOfOrgByName returns named team of organization.
|
// GetTeamOfOrgByName returns named team of organization.
|
||||||
func (org *User) GetTeam(name string) (*Team, error) {
|
func (org *User) GetTeam(name string) (*Team, error) {
|
||||||
return org.getTeam(x, name)
|
return org.getTeam(db, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (org *User) getOwnerTeam(e Engine) (*Team, error) {
|
func (org *User) getOwnerTeam(tx *gorm.DB) (*Team, error) {
|
||||||
return org.getTeam(e, ownerTeamName)
|
return org.getTeam(tx, ownerTeamName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOwnerTeam returns owner team of organization.
|
// GetOwnerTeam returns owner team of organization.
|
||||||
func (org *User) GetOwnerTeam() (*Team, error) {
|
func (org *User) GetOwnerTeam() (*Team, error) {
|
||||||
return org.getOwnerTeam(x)
|
return org.getOwnerTeam(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (org *User) getTeams(e Engine) (err error) {
|
func (org *User) getTeams(tx *gorm.DB) (err error) {
|
||||||
org.Teams, err = getTeamsByOrgID(e, org.ID)
|
org.Teams, err = getTeamsByOrgID(tx, org.ID)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTeams returns all teams that belong to organization.
|
// GetTeams returns all teams that belong to organization.
|
||||||
func (org *User) GetTeams() error {
|
func (org *User) GetTeams() error {
|
||||||
return org.getTeams(x)
|
return org.getTeams(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TeamsHaveAccessToRepo returns all teams that have given access level to the repository.
|
// TeamsHaveAccessToRepo returns all teams that have given access level to the repository.
|
||||||
@@ -86,13 +85,13 @@ func (org *User) RemoveMember(uid int64) error {
|
|||||||
return RemoveOrgUser(org.ID, uid)
|
return RemoveOrgUser(org.ID, uid)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (org *User) removeOrgRepo(e Engine, repoID int64) error {
|
func (org *User) removeOrgRepo(tx *gorm.DB, repoID int64) error {
|
||||||
return removeOrgRepo(e, org.ID, repoID)
|
return removeOrgRepo(tx, org.ID, repoID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveOrgRepo removes all team-repository relations of organization.
|
// RemoveOrgRepo removes all team-repository relations of organization.
|
||||||
func (org *User) RemoveOrgRepo(repoID int64) error {
|
func (org *User) RemoveOrgRepo(repoID int64) error {
|
||||||
return org.removeOrgRepo(x, repoID)
|
return org.removeOrgRepo(db, repoID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateOrganization creates record of a new organization.
|
// CreateOrganization creates record of a new organization.
|
||||||
@@ -121,52 +120,48 @@ func CreateOrganization(org, owner *User) (err error) {
|
|||||||
org.NumTeams = 1
|
org.NumTeams = 1
|
||||||
org.NumMembers = 1
|
org.NumMembers = 1
|
||||||
|
|
||||||
sess := x.NewSession()
|
return db.Transaction(func(tx *gorm.DB) error {
|
||||||
defer sess.Close()
|
if err := tx.Create(org).Error; err != nil {
|
||||||
if err = sess.Begin(); err != nil {
|
return errors.Newf("insert organization: %v", err)
|
||||||
return err
|
}
|
||||||
}
|
_ = userutil.GenerateRandomAvatar(org.ID, org.Name, org.Email)
|
||||||
|
|
||||||
if _, err = sess.Insert(org); err != nil {
|
// Add initial creator to organization and owner team.
|
||||||
return errors.Newf("insert organization: %v", err)
|
if err := tx.Create(&OrgUser{
|
||||||
}
|
UID: owner.ID,
|
||||||
_ = userutil.GenerateRandomAvatar(org.ID, org.Name, org.Email)
|
OrgID: org.ID,
|
||||||
|
IsOwner: true,
|
||||||
|
NumTeams: 1,
|
||||||
|
}).Error; err != nil {
|
||||||
|
return errors.Newf("insert org-user relation: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Add initial creator to organization and owner team.
|
// Create default owner team.
|
||||||
if _, err = sess.Insert(&OrgUser{
|
t := &Team{
|
||||||
UID: owner.ID,
|
OrgID: org.ID,
|
||||||
OrgID: org.ID,
|
LowerName: strings.ToLower(ownerTeamName),
|
||||||
IsOwner: true,
|
Name: ownerTeamName,
|
||||||
NumTeams: 1,
|
Authorize: AccessModeOwner,
|
||||||
}); err != nil {
|
NumMembers: 1,
|
||||||
return errors.Newf("insert org-user relation: %v", err)
|
}
|
||||||
}
|
if err := tx.Create(t).Error; err != nil {
|
||||||
|
return errors.Newf("insert owner team: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Create default owner team.
|
if err := tx.Create(&TeamUser{
|
||||||
t := &Team{
|
UID: owner.ID,
|
||||||
OrgID: org.ID,
|
OrgID: org.ID,
|
||||||
LowerName: strings.ToLower(ownerTeamName),
|
TeamID: t.ID,
|
||||||
Name: ownerTeamName,
|
}).Error; err != nil {
|
||||||
Authorize: AccessModeOwner,
|
return errors.Newf("insert team-user relation: %v", err)
|
||||||
NumMembers: 1,
|
}
|
||||||
}
|
|
||||||
if _, err = sess.Insert(t); err != nil {
|
|
||||||
return errors.Newf("insert owner team: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = sess.Insert(&TeamUser{
|
if err := os.MkdirAll(repoutil.UserPath(org.Name), os.ModePerm); err != nil {
|
||||||
UID: owner.ID,
|
return errors.Newf("create directory: %v", err)
|
||||||
OrgID: org.ID,
|
}
|
||||||
TeamID: t.ID,
|
|
||||||
}); err != nil {
|
|
||||||
return errors.Newf("insert team-user relation: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = os.MkdirAll(repoutil.UserPath(org.Name), os.ModePerm); err != nil {
|
return nil
|
||||||
return errors.Newf("create directory: %v", err)
|
})
|
||||||
}
|
|
||||||
|
|
||||||
return sess.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOrgByName returns organization by given name.
|
// GetOrgByName returns organization by given name.
|
||||||
@@ -174,35 +169,36 @@ func GetOrgByName(name string) (*User, error) {
|
|||||||
if name == "" {
|
if name == "" {
|
||||||
return nil, ErrOrgNotExist
|
return nil, ErrOrgNotExist
|
||||||
}
|
}
|
||||||
u := &User{
|
u := &User{}
|
||||||
LowerName: strings.ToLower(name),
|
err := db.Where("lower_name = ? AND type = ?", strings.ToLower(name), UserTypeOrganization).First(u).Error
|
||||||
Type: UserTypeOrganization,
|
|
||||||
}
|
|
||||||
has, err := x.Get(u)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil, ErrOrgNotExist
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if !has {
|
|
||||||
return nil, ErrOrgNotExist
|
|
||||||
}
|
}
|
||||||
return u, nil
|
return u, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CountOrganizations returns number of organizations.
|
// CountOrganizations returns number of organizations.
|
||||||
func CountOrganizations() int64 {
|
func CountOrganizations() int64 {
|
||||||
count, _ := x.Where("type=1").Count(new(User))
|
var count int64
|
||||||
|
db.Model(new(User)).Where("type = ?", UserTypeOrganization).Count(&count)
|
||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
// Organizations returns number of organizations in given page.
|
// Organizations returns number of organizations in given page.
|
||||||
func Organizations(page, pageSize int) ([]*User, error) {
|
func Organizations(page, pageSize int) ([]*User, error) {
|
||||||
orgs := make([]*User, 0, pageSize)
|
orgs := make([]*User, 0, pageSize)
|
||||||
return orgs, x.Limit(pageSize, (page-1)*pageSize).Where("type=1").Asc("id").Find(&orgs)
|
return orgs, db.Where("type = ?", UserTypeOrganization).
|
||||||
|
Offset((page - 1) * pageSize).Limit(pageSize).
|
||||||
|
Order("id ASC").Find(&orgs).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// deleteBeans deletes all given beans, beans should contain delete conditions.
|
// deleteBeans deletes all given beans, beans should contain delete conditions.
|
||||||
func deleteBeans(e Engine, beans ...any) (err error) {
|
func deleteBeans(tx *gorm.DB, beans ...any) (err error) {
|
||||||
for i := range beans {
|
for i := range beans {
|
||||||
if _, err = e.Delete(beans[i]); err != nil {
|
if err = tx.Delete(beans[i]).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -216,20 +212,13 @@ func DeleteOrganization(org *User) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
sess := x.NewSession()
|
return db.Transaction(func(tx *gorm.DB) error {
|
||||||
defer sess.Close()
|
return deleteBeans(tx,
|
||||||
if err = sess.Begin(); err != nil {
|
&Team{OrgID: org.ID},
|
||||||
return err
|
&OrgUser{OrgID: org.ID},
|
||||||
}
|
&TeamUser{OrgID: org.ID},
|
||||||
|
)
|
||||||
if err = deleteBeans(sess,
|
})
|
||||||
&Team{OrgID: org.ID},
|
|
||||||
&OrgUser{OrgID: org.ID},
|
|
||||||
&TeamUser{OrgID: org.ID},
|
|
||||||
); err != nil {
|
|
||||||
return errors.Newf("deleteBeans: %v", err)
|
|
||||||
}
|
|
||||||
return sess.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ________ ____ ___
|
// ________ ____ ___
|
||||||
@@ -251,84 +240,94 @@ type OrgUser struct {
|
|||||||
|
|
||||||
// IsOrganizationOwner returns true if given user is in the owner team.
|
// IsOrganizationOwner returns true if given user is in the owner team.
|
||||||
func IsOrganizationOwner(orgID, userID int64) bool {
|
func IsOrganizationOwner(orgID, userID int64) bool {
|
||||||
has, _ := x.Where("is_owner = ?", true).And("uid = ?", userID).And("org_id = ?", orgID).Get(new(OrgUser))
|
var count int64
|
||||||
return has
|
db.Model(new(OrgUser)).Where("is_owner = ? AND uid = ? AND org_id = ?", true, userID, orgID).Count(&count)
|
||||||
|
return count > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsOrganizationMember returns true if given user is member of organization.
|
// IsOrganizationMember returns true if given user is member of organization.
|
||||||
func IsOrganizationMember(orgID, uid int64) bool {
|
func IsOrganizationMember(orgID, uid int64) bool {
|
||||||
has, _ := x.Where("uid=?", uid).And("org_id=?", orgID).Get(new(OrgUser))
|
var count int64
|
||||||
return has
|
db.Model(new(OrgUser)).Where("uid = ? AND org_id = ?", uid, orgID).Count(&count)
|
||||||
|
return count > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsPublicMembership returns true if given user public his/her membership.
|
// IsPublicMembership returns true if given user public his/her membership.
|
||||||
func IsPublicMembership(orgID, uid int64) bool {
|
func IsPublicMembership(orgID, uid int64) bool {
|
||||||
has, _ := x.Where("uid=?", uid).And("org_id=?", orgID).And("is_public=?", true).Get(new(OrgUser))
|
var count int64
|
||||||
return has
|
db.Model(new(OrgUser)).Where("uid = ? AND org_id = ? AND is_public = ?", uid, orgID, true).Count(&count)
|
||||||
|
return count > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func getOrgsByUserID(sess *xorm.Session, userID int64, showAll bool) ([]*User, error) {
|
func getOrgsByUserID(tx *gorm.DB, userID int64, showAll bool) ([]*User, error) {
|
||||||
orgs := make([]*User, 0, 10)
|
orgs := make([]*User, 0, 10)
|
||||||
|
query := tx.Table("`user`").
|
||||||
|
Joins("INNER JOIN `org_user` ON `org_user`.org_id = `user`.id").
|
||||||
|
Where("`org_user`.uid = ?", userID)
|
||||||
if !showAll {
|
if !showAll {
|
||||||
sess.And("`org_user`.is_public=?", true)
|
query = query.Where("`org_user`.is_public = ?", true)
|
||||||
}
|
}
|
||||||
return orgs, sess.And("`org_user`.uid=?", userID).
|
return orgs, query.Find(&orgs).Error
|
||||||
Join("INNER", "`org_user`", "`org_user`.org_id=`user`.id").Find(&orgs)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOrgsByUserID returns a list of organizations that the given user ID
|
// GetOrgsByUserID returns a list of organizations that the given user ID
|
||||||
// has joined.
|
// has joined.
|
||||||
func GetOrgsByUserID(userID int64, showAll bool) ([]*User, error) {
|
func GetOrgsByUserID(userID int64, showAll bool) ([]*User, error) {
|
||||||
return getOrgsByUserID(x.NewSession(), userID, showAll)
|
return getOrgsByUserID(db, userID, showAll)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getOwnedOrgsByUserID(sess *xorm.Session, userID int64) ([]*User, error) {
|
func getOwnedOrgsByUserID(tx *gorm.DB, userID int64) ([]*User, error) {
|
||||||
orgs := make([]*User, 0, 10)
|
orgs := make([]*User, 0, 10)
|
||||||
return orgs, sess.Where("`org_user`.uid=?", userID).And("`org_user`.is_owner=?", true).
|
return orgs, tx.Table("`user`").
|
||||||
Join("INNER", "`org_user`", "`org_user`.org_id=`user`.id").Find(&orgs)
|
Joins("INNER JOIN `org_user` ON `org_user`.org_id = `user`.id").
|
||||||
|
Where("`org_user`.uid = ? AND `org_user`.is_owner = ?", userID, true).
|
||||||
|
Find(&orgs).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOwnedOrgsByUserID returns a list of organizations are owned by given user ID.
|
// GetOwnedOrgsByUserID returns a list of organizations are owned by given user ID.
|
||||||
func GetOwnedOrgsByUserID(userID int64) ([]*User, error) {
|
func GetOwnedOrgsByUserID(userID int64) ([]*User, error) {
|
||||||
sess := x.NewSession()
|
return getOwnedOrgsByUserID(db, userID)
|
||||||
return getOwnedOrgsByUserID(sess, userID)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOwnedOrganizationsByUserIDDesc returns a list of organizations are owned by
|
// GetOwnedOrganizationsByUserIDDesc returns a list of organizations are owned by
|
||||||
// given user ID, ordered descending by the given condition.
|
// given user ID, ordered descending by the given condition.
|
||||||
func GetOwnedOrgsByUserIDDesc(userID int64, desc string) ([]*User, error) {
|
func GetOwnedOrgsByUserIDDesc(userID int64, desc string) ([]*User, error) {
|
||||||
sess := x.NewSession()
|
orgs := make([]*User, 0, 10)
|
||||||
return getOwnedOrgsByUserID(sess.Desc(desc), userID)
|
return orgs, db.Table("`user`").
|
||||||
|
Joins("INNER JOIN `org_user` ON `org_user`.org_id = `user`.id").
|
||||||
|
Where("`org_user`.uid = ? AND `org_user`.is_owner = ?", userID, true).
|
||||||
|
Order(desc + " DESC").
|
||||||
|
Find(&orgs).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func getOrgUsersByOrgID(e Engine, orgID int64, limit int) ([]*OrgUser, error) {
|
func getOrgUsersByOrgID(tx *gorm.DB, orgID int64, limit int) ([]*OrgUser, error) {
|
||||||
orgUsers := make([]*OrgUser, 0, 10)
|
orgUsers := make([]*OrgUser, 0, 10)
|
||||||
|
|
||||||
sess := e.Where("org_id=?", orgID)
|
query := tx.Where("org_id = ?", orgID)
|
||||||
if limit > 0 {
|
if limit > 0 {
|
||||||
sess = sess.Limit(limit)
|
query = query.Limit(limit)
|
||||||
}
|
}
|
||||||
return orgUsers, sess.Find(&orgUsers)
|
return orgUsers, query.Find(&orgUsers).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOrgUsersByOrgID returns all organization-user relations by organization ID.
|
// GetOrgUsersByOrgID returns all organization-user relations by organization ID.
|
||||||
func GetOrgUsersByOrgID(orgID int64, limit int) ([]*OrgUser, error) {
|
func GetOrgUsersByOrgID(orgID int64, limit int) ([]*OrgUser, error) {
|
||||||
return getOrgUsersByOrgID(x, orgID, limit)
|
return getOrgUsersByOrgID(db, orgID, limit)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChangeOrgUserStatus changes public or private membership status.
|
// ChangeOrgUserStatus changes public or private membership status.
|
||||||
func ChangeOrgUserStatus(orgID, uid int64, public bool) error {
|
func ChangeOrgUserStatus(orgID, uid int64, public bool) error {
|
||||||
ou := new(OrgUser)
|
ou := new(OrgUser)
|
||||||
has, err := x.Where("uid=?", uid).And("org_id=?", orgID).Get(ou)
|
err := db.Where("uid = ? AND org_id = ?", uid, orgID).First(ou).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
} else if !has {
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ou.IsPublic = public
|
ou.IsPublic = public
|
||||||
_, err = x.Id(ou.ID).AllCols().Update(ou)
|
return db.Model(ou).Where("id = ?", ou.ID).Updates(ou).Error
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddOrgUser adds new user to given organization.
|
// AddOrgUser adds new user to given organization.
|
||||||
@@ -337,35 +336,33 @@ func AddOrgUser(orgID, uid int64) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
sess := x.NewSession()
|
return db.Transaction(func(tx *gorm.DB) error {
|
||||||
defer sess.Close()
|
ou := &OrgUser{
|
||||||
if err := sess.Begin(); err != nil {
|
UID: uid,
|
||||||
return err
|
OrgID: orgID,
|
||||||
}
|
}
|
||||||
|
|
||||||
ou := &OrgUser{
|
if err := tx.Create(ou).Error; err != nil {
|
||||||
UID: uid,
|
return err
|
||||||
OrgID: orgID,
|
}
|
||||||
}
|
if err := tx.Exec("UPDATE `user` SET num_members = num_members + 1 WHERE id = ?", orgID).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if _, err := sess.Insert(ou); err != nil {
|
return nil
|
||||||
return err
|
})
|
||||||
} else if _, err = sess.Exec("UPDATE `user` SET num_members = num_members + 1 WHERE id = ?", orgID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return sess.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveOrgUser removes user from given organization.
|
// RemoveOrgUser removes user from given organization.
|
||||||
func RemoveOrgUser(orgID, userID int64) error {
|
func RemoveOrgUser(orgID, userID int64) error {
|
||||||
ou := new(OrgUser)
|
ou := new(OrgUser)
|
||||||
|
|
||||||
has, err := x.Where("uid=?", userID).And("org_id=?", orgID).Get(ou)
|
err := db.Where("uid = ? AND org_id = ?", userID, orgID).First(ou).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return errors.Newf("get org-user: %v", err)
|
return errors.Newf("get org-user: %v", err)
|
||||||
} else if !has {
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := Handle.Users().GetByID(context.TODO(), userID)
|
user, err := Handle.Users().GetByID(context.TODO(), userID)
|
||||||
@@ -394,71 +391,69 @@ func RemoveOrgUser(orgID, userID int64) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sess := x.NewSession()
|
return db.Transaction(func(tx *gorm.DB) error {
|
||||||
defer sess.Close()
|
if err := tx.Where("id = ?", ou.ID).Delete(ou).Error; err != nil {
|
||||||
if err := sess.Begin(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := sess.ID(ou.ID).Delete(ou); err != nil {
|
|
||||||
return err
|
|
||||||
} else if _, err = sess.Exec("UPDATE `user` SET num_members=num_members-1 WHERE id=?", orgID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete all repository accesses and unwatch them.
|
|
||||||
repoIDs := make([]int64, 0, len(repos))
|
|
||||||
for i := range repos {
|
|
||||||
repoIDs = append(repoIDs, repos[i].ID)
|
|
||||||
if err = watchRepo(sess, user.ID, repos[i].ID, false); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
if err := tx.Exec("UPDATE `user` SET num_members = num_members - 1 WHERE id = ?", orgID).Error; err != nil {
|
||||||
|
|
||||||
if len(repoIDs) > 0 {
|
|
||||||
if _, err = sess.Where("user_id = ?", user.ID).In("repo_id", repoIDs).Delete(new(Access)); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Delete member in his/her teams.
|
// Delete all repository accesses and unwatch them.
|
||||||
teams, err := getUserTeams(sess, org.ID, user.ID)
|
repoIDs := make([]int64, 0, len(repos))
|
||||||
if err != nil {
|
for i := range repos {
|
||||||
return err
|
repoIDs = append(repoIDs, repos[i].ID)
|
||||||
}
|
if err = watchRepo(tx, user.ID, repos[i].ID, false); err != nil {
|
||||||
for _, t := range teams {
|
return err
|
||||||
if err = removeTeamMember(sess, org.ID, t.ID, user.ID); err != nil {
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(repoIDs) > 0 {
|
||||||
|
if err := tx.Where("user_id = ? AND repo_id IN ?", user.ID, repoIDs).Delete(new(Access)).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete member in his/her teams.
|
||||||
|
teams, err := getUserTeams(tx, org.ID, user.ID)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
for _, t := range teams {
|
||||||
|
if err = removeTeamMember(tx, org.ID, t.ID, user.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return sess.Commit()
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeOrgRepo(e Engine, orgID, repoID int64) error {
|
func removeOrgRepo(tx *gorm.DB, orgID, repoID int64) error {
|
||||||
_, err := e.Delete(&TeamRepo{
|
return tx.Where("org_id = ? AND repo_id = ?", orgID, repoID).Delete(&TeamRepo{}).Error
|
||||||
OrgID: orgID,
|
|
||||||
RepoID: repoID,
|
|
||||||
})
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveOrgRepo removes all team-repository relations of given organization.
|
// RemoveOrgRepo removes all team-repository relations of given organization.
|
||||||
func RemoveOrgRepo(orgID, repoID int64) error {
|
func RemoveOrgRepo(orgID, repoID int64) error {
|
||||||
return removeOrgRepo(x, orgID, repoID)
|
return removeOrgRepo(db, orgID, repoID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (org *User) getUserTeams(e Engine, userID int64, cols ...string) ([]*Team, error) {
|
func (org *User) getUserTeams(tx *gorm.DB, userID int64, cols ...string) ([]*Team, error) {
|
||||||
teams := make([]*Team, 0, org.NumTeams)
|
teams := make([]*Team, 0, org.NumTeams)
|
||||||
return teams, e.Where("team_user.org_id = ?", org.ID).
|
query := tx.Table("team").
|
||||||
And("team_user.uid = ?", userID).
|
Joins("INNER JOIN team_user ON team_user.team_id = team.id").
|
||||||
Join("INNER", "team_user", "team_user.team_id = team.id").
|
Where("team_user.org_id = ? AND team_user.uid = ?", org.ID, userID)
|
||||||
Cols(cols...).Find(&teams)
|
|
||||||
|
if len(cols) > 0 {
|
||||||
|
query = query.Select(cols)
|
||||||
|
}
|
||||||
|
|
||||||
|
return teams, query.Find(&teams).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUserTeamIDs returns of all team IDs of the organization that user is member of.
|
// GetUserTeamIDs returns of all team IDs of the organization that user is member of.
|
||||||
func (org *User) GetUserTeamIDs(userID int64) ([]int64, error) {
|
func (org *User) GetUserTeamIDs(userID int64) ([]int64, error) {
|
||||||
teams, err := org.getUserTeams(x, userID, "team.id")
|
teams, err := org.getUserTeams(db, userID, "team.id")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Newf("getUserTeams [%d]: %v", userID, err)
|
return nil, errors.Newf("getUserTeams [%d]: %v", userID, err)
|
||||||
}
|
}
|
||||||
@@ -473,7 +468,7 @@ func (org *User) GetUserTeamIDs(userID int64) ([]int64, error) {
|
|||||||
// GetTeams returns all teams that belong to organization,
|
// GetTeams returns all teams that belong to organization,
|
||||||
// and that the user has joined.
|
// and that the user has joined.
|
||||||
func (org *User) GetUserTeams(userID int64) ([]*Team, error) {
|
func (org *User) GetUserTeams(userID int64) ([]*Team, error) {
|
||||||
return org.getUserTeams(x, userID)
|
return org.getUserTeams(db, userID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUserRepositories returns a range of repositories in organization which the user has access to,
|
// GetUserRepositories returns a range of repositories in organization which the user has access to,
|
||||||
@@ -489,7 +484,7 @@ func (org *User) GetUserRepositories(userID int64, page, pageSize int) ([]*Repos
|
|||||||
}
|
}
|
||||||
|
|
||||||
var teamRepoIDs []int64
|
var teamRepoIDs []int64
|
||||||
if err = x.Table("team_repo").In("team_id", teamIDs).Distinct("repo_id").Find(&teamRepoIDs); err != nil {
|
if err = db.Table("team_repo").Where("team_id IN ?", teamIDs).Distinct("repo_id").Find(&teamRepoIDs).Error; err != nil {
|
||||||
return nil, 0, errors.Newf("get team repository IDs: %v", err)
|
return nil, 0, errors.Newf("get team repository IDs: %v", err)
|
||||||
}
|
}
|
||||||
if len(teamRepoIDs) == 0 {
|
if len(teamRepoIDs) == 0 {
|
||||||
@@ -501,22 +496,18 @@ func (org *User) GetUserRepositories(userID int64, page, pageSize int) ([]*Repos
|
|||||||
page = 1
|
page = 1
|
||||||
}
|
}
|
||||||
repos := make([]*Repository, 0, pageSize)
|
repos := make([]*Repository, 0, pageSize)
|
||||||
if err = x.Where("owner_id = ?", org.ID).
|
if err = db.Where("owner_id = ?", org.ID).
|
||||||
And(builder.Or(
|
Where(db.Where("is_private = ? AND is_unlisted = ?", false, false).Or("id IN ?", teamRepoIDs)).
|
||||||
builder.And(builder.Expr("is_private = ?", false), builder.Expr("is_unlisted = ?", false)),
|
Order("updated_unix DESC").
|
||||||
builder.In("id", teamRepoIDs))).
|
Limit(pageSize).Offset((page - 1) * pageSize).
|
||||||
Desc("updated_unix").
|
Find(&repos).Error; err != nil {
|
||||||
Limit(pageSize, (page-1)*pageSize).
|
|
||||||
Find(&repos); err != nil {
|
|
||||||
return nil, 0, errors.Newf("get user repositories: %v", err)
|
return nil, 0, errors.Newf("get user repositories: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
repoCount, err := x.Where("owner_id = ?", org.ID).
|
var repoCount int64
|
||||||
And(builder.Or(
|
if err = db.Model(&Repository{}).Where("owner_id = ?", org.ID).
|
||||||
builder.Expr("is_private = ?", false),
|
Where(db.Where("is_private = ?", false).Or("id IN ?", teamRepoIDs)).
|
||||||
builder.In("id", teamRepoIDs))).
|
Count(&repoCount).Error; err != nil {
|
||||||
Count(new(Repository))
|
|
||||||
if err != nil {
|
|
||||||
return nil, 0, errors.Newf("count user repositories: %v", err)
|
return nil, 0, errors.Newf("count user repositories: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -534,7 +525,7 @@ func (org *User) GetUserMirrorRepositories(userID int64) ([]*Repository, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
var teamRepoIDs []int64
|
var teamRepoIDs []int64
|
||||||
err = x.Table("team_repo").In("team_id", teamIDs).Distinct("repo_id").Find(&teamRepoIDs)
|
err = db.Table("team_repo").Where("team_id IN ?", teamIDs).Distinct("repo_id").Find(&teamRepoIDs).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Newf("get team repository ids: %v", err)
|
return nil, errors.Newf("get team repository ids: %v", err)
|
||||||
}
|
}
|
||||||
@@ -544,12 +535,12 @@ func (org *User) GetUserMirrorRepositories(userID int64) ([]*Repository, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
repos := make([]*Repository, 0, 10)
|
repos := make([]*Repository, 0, 10)
|
||||||
if err = x.Where("owner_id = ?", org.ID).
|
if err = db.Where("owner_id = ?", org.ID).
|
||||||
And("is_private = ?", false).
|
Where("is_private = ?", false).
|
||||||
Or(builder.In("id", teamRepoIDs)).
|
Or("id IN ?", teamRepoIDs).
|
||||||
And("is_mirror = ?", true). // Don't move up because it's an independent condition
|
Where("is_mirror = ?", true). // Don't move up because it's an independent condition
|
||||||
Desc("updated_unix").
|
Order("updated_unix DESC").
|
||||||
Find(&repos); err != nil {
|
Find(&repos).Error; err != nil {
|
||||||
return nil, errors.Newf("get user repositories: %v", err)
|
return nil, errors.Newf("get user repositories: %v", err)
|
||||||
}
|
}
|
||||||
return repos, nil
|
return repos, nil
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/cockroachdb/errors"
|
"github.com/cockroachdb/errors"
|
||||||
"xorm.io/xorm"
|
"gorm.io/gorm"
|
||||||
|
|
||||||
"gogs.io/gogs/internal/errutil"
|
"gogs.io/gogs/internal/errutil"
|
||||||
)
|
)
|
||||||
@@ -16,7 +16,7 @@ const ownerTeamName = "Owners"
|
|||||||
// Team represents a organization team.
|
// Team represents a organization team.
|
||||||
type Team struct {
|
type Team struct {
|
||||||
ID int64
|
ID int64
|
||||||
OrgID int64 `xorm:"INDEX"`
|
OrgID int64 `gorm:"index"`
|
||||||
LowerName string
|
LowerName string
|
||||||
Name string
|
Name string
|
||||||
Description string
|
Description string
|
||||||
@@ -27,14 +27,12 @@ type Team struct {
|
|||||||
NumMembers int
|
NumMembers int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Team) AfterSet(colName string, _ xorm.Cell) {
|
func (t *Team) AfterFind(tx *gorm.DB) error {
|
||||||
switch colName {
|
// LEGACY [1.0]: this is backward compatibility bug fix for https://gogs.io/gogs/issues/3671
|
||||||
case "num_repos":
|
if t.NumRepos < 0 {
|
||||||
// LEGACY [1.0]: this is backward compatibility bug fix for https://gogs.io/gogs/issues/3671
|
t.NumRepos = 0
|
||||||
if t.NumRepos < 0 {
|
|
||||||
t.NumRepos = 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsOwnerTeam returns true if team is owner team.
|
// IsOwnerTeam returns true if team is owner team.
|
||||||
@@ -52,15 +50,15 @@ func (t *Team) IsMember(userID int64) bool {
|
|||||||
return IsTeamMember(t.OrgID, t.ID, userID)
|
return IsTeamMember(t.OrgID, t.ID, userID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Team) getRepositories(e Engine) (err error) {
|
func (t *Team) getRepositories(tx *gorm.DB) (err error) {
|
||||||
teamRepos := make([]*TeamRepo, 0, t.NumRepos)
|
teamRepos := make([]*TeamRepo, 0, t.NumRepos)
|
||||||
if err = x.Where("team_id=?", t.ID).Find(&teamRepos); err != nil {
|
if err = tx.Where("team_id = ?", t.ID).Find(&teamRepos).Error; err != nil {
|
||||||
return errors.Newf("get team-repos: %v", err)
|
return errors.Newf("get team-repos: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Repos = make([]*Repository, 0, len(teamRepos))
|
t.Repos = make([]*Repository, 0, len(teamRepos))
|
||||||
for i := range teamRepos {
|
for i := range teamRepos {
|
||||||
repo, err := getRepositoryByID(e, teamRepos[i].RepoID)
|
repo, err := getRepositoryByID(tx, teamRepos[i].RepoID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Newf("getRepositoryById(%d): %v", teamRepos[i].RepoID, err)
|
return errors.Newf("getRepositoryById(%d): %v", teamRepos[i].RepoID, err)
|
||||||
}
|
}
|
||||||
@@ -71,17 +69,17 @@ func (t *Team) getRepositories(e Engine) (err error) {
|
|||||||
|
|
||||||
// GetRepositories returns all repositories in team of organization.
|
// GetRepositories returns all repositories in team of organization.
|
||||||
func (t *Team) GetRepositories() error {
|
func (t *Team) GetRepositories() error {
|
||||||
return t.getRepositories(x)
|
return t.getRepositories(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Team) getMembers(e Engine) (err error) {
|
func (t *Team) getMembers(tx *gorm.DB) (err error) {
|
||||||
t.Members, err = getTeamMembers(e, t.ID)
|
t.Members, err = getTeamMembers(tx, t.ID)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMembers returns all members in team of organization.
|
// GetMembers returns all members in team of organization.
|
||||||
func (t *Team) GetMembers() (err error) {
|
func (t *Team) GetMembers() (err error) {
|
||||||
return t.getMembers(x)
|
return t.getMembers(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddMember adds new membership of the team to the organization,
|
// AddMember adds new membership of the team to the organization,
|
||||||
@@ -95,34 +93,34 @@ func (t *Team) RemoveMember(uid int64) error {
|
|||||||
return RemoveTeamMember(t.OrgID, t.ID, uid)
|
return RemoveTeamMember(t.OrgID, t.ID, uid)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Team) hasRepository(e Engine, repoID int64) bool {
|
func (t *Team) hasRepository(tx *gorm.DB, repoID int64) bool {
|
||||||
return hasTeamRepo(e, t.OrgID, t.ID, repoID)
|
return hasTeamRepo(tx, t.OrgID, t.ID, repoID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasRepository returns true if given repository belong to team.
|
// HasRepository returns true if given repository belong to team.
|
||||||
func (t *Team) HasRepository(repoID int64) bool {
|
func (t *Team) HasRepository(repoID int64) bool {
|
||||||
return t.hasRepository(x, repoID)
|
return t.hasRepository(db, repoID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Team) addRepository(e Engine, repo *Repository) (err error) {
|
func (t *Team) addRepository(tx *gorm.DB, repo *Repository) (err error) {
|
||||||
if err = addTeamRepo(e, t.OrgID, t.ID, repo.ID); err != nil {
|
if err = addTeamRepo(tx, t.OrgID, t.ID, repo.ID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
t.NumRepos++
|
t.NumRepos++
|
||||||
if _, err = e.ID(t.ID).AllCols().Update(t); err != nil {
|
if err = tx.Model(&Team{}).Where("id = ?", t.ID).Updates(t).Error; err != nil {
|
||||||
return errors.Newf("update team: %v", err)
|
return errors.Newf("update team: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = repo.recalculateTeamAccesses(e, 0); err != nil {
|
if err = repo.recalculateTeamAccesses(tx, 0); err != nil {
|
||||||
return errors.Newf("recalculateAccesses: %v", err)
|
return errors.Newf("recalculateAccesses: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = t.getMembers(e); err != nil {
|
if err = t.getMembers(tx); err != nil {
|
||||||
return errors.Newf("getMembers: %v", err)
|
return errors.Newf("getMembers: %v", err)
|
||||||
}
|
}
|
||||||
for _, u := range t.Members {
|
for _, u := range t.Members {
|
||||||
if err = watchRepo(e, u.ID, repo.ID, true); err != nil {
|
if err = watchRepo(tx, u.ID, repo.ID, true); err != nil {
|
||||||
return errors.Newf("watchRepo: %v", err)
|
return errors.Newf("watchRepo: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -137,42 +135,34 @@ func (t *Team) AddRepository(repo *Repository) (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
sess := x.NewSession()
|
return db.Transaction(func(tx *gorm.DB) error {
|
||||||
defer sess.Close()
|
return t.addRepository(tx, repo)
|
||||||
if err = sess.Begin(); err != nil {
|
})
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = t.addRepository(sess, repo); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return sess.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Team) removeRepository(e Engine, repo *Repository, recalculate bool) (err error) {
|
func (t *Team) removeRepository(tx *gorm.DB, repo *Repository, recalculate bool) (err error) {
|
||||||
if err = removeTeamRepo(e, t.ID, repo.ID); err != nil {
|
if err = removeTeamRepo(tx, t.ID, repo.ID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
t.NumRepos--
|
t.NumRepos--
|
||||||
if _, err = e.ID(t.ID).AllCols().Update(t); err != nil {
|
if err = tx.Model(&Team{}).Where("id = ?", t.ID).Updates(t).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't need to recalculate when delete a repository from organization.
|
// Don't need to recalculate when delete a repository from organization.
|
||||||
if recalculate {
|
if recalculate {
|
||||||
if err = repo.recalculateTeamAccesses(e, t.ID); err != nil {
|
if err = repo.recalculateTeamAccesses(tx, t.ID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = t.getMembers(e); err != nil {
|
if err = t.getMembers(tx); err != nil {
|
||||||
return errors.Newf("get team members: %v", err)
|
return errors.Newf("get team members: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Delete me when this method is migrated to use GORM.
|
// TODO: Delete me when this method is migrated to use GORM.
|
||||||
userAccessMode := func(e Engine, userID int64, repo *Repository) (AccessMode, error) {
|
userAccessMode := func(tx *gorm.DB, userID int64, repo *Repository) (AccessMode, error) {
|
||||||
mode := AccessModeNone
|
mode := AccessModeNone
|
||||||
// Everyone has read access to public repository
|
// Everyone has read access to public repository
|
||||||
if !repo.IsPrivate {
|
if !repo.IsPrivate {
|
||||||
@@ -191,26 +181,29 @@ func (t *Team) removeRepository(e Engine, repo *Repository, recalculate bool) (e
|
|||||||
UserID: userID,
|
UserID: userID,
|
||||||
RepoID: repo.ID,
|
RepoID: repo.ID,
|
||||||
}
|
}
|
||||||
if has, err := e.Get(access); !has || err != nil {
|
err := tx.Where("user_id = ? AND repo_id = ?", userID, repo.ID).First(access).Error
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return mode, nil
|
||||||
|
} else if err != nil {
|
||||||
return mode, err
|
return mode, err
|
||||||
}
|
}
|
||||||
return access.Mode, nil
|
return access.Mode, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
hasAccess := func(e Engine, userID int64, repo *Repository, testMode AccessMode) (bool, error) {
|
hasAccess := func(tx *gorm.DB, userID int64, repo *Repository, testMode AccessMode) (bool, error) {
|
||||||
mode, err := userAccessMode(e, userID, repo)
|
mode, err := userAccessMode(tx, userID, repo)
|
||||||
return mode >= testMode, err
|
return mode >= testMode, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, member := range t.Members {
|
for _, member := range t.Members {
|
||||||
has, err := hasAccess(e, member.ID, repo, AccessModeRead)
|
has, err := hasAccess(tx, member.ID, repo, AccessModeRead)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
} else if has {
|
} else if has {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = watchRepo(e, member.ID, repo.ID, false); err != nil {
|
if err = watchRepo(tx, member.ID, repo.ID, false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -229,17 +222,9 @@ func (t *Team) RemoveRepository(repoID int64) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
sess := x.NewSession()
|
return db.Transaction(func(tx *gorm.DB) error {
|
||||||
defer sess.Close()
|
return t.removeRepository(tx, repo, true)
|
||||||
if err = sess.Begin(); err != nil {
|
})
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = t.removeRepository(sess, repo, true); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return sess.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var reservedTeamNames = map[string]struct{}{
|
var reservedTeamNames = map[string]struct{}{
|
||||||
@@ -264,37 +249,30 @@ func NewTeam(t *Team) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
has, err := x.Id(t.OrgID).Get(new(User))
|
err := db.Where("id = ?", t.OrgID).First(new(User)).Error
|
||||||
if err != nil {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
return err
|
|
||||||
} else if !has {
|
|
||||||
return ErrOrgNotExist
|
return ErrOrgNotExist
|
||||||
|
} else if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
t.LowerName = strings.ToLower(t.Name)
|
t.LowerName = strings.ToLower(t.Name)
|
||||||
existingTeam := Team{}
|
existingTeam := Team{}
|
||||||
has, err = x.Where("org_id=?", t.OrgID).And("lower_name=?", t.LowerName).Get(&existingTeam)
|
err = db.Where("org_id = ? AND lower_name = ?", t.OrgID, t.LowerName).First(&existingTeam).Error
|
||||||
if err != nil {
|
if err == nil {
|
||||||
return err
|
|
||||||
} else if has {
|
|
||||||
return ErrTeamAlreadyExist{existingTeam.ID, t.OrgID, t.LowerName}
|
return ErrTeamAlreadyExist{existingTeam.ID, t.OrgID, t.LowerName}
|
||||||
}
|
} else if !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
|
||||||
sess := x.NewSession()
|
|
||||||
defer sess.Close()
|
|
||||||
if err = sess.Begin(); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = sess.Insert(t); err != nil {
|
return db.Transaction(func(tx *gorm.DB) error {
|
||||||
return err
|
if err := tx.Create(t).Error; err != nil {
|
||||||
}
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Update organization number of teams.
|
// Update organization number of teams.
|
||||||
if _, err = sess.Exec("UPDATE `user` SET num_teams=num_teams+1 WHERE id = ?", t.OrgID); err != nil {
|
return tx.Exec("UPDATE `user` SET num_teams=num_teams+1 WHERE id = ?", t.OrgID).Error
|
||||||
return err
|
})
|
||||||
}
|
|
||||||
return sess.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ errutil.NotFound = (*ErrTeamNotExist)(nil)
|
var _ errutil.NotFound = (*ErrTeamNotExist)(nil)
|
||||||
@@ -316,49 +294,46 @@ func (ErrTeamNotExist) NotFound() bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTeamOfOrgByName(e Engine, orgID int64, name string) (*Team, error) {
|
func getTeamOfOrgByName(tx *gorm.DB, orgID int64, name string) (*Team, error) {
|
||||||
t := &Team{
|
t := new(Team)
|
||||||
OrgID: orgID,
|
err := tx.Where("org_id = ? AND lower_name = ?", orgID, strings.ToLower(name)).First(t).Error
|
||||||
LowerName: strings.ToLower(name),
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
}
|
|
||||||
has, err := e.Get(t)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else if !has {
|
|
||||||
return nil, ErrTeamNotExist{args: map[string]any{"orgID": orgID, "name": name}}
|
return nil, ErrTeamNotExist{args: map[string]any{"orgID": orgID, "name": name}}
|
||||||
|
} else if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
return t, nil
|
return t, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTeamOfOrgByName returns team by given team name and organization.
|
// GetTeamOfOrgByName returns team by given team name and organization.
|
||||||
func GetTeamOfOrgByName(orgID int64, name string) (*Team, error) {
|
func GetTeamOfOrgByName(orgID int64, name string) (*Team, error) {
|
||||||
return getTeamOfOrgByName(x, orgID, name)
|
return getTeamOfOrgByName(db, orgID, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTeamByID(e Engine, teamID int64) (*Team, error) {
|
func getTeamByID(tx *gorm.DB, teamID int64) (*Team, error) {
|
||||||
t := new(Team)
|
t := new(Team)
|
||||||
has, err := e.ID(teamID).Get(t)
|
err := tx.Where("id = ?", teamID).First(t).Error
|
||||||
if err != nil {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
return nil, err
|
|
||||||
} else if !has {
|
|
||||||
return nil, ErrTeamNotExist{args: map[string]any{"teamID": teamID}}
|
return nil, ErrTeamNotExist{args: map[string]any{"teamID": teamID}}
|
||||||
|
} else if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
return t, nil
|
return t, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTeamByID returns team by given ID.
|
// GetTeamByID returns team by given ID.
|
||||||
func GetTeamByID(teamID int64) (*Team, error) {
|
func GetTeamByID(teamID int64) (*Team, error) {
|
||||||
return getTeamByID(x, teamID)
|
return getTeamByID(db, teamID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTeamsByOrgID(e Engine, orgID int64) ([]*Team, error) {
|
func getTeamsByOrgID(tx *gorm.DB, orgID int64) ([]*Team, error) {
|
||||||
teams := make([]*Team, 0, 3)
|
teams := make([]*Team, 0, 3)
|
||||||
return teams, e.Where("org_id = ?", orgID).Find(&teams)
|
return teams, tx.Where("org_id = ?", orgID).Find(&teams).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTeamsByOrgID returns all teams belong to given organization.
|
// GetTeamsByOrgID returns all teams belong to given organization.
|
||||||
func GetTeamsByOrgID(orgID int64) ([]*Team, error) {
|
func GetTeamsByOrgID(orgID int64) ([]*Team, error) {
|
||||||
return getTeamsByOrgID(x, orgID)
|
return getTeamsByOrgID(db, orgID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateTeam updates information of team.
|
// UpdateTeam updates information of team.
|
||||||
@@ -371,39 +346,35 @@ func UpdateTeam(t *Team, authChanged bool) (err error) {
|
|||||||
t.Description = t.Description[:255]
|
t.Description = t.Description[:255]
|
||||||
}
|
}
|
||||||
|
|
||||||
sess := x.NewSession()
|
|
||||||
defer sess.Close()
|
|
||||||
if err = sess.Begin(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
t.LowerName = strings.ToLower(t.Name)
|
t.LowerName = strings.ToLower(t.Name)
|
||||||
existingTeam := new(Team)
|
existingTeam := new(Team)
|
||||||
has, err := x.Where("org_id=?", t.OrgID).And("lower_name=?", t.LowerName).And("id!=?", t.ID).Get(existingTeam)
|
err = db.Where("org_id = ? AND lower_name = ? AND id != ?", t.OrgID, t.LowerName, t.ID).First(existingTeam).Error
|
||||||
if err != nil {
|
if err == nil {
|
||||||
return err
|
|
||||||
} else if has {
|
|
||||||
return ErrTeamAlreadyExist{existingTeam.ID, t.OrgID, t.LowerName}
|
return ErrTeamAlreadyExist{existingTeam.ID, t.OrgID, t.LowerName}
|
||||||
|
} else if !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = sess.ID(t.ID).AllCols().Update(t); err != nil {
|
return db.Transaction(func(tx *gorm.DB) error {
|
||||||
return errors.Newf("update: %v", err)
|
if err := tx.Model(&Team{}).Where("id = ?", t.ID).Updates(t).Error; err != nil {
|
||||||
}
|
return errors.Newf("update: %v", err)
|
||||||
|
|
||||||
// Update access for team members if needed.
|
|
||||||
if authChanged {
|
|
||||||
if err = t.getRepositories(sess); err != nil {
|
|
||||||
return errors.Newf("getRepositories:%v", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, repo := range t.Repos {
|
// Update access for team members if needed.
|
||||||
if err = repo.recalculateTeamAccesses(sess, 0); err != nil {
|
if authChanged {
|
||||||
return errors.Newf("recalculateTeamAccesses: %v", err)
|
if err := t.getRepositories(tx); err != nil {
|
||||||
|
return errors.Newf("getRepositories:%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, repo := range t.Repos {
|
||||||
|
if err := repo.recalculateTeamAccesses(tx, 0); err != nil {
|
||||||
|
return errors.Newf("recalculateTeamAccesses: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return sess.Commit()
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteTeam deletes given team.
|
// DeleteTeam deletes given team.
|
||||||
@@ -419,34 +390,26 @@ func DeleteTeam(t *Team) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
sess := x.NewSession()
|
return db.Transaction(func(tx *gorm.DB) error {
|
||||||
defer sess.Close()
|
// Delete all accesses.
|
||||||
if err = sess.Begin(); err != nil {
|
for _, repo := range t.Repos {
|
||||||
return err
|
if err := repo.recalculateTeamAccesses(tx, t.ID); err != nil {
|
||||||
}
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Delete all accesses.
|
// Delete team-user.
|
||||||
for _, repo := range t.Repos {
|
if err := tx.Where("org_id = ? AND team_id = ?", org.ID, t.ID).Delete(new(TeamUser)).Error; err != nil {
|
||||||
if err = repo.recalculateTeamAccesses(sess, t.ID); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Delete team-user.
|
// Delete team.
|
||||||
if _, err = sess.Where("org_id=?", org.ID).Where("team_id=?", t.ID).Delete(new(TeamUser)); err != nil {
|
if err := tx.Where("id = ?", t.ID).Delete(new(Team)).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
// Update organization number of teams.
|
||||||
// Delete team.
|
return tx.Exec("UPDATE `user` SET num_teams=num_teams-1 WHERE id = ?", t.OrgID).Error
|
||||||
if _, err = sess.ID(t.ID).Delete(new(Team)); err != nil {
|
})
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Update organization number of teams.
|
|
||||||
if _, err = sess.Exec("UPDATE `user` SET num_teams=num_teams-1 WHERE id=?", t.OrgID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return sess.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ___________ ____ ___
|
// ___________ ____ ___
|
||||||
@@ -459,31 +422,30 @@ func DeleteTeam(t *Team) error {
|
|||||||
// TeamUser represents an team-user relation.
|
// TeamUser represents an team-user relation.
|
||||||
type TeamUser struct {
|
type TeamUser struct {
|
||||||
ID int64
|
ID int64
|
||||||
OrgID int64 `xorm:"INDEX"`
|
OrgID int64 `gorm:"index"`
|
||||||
TeamID int64 `xorm:"UNIQUE(s)"`
|
TeamID int64 `gorm:"uniqueIndex:team_user_team_id_uid"`
|
||||||
UID int64 `xorm:"UNIQUE(s)"`
|
UID int64 `gorm:"uniqueIndex:team_user_team_id_uid"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func isTeamMember(e Engine, orgID, teamID, uid int64) bool {
|
func isTeamMember(tx *gorm.DB, orgID, teamID, uid int64) bool {
|
||||||
has, _ := e.Where("org_id=?", orgID).And("team_id=?", teamID).And("uid=?", uid).Get(new(TeamUser))
|
err := tx.Where("org_id = ? AND team_id = ? AND uid = ?", orgID, teamID, uid).First(new(TeamUser)).Error
|
||||||
return has
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsTeamMember returns true if given user is a member of team.
|
// IsTeamMember returns true if given user is a member of team.
|
||||||
func IsTeamMember(orgID, teamID, uid int64) bool {
|
func IsTeamMember(orgID, teamID, uid int64) bool {
|
||||||
return isTeamMember(x, orgID, teamID, uid)
|
return isTeamMember(db, orgID, teamID, uid)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTeamMembers(e Engine, teamID int64) (_ []*User, err error) {
|
func getTeamMembers(tx *gorm.DB, teamID int64) (_ []*User, err error) {
|
||||||
teamUsers := make([]*TeamUser, 0, 10)
|
teamUsers := make([]*TeamUser, 0, 10)
|
||||||
if err = e.Sql("SELECT `id`, `org_id`, `team_id`, `uid` FROM `team_user` WHERE team_id = ?", teamID).
|
if err = tx.Select("id, org_id, team_id, uid").Where("team_id = ?", teamID).Find(&teamUsers).Error; err != nil {
|
||||||
Find(&teamUsers); err != nil {
|
|
||||||
return nil, errors.Newf("get team-users: %v", err)
|
return nil, errors.Newf("get team-users: %v", err)
|
||||||
}
|
}
|
||||||
members := make([]*User, 0, len(teamUsers))
|
members := make([]*User, 0, len(teamUsers))
|
||||||
for i := range teamUsers {
|
for i := range teamUsers {
|
||||||
member := new(User)
|
member := new(User)
|
||||||
if _, err = e.ID(teamUsers[i].UID).Get(member); err != nil {
|
if err = tx.Where("id = ?", teamUsers[i].UID).First(member).Error; err != nil {
|
||||||
return nil, errors.Newf("get user '%d': %v", teamUsers[i].UID, err)
|
return nil, errors.Newf("get user '%d': %v", teamUsers[i].UID, err)
|
||||||
}
|
}
|
||||||
members = append(members, member)
|
members = append(members, member)
|
||||||
@@ -493,12 +455,12 @@ func getTeamMembers(e Engine, teamID int64) (_ []*User, err error) {
|
|||||||
|
|
||||||
// GetTeamMembers returns all members in given team of organization.
|
// GetTeamMembers returns all members in given team of organization.
|
||||||
func GetTeamMembers(teamID int64) ([]*User, error) {
|
func GetTeamMembers(teamID int64) ([]*User, error) {
|
||||||
return getTeamMembers(x, teamID)
|
return getTeamMembers(db, teamID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getUserTeams(e Engine, orgID, userID int64) ([]*Team, error) {
|
func getUserTeams(tx *gorm.DB, orgID, userID int64) ([]*Team, error) {
|
||||||
teamUsers := make([]*TeamUser, 0, 5)
|
teamUsers := make([]*TeamUser, 0, 5)
|
||||||
if err := e.Where("uid = ?", userID).And("org_id = ?", orgID).Find(&teamUsers); err != nil {
|
if err := tx.Where("uid = ? AND org_id = ?", userID, orgID).Find(&teamUsers).Error; err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -509,12 +471,12 @@ func getUserTeams(e Engine, orgID, userID int64) ([]*Team, error) {
|
|||||||
teamIDs[len(teamUsers)] = -1
|
teamIDs[len(teamUsers)] = -1
|
||||||
|
|
||||||
teams := make([]*Team, 0, len(teamIDs))
|
teams := make([]*Team, 0, len(teamIDs))
|
||||||
return teams, e.Where("org_id = ?", orgID).In("id", teamIDs).Find(&teams)
|
return teams, tx.Where("org_id = ? AND id IN ?", orgID, teamIDs).Find(&teams).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUserTeams returns all teams that user belongs to in given organization.
|
// GetUserTeams returns all teams that user belongs to in given organization.
|
||||||
func GetUserTeams(orgID, userID int64) ([]*Team, error) {
|
func GetUserTeams(orgID, userID int64) ([]*Team, error) {
|
||||||
return getUserTeams(x, orgID, userID)
|
return getUserTeams(db, orgID, userID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddTeamMember adds new membership of given team to given organization,
|
// AddTeamMember adds new membership of given team to given organization,
|
||||||
@@ -539,53 +501,46 @@ func AddTeamMember(orgID, teamID, userID int64) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
sess := x.NewSession()
|
return db.Transaction(func(tx *gorm.DB) error {
|
||||||
defer sess.Close()
|
tu := &TeamUser{
|
||||||
if err = sess.Begin(); err != nil {
|
UID: userID,
|
||||||
return err
|
OrgID: orgID,
|
||||||
}
|
TeamID: teamID,
|
||||||
|
}
|
||||||
tu := &TeamUser{
|
if err := tx.Create(tu).Error; err != nil {
|
||||||
UID: userID,
|
return err
|
||||||
OrgID: orgID,
|
}
|
||||||
TeamID: teamID,
|
if err := tx.Model(&Team{}).Where("id = ?", t.ID).Updates(t).Error; err != nil {
|
||||||
}
|
|
||||||
if _, err = sess.Insert(tu); err != nil {
|
|
||||||
return err
|
|
||||||
} else if _, err = sess.ID(t.ID).Update(t); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Give access to team repositories.
|
|
||||||
for _, repo := range t.Repos {
|
|
||||||
if err = repo.recalculateTeamAccesses(sess, 0); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// We make sure it exists before.
|
// Give access to team repositories.
|
||||||
ou := new(OrgUser)
|
for _, repo := range t.Repos {
|
||||||
if _, err = sess.Where("uid = ?", userID).And("org_id = ?", orgID).Get(ou); err != nil {
|
if err := repo.recalculateTeamAccesses(tx, 0); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ou.NumTeams++
|
}
|
||||||
if t.IsOwnerTeam() {
|
|
||||||
ou.IsOwner = true
|
|
||||||
}
|
|
||||||
if _, err = sess.ID(ou.ID).AllCols().Update(ou); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return sess.Commit()
|
// We make sure it exists before.
|
||||||
|
ou := new(OrgUser)
|
||||||
|
if err := tx.Where("uid = ? AND org_id = ?", userID, orgID).First(ou).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ou.NumTeams++
|
||||||
|
if t.IsOwnerTeam() {
|
||||||
|
ou.IsOwner = true
|
||||||
|
}
|
||||||
|
return tx.Model(&OrgUser{}).Where("id = ?", ou.ID).Updates(ou).Error
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeTeamMember(e Engine, orgID, teamID, uid int64) error {
|
func removeTeamMember(tx *gorm.DB, orgID, teamID, uid int64) error {
|
||||||
if !isTeamMember(e, orgID, teamID, uid) {
|
if !isTeamMember(tx, orgID, teamID, uid) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get team and its repositories.
|
// Get team and its repositories.
|
||||||
t, err := getTeamByID(e, teamID)
|
t, err := getTeamByID(tx, teamID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -597,12 +552,12 @@ func removeTeamMember(e Engine, orgID, teamID, uid int64) error {
|
|||||||
|
|
||||||
t.NumMembers--
|
t.NumMembers--
|
||||||
|
|
||||||
if err = t.getRepositories(e); err != nil {
|
if err = t.getRepositories(tx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get organization.
|
// Get organization.
|
||||||
org, err := getUserByID(e, orgID)
|
org, err := getUserByID(tx, orgID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -612,46 +567,37 @@ func removeTeamMember(e Engine, orgID, teamID, uid int64) error {
|
|||||||
OrgID: orgID,
|
OrgID: orgID,
|
||||||
TeamID: teamID,
|
TeamID: teamID,
|
||||||
}
|
}
|
||||||
if _, err := e.Delete(tu); err != nil {
|
if err := tx.Where("uid = ? AND org_id = ? AND team_id = ?", uid, orgID, teamID).Delete(tu).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
} else if _, err = e.ID(t.ID).AllCols().Update(t); err != nil {
|
}
|
||||||
|
if err = tx.Model(&Team{}).Where("id = ?", t.ID).Updates(t).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete access to team repositories.
|
// Delete access to team repositories.
|
||||||
for _, repo := range t.Repos {
|
for _, repo := range t.Repos {
|
||||||
if err = repo.recalculateTeamAccesses(e, 0); err != nil {
|
if err = repo.recalculateTeamAccesses(tx, 0); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This must exist.
|
// This must exist.
|
||||||
ou := new(OrgUser)
|
ou := new(OrgUser)
|
||||||
_, err = e.Where("uid = ?", uid).And("org_id = ?", org.ID).Get(ou)
|
if err = tx.Where("uid = ? AND org_id = ?", uid, org.ID).First(ou).Error; err != nil {
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ou.NumTeams--
|
ou.NumTeams--
|
||||||
if t.IsOwnerTeam() {
|
if t.IsOwnerTeam() {
|
||||||
ou.IsOwner = false
|
ou.IsOwner = false
|
||||||
}
|
}
|
||||||
if _, err = e.ID(ou.ID).AllCols().Update(ou); err != nil {
|
return tx.Model(&OrgUser{}).Where("id = ?", ou.ID).Updates(ou).Error
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveTeamMember removes member from given team of given organization.
|
// RemoveTeamMember removes member from given team of given organization.
|
||||||
func RemoveTeamMember(orgID, teamID, uid int64) error {
|
func RemoveTeamMember(orgID, teamID, uid int64) error {
|
||||||
sess := x.NewSession()
|
return db.Transaction(func(tx *gorm.DB) error {
|
||||||
defer sess.Close()
|
return removeTeamMember(tx, orgID, teamID, uid)
|
||||||
if err := sess.Begin(); err != nil {
|
})
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := removeTeamMember(sess, orgID, teamID, uid); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return sess.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ___________ __________
|
// ___________ __________
|
||||||
@@ -664,54 +610,49 @@ func RemoveTeamMember(orgID, teamID, uid int64) error {
|
|||||||
// TeamRepo represents an team-repository relation.
|
// TeamRepo represents an team-repository relation.
|
||||||
type TeamRepo struct {
|
type TeamRepo struct {
|
||||||
ID int64
|
ID int64
|
||||||
OrgID int64 `xorm:"INDEX"`
|
OrgID int64 `gorm:"index"`
|
||||||
TeamID int64 `xorm:"UNIQUE(s)"`
|
TeamID int64 `gorm:"uniqueIndex:team_repo_team_id_repo_id"`
|
||||||
RepoID int64 `xorm:"UNIQUE(s)"`
|
RepoID int64 `gorm:"uniqueIndex:team_repo_team_id_repo_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func hasTeamRepo(e Engine, orgID, teamID, repoID int64) bool {
|
func hasTeamRepo(tx *gorm.DB, orgID, teamID, repoID int64) bool {
|
||||||
has, _ := e.Where("org_id = ?", orgID).And("team_id = ?", teamID).And("repo_id = ?", repoID).Get(new(TeamRepo))
|
err := tx.Where("org_id = ? AND team_id = ? AND repo_id = ?", orgID, teamID, repoID).First(new(TeamRepo)).Error
|
||||||
return has
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasTeamRepo returns true if given team has access to the repository of the organization.
|
// HasTeamRepo returns true if given team has access to the repository of the organization.
|
||||||
func HasTeamRepo(orgID, teamID, repoID int64) bool {
|
func HasTeamRepo(orgID, teamID, repoID int64) bool {
|
||||||
return hasTeamRepo(x, orgID, teamID, repoID)
|
return hasTeamRepo(db, orgID, teamID, repoID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func addTeamRepo(e Engine, orgID, teamID, repoID int64) error {
|
func addTeamRepo(tx *gorm.DB, orgID, teamID, repoID int64) error {
|
||||||
_, err := e.InsertOne(&TeamRepo{
|
return tx.Create(&TeamRepo{
|
||||||
OrgID: orgID,
|
OrgID: orgID,
|
||||||
TeamID: teamID,
|
TeamID: teamID,
|
||||||
RepoID: repoID,
|
RepoID: repoID,
|
||||||
})
|
}).Error
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddTeamRepo adds new repository relation to team.
|
// AddTeamRepo adds new repository relation to team.
|
||||||
func AddTeamRepo(orgID, teamID, repoID int64) error {
|
func AddTeamRepo(orgID, teamID, repoID int64) error {
|
||||||
return addTeamRepo(x, orgID, teamID, repoID)
|
return addTeamRepo(db, orgID, teamID, repoID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeTeamRepo(e Engine, teamID, repoID int64) error {
|
func removeTeamRepo(tx *gorm.DB, teamID, repoID int64) error {
|
||||||
_, err := e.Delete(&TeamRepo{
|
return tx.Where("team_id = ? AND repo_id = ?", teamID, repoID).Delete(new(TeamRepo)).Error
|
||||||
TeamID: teamID,
|
|
||||||
RepoID: repoID,
|
|
||||||
})
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveTeamRepo deletes repository relation to team.
|
// RemoveTeamRepo deletes repository relation to team.
|
||||||
func RemoveTeamRepo(teamID, repoID int64) error {
|
func RemoveTeamRepo(teamID, repoID int64) error {
|
||||||
return removeTeamRepo(x, teamID, repoID)
|
return removeTeamRepo(db, teamID, repoID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTeamsHaveAccessToRepo returns all teams in an organization that have given access level to the repository.
|
// GetTeamsHaveAccessToRepo returns all teams in an organization that have given access level to the repository.
|
||||||
func GetTeamsHaveAccessToRepo(orgID, repoID int64, mode AccessMode) ([]*Team, error) {
|
func GetTeamsHaveAccessToRepo(orgID, repoID int64, mode AccessMode) ([]*Team, error) {
|
||||||
teams := make([]*Team, 0, 5)
|
teams := make([]*Team, 0, 5)
|
||||||
return teams, x.Where("team.authorize >= ?", mode).
|
return teams, db.Table("team").
|
||||||
Join("INNER", "team_repo", "team_repo.team_id = team.id").
|
Where("team.authorize >= ?", mode).
|
||||||
And("team_repo.org_id = ?", orgID).
|
Joins("INNER JOIN team_repo ON team_repo.team_id = team.id").
|
||||||
And("team_repo.repo_id = ?", repoID).
|
Where("team_repo.org_id = ? AND team_repo.repo_id = ?", orgID, repoID).
|
||||||
Find(&teams)
|
Find(&teams).Error
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import (
|
|||||||
|
|
||||||
"github.com/cockroachdb/errors"
|
"github.com/cockroachdb/errors"
|
||||||
"github.com/unknwon/com"
|
"github.com/unknwon/com"
|
||||||
|
"gorm.io/gorm"
|
||||||
log "unknwon.dev/clog/v2"
|
log "unknwon.dev/clog/v2"
|
||||||
"xorm.io/xorm"
|
|
||||||
|
|
||||||
"github.com/gogs/git-module"
|
"github.com/gogs/git-module"
|
||||||
api "github.com/gogs/go-gogs-client"
|
api "github.com/gogs/go-gogs-client"
|
||||||
@@ -71,35 +71,31 @@ func (pr *PullRequest) BeforeUpdate() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Note: don't try to get Issue because will end up recursive querying.
|
// Note: don't try to get Issue because will end up recursive querying.
|
||||||
func (pr *PullRequest) AfterSet(colName string, _ xorm.Cell) {
|
func (pr *PullRequest) AfterFind(tx *gorm.DB) error {
|
||||||
switch colName {
|
if pr.HasMerged {
|
||||||
case "merged_unix":
|
|
||||||
if !pr.HasMerged {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
pr.Merged = time.Unix(pr.MergedUnix, 0).Local()
|
pr.Merged = time.Unix(pr.MergedUnix, 0).Local()
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: don't try to get Issue because will end up recursive querying.
|
// Note: don't try to get Issue because will end up recursive querying.
|
||||||
func (pr *PullRequest) loadAttributes(e Engine) (err error) {
|
func (pr *PullRequest) loadAttributes(db *gorm.DB) (err error) {
|
||||||
if pr.HeadRepo == nil {
|
if pr.HeadRepo == nil {
|
||||||
pr.HeadRepo, err = getRepositoryByID(e, pr.HeadRepoID)
|
pr.HeadRepo, err = getRepositoryByID(db, pr.HeadRepoID)
|
||||||
if err != nil && !IsErrRepoNotExist(err) {
|
if err != nil && !IsErrRepoNotExist(err) {
|
||||||
return errors.Newf("get head repository by ID: %v", err)
|
return errors.Newf("get head repository by ID: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if pr.BaseRepo == nil {
|
if pr.BaseRepo == nil {
|
||||||
pr.BaseRepo, err = getRepositoryByID(e, pr.BaseRepoID)
|
pr.BaseRepo, err = getRepositoryByID(db, pr.BaseRepoID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Newf("get base repository by ID: %v", err)
|
return errors.Newf("get base repository by ID: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if pr.HasMerged && pr.Merger == nil {
|
if pr.HasMerged && pr.Merger == nil {
|
||||||
pr.Merger, err = getUserByID(e, pr.MergerID)
|
pr.Merger, err = getUserByID(db, pr.MergerID)
|
||||||
if IsErrUserNotExist(err) {
|
if IsErrUserNotExist(err) {
|
||||||
pr.MergerID = -1
|
pr.MergerID = -1
|
||||||
pr.Merger = NewGhostUser()
|
pr.Merger = NewGhostUser()
|
||||||
@@ -112,7 +108,7 @@ func (pr *PullRequest) loadAttributes(e Engine) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (pr *PullRequest) LoadAttributes() error {
|
func (pr *PullRequest) LoadAttributes() error {
|
||||||
return pr.loadAttributes(x)
|
return pr.loadAttributes(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pr *PullRequest) LoadIssue() (err error) {
|
func (pr *PullRequest) LoadIssue() (err error) {
|
||||||
@@ -199,198 +195,190 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle
|
|||||||
go AddTestPullRequestTask(doer, pr.BaseRepo.ID, pr.BaseBranch, false)
|
go AddTestPullRequestTask(doer, pr.BaseRepo.ID, pr.BaseBranch, false)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
sess := x.NewSession()
|
return db.Transaction(func(tx *gorm.DB) error {
|
||||||
defer sess.Close()
|
if err := pr.Issue.changeStatus(tx, doer, pr.Issue.Repo, true); err != nil {
|
||||||
if err = sess.Begin(); err != nil {
|
return errors.Newf("Issue.changeStatus: %v", err)
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = pr.Issue.changeStatus(sess, doer, pr.Issue.Repo, true); err != nil {
|
|
||||||
return errors.Newf("Issue.changeStatus: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
headRepoPath := RepoPath(pr.HeadUserName, pr.HeadRepo.Name)
|
|
||||||
headGitRepo, err := git.Open(headRepoPath)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Newf("open repository: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create temporary directory to store temporary copy of the base repository,
|
|
||||||
// and clean it up when operation finished regardless of succeed or not.
|
|
||||||
tmpBasePath := filepath.Join(conf.Server.AppDataPath, "tmp", "repos", com.ToStr(time.Now().Nanosecond())+".git")
|
|
||||||
if err = os.MkdirAll(filepath.Dir(tmpBasePath), os.ModePerm); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
_ = os.RemoveAll(filepath.Dir(tmpBasePath))
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Clone the base repository to the defined temporary directory,
|
|
||||||
// and checks out to base branch directly.
|
|
||||||
var stderr string
|
|
||||||
if _, stderr, err = process.ExecTimeout(5*time.Minute,
|
|
||||||
fmt.Sprintf("PullRequest.Merge (git clone): %s", tmpBasePath),
|
|
||||||
"git", "clone", "-b", pr.BaseBranch, baseGitRepo.Path(), tmpBasePath); err != nil {
|
|
||||||
return errors.Newf("git clone: %s", stderr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add remote which points to the head repository.
|
|
||||||
if _, stderr, err = process.ExecDir(-1, tmpBasePath,
|
|
||||||
fmt.Sprintf("PullRequest.Merge (git remote add): %s", tmpBasePath),
|
|
||||||
"git", "remote", "add", "head_repo", headRepoPath); err != nil {
|
|
||||||
return errors.Newf("git remote add [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch information from head repository to the temporary copy.
|
|
||||||
if _, stderr, err = process.ExecDir(-1, tmpBasePath,
|
|
||||||
fmt.Sprintf("PullRequest.Merge (git fetch): %s", tmpBasePath),
|
|
||||||
"git", "fetch", "head_repo"); err != nil {
|
|
||||||
return errors.Newf("git fetch [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr)
|
|
||||||
}
|
|
||||||
|
|
||||||
remoteHeadBranch := "head_repo/" + pr.HeadBranch
|
|
||||||
|
|
||||||
// Check if merge style is allowed, reset to default style if not
|
|
||||||
if mergeStyle == MergeStyleRebase && !pr.BaseRepo.PullsAllowRebase {
|
|
||||||
mergeStyle = MergeStyleRegular
|
|
||||||
}
|
|
||||||
|
|
||||||
switch mergeStyle {
|
|
||||||
case MergeStyleRegular: // Create merge commit
|
|
||||||
|
|
||||||
// Merge changes from head branch.
|
|
||||||
if _, stderr, err = process.ExecDir(-1, tmpBasePath,
|
|
||||||
fmt.Sprintf("PullRequest.Merge (git merge --no-ff --no-commit): %s", tmpBasePath),
|
|
||||||
"git", "merge", "--no-ff", "--no-commit", remoteHeadBranch); err != nil {
|
|
||||||
return errors.Newf("git merge --no-ff --no-commit [%s]: %v - %s", tmpBasePath, err, stderr)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a merge commit for the base branch.
|
headRepoPath := RepoPath(pr.HeadUserName, pr.HeadRepo.Name)
|
||||||
if _, stderr, err = process.ExecDir(-1, tmpBasePath,
|
headGitRepo, err := git.Open(headRepoPath)
|
||||||
fmt.Sprintf("PullRequest.Merge (git merge): %s", tmpBasePath),
|
if err != nil {
|
||||||
"git", "commit", fmt.Sprintf("--author='%s <%s>'", doer.DisplayName(), doer.Email),
|
return errors.Newf("open repository: %v", err)
|
||||||
"-m", fmt.Sprintf("Merge branch '%s' of %s/%s into %s", pr.HeadBranch, pr.HeadUserName, pr.HeadRepo.Name, pr.BaseBranch),
|
|
||||||
"-m", commitDescription); err != nil {
|
|
||||||
return errors.Newf("git commit [%s]: %v - %s", tmpBasePath, err, stderr)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case MergeStyleRebase: // Rebase before merging
|
// Create temporary directory to store temporary copy of the base repository,
|
||||||
|
// and clean it up when operation finished regardless of succeed or not.
|
||||||
|
tmpBasePath := filepath.Join(conf.Server.AppDataPath, "tmp", "repos", com.ToStr(time.Now().Nanosecond())+".git")
|
||||||
|
if err = os.MkdirAll(filepath.Dir(tmpBasePath), os.ModePerm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = os.RemoveAll(filepath.Dir(tmpBasePath))
|
||||||
|
}()
|
||||||
|
|
||||||
// Rebase head branch based on base branch, this creates a non-branch commit state.
|
// Clone the base repository to the defined temporary directory,
|
||||||
if _, stderr, err = process.ExecDir(-1, tmpBasePath,
|
// and checks out to base branch directly.
|
||||||
fmt.Sprintf("PullRequest.Merge (git rebase): %s", tmpBasePath),
|
var stderr string
|
||||||
"git", "rebase", "--quiet", pr.BaseBranch, remoteHeadBranch); err != nil {
|
if _, stderr, err = process.ExecTimeout(5*time.Minute,
|
||||||
return errors.Newf("git rebase [%s on %s]: %s", remoteHeadBranch, pr.BaseBranch, stderr)
|
fmt.Sprintf("PullRequest.Merge (git clone): %s", tmpBasePath),
|
||||||
|
"git", "clone", "-b", pr.BaseBranch, baseGitRepo.Path(), tmpBasePath); err != nil {
|
||||||
|
return errors.Newf("git clone: %s", stderr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Name non-branch commit state to a new temporary branch in order to save changes.
|
// Add remote which points to the head repository.
|
||||||
tmpBranch := com.ToStr(time.Now().UnixNano(), 10)
|
|
||||||
if _, stderr, err = process.ExecDir(-1, tmpBasePath,
|
if _, stderr, err = process.ExecDir(-1, tmpBasePath,
|
||||||
fmt.Sprintf("PullRequest.Merge (git checkout): %s", tmpBasePath),
|
fmt.Sprintf("PullRequest.Merge (git remote add): %s", tmpBasePath),
|
||||||
"git", "checkout", "-b", tmpBranch); err != nil {
|
"git", "remote", "add", "head_repo", headRepoPath); err != nil {
|
||||||
return errors.Newf("git checkout '%s': %s", tmpBranch, stderr)
|
return errors.Newf("git remote add [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check out the base branch to be operated on.
|
// Fetch information from head repository to the temporary copy.
|
||||||
if _, stderr, err = process.ExecDir(-1, tmpBasePath,
|
if _, stderr, err = process.ExecDir(-1, tmpBasePath,
|
||||||
fmt.Sprintf("PullRequest.Merge (git checkout): %s", tmpBasePath),
|
fmt.Sprintf("PullRequest.Merge (git fetch): %s", tmpBasePath),
|
||||||
"git", "checkout", pr.BaseBranch); err != nil {
|
"git", "fetch", "head_repo"); err != nil {
|
||||||
return errors.Newf("git checkout '%s': %s", pr.BaseBranch, stderr)
|
return errors.Newf("git fetch [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge changes from temporary branch to the base branch.
|
remoteHeadBranch := "head_repo/" + pr.HeadBranch
|
||||||
if _, stderr, err = process.ExecDir(-1, tmpBasePath,
|
|
||||||
fmt.Sprintf("PullRequest.Merge (git merge): %s", tmpBasePath),
|
// Check if merge style is allowed, reset to default style if not
|
||||||
"git", "merge", tmpBranch); err != nil {
|
if mergeStyle == MergeStyleRebase && !pr.BaseRepo.PullsAllowRebase {
|
||||||
return errors.Newf("git merge [%s]: %v - %s", tmpBasePath, err, stderr)
|
mergeStyle = MergeStyleRegular
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
switch mergeStyle {
|
||||||
return errors.Newf("unknown merge style: %s", mergeStyle)
|
case MergeStyleRegular: // Create merge commit
|
||||||
}
|
|
||||||
|
|
||||||
// Push changes on base branch to upstream.
|
// Merge changes from head branch.
|
||||||
if _, stderr, err = process.ExecDir(-1, tmpBasePath,
|
if _, stderr, err = process.ExecDir(-1, tmpBasePath,
|
||||||
fmt.Sprintf("PullRequest.Merge (git push): %s", tmpBasePath),
|
fmt.Sprintf("PullRequest.Merge (git merge --no-ff --no-commit): %s", tmpBasePath),
|
||||||
"git", "push", baseGitRepo.Path(), pr.BaseBranch); err != nil {
|
"git", "merge", "--no-ff", "--no-commit", remoteHeadBranch); err != nil {
|
||||||
return errors.Newf("git push: %s", stderr)
|
return errors.Newf("git merge --no-ff --no-commit [%s]: %v - %s", tmpBasePath, err, stderr)
|
||||||
}
|
}
|
||||||
|
|
||||||
pr.MergedCommitID, err = headGitRepo.BranchCommitID(pr.HeadBranch)
|
// Create a merge commit for the base branch.
|
||||||
if err != nil {
|
if _, stderr, err = process.ExecDir(-1, tmpBasePath,
|
||||||
return errors.Newf("get head branch %q commit ID: %v", pr.HeadBranch, err)
|
fmt.Sprintf("PullRequest.Merge (git merge): %s", tmpBasePath),
|
||||||
}
|
"git", "commit", fmt.Sprintf("--author='%s <%s>'", doer.DisplayName(), doer.Email),
|
||||||
|
"-m", fmt.Sprintf("Merge branch '%s' of %s/%s into %s", pr.HeadBranch, pr.HeadUserName, pr.HeadRepo.Name, pr.BaseBranch),
|
||||||
|
"-m", commitDescription); err != nil {
|
||||||
|
return errors.Newf("git commit [%s]: %v - %s", tmpBasePath, err, stderr)
|
||||||
|
}
|
||||||
|
|
||||||
pr.HasMerged = true
|
case MergeStyleRebase: // Rebase before merging
|
||||||
pr.Merged = time.Now()
|
|
||||||
pr.MergerID = doer.ID
|
|
||||||
if _, err = sess.ID(pr.ID).AllCols().Update(pr); err != nil {
|
|
||||||
return errors.Newf("update pull request: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = sess.Commit(); err != nil {
|
// Rebase head branch based on base branch, this creates a non-branch commit state.
|
||||||
return errors.Newf("commit: %v", err)
|
if _, stderr, err = process.ExecDir(-1, tmpBasePath,
|
||||||
}
|
fmt.Sprintf("PullRequest.Merge (git rebase): %s", tmpBasePath),
|
||||||
|
"git", "rebase", "--quiet", pr.BaseBranch, remoteHeadBranch); err != nil {
|
||||||
|
return errors.Newf("git rebase [%s on %s]: %s", remoteHeadBranch, pr.BaseBranch, stderr)
|
||||||
|
}
|
||||||
|
|
||||||
if err = Handle.Actions().MergePullRequest(ctx, doer, pr.Issue.Repo.Owner, pr.Issue.Repo, pr.Issue); err != nil {
|
// Name non-branch commit state to a new temporary branch in order to save changes.
|
||||||
log.Error("Failed to create action for merge pull request, pull_request_id: %d, error: %v", pr.ID, err)
|
tmpBranch := com.ToStr(time.Now().UnixNano(), 10)
|
||||||
}
|
if _, stderr, err = process.ExecDir(-1, tmpBasePath,
|
||||||
|
fmt.Sprintf("PullRequest.Merge (git checkout): %s", tmpBasePath),
|
||||||
|
"git", "checkout", "-b", tmpBranch); err != nil {
|
||||||
|
return errors.Newf("git checkout '%s': %s", tmpBranch, stderr)
|
||||||
|
}
|
||||||
|
|
||||||
// Reload pull request information.
|
// Check out the base branch to be operated on.
|
||||||
if err = pr.LoadAttributes(); err != nil {
|
if _, stderr, err = process.ExecDir(-1, tmpBasePath,
|
||||||
log.Error("LoadAttributes: %v", err)
|
fmt.Sprintf("PullRequest.Merge (git checkout): %s", tmpBasePath),
|
||||||
|
"git", "checkout", pr.BaseBranch); err != nil {
|
||||||
|
return errors.Newf("git checkout '%s': %s", pr.BaseBranch, stderr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge changes from temporary branch to the base branch.
|
||||||
|
if _, stderr, err = process.ExecDir(-1, tmpBasePath,
|
||||||
|
fmt.Sprintf("PullRequest.Merge (git merge): %s", tmpBasePath),
|
||||||
|
"git", "merge", tmpBranch); err != nil {
|
||||||
|
return errors.Newf("git merge [%s]: %v - %s", tmpBasePath, err, stderr)
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return errors.Newf("unknown merge style: %s", mergeStyle)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push changes on base branch to upstream.
|
||||||
|
if _, stderr, err = process.ExecDir(-1, tmpBasePath,
|
||||||
|
fmt.Sprintf("PullRequest.Merge (git push): %s", tmpBasePath),
|
||||||
|
"git", "push", baseGitRepo.Path(), pr.BaseBranch); err != nil {
|
||||||
|
return errors.Newf("git push: %s", stderr)
|
||||||
|
}
|
||||||
|
|
||||||
|
pr.MergedCommitID, err = headGitRepo.BranchCommitID(pr.HeadBranch)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Newf("get head branch %q commit ID: %v", pr.HeadBranch, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pr.HasMerged = true
|
||||||
|
pr.Merged = time.Now()
|
||||||
|
pr.MergerID = doer.ID
|
||||||
|
if err := tx.Model(&PullRequest{}).Where("id = ?", pr.ID).Updates(pr).Error; err != nil {
|
||||||
|
return errors.Newf("update pull request: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = Handle.Actions().MergePullRequest(ctx, doer, pr.Issue.Repo.Owner, pr.Issue.Repo, pr.Issue); err != nil {
|
||||||
|
log.Error("Failed to create action for merge pull request, pull_request_id: %d, error: %v", pr.ID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reload pull request information.
|
||||||
|
if err = pr.LoadAttributes(); err != nil {
|
||||||
|
log.Error("LoadAttributes: %v", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err = PrepareWebhooks(pr.Issue.Repo, HookEventTypePullRequest, &api.PullRequestPayload{
|
||||||
|
Action: api.HOOK_ISSUE_CLOSED,
|
||||||
|
Index: pr.Index,
|
||||||
|
PullRequest: pr.APIFormat(),
|
||||||
|
Repository: pr.Issue.Repo.APIFormatLegacy(nil),
|
||||||
|
Sender: doer.APIFormat(),
|
||||||
|
}); err != nil {
|
||||||
|
log.Error("PrepareWebhooks: %v", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
commits, err := headGitRepo.RevList([]string{pr.MergeBase + "..." + pr.MergedCommitID})
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Failed to list commits [merge_base: %s, merged_commit_id: %s]: %v", pr.MergeBase, pr.MergedCommitID, err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: It is possible that head branch is not fully sync with base branch
|
||||||
|
// for merge commits, so we need to get latest head commit and append merge
|
||||||
|
// commit manually to avoid strange diff commits produced.
|
||||||
|
mergeCommit, err := baseGitRepo.BranchCommit(pr.BaseBranch)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Failed to get base branch %q commit: %v", pr.BaseBranch, err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if mergeStyle == MergeStyleRegular {
|
||||||
|
commits = append([]*git.Commit{mergeCommit}, commits...)
|
||||||
|
}
|
||||||
|
|
||||||
|
pcs, err := CommitsToPushCommits(commits).APIFormat(ctx, Handle.Users(), pr.BaseRepo.RepoPath(), pr.BaseRepo.HTMLURL())
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Failed to convert to API payload commits: %v", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
p := &api.PushPayload{
|
||||||
|
Ref: git.RefsHeads + pr.BaseBranch,
|
||||||
|
Before: pr.MergeBase,
|
||||||
|
After: mergeCommit.ID.String(),
|
||||||
|
CompareURL: conf.Server.ExternalURL + pr.BaseRepo.ComposeCompareURL(pr.MergeBase, pr.MergedCommitID),
|
||||||
|
Commits: pcs,
|
||||||
|
Repo: pr.BaseRepo.APIFormatLegacy(nil),
|
||||||
|
Pusher: pr.HeadRepo.MustOwner().APIFormat(),
|
||||||
|
Sender: doer.APIFormat(),
|
||||||
|
}
|
||||||
|
if err = PrepareWebhooks(pr.BaseRepo, HookEventTypePush, p); err != nil {
|
||||||
|
log.Error("Failed to prepare webhooks: %v", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
})
|
||||||
if err = PrepareWebhooks(pr.Issue.Repo, HookEventTypePullRequest, &api.PullRequestPayload{
|
|
||||||
Action: api.HOOK_ISSUE_CLOSED,
|
|
||||||
Index: pr.Index,
|
|
||||||
PullRequest: pr.APIFormat(),
|
|
||||||
Repository: pr.Issue.Repo.APIFormatLegacy(nil),
|
|
||||||
Sender: doer.APIFormat(),
|
|
||||||
}); err != nil {
|
|
||||||
log.Error("PrepareWebhooks: %v", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
commits, err := headGitRepo.RevList([]string{pr.MergeBase + "..." + pr.MergedCommitID})
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Failed to list commits [merge_base: %s, merged_commit_id: %s]: %v", pr.MergeBase, pr.MergedCommitID, err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: It is possible that head branch is not fully sync with base branch
|
|
||||||
// for merge commits, so we need to get latest head commit and append merge
|
|
||||||
// commit manually to avoid strange diff commits produced.
|
|
||||||
mergeCommit, err := baseGitRepo.BranchCommit(pr.BaseBranch)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Failed to get base branch %q commit: %v", pr.BaseBranch, err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if mergeStyle == MergeStyleRegular {
|
|
||||||
commits = append([]*git.Commit{mergeCommit}, commits...)
|
|
||||||
}
|
|
||||||
|
|
||||||
pcs, err := CommitsToPushCommits(commits).APIFormat(ctx, Handle.Users(), pr.BaseRepo.RepoPath(), pr.BaseRepo.HTMLURL())
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Failed to convert to API payload commits: %v", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
p := &api.PushPayload{
|
|
||||||
Ref: git.RefsHeads + pr.BaseBranch,
|
|
||||||
Before: pr.MergeBase,
|
|
||||||
After: mergeCommit.ID.String(),
|
|
||||||
CompareURL: conf.Server.ExternalURL + pr.BaseRepo.ComposeCompareURL(pr.MergeBase, pr.MergedCommitID),
|
|
||||||
Commits: pcs,
|
|
||||||
Repo: pr.BaseRepo.APIFormatLegacy(nil),
|
|
||||||
Pusher: pr.HeadRepo.MustOwner().APIFormat(),
|
|
||||||
Sender: doer.APIFormat(),
|
|
||||||
}
|
|
||||||
if err = PrepareWebhooks(pr.BaseRepo, HookEventTypePush, p); err != nil {
|
|
||||||
log.Error("Failed to prepare webhooks: %v", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// testPatch checks if patch can be merged to base repository without conflict.
|
// testPatch checks if patch can be merged to base repository without conflict.
|
||||||
@@ -443,45 +431,42 @@ func (pr *PullRequest) testPatch() (err error) {
|
|||||||
|
|
||||||
// NewPullRequest creates new pull request with labels for repository.
|
// NewPullRequest creates new pull request with labels for repository.
|
||||||
func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []string, pr *PullRequest, patch []byte) (err error) {
|
func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []string, pr *PullRequest, patch []byte) (err error) {
|
||||||
sess := x.NewSession()
|
err = db.Transaction(func(tx *gorm.DB) error {
|
||||||
defer sess.Close()
|
if err := newIssue(tx, NewIssueOptions{
|
||||||
if err = sess.Begin(); err != nil {
|
Repo: repo,
|
||||||
|
Issue: pull,
|
||||||
|
LableIDs: labelIDs,
|
||||||
|
Attachments: uuids,
|
||||||
|
IsPull: true,
|
||||||
|
}); err != nil {
|
||||||
|
return errors.Newf("newIssue: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pr.Index = pull.Index
|
||||||
|
if err := repo.SavePatch(pr.Index, patch); err != nil {
|
||||||
|
return errors.Newf("SavePatch: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pr.BaseRepo = repo
|
||||||
|
if err := pr.testPatch(); err != nil {
|
||||||
|
return errors.Newf("testPatch: %v", err)
|
||||||
|
}
|
||||||
|
// No conflict appears after test means mergeable.
|
||||||
|
if pr.Status == PullRequestStatusChecking {
|
||||||
|
pr.Status = PullRequestStatusMergeable
|
||||||
|
}
|
||||||
|
|
||||||
|
pr.IssueID = pull.ID
|
||||||
|
if err := tx.Create(pr).Error; err != nil {
|
||||||
|
return errors.Newf("insert pull repo: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = newIssue(sess, NewIssueOptions{
|
|
||||||
Repo: repo,
|
|
||||||
Issue: pull,
|
|
||||||
LableIDs: labelIDs,
|
|
||||||
Attachments: uuids,
|
|
||||||
IsPull: true,
|
|
||||||
}); err != nil {
|
|
||||||
return errors.Newf("newIssue: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
pr.Index = pull.Index
|
|
||||||
if err = repo.SavePatch(pr.Index, patch); err != nil {
|
|
||||||
return errors.Newf("SavePatch: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
pr.BaseRepo = repo
|
|
||||||
if err = pr.testPatch(); err != nil {
|
|
||||||
return errors.Newf("testPatch: %v", err)
|
|
||||||
}
|
|
||||||
// No conflict appears after test means mergeable.
|
|
||||||
if pr.Status == PullRequestStatusChecking {
|
|
||||||
pr.Status = PullRequestStatusMergeable
|
|
||||||
}
|
|
||||||
|
|
||||||
pr.IssueID = pull.ID
|
|
||||||
if _, err = sess.Insert(pr); err != nil {
|
|
||||||
return errors.Newf("insert pull repo: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = sess.Commit(); err != nil {
|
|
||||||
return errors.Newf("commit: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = NotifyWatchers(&Action{
|
if err = NotifyWatchers(&Action{
|
||||||
ActUserID: pull.Poster.ID,
|
ActUserID: pull.Poster.ID,
|
||||||
ActUserName: pull.Poster.Name,
|
ActUserName: pull.Poster.Name,
|
||||||
@@ -517,18 +502,20 @@ func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []str
|
|||||||
// by given head/base and repo/branch.
|
// by given head/base and repo/branch.
|
||||||
func GetUnmergedPullRequest(headRepoID, baseRepoID int64, headBranch, baseBranch string) (*PullRequest, error) {
|
func GetUnmergedPullRequest(headRepoID, baseRepoID int64, headBranch, baseBranch string) (*PullRequest, error) {
|
||||||
pr := new(PullRequest)
|
pr := new(PullRequest)
|
||||||
has, err := x.Where("head_repo_id=? AND head_branch=? AND base_repo_id=? AND base_branch=? AND has_merged=? AND issue.is_closed=?",
|
err := db.Joins("INNER JOIN issue ON issue.id = pull_request.issue_id").
|
||||||
headRepoID, headBranch, baseRepoID, baseBranch, false, false).
|
Where("pull_request.head_repo_id = ? AND pull_request.head_branch = ? AND pull_request.base_repo_id = ? AND pull_request.base_branch = ? AND pull_request.has_merged = ? AND issue.is_closed = ?",
|
||||||
Join("INNER", "issue", "issue.id=pull_request.issue_id").Get(pr)
|
headRepoID, headBranch, baseRepoID, baseBranch, false, false).
|
||||||
|
First(pr).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil, ErrPullRequestNotExist{args: map[string]any{
|
||||||
|
"headRepoID": headRepoID,
|
||||||
|
"baseRepoID": baseRepoID,
|
||||||
|
"headBranch": headBranch,
|
||||||
|
"baseBranch": baseBranch,
|
||||||
|
}}
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if !has {
|
|
||||||
return nil, ErrPullRequestNotExist{args: map[string]any{
|
|
||||||
"headRepoID": headRepoID,
|
|
||||||
"baseRepoID": baseRepoID,
|
|
||||||
"headBranch": headBranch,
|
|
||||||
"baseBranch": baseBranch,
|
|
||||||
}}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return pr, nil
|
return pr, nil
|
||||||
@@ -538,18 +525,22 @@ func GetUnmergedPullRequest(headRepoID, baseRepoID int64, headBranch, baseBranch
|
|||||||
// by given head information (repo and branch).
|
// by given head information (repo and branch).
|
||||||
func GetUnmergedPullRequestsByHeadInfo(repoID int64, branch string) ([]*PullRequest, error) {
|
func GetUnmergedPullRequestsByHeadInfo(repoID int64, branch string) ([]*PullRequest, error) {
|
||||||
prs := make([]*PullRequest, 0, 2)
|
prs := make([]*PullRequest, 0, 2)
|
||||||
return prs, x.Where("head_repo_id = ? AND head_branch = ? AND has_merged = ? AND issue.is_closed = ?",
|
err := db.Joins("INNER JOIN issue ON issue.id = pull_request.issue_id").
|
||||||
repoID, branch, false, false).
|
Where("pull_request.head_repo_id = ? AND pull_request.head_branch = ? AND pull_request.has_merged = ? AND issue.is_closed = ?",
|
||||||
Join("INNER", "issue", "issue.id = pull_request.issue_id").Find(&prs)
|
repoID, branch, false, false).
|
||||||
|
Find(&prs).Error
|
||||||
|
return prs, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUnmergedPullRequestsByBaseInfo returns all pull requests that are open and has not been merged
|
// GetUnmergedPullRequestsByBaseInfo returns all pull requests that are open and has not been merged
|
||||||
// by given base information (repo and branch).
|
// by given base information (repo and branch).
|
||||||
func GetUnmergedPullRequestsByBaseInfo(repoID int64, branch string) ([]*PullRequest, error) {
|
func GetUnmergedPullRequestsByBaseInfo(repoID int64, branch string) ([]*PullRequest, error) {
|
||||||
prs := make([]*PullRequest, 0, 2)
|
prs := make([]*PullRequest, 0, 2)
|
||||||
return prs, x.Where("base_repo_id=? AND base_branch=? AND has_merged=? AND issue.is_closed=?",
|
err := db.Joins("INNER JOIN issue ON issue.id = pull_request.issue_id").
|
||||||
repoID, branch, false, false).
|
Where("pull_request.base_repo_id = ? AND pull_request.base_branch = ? AND pull_request.has_merged = ? AND issue.is_closed = ?",
|
||||||
Join("INNER", "issue", "issue.id=pull_request.issue_id").Find(&prs)
|
repoID, branch, false, false).
|
||||||
|
Find(&prs).Error
|
||||||
|
return prs, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ errutil.NotFound = (*ErrPullRequestNotExist)(nil)
|
var _ errutil.NotFound = (*ErrPullRequestNotExist)(nil)
|
||||||
@@ -571,50 +562,65 @@ func (ErrPullRequestNotExist) NotFound() bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func getPullRequestByID(e Engine, id int64) (*PullRequest, error) {
|
func getPullRequestByID(db *gorm.DB, id int64) (*PullRequest, error) {
|
||||||
pr := new(PullRequest)
|
pr := new(PullRequest)
|
||||||
has, err := e.ID(id).Get(pr)
|
err := db.First(pr, id).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil, ErrPullRequestNotExist{args: map[string]any{"pullRequestID": id}}
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if !has {
|
|
||||||
return nil, ErrPullRequestNotExist{args: map[string]any{"pullRequestID": id}}
|
|
||||||
}
|
}
|
||||||
return pr, pr.loadAttributes(e)
|
return pr, pr.loadAttributes(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPullRequestByID returns a pull request by given ID.
|
// GetPullRequestByID returns a pull request by given ID.
|
||||||
func GetPullRequestByID(id int64) (*PullRequest, error) {
|
func GetPullRequestByID(id int64) (*PullRequest, error) {
|
||||||
return getPullRequestByID(x, id)
|
return getPullRequestByID(db, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getPullRequestByIssueID(e Engine, issueID int64) (*PullRequest, error) {
|
func getPullRequestByIssueID(db *gorm.DB, issueID int64) (*PullRequest, error) {
|
||||||
pr := &PullRequest{
|
pr := &PullRequest{}
|
||||||
IssueID: issueID,
|
err := db.Where("issue_id = ?", issueID).First(pr).Error
|
||||||
}
|
|
||||||
has, err := e.Get(pr)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil, ErrPullRequestNotExist{args: map[string]any{"issueID": issueID}}
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if !has {
|
|
||||||
return nil, ErrPullRequestNotExist{args: map[string]any{"issueID": issueID}}
|
|
||||||
}
|
}
|
||||||
return pr, pr.loadAttributes(e)
|
return pr, pr.loadAttributes(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPullRequestByIssueID returns pull request by given issue ID.
|
// GetPullRequestByIssueID returns pull request by given issue ID.
|
||||||
func GetPullRequestByIssueID(issueID int64) (*PullRequest, error) {
|
func GetPullRequestByIssueID(issueID int64) (*PullRequest, error) {
|
||||||
return getPullRequestByIssueID(x, issueID)
|
return getPullRequestByIssueID(db, issueID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update updates all fields of pull request.
|
// Update updates all fields of pull request.
|
||||||
func (pr *PullRequest) Update() error {
|
func (pr *PullRequest) Update() error {
|
||||||
_, err := x.Id(pr.ID).AllCols().Update(pr)
|
return db.Model(&PullRequest{}).Where("id = ?", pr.ID).Updates(pr).Error
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update updates specific fields of pull request.
|
// Update updates specific fields of pull request.
|
||||||
func (pr *PullRequest) UpdateCols(cols ...string) error {
|
func (pr *PullRequest) UpdateCols(cols ...string) error {
|
||||||
_, err := x.Id(pr.ID).Cols(cols...).Update(pr)
|
updates := make(map[string]any)
|
||||||
return err
|
for _, col := range cols {
|
||||||
|
switch col {
|
||||||
|
case "status":
|
||||||
|
updates["status"] = pr.Status
|
||||||
|
case "merge_base":
|
||||||
|
updates["merge_base"] = pr.MergeBase
|
||||||
|
case "has_merged":
|
||||||
|
updates["has_merged"] = pr.HasMerged
|
||||||
|
case "merged_commit_id":
|
||||||
|
updates["merged_commit_id"] = pr.MergedCommitID
|
||||||
|
case "merger_id":
|
||||||
|
updates["merger_id"] = pr.MergerID
|
||||||
|
case "merged_unix":
|
||||||
|
updates["merged_unix"] = pr.MergedUnix
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return db.Model(&PullRequest{}).Where("id = ?", pr.ID).Updates(updates).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdatePatch generates and saves a new patch.
|
// UpdatePatch generates and saves a new patch.
|
||||||
@@ -711,7 +717,7 @@ func (pr *PullRequest) AddToTaskQueue() {
|
|||||||
|
|
||||||
type PullRequestList []*PullRequest
|
type PullRequestList []*PullRequest
|
||||||
|
|
||||||
func (prs PullRequestList) loadAttributes(e Engine) (err error) {
|
func (prs PullRequestList) loadAttributes(db *gorm.DB) (err error) {
|
||||||
if len(prs) == 0 {
|
if len(prs) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -726,7 +732,7 @@ func (prs PullRequestList) loadAttributes(e Engine) (err error) {
|
|||||||
issueIDs = append(issueIDs, issueID)
|
issueIDs = append(issueIDs, issueID)
|
||||||
}
|
}
|
||||||
issues := make([]*Issue, 0, len(issueIDs))
|
issues := make([]*Issue, 0, len(issueIDs))
|
||||||
if err = e.Where("id > 0").In("id", issueIDs).Find(&issues); err != nil {
|
if err = db.Where("id IN ?", issueIDs).Find(&issues).Error; err != nil {
|
||||||
return errors.Newf("find issues: %v", err)
|
return errors.Newf("find issues: %v", err)
|
||||||
}
|
}
|
||||||
for i := range issues {
|
for i := range issues {
|
||||||
@@ -738,7 +744,7 @@ func (prs PullRequestList) loadAttributes(e Engine) (err error) {
|
|||||||
|
|
||||||
// Load attributes
|
// Load attributes
|
||||||
for i := range prs {
|
for i := range prs {
|
||||||
if err = prs[i].loadAttributes(e); err != nil {
|
if err = prs[i].loadAttributes(db); err != nil {
|
||||||
return errors.Newf("loadAttributes [%d]: %v", prs[i].ID, err)
|
return errors.Newf("loadAttributes [%d]: %v", prs[i].ID, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -747,7 +753,7 @@ func (prs PullRequestList) loadAttributes(e Engine) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (prs PullRequestList) LoadAttributes() error {
|
func (prs PullRequestList) LoadAttributes() error {
|
||||||
return prs.loadAttributes(x)
|
return prs.loadAttributes(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
func addHeadRepoTasks(prs []*PullRequest) {
|
func addHeadRepoTasks(prs []*PullRequest) {
|
||||||
@@ -838,25 +844,23 @@ func (pr *PullRequest) checkAndUpdateStatus() {
|
|||||||
// TestPullRequests checks and tests untested patches of pull requests.
|
// TestPullRequests checks and tests untested patches of pull requests.
|
||||||
// TODO: test more pull requests at same time.
|
// TODO: test more pull requests at same time.
|
||||||
func TestPullRequests() {
|
func TestPullRequests() {
|
||||||
prs := make([]*PullRequest, 0, 10)
|
var prs []*PullRequest
|
||||||
_ = x.Iterate(PullRequest{
|
_ = db.Where("status = ?", PullRequestStatusChecking).FindInBatches(&prs, 100, func(tx *gorm.DB, batch int) error {
|
||||||
Status: PullRequestStatusChecking,
|
for i := range prs {
|
||||||
},
|
pr := prs[i]
|
||||||
func(idx int, bean any) error {
|
|
||||||
pr := bean.(*PullRequest)
|
|
||||||
|
|
||||||
if err := pr.LoadAttributes(); err != nil {
|
if err := pr.LoadAttributes(); err != nil {
|
||||||
log.Error("LoadAttributes: %v", err)
|
log.Error("LoadAttributes: %v", err)
|
||||||
return nil
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := pr.testPatch(); err != nil {
|
if err := pr.testPatch(); err != nil {
|
||||||
log.Error("testPatch: %v", err)
|
log.Error("testPatch: %v", err)
|
||||||
return nil
|
continue
|
||||||
}
|
}
|
||||||
prs = append(prs, pr)
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
// Update pull request status.
|
// Update pull request status.
|
||||||
for _, pr := range prs {
|
for _, pr := range prs {
|
||||||
|
|||||||
@@ -6,12 +6,11 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
log "unknwon.dev/clog/v2"
|
|
||||||
"xorm.io/xorm"
|
|
||||||
|
|
||||||
"github.com/cockroachdb/errors"
|
"github.com/cockroachdb/errors"
|
||||||
"github.com/gogs/git-module"
|
"github.com/gogs/git-module"
|
||||||
api "github.com/gogs/go-gogs-client"
|
api "github.com/gogs/go-gogs-client"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
log "unknwon.dev/clog/v2"
|
||||||
|
|
||||||
"gogs.io/gogs/internal/errutil"
|
"gogs.io/gogs/internal/errutil"
|
||||||
"gogs.io/gogs/internal/process"
|
"gogs.io/gogs/internal/process"
|
||||||
@@ -21,40 +20,39 @@ import (
|
|||||||
type Release struct {
|
type Release struct {
|
||||||
ID int64
|
ID int64
|
||||||
RepoID int64
|
RepoID int64
|
||||||
Repo *Repository `xorm:"-" json:"-" gorm:"-"`
|
Repo *Repository `gorm:"-" json:"-"`
|
||||||
PublisherID int64
|
PublisherID int64
|
||||||
Publisher *User `xorm:"-" json:"-" gorm:"-"`
|
Publisher *User `gorm:"-" json:"-"`
|
||||||
TagName string
|
TagName string
|
||||||
LowerTagName string
|
LowerTagName string
|
||||||
Target string
|
Target string
|
||||||
Title string
|
Title string
|
||||||
Sha1 string `xorm:"VARCHAR(40)"`
|
Sha1 string `gorm:"type:varchar(40)"`
|
||||||
NumCommits int64
|
NumCommits int64
|
||||||
NumCommitsBehind int64 `xorm:"-" json:"-" gorm:"-"`
|
NumCommitsBehind int64 `gorm:"-" json:"-"`
|
||||||
Note string `xorm:"TEXT"`
|
Note string `gorm:"type:text"`
|
||||||
IsDraft bool `xorm:"NOT NULL DEFAULT false"`
|
IsDraft bool `gorm:"not null;default:false"`
|
||||||
IsPrerelease bool
|
IsPrerelease bool
|
||||||
|
|
||||||
Created time.Time `xorm:"-" json:"-" gorm:"-"`
|
Created time.Time `gorm:"-" json:"-"`
|
||||||
CreatedUnix int64
|
CreatedUnix int64
|
||||||
|
|
||||||
Attachments []*Attachment `xorm:"-" json:"-" gorm:"-"`
|
Attachments []*Attachment `gorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Release) BeforeInsert() {
|
func (r *Release) BeforeCreate(tx *gorm.DB) error {
|
||||||
if r.CreatedUnix == 0 {
|
if r.CreatedUnix == 0 {
|
||||||
r.CreatedUnix = time.Now().Unix()
|
r.CreatedUnix = tx.NowFunc().Unix()
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Release) AfterSet(colName string, _ xorm.Cell) {
|
func (r *Release) AfterFind(tx *gorm.DB) error {
|
||||||
switch colName {
|
r.Created = time.Unix(r.CreatedUnix, 0).Local()
|
||||||
case "created_unix":
|
return nil
|
||||||
r.Created = time.Unix(r.CreatedUnix, 0).Local()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Release) loadAttributes(e Engine) (err error) {
|
func (r *Release) loadAttributes(e *gorm.DB) (err error) {
|
||||||
if r.Repo == nil {
|
if r.Repo == nil {
|
||||||
r.Repo, err = getRepositoryByID(e, r.RepoID)
|
r.Repo, err = getRepositoryByID(e, r.RepoID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -85,7 +83,7 @@ func (r *Release) loadAttributes(e Engine) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *Release) LoadAttributes() error {
|
func (r *Release) LoadAttributes() error {
|
||||||
return r.loadAttributes(x)
|
return r.loadAttributes(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method assumes some fields assigned with values:
|
// This method assumes some fields assigned with values:
|
||||||
@@ -110,7 +108,9 @@ func IsReleaseExist(repoID int64, tagName string) (bool, error) {
|
|||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return x.Get(&Release{RepoID: repoID, LowerTagName: strings.ToLower(tagName)})
|
var count int64
|
||||||
|
err := db.Model(&Release{}).Where("repo_id = ? AND lower_tag_name = ?", repoID, strings.ToLower(tagName)).Count(&count).Error
|
||||||
|
return count > 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func createTag(gitRepo *git.Repository, r *Release) error {
|
func createTag(gitRepo *git.Repository, r *Release) error {
|
||||||
@@ -171,24 +171,21 @@ func NewRelease(gitRepo *git.Repository, r *Release, uuids []string) error {
|
|||||||
}
|
}
|
||||||
r.LowerTagName = strings.ToLower(r.TagName)
|
r.LowerTagName = strings.ToLower(r.TagName)
|
||||||
|
|
||||||
sess := x.NewSession()
|
err = db.Transaction(func(tx *gorm.DB) error {
|
||||||
defer sess.Close()
|
if err := tx.Create(r).Error; err != nil {
|
||||||
if err = sess.Begin(); err != nil {
|
return errors.Newf("insert: %v", err)
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = sess.Insert(r); err != nil {
|
|
||||||
return errors.Newf("insert: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(uuids) > 0 {
|
|
||||||
if _, err = sess.In("uuid", uuids).Cols("release_id").Update(&Attachment{ReleaseID: r.ID}); err != nil {
|
|
||||||
return errors.Newf("link attachments: %v", err)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if err = sess.Commit(); err != nil {
|
if len(uuids) > 0 {
|
||||||
return errors.Newf("commit: %v", err)
|
if err := tx.Model(&Attachment{}).Where("uuid IN ?", uuids).Update("release_id", r.ID).Error; err != nil {
|
||||||
|
return errors.Newf("link attachments: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only send webhook when actually published, skip drafts
|
// Only send webhook when actually published, skip drafts
|
||||||
@@ -231,8 +228,8 @@ func GetRelease(repoID int64, tagName string) (*Release, error) {
|
|||||||
return nil, ErrReleaseNotExist{args: map[string]any{"tag": tagName}}
|
return nil, ErrReleaseNotExist{args: map[string]any{"tag": tagName}}
|
||||||
}
|
}
|
||||||
|
|
||||||
r := &Release{RepoID: repoID, LowerTagName: strings.ToLower(tagName)}
|
r := &Release{}
|
||||||
if _, err = x.Get(r); err != nil {
|
if err = db.Where("repo_id = ? AND lower_tag_name = ?", repoID, strings.ToLower(tagName)).First(r).Error; err != nil {
|
||||||
return nil, errors.Newf("get: %v", err)
|
return nil, errors.Newf("get: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,11 +239,12 @@ func GetRelease(repoID int64, tagName string) (*Release, error) {
|
|||||||
// GetReleaseByID returns release with given ID.
|
// GetReleaseByID returns release with given ID.
|
||||||
func GetReleaseByID(id int64) (*Release, error) {
|
func GetReleaseByID(id int64) (*Release, error) {
|
||||||
r := new(Release)
|
r := new(Release)
|
||||||
has, err := x.Id(id).Get(r)
|
err := db.Where("id = ?", id).First(r).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil, ErrReleaseNotExist{args: map[string]any{"releaseID": id}}
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if !has {
|
|
||||||
return nil, ErrReleaseNotExist{args: map[string]any{"releaseID": id}}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return r, r.LoadAttributes()
|
return r, r.LoadAttributes()
|
||||||
@@ -256,24 +254,24 @@ func GetReleaseByID(id int64) (*Release, error) {
|
|||||||
// If matches is not empty, only published releases in matches will be returned.
|
// If matches is not empty, only published releases in matches will be returned.
|
||||||
// In any case, drafts won't be returned by this function.
|
// In any case, drafts won't be returned by this function.
|
||||||
func GetPublishedReleasesByRepoID(repoID int64, matches ...string) ([]*Release, error) {
|
func GetPublishedReleasesByRepoID(repoID int64, matches ...string) ([]*Release, error) {
|
||||||
sess := x.Where("repo_id = ?", repoID).And("is_draft = ?", false).Desc("created_unix")
|
query := db.Where("repo_id = ? AND is_draft = ?", repoID, false).Order("created_unix DESC")
|
||||||
if len(matches) > 0 {
|
if len(matches) > 0 {
|
||||||
sess.In("tag_name", matches)
|
query = query.Where("tag_name IN ?", matches)
|
||||||
}
|
}
|
||||||
releases := make([]*Release, 0, 5)
|
releases := make([]*Release, 0, 5)
|
||||||
return releases, sess.Find(&releases, new(Release))
|
return releases, query.Find(&releases).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetReleasesByRepoID returns a list of all releases (including drafts) of given repository.
|
// GetReleasesByRepoID returns a list of all releases (including drafts) of given repository.
|
||||||
func GetReleasesByRepoID(repoID int64) ([]*Release, error) {
|
func GetReleasesByRepoID(repoID int64) ([]*Release, error) {
|
||||||
releases := make([]*Release, 0)
|
releases := make([]*Release, 0)
|
||||||
return releases, x.Where("repo_id = ?", repoID).Find(&releases)
|
return releases, db.Where("repo_id = ?", repoID).Find(&releases).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDraftReleasesByRepoID returns all draft releases of repository.
|
// GetDraftReleasesByRepoID returns all draft releases of repository.
|
||||||
func GetDraftReleasesByRepoID(repoID int64) ([]*Release, error) {
|
func GetDraftReleasesByRepoID(repoID int64) ([]*Release, error) {
|
||||||
releases := make([]*Release, 0)
|
releases := make([]*Release, 0)
|
||||||
return releases, x.Where("repo_id = ?", repoID).And("is_draft = ?", true).Find(&releases)
|
return releases, db.Where("repo_id = ? AND is_draft = ?", repoID, true).Find(&releases).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
type ReleaseSorter struct {
|
type ReleaseSorter struct {
|
||||||
@@ -310,28 +308,26 @@ func UpdateRelease(doer *User, gitRepo *git.Repository, r *Release, isPublish bo
|
|||||||
|
|
||||||
r.PublisherID = doer.ID
|
r.PublisherID = doer.ID
|
||||||
|
|
||||||
sess := x.NewSession()
|
err = db.Transaction(func(tx *gorm.DB) error {
|
||||||
defer sess.Close()
|
if err := tx.Model(r).Where("id = ?", r.ID).Updates(r).Error; err != nil {
|
||||||
if err = sess.Begin(); err != nil {
|
return errors.Newf("Update: %v", err)
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err = sess.ID(r.ID).AllCols().Update(r); err != nil {
|
|
||||||
return errors.Newf("Update: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unlink all current attachments and link back later if still valid
|
|
||||||
if _, err = sess.Exec("UPDATE attachment SET release_id = 0 WHERE release_id = ?", r.ID); err != nil {
|
|
||||||
return errors.Newf("unlink current attachments: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(uuids) > 0 {
|
|
||||||
if _, err = sess.In("uuid", uuids).Cols("release_id").Update(&Attachment{ReleaseID: r.ID}); err != nil {
|
|
||||||
return errors.Newf("link attachments: %v", err)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if err = sess.Commit(); err != nil {
|
// Unlink all current attachments and link back later if still valid
|
||||||
return errors.Newf("commit: %v", err)
|
if err := tx.Exec("UPDATE attachment SET release_id = 0 WHERE release_id = ?", r.ID).Error; err != nil {
|
||||||
|
return errors.Newf("unlink current attachments: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(uuids) > 0 {
|
||||||
|
if err := tx.Model(&Attachment{}).Where("uuid IN ?", uuids).Update("release_id", r.ID).Error; err != nil {
|
||||||
|
return errors.Newf("link attachments: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !isPublish {
|
if !isPublish {
|
||||||
@@ -366,7 +362,7 @@ func DeleteReleaseOfRepoByID(repoID, id int64) error {
|
|||||||
return errors.Newf("git tag -d: %v - %s", err, stderr)
|
return errors.Newf("git tag -d: %v - %s", err, stderr)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = x.Id(rel.ID).Delete(new(Release)); err != nil {
|
if err = db.Where("id = ?", rel.ID).Delete(new(Release)).Error; err != nil {
|
||||||
return errors.Newf("delete: %v", err)
|
return errors.Newf("delete: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/cockroachdb/errors"
|
"github.com/cockroachdb/errors"
|
||||||
"github.com/gogs/git-module"
|
"github.com/gogs/git-module"
|
||||||
"github.com/unknwon/com"
|
"github.com/unknwon/com"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
|
||||||
"gogs.io/gogs/internal/errutil"
|
"gogs.io/gogs/internal/errutil"
|
||||||
"gogs.io/gogs/internal/tool"
|
"gogs.io/gogs/internal/tool"
|
||||||
@@ -93,8 +94,9 @@ type ProtectBranchWhitelist struct {
|
|||||||
|
|
||||||
// IsUserInProtectBranchWhitelist returns true if given user is in the whitelist of a branch in a repository.
|
// IsUserInProtectBranchWhitelist returns true if given user is in the whitelist of a branch in a repository.
|
||||||
func IsUserInProtectBranchWhitelist(repoID, userID int64, branch string) bool {
|
func IsUserInProtectBranchWhitelist(repoID, userID int64, branch string) bool {
|
||||||
has, err := x.Where("repo_id = ?", repoID).And("user_id = ?", userID).And("name = ?", branch).Get(new(ProtectBranchWhitelist))
|
var whitelist ProtectBranchWhitelist
|
||||||
return has && err == nil
|
err := db.Where("repo_id = ?", repoID).Where("user_id = ?", userID).Where("name = ?", branch).First(&whitelist).Error
|
||||||
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProtectBranch contains options of a protected branch.
|
// ProtectBranch contains options of a protected branch.
|
||||||
@@ -115,11 +117,11 @@ func GetProtectBranchOfRepoByName(repoID int64, name string) (*ProtectBranch, er
|
|||||||
RepoID: repoID,
|
RepoID: repoID,
|
||||||
Name: name,
|
Name: name,
|
||||||
}
|
}
|
||||||
has, err := x.Get(protectBranch)
|
err := db.Where("repo_id = ? AND name = ?", repoID, name).First(protectBranch).Error
|
||||||
if err != nil {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
return nil, err
|
|
||||||
} else if !has {
|
|
||||||
return nil, ErrBranchNotExist{args: map[string]any{"name": name}}
|
return nil, ErrBranchNotExist{args: map[string]any{"name": name}}
|
||||||
|
} else if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
return protectBranch, nil
|
return protectBranch, nil
|
||||||
}
|
}
|
||||||
@@ -136,23 +138,19 @@ func IsBranchOfRepoRequirePullRequest(repoID int64, name string) bool {
|
|||||||
// UpdateProtectBranch saves branch protection options.
|
// UpdateProtectBranch saves branch protection options.
|
||||||
// If ID is 0, it creates a new record. Otherwise, updates existing record.
|
// If ID is 0, it creates a new record. Otherwise, updates existing record.
|
||||||
func UpdateProtectBranch(protectBranch *ProtectBranch) (err error) {
|
func UpdateProtectBranch(protectBranch *ProtectBranch) (err error) {
|
||||||
sess := x.NewSession()
|
return db.Transaction(func(tx *gorm.DB) error {
|
||||||
defer sess.Close()
|
if protectBranch.ID == 0 {
|
||||||
if err = sess.Begin(); err != nil {
|
if err := tx.Create(protectBranch).Error; err != nil {
|
||||||
return err
|
return errors.Newf("insert: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if protectBranch.ID == 0 {
|
|
||||||
if _, err = sess.Insert(protectBranch); err != nil {
|
|
||||||
return errors.Newf("insert: %v", err)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = sess.ID(protectBranch.ID).AllCols().Update(protectBranch); err != nil {
|
if err := tx.Model(&ProtectBranch{}).Where("id = ?", protectBranch.ID).Updates(protectBranch).Error; err != nil {
|
||||||
return errors.Newf("update: %v", err)
|
return errors.Newf("update: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return sess.Commit()
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateOrgProtectBranch saves branch protection options of organizational repository.
|
// UpdateOrgProtectBranch saves branch protection options of organizational repository.
|
||||||
@@ -209,7 +207,7 @@ func UpdateOrgProtectBranch(repo *Repository, protectBranch *ProtectBranch, whit
|
|||||||
|
|
||||||
// Make sure protectBranch.ID is not 0 for whitelists
|
// Make sure protectBranch.ID is not 0 for whitelists
|
||||||
if protectBranch.ID == 0 {
|
if protectBranch.ID == 0 {
|
||||||
if _, err = x.Insert(protectBranch); err != nil {
|
if err = db.Create(protectBranch).Error; err != nil {
|
||||||
return errors.Newf("insert: %v", err)
|
return errors.Newf("insert: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -247,30 +245,29 @@ func UpdateOrgProtectBranch(repo *Repository, protectBranch *ProtectBranch, whit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sess := x.NewSession()
|
return db.Transaction(func(tx *gorm.DB) error {
|
||||||
defer sess.Close()
|
if err := tx.Model(&ProtectBranch{}).Where("id = ?", protectBranch.ID).Updates(protectBranch).Error; err != nil {
|
||||||
if err = sess.Begin(); err != nil {
|
return errors.Newf("Update: %v", err)
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = sess.ID(protectBranch.ID).AllCols().Update(protectBranch); err != nil {
|
|
||||||
return errors.Newf("Update: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Refresh whitelists
|
|
||||||
if hasUsersChanged || hasTeamsChanged {
|
|
||||||
if _, err = sess.Delete(&ProtectBranchWhitelist{ProtectBranchID: protectBranch.ID}); err != nil {
|
|
||||||
return errors.Newf("delete old protect branch whitelists: %v", err)
|
|
||||||
} else if _, err = sess.Insert(whitelists); err != nil {
|
|
||||||
return errors.Newf("insert new protect branch whitelists: %v", err)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return sess.Commit()
|
// Refresh whitelists
|
||||||
|
if hasUsersChanged || hasTeamsChanged {
|
||||||
|
if err := tx.Delete(&ProtectBranchWhitelist{}, "protect_branch_id = ?", protectBranch.ID).Error; err != nil {
|
||||||
|
return errors.Newf("delete old protect branch whitelists: %v", err)
|
||||||
|
}
|
||||||
|
if len(whitelists) > 0 {
|
||||||
|
if err := tx.Create(&whitelists).Error; err != nil {
|
||||||
|
return errors.Newf("insert new protect branch whitelists: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetProtectBranchesByRepoID returns a list of *ProtectBranch in given repository.
|
// GetProtectBranchesByRepoID returns a list of *ProtectBranch in given repository.
|
||||||
func GetProtectBranchesByRepoID(repoID int64) ([]*ProtectBranch, error) {
|
func GetProtectBranchesByRepoID(repoID int64) ([]*ProtectBranch, error) {
|
||||||
protectBranches := make([]*ProtectBranch, 0, 2)
|
protectBranches := make([]*ProtectBranch, 0, 2)
|
||||||
return protectBranches, x.Where("repo_id = ? and protected = ?", repoID, true).Asc("name").Find(&protectBranches)
|
return protectBranches, db.Where("repo_id = ? AND protected = ?", repoID, true).Order("name ASC").Find(&protectBranches).Error
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/cockroachdb/errors"
|
"github.com/cockroachdb/errors"
|
||||||
api "github.com/gogs/go-gogs-client"
|
api "github.com/gogs/go-gogs-client"
|
||||||
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Collaboration represent the relation between an individual and a repository.
|
// Collaboration represent the relation between an individual and a repository.
|
||||||
@@ -34,12 +35,12 @@ func IsCollaborator(repoID, userID int64) bool {
|
|||||||
RepoID: repoID,
|
RepoID: repoID,
|
||||||
UserID: userID,
|
UserID: userID,
|
||||||
}
|
}
|
||||||
has, err := x.Get(collaboration)
|
err := db.Where("repo_id = ? AND user_id = ?", repoID, userID).First(collaboration).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("get collaboration [repo_id: %d, user_id: %d]: %v", repoID, userID, err)
|
log.Error("get collaboration [repo_id: %d, user_id: %d]: %v", repoID, userID, err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return has
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Repository) IsCollaborator(userID int64) bool {
|
func (r *Repository) IsCollaborator(userID int64) bool {
|
||||||
@@ -53,32 +54,29 @@ func (r *Repository) AddCollaborator(u *User) error {
|
|||||||
UserID: u.ID,
|
UserID: u.ID,
|
||||||
}
|
}
|
||||||
|
|
||||||
has, err := x.Get(collaboration)
|
var existing Collaboration
|
||||||
if err != nil {
|
err := db.Where("repo_id = ? AND user_id = ?", r.ID, u.ID).First(&existing).Error
|
||||||
return err
|
if err == nil {
|
||||||
} else if has {
|
|
||||||
return nil
|
return nil
|
||||||
|
} else if !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
collaboration.Mode = AccessModeWrite
|
collaboration.Mode = AccessModeWrite
|
||||||
|
|
||||||
sess := x.NewSession()
|
return db.Transaction(func(tx *gorm.DB) error {
|
||||||
defer sess.Close()
|
if err := tx.Create(collaboration).Error; err != nil {
|
||||||
if err = sess.Begin(); err != nil {
|
return err
|
||||||
return err
|
}
|
||||||
}
|
if err := r.recalculateAccesses(tx); err != nil {
|
||||||
|
return errors.Newf("recalculateAccesses [repo_id: %v]: %v", r.ID, err)
|
||||||
if _, err = sess.Insert(collaboration); err != nil {
|
}
|
||||||
return err
|
return nil
|
||||||
} else if err = r.recalculateAccesses(sess); err != nil {
|
})
|
||||||
return errors.Newf("recalculateAccesses [repo_id: %v]: %v", r.ID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return sess.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Repository) getCollaborations(e Engine) ([]*Collaboration, error) {
|
func (r *Repository) getCollaborations(e *gorm.DB) ([]*Collaboration, error) {
|
||||||
collaborations := make([]*Collaboration, 0)
|
collaborations := make([]*Collaboration, 0)
|
||||||
return collaborations, e.Find(&collaborations, &Collaboration{RepoID: r.ID})
|
return collaborations, e.Where("repo_id = ?", r.ID).Find(&collaborations).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collaborator represents a user with collaboration details.
|
// Collaborator represents a user with collaboration details.
|
||||||
@@ -98,7 +96,7 @@ func (c *Collaborator) APIFormat() *api.Collaborator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Repository) getCollaborators(e Engine) ([]*Collaborator, error) {
|
func (r *Repository) getCollaborators(e *gorm.DB) ([]*Collaborator, error) {
|
||||||
collaborations, err := r.getCollaborations(e)
|
collaborations, err := r.getCollaborations(e)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Newf("getCollaborations: %v", err)
|
return nil, errors.Newf("getCollaborations: %v", err)
|
||||||
@@ -120,7 +118,7 @@ func (r *Repository) getCollaborators(e Engine) ([]*Collaborator, error) {
|
|||||||
|
|
||||||
// GetCollaborators returns the collaborators for a repository
|
// GetCollaborators returns the collaborators for a repository
|
||||||
func (r *Repository) GetCollaborators() ([]*Collaborator, error) {
|
func (r *Repository) GetCollaborators() ([]*Collaborator, error) {
|
||||||
return r.getCollaborators(x)
|
return r.getCollaborators(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChangeCollaborationAccessMode sets new access mode for the collaboration.
|
// ChangeCollaborationAccessMode sets new access mode for the collaboration.
|
||||||
@@ -134,11 +132,11 @@ func (r *Repository) ChangeCollaborationAccessMode(userID int64, mode AccessMode
|
|||||||
RepoID: r.ID,
|
RepoID: r.ID,
|
||||||
UserID: userID,
|
UserID: userID,
|
||||||
}
|
}
|
||||||
has, err := x.Get(collaboration)
|
err := db.Where("repo_id = ? AND user_id = ?", r.ID, userID).First(collaboration).Error
|
||||||
if err != nil {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
return errors.Newf("get collaboration: %v", err)
|
|
||||||
} else if !has {
|
|
||||||
return nil
|
return nil
|
||||||
|
} else if err != nil {
|
||||||
|
return errors.Newf("get collaboration: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if collaboration.Mode == mode {
|
if collaboration.Mode == mode {
|
||||||
@@ -159,35 +157,31 @@ func (r *Repository) ChangeCollaborationAccessMode(userID int64, mode AccessMode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sess := x.NewSession()
|
return db.Transaction(func(tx *gorm.DB) error {
|
||||||
defer sess.Close()
|
if err := tx.Model(&Collaboration{}).Where("id = ?", collaboration.ID).Updates(collaboration).Error; err != nil {
|
||||||
if err = sess.Begin(); err != nil {
|
return errors.Newf("update collaboration: %v", err)
|
||||||
return err
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = sess.ID(collaboration.ID).AllCols().Update(collaboration); err != nil {
|
access := &Access{
|
||||||
return errors.Newf("update collaboration: %v", err)
|
UserID: userID,
|
||||||
}
|
RepoID: r.ID,
|
||||||
|
}
|
||||||
|
err := tx.Where("user_id = ? AND repo_id = ?", userID, r.ID).First(access).Error
|
||||||
|
if err == nil {
|
||||||
|
if err := tx.Exec("UPDATE access SET mode = ? WHERE user_id = ? AND repo_id = ?", mode, userID, r.ID).Error; err != nil {
|
||||||
|
return errors.Newf("update access table: %v", err)
|
||||||
|
}
|
||||||
|
} else if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
access.Mode = mode
|
||||||
|
if err := tx.Create(access).Error; err != nil {
|
||||||
|
return errors.Newf("insert access table: %v", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return errors.Newf("get access record: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
access := &Access{
|
return nil
|
||||||
UserID: userID,
|
})
|
||||||
RepoID: r.ID,
|
|
||||||
}
|
|
||||||
has, err = sess.Get(access)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Newf("get access record: %v", err)
|
|
||||||
}
|
|
||||||
if has {
|
|
||||||
_, err = sess.Exec("UPDATE access SET mode = ? WHERE user_id = ? AND repo_id = ?", mode, userID, r.ID)
|
|
||||||
} else {
|
|
||||||
access.Mode = mode
|
|
||||||
_, err = sess.Insert(access)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return errors.Newf("update/insert access table: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return sess.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteCollaboration removes collaboration relation between the user and repository.
|
// DeleteCollaboration removes collaboration relation between the user and repository.
|
||||||
@@ -201,19 +195,20 @@ func DeleteCollaboration(repo *Repository, userID int64) (err error) {
|
|||||||
UserID: userID,
|
UserID: userID,
|
||||||
}
|
}
|
||||||
|
|
||||||
sess := x.NewSession()
|
return db.Transaction(func(tx *gorm.DB) error {
|
||||||
defer sess.Close()
|
result := tx.Delete(collaboration, "repo_id = ? AND user_id = ?", repo.ID, userID)
|
||||||
if err = sess.Begin(); err != nil {
|
if result.Error != nil {
|
||||||
return err
|
return result.Error
|
||||||
}
|
} else if result.RowsAffected == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
if has, err := sess.Delete(collaboration); err != nil || has == 0 {
|
if err := repo.recalculateAccesses(tx); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if err = repo.recalculateAccesses(sess); err != nil {
|
}
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return sess.Commit()
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Repository) DeleteCollaboration(userID int64) error {
|
func (r *Repository) DeleteCollaboration(userID int64) error {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/cockroachdb/errors"
|
"github.com/cockroachdb/errors"
|
||||||
gouuid "github.com/satori/go.uuid"
|
gouuid "github.com/satori/go.uuid"
|
||||||
"github.com/unknwon/com"
|
"github.com/unknwon/com"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
|
||||||
"github.com/gogs/git-module"
|
"github.com/gogs/git-module"
|
||||||
|
|
||||||
@@ -23,6 +24,7 @@ import (
|
|||||||
"gogs.io/gogs/internal/osutil"
|
"gogs.io/gogs/internal/osutil"
|
||||||
"gogs.io/gogs/internal/pathutil"
|
"gogs.io/gogs/internal/pathutil"
|
||||||
"gogs.io/gogs/internal/process"
|
"gogs.io/gogs/internal/process"
|
||||||
|
"gogs.io/gogs/internal/tool"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BranchAlreadyExists represents an error when branch already exists.
|
// BranchAlreadyExists represents an error when branch already exists.
|
||||||
@@ -414,10 +416,8 @@ func (upload *Upload) LocalPath() string {
|
|||||||
|
|
||||||
// NewUpload creates a new upload object.
|
// NewUpload creates a new upload object.
|
||||||
func NewUpload(name string, buf []byte, file multipart.File) (_ *Upload, err error) {
|
func NewUpload(name string, buf []byte, file multipart.File) (_ *Upload, err error) {
|
||||||
// 🚨 SECURITY: Prevent path traversal.
|
if tool.IsMaliciousPath(name) {
|
||||||
name = pathutil.Clean(name)
|
return nil, errors.Newf("malicious path detected: %s", name)
|
||||||
if name == "" {
|
|
||||||
return nil, errors.New("empty file name")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
upload := &Upload{
|
upload := &Upload{
|
||||||
@@ -442,7 +442,7 @@ func NewUpload(name string, buf []byte, file multipart.File) (_ *Upload, err err
|
|||||||
return nil, errors.Newf("copy: %v", err)
|
return nil, errors.Newf("copy: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := x.Insert(upload); err != nil {
|
if err := db.Create(upload).Error; err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -451,11 +451,12 @@ func NewUpload(name string, buf []byte, file multipart.File) (_ *Upload, err err
|
|||||||
|
|
||||||
func GetUploadByUUID(uuid string) (*Upload, error) {
|
func GetUploadByUUID(uuid string) (*Upload, error) {
|
||||||
upload := &Upload{UUID: uuid}
|
upload := &Upload{UUID: uuid}
|
||||||
has, err := x.Get(upload)
|
err := db.Where("uuid = ?", uuid).First(upload).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil, ErrUploadNotExist{0, uuid}
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if !has {
|
|
||||||
return nil, ErrUploadNotExist{0, uuid}
|
|
||||||
}
|
}
|
||||||
return upload, nil
|
return upload, nil
|
||||||
}
|
}
|
||||||
@@ -467,7 +468,7 @@ func GetUploadsByUUIDs(uuids []string) ([]*Upload, error) {
|
|||||||
|
|
||||||
// Silently drop invalid uuids.
|
// Silently drop invalid uuids.
|
||||||
uploads := make([]*Upload, 0, len(uuids))
|
uploads := make([]*Upload, 0, len(uuids))
|
||||||
return uploads, x.In("uuid", uuids).Find(&uploads)
|
return uploads, db.Where("uuid IN ?", uuids).Find(&uploads).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeleteUploads(uploads ...*Upload) (err error) {
|
func DeleteUploads(uploads ...*Upload) (err error) {
|
||||||
@@ -475,32 +476,28 @@ func DeleteUploads(uploads ...*Upload) (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
sess := x.NewSession()
|
return db.Transaction(func(tx *gorm.DB) error {
|
||||||
defer sess.Close()
|
ids := make([]int64, len(uploads))
|
||||||
if err = sess.Begin(); err != nil {
|
for i := 0; i < len(uploads); i++ {
|
||||||
return err
|
ids[i] = uploads[i].ID
|
||||||
}
|
}
|
||||||
|
if err := tx.Where("id IN ?", ids).Delete(new(Upload)).Error; err != nil {
|
||||||
ids := make([]int64, len(uploads))
|
return errors.Newf("delete uploads: %v", err)
|
||||||
for i := 0; i < len(uploads); i++ {
|
|
||||||
ids[i] = uploads[i].ID
|
|
||||||
}
|
|
||||||
if _, err = sess.In("id", ids).Delete(new(Upload)); err != nil {
|
|
||||||
return errors.Newf("delete uploads: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, upload := range uploads {
|
|
||||||
localPath := upload.LocalPath()
|
|
||||||
if !osutil.IsFile(localPath) {
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := os.Remove(localPath); err != nil {
|
for _, upload := range uploads {
|
||||||
return errors.Newf("remove upload: %v", err)
|
localPath := upload.LocalPath()
|
||||||
}
|
if !osutil.IsFile(localPath) {
|
||||||
}
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
return sess.Commit()
|
if err := os.Remove(localPath); err != nil {
|
||||||
|
return errors.Newf("remove upload: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeleteUpload(u *Upload) error {
|
func DeleteUpload(u *Upload) error {
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/cockroachdb/errors"
|
"github.com/cockroachdb/errors"
|
||||||
api "github.com/gogs/go-gogs-client"
|
api "github.com/gogs/go-gogs-client"
|
||||||
@@ -28,13 +27,6 @@ func (r *Repository) BeforeUpdate(tx *gorm.DB) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// AfterFind implements the GORM query hook.
|
|
||||||
func (r *Repository) AfterFind(_ *gorm.DB) error {
|
|
||||||
r.Created = time.Unix(r.CreatedUnix, 0).Local()
|
|
||||||
r.Updated = time.Unix(r.UpdatedUnix, 0).Local()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type RepositoryAPIFormatOptions struct {
|
type RepositoryAPIFormatOptions struct {
|
||||||
Permission *api.Permission
|
Permission *api.Permission
|
||||||
Parent *api.Repository
|
Parent *api.Repository
|
||||||
|
|||||||
@@ -8,13 +8,11 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/cockroachdb/errors"
|
"github.com/cockroachdb/errors"
|
||||||
"github.com/glebarez/sqlite"
|
|
||||||
"github.com/olekukonko/tablewriter"
|
"github.com/olekukonko/tablewriter"
|
||||||
"github.com/olekukonko/tablewriter/renderer"
|
|
||||||
"github.com/olekukonko/tablewriter/tw"
|
|
||||||
"gopkg.in/DATA-DOG/go-sqlmock.v2"
|
"gopkg.in/DATA-DOG/go-sqlmock.v2"
|
||||||
"gorm.io/driver/mysql"
|
"gorm.io/driver/mysql"
|
||||||
"gorm.io/driver/postgres"
|
"gorm.io/driver/postgres"
|
||||||
|
"gorm.io/driver/sqlite"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
"gorm.io/gorm/clause"
|
"gorm.io/gorm/clause"
|
||||||
"gorm.io/gorm/schema"
|
"gorm.io/gorm/schema"
|
||||||
@@ -63,25 +61,18 @@ func main() {
|
|||||||
|
|
||||||
_, _ = w.WriteString("```\n")
|
_, _ = w.WriteString("```\n")
|
||||||
|
|
||||||
table := tablewriter.NewTable(w,
|
table := tablewriter.NewWriter(w)
|
||||||
tablewriter.WithRenderer(renderer.NewBlueprint(tw.Rendition{
|
table.SetHeader([]string{"Field", "Column", "PostgreSQL", "MySQL", "SQLite3"})
|
||||||
Borders: tw.BorderNone,
|
table.SetBorder(false)
|
||||||
Symbols: tw.NewSymbols(tw.StyleASCII),
|
|
||||||
})),
|
|
||||||
tablewriter.WithHeaderAutoFormat(tw.Off),
|
|
||||||
)
|
|
||||||
table.Header("Field", "Column", "PostgreSQL", "MySQL", "SQLite3")
|
|
||||||
for j, f := range ti.Fields {
|
for j, f := range ti.Fields {
|
||||||
sqlite3Type := strings.ToUpper(collected[2][i].Fields[j].Type)
|
table.Append([]string{
|
||||||
sqlite3Type = strings.ReplaceAll(sqlite3Type, "PRIMARY KEY ", "")
|
|
||||||
_ = table.Append([]string{
|
|
||||||
f.Name, f.Column,
|
f.Name, f.Column,
|
||||||
strings.ToUpper(f.Type), // PostgreSQL
|
strings.ToUpper(f.Type), // PostgreSQL
|
||||||
strings.ToUpper(collected[1][i].Fields[j].Type), // MySQL
|
strings.ToUpper(collected[1][i].Fields[j].Type), // MySQL
|
||||||
sqlite3Type,
|
strings.ToUpper(collected[2][i].Fields[j].Type), // SQLite3
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
_ = table.Render()
|
table.Render()
|
||||||
_, _ = w.WriteString("\n")
|
_, _ = w.WriteString("\n")
|
||||||
|
|
||||||
_, _ = w.WriteString("Primary keys: ")
|
_, _ = w.WriteString("Primary keys: ")
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ import (
|
|||||||
"github.com/cockroachdb/errors"
|
"github.com/cockroachdb/errors"
|
||||||
"github.com/unknwon/com"
|
"github.com/unknwon/com"
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
|
"gorm.io/gorm"
|
||||||
log "unknwon.dev/clog/v2"
|
log "unknwon.dev/clog/v2"
|
||||||
"xorm.io/xorm"
|
|
||||||
|
|
||||||
"gogs.io/gogs/internal/conf"
|
"gogs.io/gogs/internal/conf"
|
||||||
"gogs.io/gogs/internal/errutil"
|
"gogs.io/gogs/internal/errutil"
|
||||||
@@ -40,38 +40,41 @@ const (
|
|||||||
// PublicKey represents a user or deploy SSH public key.
|
// PublicKey represents a user or deploy SSH public key.
|
||||||
type PublicKey struct {
|
type PublicKey struct {
|
||||||
ID int64 `gorm:"primaryKey"`
|
ID int64 `gorm:"primaryKey"`
|
||||||
OwnerID int64 `xorm:"INDEX NOT NULL" gorm:"index;not null"`
|
OwnerID int64 `gorm:"index;not null"`
|
||||||
Name string `xorm:"NOT NULL" gorm:"not null"`
|
Name string `gorm:"not null"`
|
||||||
Fingerprint string `xorm:"NOT NULL" gorm:"not null"`
|
Fingerprint string `gorm:"not null"`
|
||||||
Content string `xorm:"TEXT NOT NULL" gorm:"type:TEXT;not null"`
|
Content string `gorm:"type:text;not null"`
|
||||||
Mode AccessMode `xorm:"NOT NULL DEFAULT 2" gorm:"not null;default:2"`
|
Mode AccessMode `gorm:"not null;default:2"`
|
||||||
Type KeyType `xorm:"NOT NULL DEFAULT 1" gorm:"not null;default:1"`
|
Type KeyType `gorm:"not null;default:1"`
|
||||||
|
|
||||||
Created time.Time `xorm:"-" json:"-" gorm:"-"`
|
Created time.Time `gorm:"-" json:"-"`
|
||||||
CreatedUnix int64
|
CreatedUnix int64
|
||||||
Updated time.Time `xorm:"-" json:"-" gorm:"-"` // Note: Updated must below Created for AfterSet.
|
Updated time.Time `gorm:"-" json:"-"` // Note: Updated must below Created for AfterFind.
|
||||||
UpdatedUnix int64
|
UpdatedUnix int64
|
||||||
HasRecentActivity bool `xorm:"-" json:"-" gorm:"-"`
|
HasRecentActivity bool `gorm:"-" json:"-"`
|
||||||
HasUsed bool `xorm:"-" json:"-" gorm:"-"`
|
HasUsed bool `gorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *PublicKey) BeforeInsert() {
|
func (k *PublicKey) BeforeCreate(tx *gorm.DB) error {
|
||||||
k.CreatedUnix = time.Now().Unix()
|
if k.CreatedUnix == 0 {
|
||||||
|
k.CreatedUnix = tx.NowFunc().Unix()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *PublicKey) BeforeUpdate() {
|
func (k *PublicKey) BeforeUpdate(tx *gorm.DB) error {
|
||||||
k.UpdatedUnix = time.Now().Unix()
|
k.UpdatedUnix = tx.NowFunc().Unix()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *PublicKey) AfterSet(colName string, _ xorm.Cell) {
|
func (k *PublicKey) AfterFind(tx *gorm.DB) error {
|
||||||
switch colName {
|
k.Created = time.Unix(k.CreatedUnix, 0).Local()
|
||||||
case "created_unix":
|
if k.UpdatedUnix > 0 {
|
||||||
k.Created = time.Unix(k.CreatedUnix, 0).Local()
|
|
||||||
case "updated_unix":
|
|
||||||
k.Updated = time.Unix(k.UpdatedUnix, 0).Local()
|
k.Updated = time.Unix(k.UpdatedUnix, 0).Local()
|
||||||
k.HasUsed = k.Updated.After(k.Created)
|
k.HasUsed = k.Updated.After(k.Created)
|
||||||
k.HasRecentActivity = k.Updated.Add(7 * 24 * time.Hour).After(time.Now())
|
k.HasRecentActivity = k.Updated.Add(7 * 24 * time.Hour).After(tx.NowFunc())
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// OmitEmail returns content of public key without email address.
|
// OmitEmail returns content of public key without email address.
|
||||||
@@ -175,8 +178,8 @@ func parseKeyString(content string) (string, error) {
|
|||||||
|
|
||||||
// writeTmpKeyFile writes key content to a temporary file
|
// writeTmpKeyFile writes key content to a temporary file
|
||||||
// and returns the name of that file, along with any possible errors.
|
// and returns the name of that file, along with any possible errors.
|
||||||
func writeTmpKeyFile(content, keyTestPath string) (string, error) {
|
func writeTmpKeyFile(content string) (string, error) {
|
||||||
tmpFile, err := os.CreateTemp(keyTestPath, "gogs_keytest")
|
tmpFile, err := os.CreateTemp(conf.SSH.KeyTestPath, "gogs_keytest")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", errors.Newf("TempFile: %v", err)
|
return "", errors.Newf("TempFile: %v", err)
|
||||||
}
|
}
|
||||||
@@ -188,15 +191,15 @@ func writeTmpKeyFile(content, keyTestPath string) (string, error) {
|
|||||||
return tmpFile.Name(), nil
|
return tmpFile.Name(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SSHKeygenParsePublicKey extracts key type and length using ssh-keygen.
|
// SSHKeyGenParsePublicKey extracts key type and length using ssh-keygen.
|
||||||
func SSHKeygenParsePublicKey(key, keyTestPath, keygenPath string) (string, int, error) {
|
func SSHKeyGenParsePublicKey(key string) (string, int, error) {
|
||||||
tmpName, err := writeTmpKeyFile(key, keyTestPath)
|
tmpName, err := writeTmpKeyFile(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", 0, errors.Newf("writeTmpKeyFile: %v", err)
|
return "", 0, errors.Newf("writeTmpKeyFile: %v", err)
|
||||||
}
|
}
|
||||||
defer os.Remove(tmpName)
|
defer os.Remove(tmpName)
|
||||||
|
|
||||||
stdout, stderr, err := process.Exec("SSHKeygenParsePublicKey", keygenPath, "-lf", tmpName)
|
stdout, stderr, err := process.Exec("SSHKeyGenParsePublicKey", conf.SSH.KeygenPath, "-lf", tmpName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", 0, errors.Newf("fail to parse public key: %s - %s", err, stderr)
|
return "", 0, errors.Newf("fail to parse public key: %s - %s", err, stderr)
|
||||||
}
|
}
|
||||||
@@ -301,8 +304,8 @@ func CheckPublicKeyString(content string) (_ string, err error) {
|
|||||||
fnName = "SSHNativeParsePublicKey"
|
fnName = "SSHNativeParsePublicKey"
|
||||||
keyType, length, err = SSHNativeParsePublicKey(content)
|
keyType, length, err = SSHNativeParsePublicKey(content)
|
||||||
} else {
|
} else {
|
||||||
fnName = "SSHKeygenParsePublicKey"
|
fnName = "SSHKeyGenParsePublicKey"
|
||||||
keyType, length, err = SSHKeygenParsePublicKey(content, conf.SSH.KeyTestPath, conf.SSH.KeygenPath)
|
keyType, length, err = SSHKeyGenParsePublicKey(content)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", errors.Newf("%s: %v", fnName, err)
|
return "", errors.Newf("%s: %v", fnName, err)
|
||||||
@@ -356,19 +359,16 @@ func appendAuthorizedKeysToFile(keys ...*PublicKey) error {
|
|||||||
// checkKeyContent onlys checks if key content has been used as public key,
|
// checkKeyContent onlys checks if key content has been used as public key,
|
||||||
// it is OK to use same key as deploy key for multiple repositories/users.
|
// it is OK to use same key as deploy key for multiple repositories/users.
|
||||||
func checkKeyContent(content string) error {
|
func checkKeyContent(content string) error {
|
||||||
has, err := x.Get(&PublicKey{
|
err := db.Where("content = ? AND type = ?", content, KeyTypeUser).First(&PublicKey{}).Error
|
||||||
Content: content,
|
if err == nil {
|
||||||
Type: KeyTypeUser,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
} else if has {
|
|
||||||
return ErrKeyAlreadyExist{0, content}
|
return ErrKeyAlreadyExist{0, content}
|
||||||
|
} else if !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func addKey(e Engine, key *PublicKey) (err error) {
|
func addKey(tx *gorm.DB, key *PublicKey) (err error) {
|
||||||
// Calculate fingerprint.
|
// Calculate fingerprint.
|
||||||
tmpPath := strings.ReplaceAll(path.Join(os.TempDir(), fmt.Sprintf("%d", time.Now().Nanosecond()), "id_rsa.pub"), "\\", "/")
|
tmpPath := strings.ReplaceAll(path.Join(os.TempDir(), fmt.Sprintf("%d", time.Now().Nanosecond()), "id_rsa.pub"), "\\", "/")
|
||||||
_ = os.MkdirAll(path.Dir(tmpPath), os.ModePerm)
|
_ = os.MkdirAll(path.Dir(tmpPath), os.ModePerm)
|
||||||
@@ -385,7 +385,7 @@ func addKey(e Engine, key *PublicKey) (err error) {
|
|||||||
key.Fingerprint = strings.Split(stdout, " ")[1]
|
key.Fingerprint = strings.Split(stdout, " ")[1]
|
||||||
|
|
||||||
// Save SSH key.
|
// Save SSH key.
|
||||||
if _, err = e.Insert(key); err != nil {
|
if err = tx.Create(key).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -404,16 +404,10 @@ func AddPublicKey(ownerID int64, name, content string) (*PublicKey, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Key name of same user cannot be duplicated.
|
// Key name of same user cannot be duplicated.
|
||||||
has, err := x.Where("owner_id = ? AND name = ?", ownerID, name).Get(new(PublicKey))
|
err := db.Where("owner_id = ? AND name = ?", ownerID, name).First(new(PublicKey)).Error
|
||||||
if err != nil {
|
if err == nil {
|
||||||
return nil, err
|
|
||||||
} else if has {
|
|
||||||
return nil, ErrKeyNameAlreadyUsed{ownerID, name}
|
return nil, ErrKeyNameAlreadyUsed{ownerID, name}
|
||||||
}
|
} else if !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
|
||||||
sess := x.NewSession()
|
|
||||||
defer sess.Close()
|
|
||||||
if err = sess.Begin(); err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -424,21 +418,25 @@ func AddPublicKey(ownerID int64, name, content string) (*PublicKey, error) {
|
|||||||
Mode: AccessModeWrite,
|
Mode: AccessModeWrite,
|
||||||
Type: KeyTypeUser,
|
Type: KeyTypeUser,
|
||||||
}
|
}
|
||||||
if err = addKey(sess, key); err != nil {
|
err = db.Transaction(func(tx *gorm.DB) error {
|
||||||
|
return addKey(tx, key)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
return nil, errors.Newf("addKey: %v", err)
|
return nil, errors.Newf("addKey: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return key, sess.Commit()
|
return key, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPublicKeyByID returns public key by given ID.
|
// GetPublicKeyByID returns public key by given ID.
|
||||||
func GetPublicKeyByID(keyID int64) (*PublicKey, error) {
|
func GetPublicKeyByID(keyID int64) (*PublicKey, error) {
|
||||||
key := new(PublicKey)
|
key := new(PublicKey)
|
||||||
has, err := x.Id(keyID).Get(key)
|
err := db.Where("id = ?", keyID).First(key).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil, ErrKeyNotExist{keyID}
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if !has {
|
|
||||||
return nil, ErrKeyNotExist{keyID}
|
|
||||||
}
|
}
|
||||||
return key, nil
|
return key, nil
|
||||||
}
|
}
|
||||||
@@ -448,11 +446,12 @@ func GetPublicKeyByID(keyID int64) (*PublicKey, error) {
|
|||||||
// exists.
|
// exists.
|
||||||
func SearchPublicKeyByContent(content string) (*PublicKey, error) {
|
func SearchPublicKeyByContent(content string) (*PublicKey, error) {
|
||||||
key := new(PublicKey)
|
key := new(PublicKey)
|
||||||
has, err := x.Where("content like ?", content+"%").Get(key)
|
err := db.Where("content LIKE ?", content+"%").First(key).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil, ErrKeyNotExist{}
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if !has {
|
|
||||||
return nil, ErrKeyNotExist{}
|
|
||||||
}
|
}
|
||||||
return key, nil
|
return key, nil
|
||||||
}
|
}
|
||||||
@@ -460,23 +459,21 @@ func SearchPublicKeyByContent(content string) (*PublicKey, error) {
|
|||||||
// ListPublicKeys returns a list of public keys belongs to given user.
|
// ListPublicKeys returns a list of public keys belongs to given user.
|
||||||
func ListPublicKeys(uid int64) ([]*PublicKey, error) {
|
func ListPublicKeys(uid int64) ([]*PublicKey, error) {
|
||||||
keys := make([]*PublicKey, 0, 5)
|
keys := make([]*PublicKey, 0, 5)
|
||||||
return keys, x.Where("owner_id = ?", uid).Find(&keys)
|
return keys, db.Where("owner_id = ?", uid).Find(&keys).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdatePublicKey updates given public key.
|
// UpdatePublicKey updates given public key.
|
||||||
func UpdatePublicKey(key *PublicKey) error {
|
func UpdatePublicKey(key *PublicKey) error {
|
||||||
_, err := x.Id(key.ID).AllCols().Update(key)
|
return db.Model(key).Where("id = ?", key.ID).Updates(key).Error
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// deletePublicKeys does the actual key deletion but does not update authorized_keys file.
|
// deletePublicKeys does the actual key deletion but does not update authorized_keys file.
|
||||||
func deletePublicKeys(e *xorm.Session, keyIDs ...int64) error {
|
func deletePublicKeys(tx *gorm.DB, keyIDs ...int64) error {
|
||||||
if len(keyIDs) == 0 {
|
if len(keyIDs) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := e.In("id", keyIDs).Delete(new(PublicKey))
|
return tx.Where("id IN ?", keyIDs).Delete(new(PublicKey)).Error
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeletePublicKey deletes SSH key information both in database and authorized_keys file.
|
// DeletePublicKey deletes SSH key information both in database and authorized_keys file.
|
||||||
@@ -494,17 +491,10 @@ func DeletePublicKey(doer *User, id int64) (err error) {
|
|||||||
return ErrKeyAccessDenied{doer.ID, key.ID, "public"}
|
return ErrKeyAccessDenied{doer.ID, key.ID, "public"}
|
||||||
}
|
}
|
||||||
|
|
||||||
sess := x.NewSession()
|
err = db.Transaction(func(tx *gorm.DB) error {
|
||||||
defer sess.Close()
|
return deletePublicKeys(tx, id)
|
||||||
if err = sess.Begin(); err != nil {
|
})
|
||||||
return err
|
if err != nil {
|
||||||
}
|
|
||||||
|
|
||||||
if err = deletePublicKeys(sess, id); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = sess.Commit(); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -531,14 +521,24 @@ func RewriteAuthorizedKeys() error {
|
|||||||
}
|
}
|
||||||
defer os.Remove(tmpPath)
|
defer os.Remove(tmpPath)
|
||||||
|
|
||||||
err = x.Iterate(new(PublicKey), func(idx int, bean any) (err error) {
|
// Use FindInBatches to process keys in chunks to avoid memory issues with large datasets
|
||||||
_, err = f.WriteString((bean.(*PublicKey)).AuthorizedString())
|
err = db.FindInBatches(&[]PublicKey{}, 100, func(tx *gorm.DB, batch int) error {
|
||||||
return err
|
var keys []PublicKey
|
||||||
})
|
if err := tx.Find(&keys).Error; err != nil {
|
||||||
_ = f.Close()
|
return err
|
||||||
|
}
|
||||||
|
for _, key := range keys {
|
||||||
|
if _, err := f.WriteString(key.AuthorizedString()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
_ = f.Close()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
_ = f.Close()
|
||||||
|
|
||||||
if com.IsExist(fpath) {
|
if com.IsExist(fpath) {
|
||||||
if err = os.Remove(fpath); err != nil {
|
if err = os.Remove(fpath); err != nil {
|
||||||
@@ -562,37 +562,40 @@ func RewriteAuthorizedKeys() error {
|
|||||||
// DeployKey represents deploy key information and its relation with repository.
|
// DeployKey represents deploy key information and its relation with repository.
|
||||||
type DeployKey struct {
|
type DeployKey struct {
|
||||||
ID int64
|
ID int64
|
||||||
KeyID int64 `xorm:"UNIQUE(s) INDEX"`
|
KeyID int64 `gorm:"uniqueIndex:s;index"`
|
||||||
RepoID int64 `xorm:"UNIQUE(s) INDEX"`
|
RepoID int64 `gorm:"uniqueIndex:s;index"`
|
||||||
Name string
|
Name string
|
||||||
Fingerprint string
|
Fingerprint string
|
||||||
Content string `xorm:"-" json:"-" gorm:"-"`
|
Content string `gorm:"-" json:"-"`
|
||||||
|
|
||||||
Created time.Time `xorm:"-" json:"-" gorm:"-"`
|
Created time.Time `gorm:"-" json:"-"`
|
||||||
CreatedUnix int64
|
CreatedUnix int64
|
||||||
Updated time.Time `xorm:"-" json:"-" gorm:"-"` // Note: Updated must below Created for AfterSet.
|
Updated time.Time `gorm:"-" json:"-"` // Note: Updated must below Created for AfterFind.
|
||||||
UpdatedUnix int64
|
UpdatedUnix int64
|
||||||
HasRecentActivity bool `xorm:"-" json:"-" gorm:"-"`
|
HasRecentActivity bool `gorm:"-" json:"-"`
|
||||||
HasUsed bool `xorm:"-" json:"-" gorm:"-"`
|
HasUsed bool `gorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *DeployKey) BeforeInsert() {
|
func (k *DeployKey) BeforeCreate(tx *gorm.DB) error {
|
||||||
k.CreatedUnix = time.Now().Unix()
|
if k.CreatedUnix == 0 {
|
||||||
|
k.CreatedUnix = tx.NowFunc().Unix()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *DeployKey) BeforeUpdate() {
|
func (k *DeployKey) BeforeUpdate(tx *gorm.DB) error {
|
||||||
k.UpdatedUnix = time.Now().Unix()
|
k.UpdatedUnix = tx.NowFunc().Unix()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *DeployKey) AfterSet(colName string, _ xorm.Cell) {
|
func (k *DeployKey) AfterFind(tx *gorm.DB) error {
|
||||||
switch colName {
|
k.Created = time.Unix(k.CreatedUnix, 0).Local()
|
||||||
case "created_unix":
|
if k.UpdatedUnix > 0 {
|
||||||
k.Created = time.Unix(k.CreatedUnix, 0).Local()
|
|
||||||
case "updated_unix":
|
|
||||||
k.Updated = time.Unix(k.UpdatedUnix, 0).Local()
|
k.Updated = time.Unix(k.UpdatedUnix, 0).Local()
|
||||||
k.HasUsed = k.Updated.After(k.Created)
|
k.HasUsed = k.Updated.After(k.Created)
|
||||||
k.HasRecentActivity = k.Updated.Add(7 * 24 * time.Hour).After(time.Now())
|
k.HasRecentActivity = k.Updated.Add(7 * 24 * time.Hour).After(tx.NowFunc())
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetContent gets associated public key content.
|
// GetContent gets associated public key content.
|
||||||
@@ -605,28 +608,28 @@ func (k *DeployKey) GetContent() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkDeployKey(e Engine, keyID, repoID int64, name string) error {
|
func checkDeployKey(tx *gorm.DB, keyID, repoID int64, name string) error {
|
||||||
// Note: We want error detail, not just true or false here.
|
// Note: We want error detail, not just true or false here.
|
||||||
has, err := e.Where("key_id = ? AND repo_id = ?", keyID, repoID).Get(new(DeployKey))
|
err := tx.Where("key_id = ? AND repo_id = ?", keyID, repoID).First(new(DeployKey)).Error
|
||||||
if err != nil {
|
if err == nil {
|
||||||
return err
|
|
||||||
} else if has {
|
|
||||||
return ErrDeployKeyAlreadyExist{keyID, repoID}
|
return ErrDeployKeyAlreadyExist{keyID, repoID}
|
||||||
|
} else if !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
has, err = e.Where("repo_id = ? AND name = ?", repoID, name).Get(new(DeployKey))
|
err = tx.Where("repo_id = ? AND name = ?", repoID, name).First(new(DeployKey)).Error
|
||||||
if err != nil {
|
if err == nil {
|
||||||
return err
|
|
||||||
} else if has {
|
|
||||||
return ErrDeployKeyNameAlreadyUsed{repoID, name}
|
return ErrDeployKeyNameAlreadyUsed{repoID, name}
|
||||||
|
} else if !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// addDeployKey adds new key-repo relation.
|
// addDeployKey adds new key-repo relation.
|
||||||
func addDeployKey(e *xorm.Session, keyID, repoID int64, name, fingerprint string) (*DeployKey, error) {
|
func addDeployKey(tx *gorm.DB, keyID, repoID int64, name, fingerprint string) (*DeployKey, error) {
|
||||||
if err := checkDeployKey(e, keyID, repoID, name); err != nil {
|
if err := checkDeployKey(tx, keyID, repoID, name); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -636,14 +639,14 @@ func addDeployKey(e *xorm.Session, keyID, repoID int64, name, fingerprint string
|
|||||||
Name: name,
|
Name: name,
|
||||||
Fingerprint: fingerprint,
|
Fingerprint: fingerprint,
|
||||||
}
|
}
|
||||||
_, err := e.Insert(key)
|
err := tx.Create(key).Error
|
||||||
return key, err
|
return key, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasDeployKey returns true if public key is a deploy key of given repository.
|
// HasDeployKey returns true if public key is a deploy key of given repository.
|
||||||
func HasDeployKey(keyID, repoID int64) bool {
|
func HasDeployKey(keyID, repoID int64) bool {
|
||||||
has, _ := x.Where("key_id = ? AND repo_id = ?", keyID, repoID).Get(new(DeployKey))
|
err := db.Where("key_id = ? AND repo_id = ?", keyID, repoID).First(new(DeployKey)).Error
|
||||||
return has
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddDeployKey add new deploy key to database and authorized_keys file.
|
// AddDeployKey add new deploy key to database and authorized_keys file.
|
||||||
@@ -657,30 +660,34 @@ func AddDeployKey(repoID int64, name, content string) (*DeployKey, error) {
|
|||||||
Mode: AccessModeRead,
|
Mode: AccessModeRead,
|
||||||
Type: KeyTypeDeploy,
|
Type: KeyTypeDeploy,
|
||||||
}
|
}
|
||||||
has, err := x.Get(pkey)
|
err := db.Where("content = ? AND mode = ? AND type = ?", content, AccessModeRead, KeyTypeDeploy).First(pkey).Error
|
||||||
if err != nil {
|
has := err == nil
|
||||||
|
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
sess := x.NewSession()
|
var key *DeployKey
|
||||||
defer sess.Close()
|
err = db.Transaction(func(tx *gorm.DB) error {
|
||||||
if err = sess.Begin(); err != nil {
|
// First time use this deploy key.
|
||||||
return nil, err
|
if !has {
|
||||||
}
|
if err := addKey(tx, pkey); err != nil {
|
||||||
|
return errors.Newf("addKey: %v", err)
|
||||||
// First time use this deploy key.
|
}
|
||||||
if !has {
|
|
||||||
if err = addKey(sess, pkey); err != nil {
|
|
||||||
return nil, errors.Newf("addKey: %v", err)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
key, err := addDeployKey(sess, pkey.ID, repoID, name, pkey.Fingerprint)
|
var err error
|
||||||
|
key, err = addDeployKey(tx, pkey.ID, repoID, name, pkey.Fingerprint)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Newf("addDeployKey: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Newf("addDeployKey: %v", err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return key, sess.Commit()
|
return key, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ errutil.NotFound = (*ErrDeployKeyNotExist)(nil)
|
var _ errutil.NotFound = (*ErrDeployKeyNotExist)(nil)
|
||||||
@@ -705,11 +712,12 @@ func (ErrDeployKeyNotExist) NotFound() bool {
|
|||||||
// GetDeployKeyByID returns deploy key by given ID.
|
// GetDeployKeyByID returns deploy key by given ID.
|
||||||
func GetDeployKeyByID(id int64) (*DeployKey, error) {
|
func GetDeployKeyByID(id int64) (*DeployKey, error) {
|
||||||
key := new(DeployKey)
|
key := new(DeployKey)
|
||||||
has, err := x.Id(id).Get(key)
|
err := db.Where("id = ?", id).First(key).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil, ErrDeployKeyNotExist{args: map[string]any{"deployKeyID": id}}
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if !has {
|
|
||||||
return nil, ErrDeployKeyNotExist{args: map[string]any{"deployKeyID": id}}
|
|
||||||
}
|
}
|
||||||
return key, nil
|
return key, nil
|
||||||
}
|
}
|
||||||
@@ -720,19 +728,19 @@ func GetDeployKeyByRepo(keyID, repoID int64) (*DeployKey, error) {
|
|||||||
KeyID: keyID,
|
KeyID: keyID,
|
||||||
RepoID: repoID,
|
RepoID: repoID,
|
||||||
}
|
}
|
||||||
has, err := x.Get(key)
|
err := db.Where("key_id = ? AND repo_id = ?", keyID, repoID).First(key).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil, ErrDeployKeyNotExist{args: map[string]any{"keyID": keyID, "repoID": repoID}}
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if !has {
|
|
||||||
return nil, ErrDeployKeyNotExist{args: map[string]any{"keyID": keyID, "repoID": repoID}}
|
|
||||||
}
|
}
|
||||||
return key, nil
|
return key, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateDeployKey updates deploy key information.
|
// UpdateDeployKey updates deploy key information.
|
||||||
func UpdateDeployKey(key *DeployKey) error {
|
func UpdateDeployKey(key *DeployKey) error {
|
||||||
_, err := x.Id(key.ID).AllCols().Update(key)
|
return db.Model(key).Where("id = ?", key.ID).Updates(key).Error
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteDeployKey deletes deploy key from its repository authorized_keys file if needed.
|
// DeleteDeployKey deletes deploy key from its repository authorized_keys file if needed.
|
||||||
@@ -761,31 +769,27 @@ func DeleteDeployKey(doer *User, id int64) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sess := x.NewSession()
|
return db.Transaction(func(tx *gorm.DB) error {
|
||||||
defer sess.Close()
|
if err := tx.Where("id = ?", key.ID).Delete(new(DeployKey)).Error; err != nil {
|
||||||
if err = sess.Begin(); err != nil {
|
return errors.Newf("delete deploy key [%d]: %v", key.ID, err)
|
||||||
return err
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = sess.ID(key.ID).Delete(new(DeployKey)); err != nil {
|
// Check if this is the last reference to same key content.
|
||||||
return errors.Newf("delete deploy key [%d]: %v", key.ID, err)
|
err := tx.Where("key_id = ?", key.KeyID).First(new(DeployKey)).Error
|
||||||
}
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
if err = deletePublicKeys(tx, key.KeyID); err != nil {
|
||||||
// Check if this is the last reference to same key content.
|
return err
|
||||||
has, err := sess.Where("key_id = ?", key.KeyID).Get(new(DeployKey))
|
}
|
||||||
if err != nil {
|
} else if err != nil {
|
||||||
return err
|
|
||||||
} else if !has {
|
|
||||||
if err = deletePublicKeys(sess, key.KeyID); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return sess.Commit()
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListDeployKeys returns all deploy keys by given repository ID.
|
// ListDeployKeys returns all deploy keys by given repository ID.
|
||||||
func ListDeployKeys(repoID int64) ([]*DeployKey, error) {
|
func ListDeployKeys(repoID int64) ([]*DeployKey, error) {
|
||||||
keys := make([]*DeployKey, 0, 5)
|
keys := make([]*DeployKey, 0, 5)
|
||||||
return keys, x.Where("repo_id = ?", repoID).Find(&keys)
|
return keys, db.Where("repo_id = ?", repoID).Find(&keys).Error
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,13 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
"gogs.io/gogs/internal/conf"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSSHParsePublicKey(t *testing.T) {
|
func Test_SSHParsePublicKey(t *testing.T) {
|
||||||
tempPath := t.TempDir()
|
// TODO: Refactor SSHKeyGenParsePublicKey to accept a tempPath and remove this init.
|
||||||
|
conf.MustInit("")
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
content string
|
content string
|
||||||
@@ -51,22 +53,20 @@ func TestSSHParsePublicKey(t *testing.T) {
|
|||||||
expType: "ecdsa",
|
expType: "ecdsa",
|
||||||
expLength: 521,
|
expLength: 521,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "ed25519-256",
|
|
||||||
content: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICGYutovQfTewtcodVN1E1UUzMk4GQfiRI5ZoP/kTlDb nocomment",
|
|
||||||
expType: "ed25519",
|
|
||||||
expLength: 256,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
typ, length, err := SSHNativeParsePublicKey(test.content)
|
typ, length, err := SSHNativeParsePublicKey(test.content)
|
||||||
require.NoError(t, err)
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
assert.Equal(t, test.expType, typ)
|
assert.Equal(t, test.expType, typ)
|
||||||
assert.Equal(t, test.expLength, length)
|
assert.Equal(t, test.expLength, length)
|
||||||
|
|
||||||
typ, length, err = SSHKeygenParsePublicKey(test.content, tempPath, "ssh-keygen")
|
typ, length, err = SSHKeyGenParsePublicKey(test.content)
|
||||||
require.NoError(t, err)
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
assert.Equal(t, test.expType, typ)
|
assert.Equal(t, test.expType, typ)
|
||||||
assert.Equal(t, test.expLength, length)
|
assert.Equal(t, test.expLength, length)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/cockroachdb/errors"
|
"github.com/cockroachdb/errors"
|
||||||
"github.com/pquerna/otp/totp"
|
"github.com/pquerna/otp/totp"
|
||||||
"github.com/unknwon/com"
|
"github.com/unknwon/com"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
|
||||||
"gogs.io/gogs/internal/conf"
|
"gogs.io/gogs/internal/conf"
|
||||||
"gogs.io/gogs/internal/cryptoutil"
|
"gogs.io/gogs/internal/cryptoutil"
|
||||||
@@ -37,20 +38,16 @@ func (t *TwoFactor) ValidateTOTP(passcode string) (bool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DeleteTwoFactor removes two-factor authentication token and recovery codes of given user.
|
// DeleteTwoFactor removes two-factor authentication token and recovery codes of given user.
|
||||||
func DeleteTwoFactor(userID int64) (err error) {
|
func DeleteTwoFactor(userID int64) error {
|
||||||
sess := x.NewSession()
|
return db.Transaction(func(tx *gorm.DB) error {
|
||||||
defer sess.Close()
|
if err := tx.Where("user_id = ?", userID).Delete(new(TwoFactor)).Error; err != nil {
|
||||||
if err = sess.Begin(); err != nil {
|
return errors.Newf("delete two-factor: %v", err)
|
||||||
return err
|
}
|
||||||
}
|
if err := deleteRecoveryCodesByUserID(tx, userID); err != nil {
|
||||||
|
return errors.Newf("deleteRecoveryCodesByUserID: %v", err)
|
||||||
if _, err = sess.Where("user_id = ?", userID).Delete(new(TwoFactor)); err != nil {
|
}
|
||||||
return errors.Newf("delete two-factor: %v", err)
|
return nil
|
||||||
} else if err = deleteRecoveryCodesByUserID(sess, userID); err != nil {
|
})
|
||||||
return errors.Newf("deleteRecoveryCodesByUserID: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return sess.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TwoFactorRecoveryCode represents a two-factor authentication recovery code.
|
// TwoFactorRecoveryCode represents a two-factor authentication recovery code.
|
||||||
@@ -64,12 +61,11 @@ type TwoFactorRecoveryCode struct {
|
|||||||
// GetRecoveryCodesByUserID returns all recovery codes of given user.
|
// GetRecoveryCodesByUserID returns all recovery codes of given user.
|
||||||
func GetRecoveryCodesByUserID(userID int64) ([]*TwoFactorRecoveryCode, error) {
|
func GetRecoveryCodesByUserID(userID int64) ([]*TwoFactorRecoveryCode, error) {
|
||||||
recoveryCodes := make([]*TwoFactorRecoveryCode, 0, 10)
|
recoveryCodes := make([]*TwoFactorRecoveryCode, 0, 10)
|
||||||
return recoveryCodes, x.Where("user_id = ?", userID).Find(&recoveryCodes)
|
return recoveryCodes, db.Where("user_id = ?", userID).Find(&recoveryCodes).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteRecoveryCodesByUserID(e Engine, userID int64) error {
|
func deleteRecoveryCodesByUserID(e *gorm.DB, userID int64) error {
|
||||||
_, err := e.Where("user_id = ?", userID).Delete(new(TwoFactorRecoveryCode))
|
return e.Where("user_id = ?", userID).Delete(&TwoFactorRecoveryCode{}).Error
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegenerateRecoveryCodes regenerates new set of recovery codes for given user.
|
// RegenerateRecoveryCodes regenerates new set of recovery codes for given user.
|
||||||
@@ -79,19 +75,15 @@ func RegenerateRecoveryCodes(userID int64) error {
|
|||||||
return errors.Newf("generateRecoveryCodes: %v", err)
|
return errors.Newf("generateRecoveryCodes: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sess := x.NewSession()
|
return db.Transaction(func(tx *gorm.DB) error {
|
||||||
defer sess.Close()
|
if err := deleteRecoveryCodesByUserID(tx, userID); err != nil {
|
||||||
if err = sess.Begin(); err != nil {
|
return errors.Newf("deleteRecoveryCodesByUserID: %v", err)
|
||||||
return err
|
}
|
||||||
}
|
if err := tx.Create(recoveryCodes).Error; err != nil {
|
||||||
|
return errors.Newf("insert new recovery codes: %v", err)
|
||||||
if err = deleteRecoveryCodesByUserID(sess, userID); err != nil {
|
}
|
||||||
return errors.Newf("deleteRecoveryCodesByUserID: %v", err)
|
return nil
|
||||||
} else if _, err = sess.Insert(recoveryCodes); err != nil {
|
})
|
||||||
return errors.Newf("insert new recovery codes: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return sess.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ErrTwoFactorRecoveryCodeNotFound struct {
|
type ErrTwoFactorRecoveryCodeNotFound struct {
|
||||||
|
|||||||
@@ -465,7 +465,7 @@ func usersDeleteByID(t *testing.T, ctx context.Context, s *UsersStore) {
|
|||||||
reposStore := newReposStore(s.db)
|
reposStore := newReposStore(s.db)
|
||||||
|
|
||||||
t.Run("user still has repository ownership", func(t *testing.T) {
|
t.Run("user still has repository ownership", func(t *testing.T) {
|
||||||
alice, err := s.Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
|
alice, err := s.Create(ctx, "alice", "alice@exmaple.com", CreateUserOptions{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = reposStore.Create(ctx, alice.ID, CreateRepoOptions{Name: "repo1"})
|
_, err = reposStore.Create(ctx, alice.ID, CreateRepoOptions{Name: "repo1"})
|
||||||
@@ -477,7 +477,7 @@ func usersDeleteByID(t *testing.T, ctx context.Context, s *UsersStore) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("user still has organization membership", func(t *testing.T) {
|
t.Run("user still has organization membership", func(t *testing.T) {
|
||||||
bob, err := s.Create(ctx, "bob", "bob@example.com", CreateUserOptions{})
|
bob, err := s.Create(ctx, "bob", "bob@exmaple.com", CreateUserOptions{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// TODO: Use Orgs.Create to replace SQL hack when the method is available.
|
// TODO: Use Orgs.Create to replace SQL hack when the method is available.
|
||||||
@@ -498,14 +498,14 @@ func usersDeleteByID(t *testing.T, ctx context.Context, s *UsersStore) {
|
|||||||
assert.Equal(t, wantErr, err)
|
assert.Equal(t, wantErr, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
cindy, err := s.Create(ctx, "cindy", "cindy@example.com", CreateUserOptions{})
|
cindy, err := s.Create(ctx, "cindy", "cindy@exmaple.com", CreateUserOptions{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
frank, err := s.Create(ctx, "frank", "frank@example.com", CreateUserOptions{})
|
frank, err := s.Create(ctx, "frank", "frank@exmaple.com", CreateUserOptions{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
repo2, err := reposStore.Create(ctx, cindy.ID, CreateRepoOptions{Name: "repo2"})
|
repo2, err := reposStore.Create(ctx, cindy.ID, CreateRepoOptions{Name: "repo2"})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
testUser, err := s.Create(ctx, "testUser", "testUser@example.com", CreateUserOptions{})
|
testUser, err := s.Create(ctx, "testUser", "testUser@exmaple.com", CreateUserOptions{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Mock watches, stars and follows
|
// Mock watches, stars and follows
|
||||||
@@ -673,14 +673,14 @@ func usersDeleteByID(t *testing.T, ctx context.Context, s *UsersStore) {
|
|||||||
|
|
||||||
func usersDeleteInactivated(t *testing.T, ctx context.Context, s *UsersStore) {
|
func usersDeleteInactivated(t *testing.T, ctx context.Context, s *UsersStore) {
|
||||||
// User with repository ownership should be skipped
|
// User with repository ownership should be skipped
|
||||||
alice, err := s.Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
|
alice, err := s.Create(ctx, "alice", "alice@exmaple.com", CreateUserOptions{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
reposStore := newReposStore(s.db)
|
reposStore := newReposStore(s.db)
|
||||||
_, err = reposStore.Create(ctx, alice.ID, CreateRepoOptions{Name: "repo1"})
|
_, err = reposStore.Create(ctx, alice.ID, CreateRepoOptions{Name: "repo1"})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// User with organization membership should be skipped
|
// User with organization membership should be skipped
|
||||||
bob, err := s.Create(ctx, "bob", "bob@example.com", CreateUserOptions{})
|
bob, err := s.Create(ctx, "bob", "bob@exmaple.com", CreateUserOptions{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
// TODO: Use Orgs.Create to replace SQL hack when the method is available.
|
// TODO: Use Orgs.Create to replace SQL hack when the method is available.
|
||||||
org1, err := s.Create(ctx, "org1", "org1@example.com", CreateUserOptions{})
|
org1, err := s.Create(ctx, "org1", "org1@example.com", CreateUserOptions{})
|
||||||
@@ -695,11 +695,11 @@ func usersDeleteInactivated(t *testing.T, ctx context.Context, s *UsersStore) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// User activated state should be skipped
|
// User activated state should be skipped
|
||||||
_, err = s.Create(ctx, "cindy", "cindy@example.com", CreateUserOptions{Activated: true})
|
_, err = s.Create(ctx, "cindy", "cindy@exmaple.com", CreateUserOptions{Activated: true})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// User meant to be deleted
|
// User meant to be deleted
|
||||||
david, err := s.Create(ctx, "david", "david@example.com", CreateUserOptions{})
|
david, err := s.Create(ctx, "david", "david@exmaple.com", CreateUserOptions{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
tempSSHRootPath := filepath.Join(os.TempDir(), "usersDeleteInactivated-tempSSHRootPath")
|
tempSSHRootPath := filepath.Join(os.TempDir(), "usersDeleteInactivated-tempSSHRootPath")
|
||||||
@@ -726,7 +726,7 @@ func usersGetByEmail(t *testing.T, ctx context.Context, s *UsersStore) {
|
|||||||
|
|
||||||
t.Run("ignore organization", func(t *testing.T) {
|
t.Run("ignore organization", func(t *testing.T) {
|
||||||
// TODO: Use Orgs.Create to replace SQL hack when the method is available.
|
// TODO: Use Orgs.Create to replace SQL hack when the method is available.
|
||||||
org, err := s.Create(ctx, "gogs", "gogs@example.com", CreateUserOptions{})
|
org, err := s.Create(ctx, "gogs", "gogs@exmaple.com", CreateUserOptions{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = s.db.Model(&User{}).Where("id", org.ID).UpdateColumn("type", UserTypeOrganization).Error
|
err = s.db.Model(&User{}).Where("id", org.ID).UpdateColumn("type", UserTypeOrganization).Error
|
||||||
@@ -738,7 +738,7 @@ func usersGetByEmail(t *testing.T, ctx context.Context, s *UsersStore) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("by primary email", func(t *testing.T) {
|
t.Run("by primary email", func(t *testing.T) {
|
||||||
alice, err := s.Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
|
alice, err := s.Create(ctx, "alice", "alice@exmaple.com", CreateUserOptions{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = s.GetByEmail(ctx, alice.Email)
|
_, err = s.GetByEmail(ctx, alice.Email)
|
||||||
@@ -760,7 +760,7 @@ func usersGetByEmail(t *testing.T, ctx context.Context, s *UsersStore) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// TODO: Use UserEmails.Create to replace SQL hack when the method is available.
|
// TODO: Use UserEmails.Create to replace SQL hack when the method is available.
|
||||||
email2 := "bob2@example.com"
|
email2 := "bob2@exmaple.com"
|
||||||
err = s.db.Exec(`INSERT INTO email_address (uid, email) VALUES (?, ?)`, bob.ID, email2).Error
|
err = s.db.Exec(`INSERT INTO email_address (uid, email) VALUES (?, ?)`, bob.ID, email2).Error
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
@@ -779,7 +779,7 @@ func usersGetByEmail(t *testing.T, ctx context.Context, s *UsersStore) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func usersGetByID(t *testing.T, ctx context.Context, s *UsersStore) {
|
func usersGetByID(t *testing.T, ctx context.Context, s *UsersStore) {
|
||||||
alice, err := s.Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
|
alice, err := s.Create(ctx, "alice", "alice@exmaple.com", CreateUserOptions{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
user, err := s.GetByID(ctx, alice.ID)
|
user, err := s.GetByID(ctx, alice.ID)
|
||||||
@@ -792,7 +792,7 @@ func usersGetByID(t *testing.T, ctx context.Context, s *UsersStore) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func usersGetByUsername(t *testing.T, ctx context.Context, s *UsersStore) {
|
func usersGetByUsername(t *testing.T, ctx context.Context, s *UsersStore) {
|
||||||
alice, err := s.Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
|
alice, err := s.Create(ctx, "alice", "alice@exmaple.com", CreateUserOptions{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
user, err := s.GetByUsername(ctx, alice.Name)
|
user, err := s.GetByUsername(ctx, alice.Name)
|
||||||
@@ -805,7 +805,7 @@ func usersGetByUsername(t *testing.T, ctx context.Context, s *UsersStore) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func usersGetByKeyID(t *testing.T, ctx context.Context, s *UsersStore) {
|
func usersGetByKeyID(t *testing.T, ctx context.Context, s *UsersStore) {
|
||||||
alice, err := s.Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
|
alice, err := s.Create(ctx, "alice", "alice@exmaple.com", CreateUserOptions{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// TODO: Use PublicKeys.Create to replace SQL hack when the method is available.
|
// TODO: Use PublicKeys.Create to replace SQL hack when the method is available.
|
||||||
@@ -830,11 +830,11 @@ func usersGetByKeyID(t *testing.T, ctx context.Context, s *UsersStore) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func usersGetMailableEmailsByUsernames(t *testing.T, ctx context.Context, s *UsersStore) {
|
func usersGetMailableEmailsByUsernames(t *testing.T, ctx context.Context, s *UsersStore) {
|
||||||
alice, err := s.Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
|
alice, err := s.Create(ctx, "alice", "alice@exmaple.com", CreateUserOptions{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
bob, err := s.Create(ctx, "bob", "bob@example.com", CreateUserOptions{Activated: true})
|
bob, err := s.Create(ctx, "bob", "bob@exmaple.com", CreateUserOptions{Activated: true})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
_, err = s.Create(ctx, "cindy", "cindy@example.com", CreateUserOptions{Activated: true})
|
_, err = s.Create(ctx, "cindy", "cindy@exmaple.com", CreateUserOptions{Activated: true})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
got, err := s.GetMailableEmailsByUsernames(ctx, []string{alice.Name, bob.Name, "ignore-non-exist"})
|
got, err := s.GetMailableEmailsByUsernames(ctx, []string{alice.Name, bob.Name, "ignore-non-exist"})
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ import (
|
|||||||
"github.com/cockroachdb/errors"
|
"github.com/cockroachdb/errors"
|
||||||
jsoniter "github.com/json-iterator/go"
|
jsoniter "github.com/json-iterator/go"
|
||||||
gouuid "github.com/satori/go.uuid"
|
gouuid "github.com/satori/go.uuid"
|
||||||
|
"gorm.io/gorm"
|
||||||
log "unknwon.dev/clog/v2"
|
log "unknwon.dev/clog/v2"
|
||||||
"xorm.io/xorm"
|
|
||||||
|
|
||||||
api "github.com/gogs/go-gogs-client"
|
api "github.com/gogs/go-gogs-client"
|
||||||
|
|
||||||
@@ -95,45 +95,42 @@ type Webhook struct {
|
|||||||
ID int64
|
ID int64
|
||||||
RepoID int64
|
RepoID int64
|
||||||
OrgID int64
|
OrgID int64
|
||||||
URL string `xorm:"url TEXT"`
|
URL string `gorm:"type:text;column:url"`
|
||||||
ContentType HookContentType
|
ContentType HookContentType
|
||||||
Secret string `xorm:"TEXT"`
|
Secret string `gorm:"type:text"`
|
||||||
Events string `xorm:"TEXT"`
|
Events string `gorm:"type:text"`
|
||||||
*HookEvent `xorm:"-"` // LEGACY [1.0]: Cannot ignore JSON (i.e. json:"-") here, it breaks old backup archive
|
*HookEvent `gorm:"-"` // LEGACY [1.0]: Cannot ignore JSON (i.e. json:"-") here, it breaks old backup archive
|
||||||
IsSSL bool `xorm:"is_ssl"`
|
IsSSL bool `gorm:"column:is_ssl"`
|
||||||
IsActive bool
|
IsActive bool
|
||||||
HookTaskType HookTaskType
|
HookTaskType HookTaskType
|
||||||
Meta string `xorm:"TEXT"` // store hook-specific attributes
|
Meta string `gorm:"type:text"` // store hook-specific attributes
|
||||||
LastStatus HookStatus // Last delivery status
|
LastStatus HookStatus // Last delivery status
|
||||||
|
|
||||||
Created time.Time `xorm:"-" json:"-" gorm:"-"`
|
Created time.Time `gorm:"-" json:"-"`
|
||||||
CreatedUnix int64
|
CreatedUnix int64
|
||||||
Updated time.Time `xorm:"-" json:"-" gorm:"-"`
|
Updated time.Time `gorm:"-" json:"-"`
|
||||||
UpdatedUnix int64
|
UpdatedUnix int64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Webhook) BeforeInsert() {
|
func (w *Webhook) BeforeCreate(tx *gorm.DB) error {
|
||||||
w.CreatedUnix = time.Now().Unix()
|
w.CreatedUnix = tx.NowFunc().Unix()
|
||||||
w.UpdatedUnix = w.CreatedUnix
|
w.UpdatedUnix = w.CreatedUnix
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Webhook) BeforeUpdate() {
|
func (w *Webhook) BeforeUpdate(tx *gorm.DB) error {
|
||||||
w.UpdatedUnix = time.Now().Unix()
|
w.UpdatedUnix = tx.NowFunc().Unix()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Webhook) AfterSet(colName string, _ xorm.Cell) {
|
func (w *Webhook) AfterFind(tx *gorm.DB) error {
|
||||||
var err error
|
w.HookEvent = &HookEvent{}
|
||||||
switch colName {
|
if err := jsoniter.Unmarshal([]byte(w.Events), w.HookEvent); err != nil {
|
||||||
case "events":
|
log.Error("Unmarshal [%d]: %v", w.ID, err)
|
||||||
w.HookEvent = &HookEvent{}
|
|
||||||
if err = jsoniter.Unmarshal([]byte(w.Events), w.HookEvent); err != nil {
|
|
||||||
log.Error("Unmarshal [%d]: %v", w.ID, err)
|
|
||||||
}
|
|
||||||
case "created_unix":
|
|
||||||
w.Created = time.Unix(w.CreatedUnix, 0).Local()
|
|
||||||
case "updated_unix":
|
|
||||||
w.Updated = time.Unix(w.UpdatedUnix, 0).Local()
|
|
||||||
}
|
}
|
||||||
|
w.Created = time.Unix(w.CreatedUnix, 0).Local()
|
||||||
|
w.Updated = time.Unix(w.UpdatedUnix, 0).Local()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Webhook) SlackMeta() *SlackMeta {
|
func (w *Webhook) SlackMeta() *SlackMeta {
|
||||||
@@ -231,8 +228,7 @@ func (w *Webhook) EventsArray() []string {
|
|||||||
|
|
||||||
// CreateWebhook creates a new web hook.
|
// CreateWebhook creates a new web hook.
|
||||||
func CreateWebhook(w *Webhook) error {
|
func CreateWebhook(w *Webhook) error {
|
||||||
_, err := x.Insert(w)
|
return db.Create(w).Error
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ errutil.NotFound = (*ErrWebhookNotExist)(nil)
|
var _ errutil.NotFound = (*ErrWebhookNotExist)(nil)
|
||||||
@@ -257,11 +253,12 @@ func (ErrWebhookNotExist) NotFound() bool {
|
|||||||
// getWebhook uses argument bean as query condition,
|
// getWebhook uses argument bean as query condition,
|
||||||
// ID must be specified and do not assign unnecessary fields.
|
// ID must be specified and do not assign unnecessary fields.
|
||||||
func getWebhook(bean *Webhook) (*Webhook, error) {
|
func getWebhook(bean *Webhook) (*Webhook, error) {
|
||||||
has, err := x.Get(bean)
|
err := db.Where(bean).First(bean).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil, ErrWebhookNotExist{args: map[string]any{"webhookID": bean.ID}}
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if !has {
|
|
||||||
return nil, ErrWebhookNotExist{args: map[string]any{"webhookID": bean.ID}}
|
|
||||||
}
|
}
|
||||||
return bean, nil
|
return bean, nil
|
||||||
}
|
}
|
||||||
@@ -292,39 +289,34 @@ func GetWebhookByOrgID(orgID, id int64) (*Webhook, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getActiveWebhooksByRepoID returns all active webhooks of repository.
|
// getActiveWebhooksByRepoID returns all active webhooks of repository.
|
||||||
func getActiveWebhooksByRepoID(e Engine, repoID int64) ([]*Webhook, error) {
|
func getActiveWebhooksByRepoID(tx *gorm.DB, repoID int64) ([]*Webhook, error) {
|
||||||
webhooks := make([]*Webhook, 0, 5)
|
webhooks := make([]*Webhook, 0, 5)
|
||||||
return webhooks, e.Where("repo_id = ?", repoID).And("is_active = ?", true).Find(&webhooks)
|
return webhooks, tx.Where("repo_id = ? AND is_active = ?", repoID, true).Find(&webhooks).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetWebhooksByRepoID returns all webhooks of a repository.
|
// GetWebhooksByRepoID returns all webhooks of a repository.
|
||||||
func GetWebhooksByRepoID(repoID int64) ([]*Webhook, error) {
|
func GetWebhooksByRepoID(repoID int64) ([]*Webhook, error) {
|
||||||
webhooks := make([]*Webhook, 0, 5)
|
webhooks := make([]*Webhook, 0, 5)
|
||||||
return webhooks, x.Find(&webhooks, &Webhook{RepoID: repoID})
|
return webhooks, db.Where("repo_id = ?", repoID).Find(&webhooks).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateWebhook updates information of webhook.
|
// UpdateWebhook updates information of webhook.
|
||||||
func UpdateWebhook(w *Webhook) error {
|
func UpdateWebhook(w *Webhook) error {
|
||||||
_, err := x.Id(w.ID).AllCols().Update(w)
|
return db.Model(w).Where("id = ?", w.ID).Updates(w).Error
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// deleteWebhook uses argument bean as query condition,
|
// deleteWebhook uses argument bean as query condition,
|
||||||
// ID must be specified and do not assign unnecessary fields.
|
// ID must be specified and do not assign unnecessary fields.
|
||||||
func deleteWebhook(bean *Webhook) (err error) {
|
func deleteWebhook(bean *Webhook) error {
|
||||||
sess := x.NewSession()
|
return db.Transaction(func(tx *gorm.DB) error {
|
||||||
defer sess.Close()
|
if err := tx.Delete(bean).Error; err != nil {
|
||||||
if err = sess.Begin(); err != nil {
|
return err
|
||||||
return err
|
}
|
||||||
}
|
if err := tx.Where("hook_id = ?", bean.ID).Delete(&HookTask{}).Error; err != nil {
|
||||||
|
return err
|
||||||
if _, err = sess.Delete(bean); err != nil {
|
}
|
||||||
return err
|
return nil
|
||||||
} else if _, err = sess.Delete(&HookTask{HookID: bean.ID}); err != nil {
|
})
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return sess.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteWebhookOfRepoByID deletes webhook of repository by given ID.
|
// DeleteWebhookOfRepoByID deletes webhook of repository by given ID.
|
||||||
@@ -345,14 +337,14 @@ func DeleteWebhookOfOrgByID(orgID, id int64) error {
|
|||||||
|
|
||||||
// GetWebhooksByOrgID returns all webhooks for an organization.
|
// GetWebhooksByOrgID returns all webhooks for an organization.
|
||||||
func GetWebhooksByOrgID(orgID int64) (ws []*Webhook, err error) {
|
func GetWebhooksByOrgID(orgID int64) (ws []*Webhook, err error) {
|
||||||
err = x.Find(&ws, &Webhook{OrgID: orgID})
|
err = db.Where("org_id = ?", orgID).Find(&ws).Error
|
||||||
return ws, err
|
return ws, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// getActiveWebhooksByOrgID returns all active webhooks for an organization.
|
// getActiveWebhooksByOrgID returns all active webhooks for an organization.
|
||||||
func getActiveWebhooksByOrgID(e Engine, orgID int64) ([]*Webhook, error) {
|
func getActiveWebhooksByOrgID(tx *gorm.DB, orgID int64) ([]*Webhook, error) {
|
||||||
ws := make([]*Webhook, 0, 3)
|
ws := make([]*Webhook, 0, 3)
|
||||||
return ws, e.Where("org_id=?", orgID).And("is_active=?", true).Find(&ws)
|
return ws, tx.Where("org_id = ? AND is_active = ?", orgID, true).Find(&ws).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// ___ ___ __ ___________ __
|
// ___ ___ __ ___________ __
|
||||||
@@ -431,64 +423,56 @@ type HookResponse struct {
|
|||||||
// HookTask represents a hook task.
|
// HookTask represents a hook task.
|
||||||
type HookTask struct {
|
type HookTask struct {
|
||||||
ID int64
|
ID int64
|
||||||
RepoID int64 `xorm:"INDEX"`
|
RepoID int64 `gorm:"index"`
|
||||||
HookID int64
|
HookID int64
|
||||||
UUID string
|
UUID string
|
||||||
Type HookTaskType
|
Type HookTaskType
|
||||||
URL string `xorm:"TEXT"`
|
URL string `gorm:"type:text"`
|
||||||
Signature string `xorm:"TEXT"`
|
Signature string `gorm:"type:text"`
|
||||||
api.Payloader `xorm:"-" json:"-" gorm:"-"`
|
api.Payloader `gorm:"-" json:"-"`
|
||||||
PayloadContent string `xorm:"TEXT"`
|
PayloadContent string `gorm:"type:text"`
|
||||||
ContentType HookContentType
|
ContentType HookContentType
|
||||||
EventType HookEventType
|
EventType HookEventType
|
||||||
IsSSL bool
|
IsSSL bool
|
||||||
IsDelivered bool
|
IsDelivered bool
|
||||||
Delivered int64
|
Delivered int64
|
||||||
DeliveredString string `xorm:"-" json:"-" gorm:"-"`
|
DeliveredString string `gorm:"-" json:"-"`
|
||||||
|
|
||||||
// History info.
|
// History info.
|
||||||
IsSucceed bool
|
IsSucceed bool
|
||||||
RequestContent string `xorm:"TEXT"`
|
RequestContent string `gorm:"type:text"`
|
||||||
RequestInfo *HookRequest `xorm:"-" json:"-" gorm:"-"`
|
RequestInfo *HookRequest `gorm:"-" json:"-"`
|
||||||
ResponseContent string `xorm:"TEXT"`
|
ResponseContent string `gorm:"type:text"`
|
||||||
ResponseInfo *HookResponse `xorm:"-" json:"-" gorm:"-"`
|
ResponseInfo *HookResponse `gorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *HookTask) BeforeUpdate() {
|
func (t *HookTask) BeforeUpdate(tx *gorm.DB) error {
|
||||||
if t.RequestInfo != nil {
|
if t.RequestInfo != nil {
|
||||||
t.RequestContent = t.ToJSON(t.RequestInfo)
|
t.RequestContent = t.ToJSON(t.RequestInfo)
|
||||||
}
|
}
|
||||||
if t.ResponseInfo != nil {
|
if t.ResponseInfo != nil {
|
||||||
t.ResponseContent = t.ToJSON(t.ResponseInfo)
|
t.ResponseContent = t.ToJSON(t.ResponseInfo)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *HookTask) AfterSet(colName string, _ xorm.Cell) {
|
func (t *HookTask) AfterFind(tx *gorm.DB) error {
|
||||||
var err error
|
t.DeliveredString = time.Unix(0, t.Delivered).Format("2006-01-02 15:04:05 MST")
|
||||||
switch colName {
|
|
||||||
case "delivered":
|
|
||||||
t.DeliveredString = time.Unix(0, t.Delivered).Format("2006-01-02 15:04:05 MST")
|
|
||||||
|
|
||||||
case "request_content":
|
|
||||||
if t.RequestContent == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if t.RequestContent != "" {
|
||||||
t.RequestInfo = &HookRequest{}
|
t.RequestInfo = &HookRequest{}
|
||||||
if err = jsoniter.Unmarshal([]byte(t.RequestContent), t.RequestInfo); err != nil {
|
if err := jsoniter.Unmarshal([]byte(t.RequestContent), t.RequestInfo); err != nil {
|
||||||
log.Error("Unmarshal[%d]: %v", t.ID, err)
|
log.Error("Unmarshal[%d]: %v", t.ID, err)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case "response_content":
|
if t.ResponseContent != "" {
|
||||||
if t.ResponseContent == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
t.ResponseInfo = &HookResponse{}
|
t.ResponseInfo = &HookResponse{}
|
||||||
if err = jsoniter.Unmarshal([]byte(t.ResponseContent), t.ResponseInfo); err != nil {
|
if err := jsoniter.Unmarshal([]byte(t.ResponseContent), t.ResponseInfo); err != nil {
|
||||||
log.Error("Unmarshal [%d]: %v", t.ID, err)
|
log.Error("Unmarshal [%d]: %v", t.ID, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *HookTask) ToJSON(v any) string {
|
func (t *HookTask) ToJSON(v any) string {
|
||||||
@@ -502,20 +486,19 @@ func (t *HookTask) ToJSON(v any) string {
|
|||||||
// HookTasks returns a list of hook tasks by given conditions.
|
// HookTasks returns a list of hook tasks by given conditions.
|
||||||
func HookTasks(hookID int64, page int) ([]*HookTask, error) {
|
func HookTasks(hookID int64, page int) ([]*HookTask, error) {
|
||||||
tasks := make([]*HookTask, 0, conf.Webhook.PagingNum)
|
tasks := make([]*HookTask, 0, conf.Webhook.PagingNum)
|
||||||
return tasks, x.Limit(conf.Webhook.PagingNum, (page-1)*conf.Webhook.PagingNum).Where("hook_id=?", hookID).Desc("id").Find(&tasks)
|
return tasks, db.Where("hook_id = ?", hookID).Order("id DESC").Limit(conf.Webhook.PagingNum).Offset((page - 1) * conf.Webhook.PagingNum).Find(&tasks).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// createHookTask creates a new hook task,
|
// createHookTask creates a new hook task,
|
||||||
// it handles conversion from Payload to PayloadContent.
|
// it handles conversion from Payload to PayloadContent.
|
||||||
func createHookTask(e Engine, t *HookTask) error {
|
func createHookTask(tx *gorm.DB, t *HookTask) error {
|
||||||
data, err := t.JSONPayload()
|
data, err := t.JSONPayload()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
t.UUID = gouuid.NewV4().String()
|
t.UUID = gouuid.NewV4().String()
|
||||||
t.PayloadContent = string(data)
|
t.PayloadContent = string(data)
|
||||||
_, err = e.Insert(t)
|
return tx.Create(t).Error
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ errutil.NotFound = (*ErrHookTaskNotExist)(nil)
|
var _ errutil.NotFound = (*ErrHookTaskNotExist)(nil)
|
||||||
@@ -543,23 +526,23 @@ func GetHookTaskOfWebhookByUUID(webhookID int64, uuid string) (*HookTask, error)
|
|||||||
HookID: webhookID,
|
HookID: webhookID,
|
||||||
UUID: uuid,
|
UUID: uuid,
|
||||||
}
|
}
|
||||||
has, err := x.Get(hookTask)
|
err := db.Where("hook_id = ? AND uuid = ?", webhookID, uuid).First(hookTask).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil, ErrHookTaskNotExist{args: map[string]any{"webhookID": webhookID, "uuid": uuid}}
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if !has {
|
|
||||||
return nil, ErrHookTaskNotExist{args: map[string]any{"webhookID": webhookID, "uuid": uuid}}
|
|
||||||
}
|
}
|
||||||
return hookTask, nil
|
return hookTask, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateHookTask updates information of hook task.
|
// UpdateHookTask updates information of hook task.
|
||||||
func UpdateHookTask(t *HookTask) error {
|
func UpdateHookTask(t *HookTask) error {
|
||||||
_, err := x.Id(t.ID).AllCols().Update(t)
|
return db.Model(t).Where("id = ?", t.ID).Updates(t).Error
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// prepareHookTasks adds list of webhooks to task queue.
|
// prepareHookTasks adds list of webhooks to task queue.
|
||||||
func prepareHookTasks(e Engine, repo *Repository, event HookEventType, p api.Payloader, webhooks []*Webhook) (err error) {
|
func prepareHookTasks(tx *gorm.DB, repo *Repository, event HookEventType, p api.Payloader, webhooks []*Webhook) (err error) {
|
||||||
if len(webhooks) == 0 {
|
if len(webhooks) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -633,7 +616,7 @@ func prepareHookTasks(e Engine, repo *Repository, event HookEventType, p api.Pay
|
|||||||
signature = hex.EncodeToString(sig.Sum(nil))
|
signature = hex.EncodeToString(sig.Sum(nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = createHookTask(e, &HookTask{
|
if err = createHookTask(tx, &HookTask{
|
||||||
RepoID: repo.ID,
|
RepoID: repo.ID,
|
||||||
HookID: w.ID,
|
HookID: w.ID,
|
||||||
Type: w.HookTaskType,
|
Type: w.HookTaskType,
|
||||||
@@ -655,32 +638,32 @@ func prepareHookTasks(e Engine, repo *Repository, event HookEventType, p api.Pay
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func prepareWebhooks(e Engine, repo *Repository, event HookEventType, p api.Payloader) error {
|
func prepareWebhooks(tx *gorm.DB, repo *Repository, event HookEventType, p api.Payloader) error {
|
||||||
webhooks, err := getActiveWebhooksByRepoID(e, repo.ID)
|
webhooks, err := getActiveWebhooksByRepoID(tx, repo.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Newf("getActiveWebhooksByRepoID [%d]: %v", repo.ID, err)
|
return errors.Newf("getActiveWebhooksByRepoID [%d]: %v", repo.ID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if repo belongs to org and append additional webhooks
|
// check if repo belongs to org and append additional webhooks
|
||||||
if repo.mustOwner(e).IsOrganization() {
|
if repo.mustOwner(tx).IsOrganization() {
|
||||||
// get hooks for org
|
// get hooks for org
|
||||||
orgws, err := getActiveWebhooksByOrgID(e, repo.OwnerID)
|
orgws, err := getActiveWebhooksByOrgID(tx, repo.OwnerID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Newf("getActiveWebhooksByOrgID [%d]: %v", repo.OwnerID, err)
|
return errors.Newf("getActiveWebhooksByOrgID [%d]: %v", repo.OwnerID, err)
|
||||||
}
|
}
|
||||||
webhooks = append(webhooks, orgws...)
|
webhooks = append(webhooks, orgws...)
|
||||||
}
|
}
|
||||||
return prepareHookTasks(e, repo, event, p, webhooks)
|
return prepareHookTasks(tx, repo, event, p, webhooks)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrepareWebhooks adds all active webhooks to task queue.
|
// PrepareWebhooks adds all active webhooks to task queue.
|
||||||
func PrepareWebhooks(repo *Repository, event HookEventType, p api.Payloader) error {
|
func PrepareWebhooks(repo *Repository, event HookEventType, p api.Payloader) error {
|
||||||
// NOTE: To prevent too many cascading changes in a single refactoring PR, we
|
// NOTE: To prevent too many cascading changes in a single refactoring PR, we
|
||||||
// choose to ignore this function in tests.
|
// choose to ignore this function in tests.
|
||||||
if x == nil && testutil.InTest {
|
if db == nil && testutil.InTest {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return prepareWebhooks(x, repo, event, p)
|
return prepareWebhooks(db, repo, event, p)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestWebhook adds the test webhook matches the ID to task queue.
|
// TestWebhook adds the test webhook matches the ID to task queue.
|
||||||
@@ -689,7 +672,7 @@ func TestWebhook(repo *Repository, event HookEventType, p api.Payloader, webhook
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Newf("GetWebhookOfRepoByID [repo_id: %d, id: %d]: %v", repo.ID, webhookID, err)
|
return errors.Newf("GetWebhookOfRepoByID [repo_id: %d, id: %d]: %v", repo.ID, webhookID, err)
|
||||||
}
|
}
|
||||||
return prepareHookTasks(x, repo, event, p, []*Webhook{webhook})
|
return prepareHookTasks(db, repo, event, p, []*Webhook{webhook})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *HookTask) deliver() {
|
func (t *HookTask) deliver() {
|
||||||
@@ -784,18 +767,15 @@ func (t *HookTask) deliver() {
|
|||||||
// TODO: shoot more hooks at same time.
|
// TODO: shoot more hooks at same time.
|
||||||
func DeliverHooks() {
|
func DeliverHooks() {
|
||||||
tasks := make([]*HookTask, 0, 10)
|
tasks := make([]*HookTask, 0, 10)
|
||||||
_ = x.Where("is_delivered = ?", false).Iterate(new(HookTask),
|
err := db.Where("is_delivered = ?", false).Find(&tasks).Error
|
||||||
func(idx int, bean any) error {
|
if err != nil {
|
||||||
t := bean.(*HookTask)
|
log.Error("Find undelivered hook tasks: %v", err)
|
||||||
|
} else {
|
||||||
|
for _, t := range tasks {
|
||||||
t.deliver()
|
t.deliver()
|
||||||
tasks = append(tasks, t)
|
if err := UpdateHookTask(t); err != nil {
|
||||||
return nil
|
log.Error("UpdateHookTask [%d]: %v", t.ID, err)
|
||||||
})
|
}
|
||||||
|
|
||||||
// Update hook task status.
|
|
||||||
for _, t := range tasks {
|
|
||||||
if err := UpdateHookTask(t); err != nil {
|
|
||||||
log.Error("UpdateHookTask [%d]: %v", t.ID, err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -805,7 +785,7 @@ func DeliverHooks() {
|
|||||||
HookQueue.Remove(repoID)
|
HookQueue.Remove(repoID)
|
||||||
|
|
||||||
tasks = make([]*HookTask, 0, 5)
|
tasks = make([]*HookTask, 0, 5)
|
||||||
if err := x.Where("repo_id = ?", repoID).And("is_delivered = ?", false).Find(&tasks); err != nil {
|
if err := db.Where("repo_id = ? AND is_delivered = ?", repoID, false).Find(&tasks).Error; err != nil {
|
||||||
log.Error("Get repository [%s] hook tasks: %v", repoID, err)
|
log.Error("Get repository [%s] hook tasks: %v", repoID, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user