mirror of
https://github.com/gogs/gogs.git
synced 2026-03-01 17:50:59 +01:00
Compare commits
42 Commits
copilot/re
...
release/0.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5dcb6c64bd | ||
|
|
b4bdb270d1 | ||
|
|
0120bf0c86 | ||
|
|
094b632182 | ||
|
|
a5c2cc0c6e | ||
|
|
41b186cbfd | ||
|
|
51cf4cbe7e | ||
|
|
5e6014c421 | ||
|
|
f5c8030c1f | ||
|
|
8c5c0125c4 | ||
|
|
3f03530042 | ||
|
|
36c26c4ccc | ||
|
|
b68e6886c6 | ||
|
|
ac7ba9c8a7 | ||
|
|
dd862ee058 | ||
|
|
f94042ce6f | ||
|
|
628216d588 | ||
|
|
7306b955a9 | ||
|
|
fc6d1e2055 | ||
|
|
3b01892d85 | ||
|
|
7b7e38c880 | ||
|
|
bb68c0a042 | ||
|
|
68271e6af0 | ||
|
|
4f5b00f8c4 | ||
|
|
5d3ffd132b | ||
|
|
ee65aa89ca | ||
|
|
a1a97de76f | ||
|
|
9963268267 | ||
|
|
49a45290ae | ||
|
|
3cc8e7aa6d | ||
|
|
9f1499f3ab | ||
|
|
77dba1b5ea | ||
|
|
f70f29fdb0 | ||
|
|
ed6109d35d | ||
|
|
54e08ba678 | ||
|
|
87c8faaf08 | ||
|
|
1b226ca48d | ||
|
|
e3bb4165dc | ||
|
|
df3d945a2c | ||
|
|
ae41bab5f2 | ||
|
|
2316b09eaf | ||
|
|
3477bbac0e |
13
.claude/commands/ghsa.md
Normal file
13
.claude/commands/ghsa.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
Analyze and help fix the GitHub Security Advisory (GHSA) at: $ARGUMENTS
|
||||||
|
|
||||||
|
Steps:
|
||||||
|
1. Fetch the GHSA page using `gh api repos/gogs/gogs/security-advisories` and understand the vulnerability details (description, severity, affected versions, CWE).
|
||||||
|
2. Verify the reported vulnerability actually exists, and why.
|
||||||
|
3. Identify the affected code in this repository.
|
||||||
|
4. Propose a fix with a clear explanation of the root cause and how the fix addresses it. Check for prior art in the codebase to stay consistent with existing patterns.
|
||||||
|
5. Implement the fix. Only add tests when there is something meaningful to test at our layer.
|
||||||
|
6. Run all the usual build and test commands.
|
||||||
|
7. If a changelog entry is warranted (user will specify), add it to CHANGELOG.md with a placeholder for the PR link.
|
||||||
|
8. Create a branch named after the GHSA ID, commit, and push.
|
||||||
|
9. Create a pull request with a proper title and description, do not reveal too much detail and link the GHSA.
|
||||||
|
10. If a changelog entry was added, update it with the PR link, then commit and push again.
|
||||||
@@ -15,29 +15,25 @@ 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/f1102a7a7c545ec221d2906f02fa19170d96f96d) to include entries for the current minor release.
|
||||||
- [ ] Cut a new release branch `release/<MAJOR>.<MINOR>`, e.g. `release/0.12`.
|
- Do not forget adding entries for GHSA patches.
|
||||||
|
- [ ] 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/f17e7d5a2c36c52a1121d2315f3d75dcd8053b89) to the current release, e.g. `0.12.0+dev` -> `0.12.0`.
|
- [ ] [Update the hard-coded version](https://github.com/gogs/gogs/commit/f17e7d5a2c36c52a1121d2315f3d75dcd8053b89) to the current release, e.g. `0.14.0+dev` -> `0.14.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.12.0-rc.1`, `v0.12.0-rc.2`) to ensure Docker workflow succeeds. **Make sure the tag is created on the release branch**.
|
- [ ] Publish new RC releases (e.g. `v0.14.0-rc.1`, `v0.14.0-rc.2`) to ensure Docker and release workflows both succeed.
|
||||||
- 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**.
|
||||||
- [ ] 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**.
|
- [ ] 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) 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**.
|
||||||
- [ ] [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).
|
- [ ] [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.:
|
- [ ] [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_`):
|
- [ ] 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`.
|
||||||
- [ ] macOS: `darwin_amd64.zip`, `darwin_arm64.zip`
|
- [ ] Upload all archives and `checksum_sha256.txt` to https://dl.gogs.io.
|
||||||
- [ ] 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
|
||||||
|
|
||||||
@@ -47,7 +43,7 @@ On the `main` branch:
|
|||||||
- [ ] 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.
|
||||||
- [ ] Publish a new release article on [OSChina](http://my.oschina.net/Obahua/admin/releases).
|
- [ ] Close the milestone for the minor release.
|
||||||
- [ ] Close the minor milestone.
|
- [ ] [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`.
|
||||||
- [ ] [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.
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ On the release branch:
|
|||||||
- [ ] 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.
|
- [ ] 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.
|
||||||
- ⚠️ **Make sure the tag is created on the release branch**.
|
- ⚠️ **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.
|
- 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) 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**.
|
- ⚠️ **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:
|
||||||
@@ -34,16 +35,8 @@ On the release branch:
|
|||||||
- [ ] [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).
|
- [ ] [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.
|
||||||
- [ ] [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).
|
- [ ] [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).
|
||||||
- [ ] [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_`):
|
- [ ] 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`.
|
||||||
- [ ] macOS: `darwin_arm64.zip`, `darwin_amd64.zip`
|
- [ ] Upload all archives and `checksum_sha256.txt` to https://dl.gogs.io.
|
||||||
- [ ] 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
|
||||||
|
|
||||||
@@ -55,5 +48,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 patch milestone.
|
- [ ] Close the milestone for the patch release.
|
||||||
- [ ] **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,18 +19,8 @@ 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: dawidd6/action-send-mail@2cea9617b09d79a095af21254fbcb7ae95903dde # v3.12.0
|
uses: unknwon/send-email-on-failure@89339a1bc93f4ad1d30f3b7e4911fcba985c9adb # v1
|
||||||
if: ${{ failure() }}
|
if: ${{ failure() }}
|
||||||
with:
|
with:
|
||||||
server_address: smtp.mailgun.org
|
smtp_username: ${{ secrets.SMTP_USERNAME }}
|
||||||
server_port: 465
|
smtp_password: ${{ secrets.SMTP_PASSWORD }}
|
||||||
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,21 +68,11 @@ 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: dawidd6/action-send-mail@2cea9617b09d79a095af21254fbcb7ae95903dde # v3.12.0
|
uses: unknwon/send-email-on-failure@89339a1bc93f4ad1d30f3b7e4911fcba985c9adb # v1
|
||||||
if: ${{ failure() }}
|
if: ${{ failure() }}
|
||||||
with:
|
with:
|
||||||
server_address: smtp.mailgun.org
|
smtp_username: ${{ secrets.SMTP_USERNAME }}
|
||||||
server_port: 465
|
smtp_password: ${{ secrets.SMTP_PASSWORD }}
|
||||||
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' }}
|
||||||
@@ -145,21 +135,11 @@ 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: dawidd6/action-send-mail@2cea9617b09d79a095af21254fbcb7ae95903dde # v3.12.0
|
uses: unknwon/send-email-on-failure@89339a1bc93f4ad1d30f3b7e4911fcba985c9adb # v1
|
||||||
if: ${{ failure() }}
|
if: ${{ failure() }}
|
||||||
with:
|
with:
|
||||||
server_address: smtp.mailgun.org
|
smtp_username: ${{ secrets.SMTP_USERNAME }}
|
||||||
server_port: 465
|
smtp_password: ${{ secrets.SMTP_PASSWORD }}
|
||||||
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' }}
|
||||||
@@ -181,21 +161,11 @@ 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: dawidd6/action-send-mail@2cea9617b09d79a095af21254fbcb7ae95903dde # v3.12.0
|
uses: unknwon/send-email-on-failure@89339a1bc93f4ad1d30f3b7e4911fcba985c9adb # v1
|
||||||
if: ${{ failure() }}
|
if: ${{ failure() }}
|
||||||
with:
|
with:
|
||||||
server_address: smtp.mailgun.org
|
smtp_username: ${{ secrets.SMTP_USERNAME }}
|
||||||
server_port: 465
|
smtp_password: ${{ secrets.SMTP_PASSWORD }}
|
||||||
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'}}
|
||||||
@@ -285,8 +255,25 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
steps:
|
steps:
|
||||||
- name: Compute image tag name
|
- name: Compute image tags
|
||||||
run: echo "IMAGE_TAG=$(echo $GITHUB_REF_NAME | cut -c 2-)" >> $GITHUB_ENV
|
run: |
|
||||||
|
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
|
||||||
@@ -320,25 +307,13 @@ jobs:
|
|||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||||
push: true
|
push: true
|
||||||
tags: |
|
tags: ${{ env.TAGS }}
|
||||||
gogs/gogs:${{ env.IMAGE_TAG }}
|
|
||||||
ghcr.io/gogs/gogs:${{ env.IMAGE_TAG }}
|
|
||||||
- name: Send email on failure
|
- name: Send email on failure
|
||||||
uses: dawidd6/action-send-mail@2cea9617b09d79a095af21254fbcb7ae95903dde # v3.12.0
|
uses: unknwon/send-email-on-failure@89339a1bc93f4ad1d30f3b7e4911fcba985c9adb # v1
|
||||||
if: ${{ failure() }}
|
if: ${{ failure() }}
|
||||||
with:
|
with:
|
||||||
server_address: smtp.mailgun.org
|
smtp_username: ${{ secrets.SMTP_USERNAME }}
|
||||||
server_port: 465
|
smtp_password: ${{ secrets.SMTP_PASSWORD }}
|
||||||
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:
|
||||||
@@ -349,8 +324,25 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
steps:
|
steps:
|
||||||
- name: Compute image tag name
|
- name: Compute image tags
|
||||||
run: echo "IMAGE_TAG=$(echo $GITHUB_REF_NAME | cut -c 2-)" >> $GITHUB_ENV
|
run: |
|
||||||
|
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
|
||||||
@@ -385,25 +377,13 @@ 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: |
|
tags: ${{ env.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: dawidd6/action-send-mail@2cea9617b09d79a095af21254fbcb7ae95903dde # v3.12.0
|
uses: unknwon/send-email-on-failure@89339a1bc93f4ad1d30f3b7e4911fcba985c9adb # v1
|
||||||
if: ${{ failure() }}
|
if: ${{ failure() }}
|
||||||
with:
|
with:
|
||||||
server_address: smtp.mailgun.org
|
smtp_username: ${{ secrets.SMTP_USERNAME }}
|
||||||
server_port: 465
|
smtp_password: ${{ secrets.SMTP_PASSWORD }}
|
||||||
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' }}
|
||||||
|
|||||||
51
.github/workflows/go.yml
vendored
51
.github/workflows/go.yml
vendored
@@ -79,21 +79,11 @@ jobs:
|
|||||||
file: ./coverage
|
file: ./coverage
|
||||||
flags: unittests
|
flags: unittests
|
||||||
- name: Send email on failure
|
- name: Send email on failure
|
||||||
uses: dawidd6/action-send-mail@2cea9617b09d79a095af21254fbcb7ae95903dde # v3.12.0
|
uses: unknwon/send-email-on-failure@89339a1bc93f4ad1d30f3b7e4911fcba985c9adb # v1
|
||||||
if: ${{ failure() && github.event_name == 'push' && github.ref == 'refs/heads/main' }}
|
if: ${{ failure() && github.event_name == 'push' && github.ref == 'refs/heads/main' }}
|
||||||
with:
|
with:
|
||||||
server_address: smtp.mailgun.org
|
smtp_username: ${{ secrets.SMTP_USERNAME }}
|
||||||
server_port: 465
|
smtp_password: ${{ secrets.SMTP_PASSWORD }}
|
||||||
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.
|
||||||
@@ -119,21 +109,11 @@ jobs:
|
|||||||
file: ./coverage
|
file: ./coverage
|
||||||
flags: unittests
|
flags: unittests
|
||||||
- name: Send email on failure
|
- name: Send email on failure
|
||||||
uses: dawidd6/action-send-mail@2cea9617b09d79a095af21254fbcb7ae95903dde # v3.12.0
|
uses: unknwon/send-email-on-failure@89339a1bc93f4ad1d30f3b7e4911fcba985c9adb # v1
|
||||||
if: ${{ failure() && github.event_name == 'push' && github.ref == 'refs/heads/main' }}
|
if: ${{ failure() && github.event_name == 'push' && github.ref == 'refs/heads/main' }}
|
||||||
with:
|
with:
|
||||||
server_address: smtp.mailgun.org
|
smtp_username: ${{ secrets.SMTP_USERNAME }}
|
||||||
server_port: 465
|
smtp_password: ${{ secrets.SMTP_PASSWORD }}
|
||||||
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
|
||||||
@@ -195,22 +175,3 @@ jobs:
|
|||||||
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
Normal file
146
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
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 }}" >> "$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"
|
||||||
|
- 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,18 +1,16 @@
|
|||||||
.DS_Store
|
# Build artifacts
|
||||||
*.db
|
.bin/
|
||||||
*.log
|
dist/
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
log/
|
log/
|
||||||
custom/
|
custom/
|
||||||
data/
|
data/
|
||||||
|
|
||||||
|
# Configuration and application files
|
||||||
.idea/
|
.idea/
|
||||||
*.iml
|
.task/
|
||||||
public/img/avatar/
|
|
||||||
*.exe
|
|
||||||
*.exe~
|
|
||||||
/gogs
|
|
||||||
profile/
|
|
||||||
*.pem
|
|
||||||
output*
|
|
||||||
/release
|
|
||||||
.task
|
|
||||||
.envrc
|
.envrc
|
||||||
|
|
||||||
|
# System junk
|
||||||
|
.DS_Store
|
||||||
|
|||||||
14
AGENTS.md
14
AGENTS.md
@@ -1,6 +1,7 @@
|
|||||||
## Core principles
|
## Core principles
|
||||||
|
|
||||||
- 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!
|
- 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 pattern in the surrounding context!
|
||||||
|
|
||||||
## Style and mechanics
|
## Style and mechanics
|
||||||
|
|
||||||
@@ -15,6 +16,17 @@ 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.
|
||||||
|
|||||||
28
CHANGELOG.md
28
CHANGELOG.md
@@ -2,26 +2,50 @@
|
|||||||
|
|
||||||
All notable changes to Gogs are documented in this file.
|
All notable changes to Gogs are documented in this file.
|
||||||
|
|
||||||
## 0.14.0+dev (`main`)
|
## 0.15.0+dev (`main`)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- _Security:_ Cross-repository LFS object overwrite via missing content hash verification. [#8166](https://github.com/gogs/gogs/pull/8166) - [GHSA-gmf8-978x-2fg2](https://github.com/gogs/gogs/security/advisories/GHSA-gmf8-978x-2fg2)
|
||||||
|
- _Security:_ DOM-based XSS via issue meta selection on the issue page. [#8178](https://github.com/gogs/gogs/pull/8178) - [GHSA-vgjm-2cpf-4g7c](https://github.com/gogs/gogs/security/advisories/GHSA-vgjm-2cpf-4g7c)
|
||||||
|
- Unable to update files via web editor and API. [#8184](https://github.com/gogs/gogs/pull/8184)
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
- Support for passing API access tokens via URL query parameters (`token`, `access_token`). Use the `Authorization` header instead. [#8177](https://github.com/gogs/gogs/pull/8177) - [GHSA-x9p5-w45c-7ffc](https://github.com/gogs/gogs/security/advisories/GHSA-x9p5-w45c-7ffc)
|
||||||
|
|
||||||
|
- Git clone via the built-in SSH server hangs. [#8132](https://github.com/gogs/gogs/issues/8132)
|
||||||
|
|
||||||
|
## 0.14.0
|
||||||
|
|
||||||
### 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.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)
|
- 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)
|
||||||
|
|
||||||
### 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/gogs .
|
COPY --from=binarybuilder /gogs.io/gogs/.bin/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 -o /dev/null -sS http://localhost:3000/healthcheck) || exit 1
|
HEALTHCHECK CMD (curl --noproxy localhost -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/gogs .
|
COPY --from=binarybuilder /gogs.io/gogs/.bin/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 -o /dev/null -sS http://localhost:3000/healthcheck) || exit 1
|
HEALTHCHECK CMD (curl --noproxy localhost -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
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ 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
|
||||||
|
|||||||
23
Taskfile.yml
23
Taskfile.yml
@@ -10,8 +10,10 @@ 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:
|
||||||
- ./gogs web
|
- .bin/gogs web
|
||||||
|
|
||||||
build:
|
build:
|
||||||
desc: Build the binary
|
desc: Build the binary
|
||||||
@@ -22,7 +24,7 @@ tasks:
|
|||||||
-X "{{.PKG_PATH}}.BuildCommit={{.BUILD_COMMIT}}"
|
-X "{{.PKG_PATH}}.BuildCommit={{.BUILD_COMMIT}}"
|
||||||
'
|
'
|
||||||
-tags '{{.TAGS}}'
|
-tags '{{.TAGS}}'
|
||||||
-trimpath -o gogs{{.BINARY_EXT}}
|
-trimpath -o .bin/gogs{{.BINARY_EXT}}
|
||||||
vars:
|
vars:
|
||||||
PKG_PATH: gogs.io/gogs/internal/conf
|
PKG_PATH: gogs.io/gogs/internal/conf
|
||||||
BUILD_TIME:
|
BUILD_TIME:
|
||||||
@@ -59,18 +61,6 @@ 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:
|
||||||
@@ -99,3 +89,8 @@ tasks:
|
|||||||
dropdb "$dbname"
|
dropdb "$dbname"
|
||||||
echo "dropped $dbname"
|
echo "dropped $dbname"
|
||||||
done
|
done
|
||||||
|
|
||||||
|
lint:
|
||||||
|
desc: Run all linters
|
||||||
|
cmds:
|
||||||
|
- golangci-lint run
|
||||||
|
|||||||
@@ -279,6 +279,8 @@ ACCESS_CONTROL_ALLOW_ORIGIN =
|
|||||||
STORAGE = local
|
STORAGE = local
|
||||||
; The root path to store LFS objects on local file system.
|
; The root path to store LFS objects on local file system.
|
||||||
OBJECTS_PATH = data/lfs-objects
|
OBJECTS_PATH = data/lfs-objects
|
||||||
|
; The path to temporarily store LFS objects during upload verification.
|
||||||
|
OBJECTS_TEMP_PATH = data/tmp/lfs-objects
|
||||||
|
|
||||||
[attachment]
|
[attachment]
|
||||||
; Whether to enabled upload attachments in general.
|
; Whether to enabled upload attachments in general.
|
||||||
|
|||||||
@@ -494,6 +494,8 @@ 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=Качи файл
|
||||||
@@ -1274,6 +1276,7 @@ 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,6 +494,8 @@ 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
|
||||||
@@ -1274,6 +1276,7 @@ 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,6 +494,8 @@ 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
|
||||||
@@ -1274,6 +1276,7 @@ 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,6 +501,8 @@ 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
|
||||||
@@ -1346,6 +1348,7 @@ 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,6 +494,8 @@ 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,6 +494,8 @@ 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
|
||||||
@@ -1274,6 +1276,7 @@ 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,6 +496,8 @@ 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,6 +1278,7 @@ 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,6 +494,8 @@ 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
|
||||||
@@ -1274,6 +1276,7 @@ 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,6 +494,8 @@ 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
|
||||||
@@ -1274,6 +1276,7 @@ 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,6 +495,8 @@ 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
|
||||||
@@ -1275,6 +1277,7 @@ 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,6 +494,8 @@ 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
|
||||||
@@ -1274,6 +1276,7 @@ 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,6 +494,8 @@ 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
|
||||||
@@ -1274,6 +1276,7 @@ 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,6 +494,8 @@ 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
|
||||||
@@ -1274,6 +1276,7 @@ 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,6 +494,8 @@ 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=ファイルをアップロード
|
||||||
@@ -1274,6 +1276,7 @@ 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,6 +495,8 @@ 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,6 +1278,7 @@ 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,6 +494,8 @@ 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
|
||||||
@@ -1274,6 +1276,7 @@ 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,6 +494,8 @@ 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=Файл хуулах
|
||||||
@@ -1274,6 +1276,7 @@ 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,6 +494,8 @@ 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
|
||||||
@@ -1274,6 +1276,7 @@ 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,6 +494,8 @@ 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
|
||||||
@@ -1274,6 +1276,7 @@ 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,6 +494,8 @@ 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
|
||||||
@@ -1274,6 +1276,7 @@ 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=Clone address resolved to a local network address that is implicitly blocked.
|
migrate.clone_address_resolved_to_blocked_local_address=Clonar endereço resolvido para um endereço de rede local implicitamente bloqueado.
|
||||||
migrate.failed=Migração falhada: %v
|
migrate.failed=Migração falhada: %v
|
||||||
|
|
||||||
mirror_from=mirror de
|
mirror_from=mirror de
|
||||||
@@ -494,6 +494,8 @@ 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
|
||||||
@@ -1202,119 +1204,120 @@ 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=Minimum key size check
|
config.ssh.minimum_key_size_check=Verificação de tamanho mínimo da chave
|
||||||
config.ssh.minimum_key_sizes=Minimum key sizes
|
config.ssh.minimum_key_sizes=Tamanhos mínimos de chaves
|
||||||
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=Start builtin server
|
config.ssh.start_builtin_server=Iniciar servidor embutido
|
||||||
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=Server MACs
|
config.ssh.server_macs=MACs do servidor
|
||||||
config.ssh.server_algorithms=Server algorithms
|
config.ssh.server_algorithms=Algoritmos do servidor
|
||||||
|
|
||||||
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=Force private
|
config.repo.force_private=Forçar privado
|
||||||
config.repo.max_creation_limit=Max creation limit
|
config.repo.max_creation_limit=Limite máximo de criação
|
||||||
config.repo.preferred_licenses=Preferred licenses
|
config.repo.preferred_licenses=Licenças preferidas
|
||||||
config.repo.disable_http_git=Disable HTTP Git
|
config.repo.disable_http_git=Desativar Git HTTP
|
||||||
config.repo.enable_local_path_migration=Enable local path migration
|
config.repo.enable_local_path_migration=Ativar a migração de caminho local
|
||||||
config.repo.enable_raw_file_render_mode=Enable raw file render mode
|
config.repo.enable_raw_file_render_mode=Ativar o modo de renderização do ficheiro bruto
|
||||||
config.repo.commits_fetch_concurrency=Commits fetch concurrency
|
config.repo.commits_fetch_concurrency=Commits fetch concurrency
|
||||||
config.repo.editor.line_wrap_extensions=Editor line wrap extensions
|
config.repo.editor.line_wrap_extensions=Extensões de quebra automática de linha do editor
|
||||||
config.repo.editor.previewable_file_modes=Editor previewable file modes
|
config.repo.editor.previewable_file_modes=Editor previewable file modes
|
||||||
config.repo.upload.enabled=Upload enabled
|
config.repo.upload.enabled=Envio ativado
|
||||||
config.repo.upload.temp_path=Upload temporary path
|
config.repo.upload.temp_path=Caminho temporário para envios
|
||||||
config.repo.upload.allowed_types=Upload allowed types
|
config.repo.upload.allowed_types=Tipos de envios permitidos
|
||||||
config.repo.upload.file_max_size=Upload file size limit
|
config.repo.upload.file_max_size=Tamanho limite de ficheiros enviados
|
||||||
config.repo.upload.max_files=Upload files limit
|
config.repo.upload.max_files=Quantidade limite de ficheiros enviados
|
||||||
|
|
||||||
config.db_config=Configuração da base de dados
|
config.db_config=Configuração da base de dados
|
||||||
config.db.type=Type
|
config.db.type=Tipo
|
||||||
config.db.host=Host
|
config.db.host=Anfitrião
|
||||||
config.db.name=Name
|
config.db.name=Nome
|
||||||
config.db.schema=Schema
|
config.db.schema=Esquema
|
||||||
config.db.schema_helper=(for "postgres" only)
|
config.db.schema_helper=(apenas para "postgres")
|
||||||
config.db.user=User
|
config.db.user=Utilizador
|
||||||
config.db.ssl_mode=SSL mode
|
config.db.ssl_mode=Modo SSL
|
||||||
config.db.ssl_mode_helper=(for "postgres" only)
|
config.db.ssl_mode_helper=(apenas para "postgres")
|
||||||
config.db.path=Path
|
config.db.path=Caminho
|
||||||
config.db.path_helper=(for "sqlite3"only)
|
config.db.path_helper=(apenas para "sqlite3")
|
||||||
config.db.max_open_conns=Maximum open connections
|
config.db.max_open_conns=Máximo de conexões abertas
|
||||||
config.db.max_idle_conns=Maximum idle connections
|
config.db.max_idle_conns=Máximo de conexões ociosas
|
||||||
|
|
||||||
config.security_config=Security configuration
|
config.security_config=Configuração da segurança
|
||||||
config.security.login_remember_days=Login remember days
|
config.security.login_remember_days=Dias lembrados de login
|
||||||
config.security.cookie_remember_name=Remember cookie
|
config.security.cookie_remember_name=Lembrar do cookie
|
||||||
config.security.cookie_username=Username cookie
|
config.security.cookie_username=Cookie do nome do utilizador
|
||||||
config.security.cookie_secure=Enable secure cookie
|
config.security.cookie_secure=Ativar cookie seguro
|
||||||
config.security.reverse_proxy_auth_user=Reverse proxy authentication header
|
config.security.reverse_proxy_auth_user=Cabeçalho de autenticação de proxy reverso
|
||||||
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=Email configuration
|
config.email_config=Configuração de E-mail
|
||||||
config.email.enabled=Enabled
|
config.email.enabled=Ativado
|
||||||
config.email.subject_prefix=Subject prefix
|
config.email.subject_prefix=Prefixo do assunto
|
||||||
config.email.host=Host
|
config.email.host=Anfitrião
|
||||||
config.email.from=From
|
config.email.from=De
|
||||||
config.email.user=User
|
config.email.user=Utilizador
|
||||||
config.email.disable_helo=Disable HELO
|
config.email.disable_helo=Desativar HELO
|
||||||
config.email.helo_hostname=HELO hostname
|
config.email.helo_hostname=Nome de anfitrião HELO
|
||||||
config.email.skip_verify=Skip certificate verify
|
config.email.skip_verify=Skip certificate verify
|
||||||
config.email.use_certificate=Use custom certificate
|
config.email.use_certificate=Usar certificado personalizado
|
||||||
config.email.cert_file=Certificate file
|
config.email.cert_file=Ficheiro de certificado criptográfico
|
||||||
config.email.key_file=Key file
|
config.email.key_file=Ficheiro da chave criptográfica
|
||||||
config.email.use_plain_text=Use plain text
|
config.email.use_plain_text=Usar texto simples
|
||||||
config.email.add_plain_text_alt=Add plain text alternative
|
config.email.add_plain_text_alt=Adicionar alternativa de texto simples
|
||||||
config.email.send_test_mail=Send test email
|
config.email.send_test_mail=Enviar e-mail de teste
|
||||||
config.email.test_mail_failed=Failed to send test email to '%s': %v
|
config.email.test_mail_failed=Falhou o envio do e-mail de teste para '%s': %v
|
||||||
config.email.test_mail_sent=Test email has been sent to '%s'.
|
config.email.test_mail_sent=O e-mail de teste foi enviado para '%s'.
|
||||||
|
|
||||||
config.auth_config=Authentication configuration
|
config.auth_config=Configuração da autenticação
|
||||||
|
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=Exigir confirmação por e-mail
|
||||||
config.auth.require_sign_in_view=Require sign in view
|
config.auth.require_sign_in_view=Exigir login para ver
|
||||||
config.auth.disable_registration=Disable registration
|
config.auth.disable_registration=Desativar registo
|
||||||
config.auth.enable_registration_captcha=Enable registration captcha
|
config.auth.enable_registration_captcha=Ativar captcha para registar
|
||||||
config.auth.enable_reverse_proxy_authentication=Enable reverse proxy authentication
|
config.auth.enable_reverse_proxy_authentication=Ativar autenticação do proxy reverso
|
||||||
config.auth.enable_reverse_proxy_auto_registration=Enable reverse proxy auto registration
|
config.auth.enable_reverse_proxy_auto_registration=Ativar o registo automático do proxy reverso
|
||||||
config.auth.reverse_proxy_authentication_header=Reverse proxy authentication header
|
config.auth.reverse_proxy_authentication_header=Cabeçalho de autenticação de proxy reverso
|
||||||
|
|
||||||
config.user_config=User configuration
|
config.user_config=Configuração do utilizador
|
||||||
config.user.enable_email_notify=Enable email notification
|
config.user.enable_email_notify=Ativar a notificação por e-mail
|
||||||
|
|
||||||
config.session_config=Configuração de sessão
|
config.session_config=Configuração de sessão
|
||||||
config.session.provider=Provider
|
config.session.provider=Provedor
|
||||||
config.session.provider_config=Provider config
|
config.session.provider_config=Configuração do provedor
|
||||||
config.session.cookie_name=Cookie
|
config.session.cookie_name=Cookie
|
||||||
config.session.https_only=HTTPS only
|
config.session.https_only=Somente HTTPS
|
||||||
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=Adapter
|
config.cache.adapter=Adaptador
|
||||||
config.cache.interval=GC interval
|
config.cache.interval=Intervalo de GC
|
||||||
config.cache.host=Host
|
config.cache.host=Anfitrião
|
||||||
|
|
||||||
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=Attachment configuration
|
config.attachment_config=Configuração de anexos
|
||||||
config.attachment.enabled=Enabled
|
config.attachment.enabled=Ativado
|
||||||
config.attachment.path=Path
|
config.attachment.path=Caminho
|
||||||
config.attachment.allowed_types=Allowed types
|
config.attachment.allowed_types=Tipos permitidos
|
||||||
config.attachment.max_size=Size limit
|
config.attachment.max_size=Limite de tamanho
|
||||||
config.attachment.max_files=Files limit
|
config.attachment.max_files=Limite de ficheiros
|
||||||
|
|
||||||
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=Attachment size limit
|
config.release.attachment.max_size=Tamanho máximo dos anexos
|
||||||
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
|
||||||
@@ -1325,10 +1328,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=Default interval
|
config.mirror.default_interval=Intervalo predefinido
|
||||||
|
|
||||||
config.webhook_config=Configuração de WebHook
|
config.webhook_config=Configuração de WebHook
|
||||||
config.webhook.types=Types
|
config.webhook.types=Tipos
|
||||||
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,6 +494,8 @@ 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
|
||||||
@@ -1274,6 +1276,7 @@ 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,6 +494,8 @@ 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=Загрузить файл
|
||||||
@@ -1274,6 +1276,7 @@ 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,6 +494,8 @@ 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
|
||||||
@@ -1274,6 +1276,7 @@ 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,6 +494,8 @@ 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=Отпреми датотеку
|
||||||
@@ -1274,6 +1276,7 @@ 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,6 +494,8 @@ 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
|
||||||
@@ -1274,6 +1276,7 @@ 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,6 +494,8 @@ 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
|
||||||
@@ -1274,6 +1276,7 @@ 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,6 +494,8 @@ 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=Завантажити файл
|
||||||
@@ -1274,6 +1276,7 @@ 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,6 +494,8 @@ 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
|
||||||
@@ -1274,6 +1276,7 @@ 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,6 +494,8 @@ 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=上传文件
|
||||||
@@ -1275,6 +1277,7 @@ 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,6 +494,8 @@ 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
|
||||||
@@ -1274,6 +1276,7 @@ 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,6 +494,8 @@ 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=上傳檔案
|
||||||
@@ -1274,6 +1276,7 @@ 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.15.0.
|
> This is the next-generation, security-focused Docker image. This will become the default image distribution (`gogs/gogs:latest`) starting 0.16.0.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
# 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.15.0, and be completely removed starting 0.16.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.16.0, and be completely removed no earlier than 0.17.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,12 +1,12 @@
|
|||||||
# Table "access"
|
# Table "access"
|
||||||
|
|
||||||
```
|
```
|
||||||
FIELD | COLUMN | POSTGRESQL | MYSQL | SQLITE3
|
FIELD | COLUMN | POSTGRESQL | MYSQL | SQLITE3
|
||||||
---------+---------+-----------------+-----------------------+-------------------
|
---------+---------+-----------------+-----------------------+------------------------
|
||||||
ID | id | BIGSERIAL | BIGINT AUTO_INCREMENT | INTEGER
|
ID | id | BIGSERIAL | BIGINT AUTO_INCREMENT | INTEGER AUTOINCREMENT
|
||||||
UserID | user_id | BIGINT NOT NULL | BIGINT NOT NULL | INTEGER NOT NULL
|
UserID | user_id | BIGINT NOT NULL | BIGINT NOT NULL | INTEGER NOT NULL
|
||||||
RepoID | repo_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
|
Mode | mode | BIGINT NOT NULL | BIGINT NOT NULL | INTEGER NOT NULL
|
||||||
|
|
||||||
Primary keys: id
|
Primary keys: id
|
||||||
Indexes:
|
Indexes:
|
||||||
@@ -18,7 +18,7 @@ Indexes:
|
|||||||
```
|
```
|
||||||
FIELD | COLUMN | POSTGRESQL | MYSQL | SQLITE3
|
FIELD | COLUMN | POSTGRESQL | MYSQL | SQLITE3
|
||||||
--------------+--------------+-----------------------------+-----------------------------+------------------------------
|
--------------+--------------+-----------------------------+-----------------------------+------------------------------
|
||||||
ID | id | BIGSERIAL | BIGINT AUTO_INCREMENT | INTEGER
|
ID | id | BIGSERIAL | BIGINT AUTO_INCREMENT | INTEGER AUTOINCREMENT
|
||||||
UserID | uid | BIGINT | BIGINT | INTEGER
|
UserID | uid | BIGINT | BIGINT | INTEGER
|
||||||
Name | name | TEXT | LONGTEXT | TEXT
|
Name | name | TEXT | LONGTEXT | TEXT
|
||||||
Sha1 | sha1 | VARCHAR(40) UNIQUE | VARCHAR(40) UNIQUE | VARCHAR(40) UNIQUE
|
Sha1 | sha1 | VARCHAR(40) UNIQUE | VARCHAR(40) UNIQUE | VARCHAR(40) UNIQUE
|
||||||
@@ -36,7 +36,7 @@ Indexes:
|
|||||||
```
|
```
|
||||||
FIELD | COLUMN | POSTGRESQL | MYSQL | SQLITE3
|
FIELD | COLUMN | POSTGRESQL | MYSQL | SQLITE3
|
||||||
---------------+----------------+--------------------------------+--------------------------------+---------------------------------
|
---------------+----------------+--------------------------------+--------------------------------+---------------------------------
|
||||||
ID | id | BIGSERIAL | BIGINT AUTO_INCREMENT | INTEGER
|
ID | id | BIGSERIAL | BIGINT AUTO_INCREMENT | INTEGER AUTOINCREMENT
|
||||||
UserID | user_id | BIGINT | BIGINT | INTEGER
|
UserID | user_id | BIGINT | BIGINT | INTEGER
|
||||||
OpType | op_type | BIGINT | BIGINT | INTEGER
|
OpType | op_type | BIGINT | BIGINT | INTEGER
|
||||||
ActUserID | act_user_id | BIGINT | BIGINT | INTEGER
|
ActUserID | act_user_id | BIGINT | BIGINT | INTEGER
|
||||||
@@ -60,7 +60,7 @@ Indexes:
|
|||||||
```
|
```
|
||||||
FIELD | COLUMN | POSTGRESQL | MYSQL | SQLITE3
|
FIELD | COLUMN | POSTGRESQL | MYSQL | SQLITE3
|
||||||
--------------+--------------+--------------------------------+--------------------------------+---------------------------------
|
--------------+--------------+--------------------------------+--------------------------------+---------------------------------
|
||||||
ID | id | BIGSERIAL | BIGINT AUTO_INCREMENT | INTEGER
|
ID | id | BIGSERIAL | BIGINT AUTO_INCREMENT | INTEGER AUTOINCREMENT
|
||||||
UserID | uid | BIGINT NOT NULL | BIGINT NOT NULL | INTEGER NOT NULL
|
UserID | uid | BIGINT NOT NULL | BIGINT NOT NULL | INTEGER NOT NULL
|
||||||
Email | email | VARCHAR(254) NOT NULL | VARCHAR(254) NOT NULL | TEXT 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
|
IsActivated | is_activated | BOOLEAN NOT NULL DEFAULT FALSE | BOOLEAN NOT NULL DEFAULT FALSE | NUMERIC NOT NULL DEFAULT FALSE
|
||||||
@@ -74,11 +74,11 @@ Indexes:
|
|||||||
# Table "follow"
|
# Table "follow"
|
||||||
|
|
||||||
```
|
```
|
||||||
FIELD | COLUMN | POSTGRESQL | MYSQL | SQLITE3
|
FIELD | COLUMN | POSTGRESQL | MYSQL | SQLITE3
|
||||||
-----------+-----------+-----------------+-----------------------+-------------------
|
-----------+-----------+-----------------+-----------------------+------------------------
|
||||||
ID | id | BIGSERIAL | BIGINT AUTO_INCREMENT | INTEGER
|
ID | id | BIGSERIAL | BIGINT AUTO_INCREMENT | INTEGER AUTOINCREMENT
|
||||||
UserID | user_id | BIGINT NOT NULL | BIGINT NOT NULL | INTEGER NOT NULL
|
UserID | user_id | BIGINT NOT NULL | BIGINT NOT NULL | INTEGER NOT NULL
|
||||||
FollowID | follow_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
|
Primary keys: id
|
||||||
Indexes:
|
Indexes:
|
||||||
@@ -102,16 +102,16 @@ Primary keys: repo_id, oid
|
|||||||
# Table "login_source"
|
# Table "login_source"
|
||||||
|
|
||||||
```
|
```
|
||||||
FIELD | COLUMN | POSTGRESQL | MYSQL | SQLITE3
|
FIELD | COLUMN | POSTGRESQL | MYSQL | SQLITE3
|
||||||
--------------+--------------+------------------+-----------------------+-------------------
|
--------------+--------------+------------------+-----------------------+------------------------
|
||||||
ID | id | BIGSERIAL | BIGINT AUTO_INCREMENT | INTEGER
|
ID | id | BIGSERIAL | BIGINT AUTO_INCREMENT | INTEGER AUTOINCREMENT
|
||||||
Type | type | BIGINT | BIGINT | INTEGER
|
Type | type | BIGINT | BIGINT | INTEGER
|
||||||
Name | name | TEXT UNIQUE | VARCHAR(191) UNIQUE | TEXT UNIQUE
|
Name | name | TEXT UNIQUE | VARCHAR(191) UNIQUE | TEXT UNIQUE
|
||||||
IsActived | is_actived | BOOLEAN NOT NULL | BOOLEAN NOT NULL | NUMERIC NOT NULL
|
IsActived | is_actived | BOOLEAN NOT NULL | BOOLEAN NOT NULL | NUMERIC NOT NULL
|
||||||
IsDefault | is_default | BOOLEAN | BOOLEAN | NUMERIC
|
IsDefault | is_default | BOOLEAN | BOOLEAN | NUMERIC
|
||||||
Config | cfg | TEXT | TEXT | TEXT
|
Config | cfg | TEXT | TEXT | TEXT
|
||||||
CreatedUnix | created_unix | BIGINT | BIGINT | INTEGER
|
CreatedUnix | created_unix | BIGINT | BIGINT | INTEGER
|
||||||
UpdatedUnix | updated_unix | BIGINT | BIGINT | INTEGER
|
UpdatedUnix | updated_unix | BIGINT | BIGINT | INTEGER
|
||||||
|
|
||||||
Primary keys: id
|
Primary keys: id
|
||||||
```
|
```
|
||||||
@@ -119,12 +119,12 @@ Primary keys: id
|
|||||||
# Table "notice"
|
# Table "notice"
|
||||||
|
|
||||||
```
|
```
|
||||||
FIELD | COLUMN | POSTGRESQL | MYSQL | SQLITE3
|
FIELD | COLUMN | POSTGRESQL | MYSQL | SQLITE3
|
||||||
--------------+--------------+------------+-----------------------+----------
|
--------------+--------------+------------+-----------------------+------------------------
|
||||||
ID | id | BIGSERIAL | BIGINT AUTO_INCREMENT | INTEGER
|
ID | id | BIGSERIAL | BIGINT AUTO_INCREMENT | INTEGER AUTOINCREMENT
|
||||||
Type | type | BIGINT | BIGINT | INTEGER
|
Type | type | BIGINT | BIGINT | INTEGER
|
||||||
Description | description | TEXT | TEXT | TEXT
|
Description | description | TEXT | TEXT | TEXT
|
||||||
CreatedUnix | created_unix | BIGINT | BIGINT | INTEGER
|
CreatedUnix | created_unix | BIGINT | BIGINT | INTEGER
|
||||||
|
|
||||||
Primary keys: id
|
Primary keys: id
|
||||||
```
|
```
|
||||||
|
|||||||
11
go.mod
11
go.mod
@@ -7,6 +7,8 @@ require (
|
|||||||
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.3
|
github.com/editorconfig/editorconfig-core-go/v2 v2.6.3
|
||||||
|
github.com/glebarez/go-sqlite v1.21.2
|
||||||
|
github.com/glebarez/sqlite v1.11.0
|
||||||
github.com/go-ldap/ldap/v3 v3.4.11
|
github.com/go-ldap/ldap/v3 v3.4.11
|
||||||
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
|
||||||
@@ -18,7 +20,7 @@ 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.4
|
github.com/gogs/git-module v1.8.7
|
||||||
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
|
||||||
@@ -49,13 +51,11 @@ require (
|
|||||||
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.0
|
gopkg.in/macaron.v1 v1.5.1
|
||||||
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/builder v0.3.6
|
||||||
xorm.io/core v0.7.2
|
xorm.io/core v0.7.2
|
||||||
@@ -131,7 +131,7 @@ require (
|
|||||||
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.29.0 // indirect
|
golang.org/x/mod v0.29.0 // indirect
|
||||||
golang.org/x/sync v0.18.0 // indirect
|
golang.org/x/sync v0.19.0 // indirect
|
||||||
golang.org/x/sys v0.38.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
|
||||||
@@ -141,6 +141,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
|
||||||
|
|||||||
34
go.sum
34
go.sum
@@ -92,6 +92,10 @@ 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=
|
||||||
@@ -142,8 +146,8 @@ github.com/gogs/chardet v0.0.0-20150115103509-2404f7772561 h1:aBzukfDxQlCTVS0NBU
|
|||||||
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.4 h1:oSt8sOL4NWOGrSo/CwbS+C4YXtk76QvxyPofem/ViTU=
|
github.com/gogs/git-module v1.8.7 h1:GDyfzB1Z8ytld3LajTfUE4PuIcGcuCHpWB6j8/oD7Tk=
|
||||||
github.com/gogs/git-module v1.8.4/go.mod h1:bQY0aoMK5Q5+NKgy4jXe3K1GFW+GnsSk0SJK0jh6yD0=
|
github.com/gogs/git-module v1.8.7/go.mod h1:IiMSJqi8XH62Kjqjt5Rw8IawSo+DHfM2dDjkSzWLjhs=
|
||||||
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=
|
||||||
@@ -201,8 +205,9 @@ github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
|
|||||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
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/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=
|
||||||
@@ -297,11 +302,9 @@ github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWV
|
|||||||
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
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.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
|
||||||
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
|
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
|
||||||
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
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=
|
||||||
@@ -405,14 +408,16 @@ github.com/siddontang/go-snappy v0.0.0-20140704025258-d8f7bb82a96d/go.mod h1:vq0
|
|||||||
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/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=
|
||||||
@@ -515,9 +520,8 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ
|
|||||||
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.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||||
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||||
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
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=
|
||||||
@@ -626,8 +630,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.0 h1:/dXJaeQagWLjVjCrKH8dgSSU7yG4qTv6rBKpqhYaCyc=
|
gopkg.in/macaron.v1 v1.5.1 h1:0ytdXYcf6//a8bzedl1fVXzPeIXblEqoNPntWAo9YLU=
|
||||||
gopkg.in/macaron.v1 v1.5.0/go.mod h1:sAYUd2r8Q+jLnCN4/ZmdAYHzQn67agV5sAqKFQgrRrw=
|
gopkg.in/macaron.v1 v1.5.1/go.mod h1:AiquOw8YeZJC8sUe11vIO6NeA1/TKSlzQXuJ7Tc4cCQ=
|
||||||
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=
|
||||||
@@ -648,8 +652,6 @@ 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=
|
||||||
@@ -679,8 +681,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.38.2 h1:Aclu7+tgjgcQVShZqim41Bbw9Cho0y/7WzYptXqkEek=
|
modernc.org/sqlite v1.39.0 h1:6bwu9Ooim0yVYA7IZn9demiQk/Ejp0BtTjBWFLymSeY=
|
||||||
modernc.org/sqlite v1.38.2/go.mod h1:cPTJYSlgg3Sfg046yBShXENNtPrWrDX8bsbAQBzgQ5E=
|
modernc.org/sqlite v1.39.0/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=
|
||||||
|
|||||||
2
gogs.go
2
gogs.go
@@ -14,7 +14,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
conf.App.Version = "0.14.0+dev"
|
conf.App.Version = "0.14.2"
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|||||||
@@ -329,9 +329,12 @@ 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)
|
||||||
}, ignSignIn)
|
}, reqSignIn)
|
||||||
|
|
||||||
m.Group("/:username", func() {
|
m.Group("/:username", func() {
|
||||||
m.Post("/action/:action", user.Action)
|
m.Post("/action/:action", user.Action)
|
||||||
@@ -476,7 +479,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 reuqest.
|
// FIXME: should use different URLs but mostly same logic for comments of issue and pull request.
|
||||||
// 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).
|
||||||
@@ -501,7 +504,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 reuqest.
|
// FIXME: should use different URLs but mostly same logic for comments of issue and pull request.
|
||||||
// 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 {
|
} else if !HookMode {
|
||||||
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,9 +142,11 @@ func Init(customConf string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if IsWindowsRuntime() || semverutil.Compare(sshVersion, "<", "5.1") {
|
if IsWindowsRuntime() || semverutil.Compare(sshVersion, "<", "5.1") {
|
||||||
log.Warn(`SSH minimum key size check is forced to be disabled because server is not eligible:
|
if !HookMode {
|
||||||
|
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() {
|
||||||
@@ -344,8 +346,14 @@ func Init(customConf string) error {
|
|||||||
return errors.Wrap(err, "mapping [lfs] section")
|
return errors.Wrap(err, "mapping [lfs] section")
|
||||||
}
|
}
|
||||||
LFS.ObjectsPath = ensureAbs(LFS.ObjectsPath)
|
LFS.ObjectsPath = ensureAbs(LFS.ObjectsPath)
|
||||||
|
LFS.ObjectsTempPath = ensureAbs(LFS.ObjectsTempPath)
|
||||||
|
|
||||||
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,11 +1,15 @@
|
|||||||
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.
|
||||||
@@ -357,8 +361,9 @@ type DatabaseOpts struct {
|
|||||||
var Database DatabaseOpts
|
var Database DatabaseOpts
|
||||||
|
|
||||||
type LFSOpts struct {
|
type LFSOpts struct {
|
||||||
Storage string
|
Storage string
|
||||||
ObjectsPath string
|
ObjectsPath string
|
||||||
|
ObjectsTempPath string
|
||||||
}
|
}
|
||||||
|
|
||||||
// LFS settings
|
// LFS settings
|
||||||
@@ -430,6 +435,83 @@ func handleDeprecated() {
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// checkInvalidOptions checks invalid (renamed/deleted) configuration sections
|
||||||
|
// and options and returns a list of warnings.
|
||||||
|
//
|
||||||
|
// LEGACY [0.15]: Delete this function.
|
||||||
|
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{
|
||||||
|
{"security", "REVERSE_PROXY_AUTHENTICATION_USER"}: {"auth", "REVERSE_PROXY_AUTHENTICATION_HEADER"},
|
||||||
|
{"auth", "ACTIVE_CODE_LIVE_MINUTES"}: {"auth", "ACTIVATE_CODE_LIVES"},
|
||||||
|
{"auth", "RESET_PASSWD_CODE_LIVE_MINUTES"}: {"auth", "RESET_PASSWORD_CODE_LIVES"},
|
||||||
|
{"auth", "ENABLE_CAPTCHA"}: {"auth", "ENABLE_REGISTRATION_CAPTCHA"},
|
||||||
|
{"auth", "ENABLE_NOTIFY_MAIL"}: {"user", "ENABLE_EMAIL_NOTIFICATION"},
|
||||||
|
{"auth", "REGISTER_EMAIL_CONFIRM"}: {"auth", "REQUIRE_EMAIL_CONFIRMATION"},
|
||||||
|
{"session", "GC_INTERVAL_TIME"}: {"session", "GC_INTERVAL"},
|
||||||
|
{"session", "SESSION_LIFE_TIME"}: {"session", "MAX_LIFE_TIME"},
|
||||||
|
{"server", "ROOT_URL"}: {"server", "EXTERNAL_URL"},
|
||||||
|
{"server", "LANDING_PAGE"}: {"server", "LANDING_URL"},
|
||||||
|
{"database", "DB_TYPE"}: {"database", "TYPE"},
|
||||||
|
{"database", "PASSWD"}: {"database", "PASSWORD"},
|
||||||
|
}
|
||||||
|
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.
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
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) {
|
||||||
@@ -29,3 +31,48 @@ 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, use [auth] ACTIVATE_CODE_LIVES instead",
|
||||||
|
"option [auth] ENABLE_CAPTCHA is invalid, use [auth] ENABLE_REGISTRATION_CAPTCHA instead",
|
||||||
|
"option [auth] ENABLE_NOTIFY_MAIL is invalid, use [user] ENABLE_EMAIL_NOTIFICATION instead",
|
||||||
|
"option [auth] REGISTER_EMAIL_CONFIRM is invalid, use [auth] REQUIRE_EMAIL_CONFIRMATION instead",
|
||||||
|
"option [auth] RESET_PASSWD_CODE_LIVE_MINUTES is invalid, use [auth] RESET_PASSWORD_CODE_LIVES instead",
|
||||||
|
"option [database] DB_TYPE is invalid, use [database] TYPE instead",
|
||||||
|
"option [database] PASSWD is invalid, use [database] PASSWORD instead",
|
||||||
|
"option [security] REVERSE_PROXY_AUTHENTICATION_USER is invalid, use [auth] REVERSE_PROXY_AUTHENTICATION_HEADER instead",
|
||||||
|
"option [session] GC_INTERVAL_TIME is invalid, use [session] GC_INTERVAL instead",
|
||||||
|
"option [session] SESSION_LIFE_TIME is invalid, use [session] MAX_LIFE_TIME instead",
|
||||||
|
"section [mailer] is invalid, use [email] instead",
|
||||||
|
"section [service] is invalid, use [auth] instead",
|
||||||
|
"option [server] ROOT_URL is invalid, use [server] EXTERNAL_URL instead",
|
||||||
|
"option [server] LANDING_PAGE is invalid, use [server] LANDING_URL instead",
|
||||||
|
"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,9 +28,8 @@ 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_CAPTCHA = true
|
ENABLE_REGISTRATION_CAPTCHA = true
|
||||||
ENABLE_NOTIFY_MAIL = true
|
REVERSE_PROXY_AUTHENTICATION_HEADER = X-FORWARDED-FOR
|
||||||
REVERSE_PROXY_AUTHENTICATION_HEADER=X-FORWARDED-FOR
|
|
||||||
|
|
||||||
[user]
|
[user]
|
||||||
ENABLE_EMAIL_NOTIFICATION = true
|
ENABLE_EMAIL_NOTIFICATION = true
|
||||||
|
|||||||
@@ -146,18 +146,12 @@ func authenticatedUserID(store AuthStore, c *macaron.Context, sess session.Store
|
|||||||
|
|
||||||
// Check access token.
|
// Check access token.
|
||||||
if isAPIPath(c.Req.URL.Path) {
|
if isAPIPath(c.Req.URL.Path) {
|
||||||
tokenSHA := c.Query("token")
|
var tokenSHA string
|
||||||
if len(tokenSHA) <= 0 {
|
auHead := c.Req.Header.Get("Authorization")
|
||||||
tokenSHA = c.Query("access_token")
|
if auHead != "" {
|
||||||
}
|
auths := strings.Fields(auHead)
|
||||||
if tokenSHA == "" {
|
if len(auths) == 2 && auths[0] == "token" {
|
||||||
// Well, check with header again.
|
tokenSHA = auths[1]
|
||||||
auHead := c.Req.Header.Get("Authorization")
|
|
||||||
if len(auHead) > 0 {
|
|
||||||
auths := strings.Fields(auHead)
|
|
||||||
if len(auths) == 2 && auths[0] == "token" {
|
|
||||||
tokenSHA = auths[1]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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, tpl string, f any) {
|
func (c *Context) RenderWithErr(msg string, status int, 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(http.StatusOK, tpl)
|
c.HTML(status, tpl)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NotFound renders the 404 page.
|
// NotFound renders the 404 page.
|
||||||
|
|||||||
@@ -142,6 +142,11 @@ 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() {
|
||||||
@@ -432,6 +437,12 @@ 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,
|
||||||
@@ -477,6 +488,12 @@ 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,
|
||||||
@@ -518,6 +535,12 @@ 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,
|
||||||
@@ -559,6 +582,12 @@ 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,
|
||||||
@@ -624,6 +653,12 @@ 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,
|
||||||
@@ -703,6 +738,11 @@ 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)
|
||||||
@@ -796,6 +836,12 @@ 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,
|
||||||
@@ -833,6 +879,12 @@ 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{})
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ 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,7 +7,6 @@ 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"
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ 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"
|
||||||
log "unknwon.dev/clog/v2"
|
log "unknwon.dev/clog/v2"
|
||||||
@@ -45,6 +46,9 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
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),
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import (
|
|||||||
api "github.com/gogs/go-gogs-client"
|
api "github.com/gogs/go-gogs-client"
|
||||||
|
|
||||||
"gogs.io/gogs/internal/errutil"
|
"gogs.io/gogs/internal/errutil"
|
||||||
"gogs.io/gogs/internal/process"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Release represents a release of repository.
|
// Release represents a release of repository.
|
||||||
@@ -359,11 +358,13 @@ func DeleteReleaseOfRepoByID(repoID, id int64) error {
|
|||||||
return errors.Newf("GetRepositoryByID: %v", err)
|
return errors.Newf("GetRepositoryByID: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, stderr, err := process.ExecDir(-1, repo.RepoPath(),
|
gitRepo, err := git.Open(repo.RepoPath())
|
||||||
fmt.Sprintf("DeleteReleaseByID (git tag -d): %d", rel.ID),
|
if err != nil {
|
||||||
"git", "tag", "-d", rel.TagName)
|
return errors.Newf("open repository: %v", err)
|
||||||
if err != nil && !strings.Contains(stderr, "not found") {
|
}
|
||||||
return errors.Newf("git tag -d: %v - %s", err, stderr)
|
err = gitRepo.DeleteTag(rel.TagName)
|
||||||
|
if err != nil && !strings.Contains(err.Error(), "not found") {
|
||||||
|
return errors.Newf("delete tag: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = x.Id(rel.ID).Delete(new(Release)); err != nil {
|
if _, err = x.Id(rel.ID).Delete(new(Release)); err != nil {
|
||||||
|
|||||||
@@ -158,6 +158,7 @@ func NewRepoContext() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
RemoveAllWithNotice("Clean up repository temporary data", filepath.Join(conf.Server.AppDataPath, "tmp"))
|
RemoveAllWithNotice("Clean up repository temporary data", filepath.Join(conf.Server.AppDataPath, "tmp"))
|
||||||
|
RemoveAllWithNotice("Clean up LFS temporary data", conf.LFS.ObjectsTempPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Repository contains information of a repository.
|
// Repository contains information of a repository.
|
||||||
@@ -2408,7 +2409,7 @@ func GetWatchers(repoID int64) ([]*Watch, error) {
|
|||||||
func (r *Repository) GetWatchers(page int) ([]*User, error) {
|
func (r *Repository) GetWatchers(page int) ([]*User, error) {
|
||||||
users := make([]*User, 0, ItemsPerPage)
|
users := make([]*User, 0, ItemsPerPage)
|
||||||
sess := x.Limit(ItemsPerPage, (page-1)*ItemsPerPage).Where("watch.repo_id=?", r.ID)
|
sess := x.Limit(ItemsPerPage, (page-1)*ItemsPerPage).Where("watch.repo_id=?", r.ID)
|
||||||
if conf.UsePostgreSQL {
|
if conf.UsePostgreSQL || conf.UseMSSQL {
|
||||||
sess = sess.Join("LEFT", "watch", `"user".id=watch.user_id`)
|
sess = sess.Join("LEFT", "watch", `"user".id=watch.user_id`)
|
||||||
} else {
|
} else {
|
||||||
sess = sess.Join("LEFT", "watch", "user.id=watch.user_id")
|
sess = sess.Join("LEFT", "watch", "user.id=watch.user_id")
|
||||||
@@ -2508,7 +2509,7 @@ func IsStaring(userID, repoID int64) bool {
|
|||||||
func (r *Repository) GetStargazers(page int) ([]*User, error) {
|
func (r *Repository) GetStargazers(page int) ([]*User, error) {
|
||||||
users := make([]*User, 0, ItemsPerPage)
|
users := make([]*User, 0, ItemsPerPage)
|
||||||
sess := x.Limit(ItemsPerPage, (page-1)*ItemsPerPage).Where("star.repo_id=?", r.ID)
|
sess := x.Limit(ItemsPerPage, (page-1)*ItemsPerPage).Where("star.repo_id=?", r.ID)
|
||||||
if conf.UsePostgreSQL {
|
if conf.UsePostgreSQL || conf.UseMSSQL {
|
||||||
sess = sess.Join("LEFT", "star", `"user".id=star.uid`)
|
sess = sess.Join("LEFT", "star", `"user".id=star.uid`)
|
||||||
} else {
|
} else {
|
||||||
sess = sess.Join("LEFT", "star", "user.id=star.uid")
|
sess = sess.Join("LEFT", "star", "user.id=star.uid")
|
||||||
|
|||||||
@@ -8,11 +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"
|
||||||
"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"
|
||||||
@@ -65,11 +65,13 @@ func main() {
|
|||||||
table.SetHeader([]string{"Field", "Column", "PostgreSQL", "MySQL", "SQLite3"})
|
table.SetHeader([]string{"Field", "Column", "PostgreSQL", "MySQL", "SQLite3"})
|
||||||
table.SetBorder(false)
|
table.SetBorder(false)
|
||||||
for j, f := range ti.Fields {
|
for j, f := range ti.Fields {
|
||||||
|
sqlite3Type := strings.ToUpper(collected[2][i].Fields[j].Type)
|
||||||
|
sqlite3Type = strings.ReplaceAll(sqlite3Type, "PRIMARY KEY ", "")
|
||||||
table.Append([]string{
|
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
|
||||||
strings.ToUpper(collected[2][i].Fields[j].Type), // SQLite3
|
sqlite3Type,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
table.Render()
|
table.Render()
|
||||||
|
|||||||
@@ -175,8 +175,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 string) (string, error) {
|
func writeTmpKeyFile(content, keyTestPath string) (string, error) {
|
||||||
tmpFile, err := os.CreateTemp(conf.SSH.KeyTestPath, "gogs_keytest")
|
tmpFile, err := os.CreateTemp(keyTestPath, "gogs_keytest")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", errors.Newf("TempFile: %v", err)
|
return "", errors.Newf("TempFile: %v", err)
|
||||||
}
|
}
|
||||||
@@ -188,15 +188,15 @@ func writeTmpKeyFile(content 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 string) (string, int, error) {
|
func SSHKeygenParsePublicKey(key, keyTestPath, keygenPath string) (string, int, error) {
|
||||||
tmpName, err := writeTmpKeyFile(key)
|
tmpName, err := writeTmpKeyFile(key, keyTestPath)
|
||||||
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", conf.SSH.KeygenPath, "-lf", tmpName)
|
stdout, stderr, err := process.Exec("SSHKeygenParsePublicKey", 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 +301,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)
|
keyType, length, err = SSHKeygenParsePublicKey(content, conf.SSH.KeyTestPath, conf.SSH.KeygenPath)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", errors.Newf("%s: %v", fnName, err)
|
return "", errors.Newf("%s: %v", fnName, err)
|
||||||
|
|||||||
@@ -4,13 +4,11 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"gogs.io/gogs/internal/conf"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_SSHParsePublicKey(t *testing.T) {
|
func TestSSHParsePublicKey(t *testing.T) {
|
||||||
// TODO: Refactor SSHKeyGenParsePublicKey to accept a tempPath and remove this init.
|
tempPath := t.TempDir()
|
||||||
conf.MustInit("")
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
content string
|
content string
|
||||||
@@ -53,20 +51,22 @@ func Test_SSHParsePublicKey(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)
|
||||||
if err != nil {
|
require.NoError(t, err)
|
||||||
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)
|
typ, length, err = SSHKeygenParsePublicKey(test.content, tempPath, "ssh-keygen")
|
||||||
if err != nil {
|
require.NoError(t, err)
|
||||||
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)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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@exmaple.com", CreateUserOptions{})
|
alice, err := s.Create(ctx, "alice", "alice@example.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@exmaple.com", CreateUserOptions{})
|
bob, err := s.Create(ctx, "bob", "bob@example.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@exmaple.com", CreateUserOptions{})
|
cindy, err := s.Create(ctx, "cindy", "cindy@example.com", CreateUserOptions{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
frank, err := s.Create(ctx, "frank", "frank@exmaple.com", CreateUserOptions{})
|
frank, err := s.Create(ctx, "frank", "frank@example.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@exmaple.com", CreateUserOptions{})
|
testUser, err := s.Create(ctx, "testUser", "testUser@example.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@exmaple.com", CreateUserOptions{})
|
alice, err := s.Create(ctx, "alice", "alice@example.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@exmaple.com", CreateUserOptions{})
|
bob, err := s.Create(ctx, "bob", "bob@example.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@exmaple.com", CreateUserOptions{Activated: true})
|
_, err = s.Create(ctx, "cindy", "cindy@example.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@exmaple.com", CreateUserOptions{})
|
david, err := s.Create(ctx, "david", "david@example.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@exmaple.com", CreateUserOptions{})
|
org, err := s.Create(ctx, "gogs", "gogs@example.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@exmaple.com", CreateUserOptions{})
|
alice, err := s.Create(ctx, "alice", "alice@example.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@exmaple.com"
|
email2 := "bob2@example.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@exmaple.com", CreateUserOptions{})
|
alice, err := s.Create(ctx, "alice", "alice@example.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@exmaple.com", CreateUserOptions{})
|
alice, err := s.Create(ctx, "alice", "alice@example.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@exmaple.com", CreateUserOptions{})
|
alice, err := s.Create(ctx, "alice", "alice@example.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@exmaple.com", CreateUserOptions{})
|
alice, err := s.Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
bob, err := s.Create(ctx, "bob", "bob@exmaple.com", CreateUserOptions{Activated: true})
|
bob, err := s.Create(ctx, "bob", "bob@example.com", CreateUserOptions{Activated: true})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
_, err = s.Create(ctx, "cindy", "cindy@exmaple.com", CreateUserOptions{Activated: true})
|
_, err = s.Create(ctx, "cindy", "cindy@example.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"})
|
||||||
|
|||||||
@@ -96,19 +96,6 @@ func NewDB(t *testing.T, suite string, tables ...any) *gorm.DB {
|
|||||||
_, _ = sqlDB.Exec(fmt.Sprintf(`DROP DATABASE %q`, dbName))
|
_, _ = sqlDB.Exec(fmt.Sprintf(`DROP DATABASE %q`, dbName))
|
||||||
_ = sqlDB.Close()
|
_ = sqlDB.Close()
|
||||||
}
|
}
|
||||||
case "sqlite":
|
|
||||||
dbName = filepath.Join(os.TempDir(), fmt.Sprintf("gogs-%s-%d.db", suite, time.Now().Unix()))
|
|
||||||
dbOpts = conf.DatabaseOpts{
|
|
||||||
Type: "sqlite",
|
|
||||||
Path: dbName,
|
|
||||||
}
|
|
||||||
cleanup = func(db *gorm.DB) {
|
|
||||||
sqlDB, err := db.DB()
|
|
||||||
if err == nil {
|
|
||||||
_ = sqlDB.Close()
|
|
||||||
}
|
|
||||||
_ = os.Remove(dbName)
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
dbName = filepath.Join(os.TempDir(), fmt.Sprintf("gogs-%s-%d.db", suite, time.Now().Unix()))
|
dbName = filepath.Join(os.TempDir(), fmt.Sprintf("gogs-%s-%d.db", suite, time.Now().Unix()))
|
||||||
dbOpts = conf.DatabaseOpts{
|
dbOpts = conf.DatabaseOpts{
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/cockroachdb/errors"
|
"github.com/cockroachdb/errors"
|
||||||
|
"github.com/glebarez/sqlite"
|
||||||
"gorm.io/driver/mysql"
|
"gorm.io/driver/mysql"
|
||||||
"gorm.io/driver/postgres"
|
"gorm.io/driver/postgres"
|
||||||
"gorm.io/driver/sqlite"
|
|
||||||
"gorm.io/driver/sqlserver"
|
"gorm.io/driver/sqlserver"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
|
||||||
@@ -73,7 +73,7 @@ func NewDSN(opts conf.DatabaseOpts) (dsn string, err error) {
|
|||||||
dsn = fmt.Sprintf("server=%s; port=%s; database=%s; user id=%s; password=%s;",
|
dsn = fmt.Sprintf("server=%s; port=%s; database=%s; user id=%s; password=%s;",
|
||||||
host, port, opts.Name, opts.User, opts.Password)
|
host, port, opts.Name, opts.User, opts.Password)
|
||||||
|
|
||||||
case "sqlite3", "sqlite":
|
case "sqlite3":
|
||||||
dsn = "file:" + opts.Path + "?cache=shared&mode=rwc"
|
dsn = "file:" + opts.Path + "?cache=shared&mode=rwc"
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -101,9 +101,6 @@ func OpenDB(opts conf.DatabaseOpts, cfg *gorm.Config) (*gorm.DB, error) {
|
|||||||
dialector = sqlserver.Open(dsn)
|
dialector = sqlserver.Open(dsn)
|
||||||
case "sqlite3":
|
case "sqlite3":
|
||||||
dialector = sqlite.Open(dsn)
|
dialector = sqlite.Open(dsn)
|
||||||
case "sqlite":
|
|
||||||
dialector = sqlite.Open(dsn)
|
|
||||||
dialector.(*sqlite.Dialector).DriverName = "sqlite"
|
|
||||||
default:
|
default:
|
||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ func (s *DiffSection) ComputedInlineDiffFor(line *git.DiffLine) template.HTML {
|
|||||||
func diffsToHTML(diffs []diffmatchpatch.Diff, lineType git.DiffLineType) template.HTML {
|
func diffsToHTML(diffs []diffmatchpatch.Diff, lineType git.DiffLineType) template.HTML {
|
||||||
buf := bytes.NewBuffer(nil)
|
buf := bytes.NewBuffer(nil)
|
||||||
|
|
||||||
// Reproduce signs which are cutted for inline diff before.
|
// Reproduce signs which are cut for inline diff before.
|
||||||
switch lineType {
|
switch lineType {
|
||||||
case git.DiffLineAdd:
|
case git.DiffLineAdd:
|
||||||
buf.WriteByte('+')
|
buf.WriteByte('+')
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package lfsutil
|
package lfsutil
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/hex"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -10,7 +12,10 @@ import (
|
|||||||
"gogs.io/gogs/internal/osutil"
|
"gogs.io/gogs/internal/osutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrObjectNotExist = errors.New("Object does not exist")
|
var (
|
||||||
|
ErrObjectNotExist = errors.New("object does not exist")
|
||||||
|
ErrOIDMismatch = errors.New("content hash does not match OID")
|
||||||
|
)
|
||||||
|
|
||||||
// Storager is an storage backend for uploading and downloading LFS objects.
|
// Storager is an storage backend for uploading and downloading LFS objects.
|
||||||
type Storager interface {
|
type Storager interface {
|
||||||
@@ -39,6 +44,8 @@ var _ Storager = (*LocalStorage)(nil)
|
|||||||
type LocalStorage struct {
|
type LocalStorage struct {
|
||||||
// The root path for storing LFS objects.
|
// The root path for storing LFS objects.
|
||||||
Root string
|
Root string
|
||||||
|
// The path for storing temporary files during upload verification.
|
||||||
|
TempDir string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*LocalStorage) Storage() Storage {
|
func (*LocalStorage) Storage() Storage {
|
||||||
@@ -58,29 +65,50 @@ func (s *LocalStorage) Upload(oid OID, rc io.ReadCloser) (int64, error) {
|
|||||||
return 0, ErrInvalidOID
|
return 0, ErrInvalidOID
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
|
||||||
fpath := s.storagePath(oid)
|
fpath := s.storagePath(oid)
|
||||||
defer func() {
|
dir := filepath.Dir(fpath)
|
||||||
rc.Close()
|
|
||||||
|
|
||||||
if err != nil {
|
defer rc.Close()
|
||||||
_ = os.Remove(fpath)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm)
|
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
|
||||||
if err != nil {
|
|
||||||
return 0, errors.Wrap(err, "create directories")
|
return 0, errors.Wrap(err, "create directories")
|
||||||
}
|
}
|
||||||
w, err := os.Create(fpath)
|
|
||||||
if err != nil {
|
|
||||||
return 0, errors.Wrap(err, "create file")
|
|
||||||
}
|
|
||||||
defer w.Close()
|
|
||||||
|
|
||||||
written, err := io.Copy(w, rc)
|
// If the object file already exists, skip the upload and return the
|
||||||
|
// existing file's size.
|
||||||
|
if fi, err := os.Stat(fpath); err == nil {
|
||||||
|
_, _ = io.Copy(io.Discard, rc)
|
||||||
|
return fi.Size(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write to a temp file and verify the content hash before publishing.
|
||||||
|
// This ensures the final path always contains a complete, hash-verified
|
||||||
|
// file, even when concurrent uploads of the same OID race.
|
||||||
|
if err := os.MkdirAll(s.TempDir, os.ModePerm); err != nil {
|
||||||
|
return 0, errors.Wrap(err, "create temp directory")
|
||||||
|
}
|
||||||
|
tmp, err := os.CreateTemp(s.TempDir, "upload-*")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, errors.Wrap(err, "copy file")
|
return 0, errors.Wrap(err, "create temp file")
|
||||||
|
}
|
||||||
|
tmpPath := tmp.Name()
|
||||||
|
defer os.Remove(tmpPath)
|
||||||
|
|
||||||
|
hash := sha256.New()
|
||||||
|
written, err := io.Copy(tmp, io.TeeReader(rc, hash))
|
||||||
|
if closeErr := tmp.Close(); err == nil && closeErr != nil {
|
||||||
|
err = closeErr
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return 0, errors.Wrap(err, "write object file")
|
||||||
|
}
|
||||||
|
|
||||||
|
if computed := hex.EncodeToString(hash.Sum(nil)); computed != string(oid) {
|
||||||
|
return 0, ErrOIDMismatch
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.Rename(tmpPath, fpath); err != nil && !os.IsExist(err) {
|
||||||
|
return 0, errors.Wrap(err, "publish object file")
|
||||||
}
|
}
|
||||||
return written, nil
|
return written, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"gogs.io/gogs/internal/osutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestLocalStorage_storagePath(t *testing.T) {
|
func TestLocalStorage_storagePath(t *testing.T) {
|
||||||
@@ -46,50 +49,54 @@ func TestLocalStorage_storagePath(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestLocalStorage_Upload(t *testing.T) {
|
func TestLocalStorage_Upload(t *testing.T) {
|
||||||
|
base := t.TempDir()
|
||||||
s := &LocalStorage{
|
s := &LocalStorage{
|
||||||
Root: filepath.Join(os.TempDir(), "lfs-objects"),
|
Root: filepath.Join(base, "lfs-objects"),
|
||||||
|
TempDir: filepath.Join(base, "tmp", "lfs"),
|
||||||
}
|
}
|
||||||
t.Cleanup(func() {
|
|
||||||
_ = os.RemoveAll(s.Root)
|
const helloWorldOID = OID("c0535e4be2b79ffd93291305436bf889314e4a3faec05ecffcbb7df31ad9e51a") // "Hello world!"
|
||||||
|
|
||||||
|
t.Run("invalid OID", func(t *testing.T) {
|
||||||
|
written, err := s.Upload("bad_oid", io.NopCloser(strings.NewReader("")))
|
||||||
|
assert.Equal(t, int64(0), written)
|
||||||
|
assert.Equal(t, ErrInvalidOID, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
tests := []struct {
|
t.Run("valid OID", func(t *testing.T) {
|
||||||
name string
|
written, err := s.Upload(helloWorldOID, io.NopCloser(strings.NewReader("Hello world!")))
|
||||||
oid OID
|
require.NoError(t, err)
|
||||||
content string
|
assert.Equal(t, int64(12), written)
|
||||||
expWritten int64
|
})
|
||||||
expErr error
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "invalid oid",
|
|
||||||
oid: "bad_oid",
|
|
||||||
expErr: ErrInvalidOID,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
t.Run("valid OID but wrong content", func(t *testing.T) {
|
||||||
name: "valid oid",
|
oid := OID("ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f")
|
||||||
oid: "ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f",
|
written, err := s.Upload(oid, io.NopCloser(strings.NewReader("Hello world!")))
|
||||||
content: "Hello world!",
|
assert.Equal(t, int64(0), written)
|
||||||
expWritten: 12,
|
assert.Equal(t, ErrOIDMismatch, err)
|
||||||
},
|
|
||||||
}
|
// File should have been cleaned up.
|
||||||
for _, test := range tests {
|
assert.False(t, osutil.IsFile(s.storagePath(oid)))
|
||||||
t.Run(test.name, func(t *testing.T) {
|
})
|
||||||
written, err := s.Upload(test.oid, io.NopCloser(strings.NewReader(test.content)))
|
|
||||||
assert.Equal(t, test.expWritten, written)
|
t.Run("duplicate upload returns existing size", func(t *testing.T) {
|
||||||
assert.Equal(t, test.expErr, err)
|
written, err := s.Upload(helloWorldOID, io.NopCloser(strings.NewReader("should be ignored")))
|
||||||
})
|
require.NoError(t, err)
|
||||||
}
|
assert.Equal(t, int64(12), written)
|
||||||
|
|
||||||
|
// Verify original content is preserved.
|
||||||
|
var buf bytes.Buffer
|
||||||
|
err = s.Download(helloWorldOID, &buf)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, "Hello world!", buf.String())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLocalStorage_Download(t *testing.T) {
|
func TestLocalStorage_Download(t *testing.T) {
|
||||||
oid := OID("ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f")
|
oid := OID("ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f")
|
||||||
s := &LocalStorage{
|
s := &LocalStorage{
|
||||||
Root: filepath.Join(os.TempDir(), "lfs-objects"),
|
Root: filepath.Join(t.TempDir(), "lfs-objects"),
|
||||||
}
|
}
|
||||||
t.Cleanup(func() {
|
|
||||||
_ = os.RemoveAll(s.Root)
|
|
||||||
})
|
|
||||||
|
|
||||||
fpath := s.storagePath(oid)
|
fpath := s.storagePath(oid)
|
||||||
err := os.MkdirAll(filepath.Dir(fpath), os.ModePerm)
|
err := os.MkdirAll(filepath.Dir(fpath), os.ModePerm)
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package markup
|
package markup
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/microcosm-cc/bluemonday"
|
"github.com/microcosm-cc/bluemonday"
|
||||||
@@ -32,14 +34,28 @@ func NewSanitizer() {
|
|||||||
sanitizer.policy.AllowAttrs("type").Matching(lazyregexp.New(`^checkbox$`).Regexp()).OnElements("input")
|
sanitizer.policy.AllowAttrs("type").Matching(lazyregexp.New(`^checkbox$`).Regexp()).OnElements("input")
|
||||||
sanitizer.policy.AllowAttrs("checked", "disabled").OnElements("input")
|
sanitizer.policy.AllowAttrs("checked", "disabled").OnElements("input")
|
||||||
|
|
||||||
// Data URLs
|
// Only allow data URIs with safe image MIME types to prevent XSS via
|
||||||
sanitizer.policy.AllowURLSchemes("data")
|
// "data:text/html" payloads.
|
||||||
|
sanitizer.policy.AllowURLSchemeWithCustomPolicy("data", isSafeDataURI)
|
||||||
|
|
||||||
// Custom URL-Schemes
|
// Custom URL-Schemes
|
||||||
sanitizer.policy.AllowURLSchemes(conf.Markdown.CustomURLSchemes...)
|
sanitizer.policy.AllowURLSchemes(conf.Markdown.CustomURLSchemes...)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isSafeDataURI returns whether the given data URI uses a safe image MIME type.
|
||||||
|
func isSafeDataURI(u *url.URL) bool {
|
||||||
|
// The opaque data of a data URI has the form "mediatype;base64,data" or
|
||||||
|
// "mediatype,data". We only allow common image MIME types.
|
||||||
|
mediatype, _, _ := strings.Cut(u.Opaque, ";")
|
||||||
|
mediatype, _, _ = strings.Cut(mediatype, ",")
|
||||||
|
switch strings.TrimSpace(strings.ToLower(mediatype)) {
|
||||||
|
case "image/png", "image/jpeg", "image/gif", "image/webp", "image/x-icon":
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// Sanitize takes a string that contains a HTML fragment or document and applies policy whitelist.
|
// Sanitize takes a string that contains a HTML fragment or document and applies policy whitelist.
|
||||||
func Sanitize(s string) string {
|
func Sanitize(s string) string {
|
||||||
return sanitizer.policy.Sanitize(s)
|
return sanitizer.policy.Sanitize(s)
|
||||||
|
|||||||
@@ -26,6 +26,20 @@ func Test_Sanitizer(t *testing.T) {
|
|||||||
{input: `<input type="hidden">`, expVal: ``},
|
{input: `<input type="hidden">`, expVal: ``},
|
||||||
{input: `<input type="checkbox">`, expVal: `<input type="checkbox">`},
|
{input: `<input type="checkbox">`, expVal: `<input type="checkbox">`},
|
||||||
{input: `<input checked disabled autofocus>`, expVal: `<input checked="" disabled="">`},
|
{input: `<input checked disabled autofocus>`, expVal: `<input checked="" disabled="">`},
|
||||||
|
|
||||||
|
// Data URIs: safe image types should be allowed
|
||||||
|
{input: `<img src="data:image/png;base64,abc">`, expVal: `<img src="data:image/png;base64,abc">`},
|
||||||
|
{input: `<img src="data:image/jpeg;base64,abc">`, expVal: `<img src="data:image/jpeg;base64,abc">`},
|
||||||
|
{input: `<img src="data:image/gif;base64,abc">`, expVal: `<img src="data:image/gif;base64,abc">`},
|
||||||
|
{input: `<img src="data:image/webp;base64,abc">`, expVal: `<img src="data:image/webp;base64,abc">`},
|
||||||
|
|
||||||
|
// Data URIs: text/html must be stripped to prevent XSS (GHSA-xrcr-gmf5-2r8j)
|
||||||
|
{input: `<a href="data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4=">Click</a>`, expVal: `Click`},
|
||||||
|
{input: `<a href="data:text/html,<script>alert(1)</script>">XSS</a>`, expVal: `XSS`},
|
||||||
|
{input: `<img src="data:text/html;base64,abc">`, expVal: ``},
|
||||||
|
|
||||||
|
// Data URIs: SVG must be stripped (can contain embedded JavaScript)
|
||||||
|
{input: `<img src="data:image/svg+xml;base64,abc">`, expVal: ``},
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.input, func(t *testing.T) {
|
t.Run(test.input, func(t *testing.T) {
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ func NewAuthSourcePost(c *context.Context, f form.Authentication) {
|
|||||||
c.Data["HasTLS"] = hasTLS
|
c.Data["HasTLS"] = hasTLS
|
||||||
|
|
||||||
if c.HasError() {
|
if c.HasError() {
|
||||||
c.Success(tmplAdminAuthNew)
|
c.HTML(http.StatusBadRequest, tmplAdminAuthNew)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,7 +167,7 @@ func NewAuthSourcePost(c *context.Context, f form.Authentication) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
if database.IsErrLoginSourceAlreadyExist(err) {
|
if database.IsErrLoginSourceAlreadyExist(err) {
|
||||||
c.FormErr("Name")
|
c.FormErr("Name")
|
||||||
c.RenderWithErr(c.Tr("admin.auths.login_source_exist", f.Name), tmplAdminAuthNew, f)
|
c.RenderWithErr(c.Tr("admin.auths.login_source_exist", f.Name), http.StatusUnprocessableEntity, tmplAdminAuthNew, f)
|
||||||
} else {
|
} else {
|
||||||
c.Error(err, "create login source")
|
c.Error(err, "create login source")
|
||||||
}
|
}
|
||||||
@@ -223,7 +223,7 @@ func EditAuthSourcePost(c *context.Context, f form.Authentication) {
|
|||||||
c.Data["HasTLS"] = source.Provider.HasTLS()
|
c.Data["HasTLS"] = source.Provider.HasTLS()
|
||||||
|
|
||||||
if c.HasError() {
|
if c.HasError() {
|
||||||
c.Success(tmplAdminAuthEdit)
|
c.HTML(http.StatusBadRequest, tmplAdminAuthEdit)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package admin
|
package admin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -68,7 +69,7 @@ func NewUserPost(c *context.Context, f form.AdminCrateUser) {
|
|||||||
c.Data["CanSendEmail"] = conf.Email.Enabled
|
c.Data["CanSendEmail"] = conf.Email.Enabled
|
||||||
|
|
||||||
if c.HasError() {
|
if c.HasError() {
|
||||||
c.Success(tmplAdminUserNew)
|
c.HTML(http.StatusBadRequest, tmplAdminUserNew)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,13 +90,13 @@ func NewUserPost(c *context.Context, f form.AdminCrateUser) {
|
|||||||
switch {
|
switch {
|
||||||
case database.IsErrUserAlreadyExist(err):
|
case database.IsErrUserAlreadyExist(err):
|
||||||
c.Data["Err_UserName"] = true
|
c.Data["Err_UserName"] = true
|
||||||
c.RenderWithErr(c.Tr("form.username_been_taken"), tmplAdminUserNew, &f)
|
c.RenderWithErr(c.Tr("form.username_been_taken"), http.StatusUnprocessableEntity, tmplAdminUserNew, &f)
|
||||||
case database.IsErrEmailAlreadyUsed(err):
|
case database.IsErrEmailAlreadyUsed(err):
|
||||||
c.Data["Err_Email"] = true
|
c.Data["Err_Email"] = true
|
||||||
c.RenderWithErr(c.Tr("form.email_been_used"), tmplAdminUserNew, &f)
|
c.RenderWithErr(c.Tr("form.email_been_used"), http.StatusUnprocessableEntity, tmplAdminUserNew, &f)
|
||||||
case database.IsErrNameNotAllowed(err):
|
case database.IsErrNameNotAllowed(err):
|
||||||
c.Data["Err_UserName"] = true
|
c.Data["Err_UserName"] = true
|
||||||
c.RenderWithErr(c.Tr("user.form.name_not_allowed", err.(database.ErrNameNotAllowed).Value()), tmplAdminUserNew, &f)
|
c.RenderWithErr(c.Tr("user.form.name_not_allowed", err.(database.ErrNameNotAllowed).Value()), http.StatusBadRequest, tmplAdminUserNew, &f)
|
||||||
default:
|
default:
|
||||||
c.Error(err, "create user")
|
c.Error(err, "create user")
|
||||||
}
|
}
|
||||||
@@ -166,7 +167,7 @@ func EditUserPost(c *context.Context, f form.AdminEditUser) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if c.HasError() {
|
if c.HasError() {
|
||||||
c.Success(tmplAdminUserEdit)
|
c.HTML(http.StatusBadRequest, tmplAdminUserEdit)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,7 +204,7 @@ func EditUserPost(c *context.Context, f form.AdminEditUser) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
if database.IsErrEmailAlreadyUsed(err) {
|
if database.IsErrEmailAlreadyUsed(err) {
|
||||||
c.Data["Err_Email"] = true
|
c.Data["Err_Email"] = true
|
||||||
c.RenderWithErr(c.Tr("form.email_been_used"), tmplAdminUserEdit, &f)
|
c.RenderWithErr(c.Tr("form.email_been_used"), http.StatusUnprocessableEntity, tmplAdminUserEdit, &f)
|
||||||
} else {
|
} else {
|
||||||
c.Error(err, "update user")
|
c.Error(err, "update user")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -137,13 +137,13 @@ func GetContents(c *context.APIContext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The entry is a directory
|
// The entry is a directory
|
||||||
dir, err := gitRepo.LsTree(entry.ID().String())
|
dir, err := gitRepo.LsTree(entry.ID().String(), git.LsTreeOptions{Verbatim: true})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.NotFoundOrError(gitutil.NewError(err), "get tree")
|
c.NotFoundOrError(gitutil.NewError(err), "get tree")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
entries, err := dir.Entries()
|
entries, err := dir.Entries(git.LsTreeOptions{Verbatim: true})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.NotFoundOrError(gitutil.NewError(err), "list entries")
|
c.NotFoundOrError(gitutil.NewError(err), "list entries")
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -88,6 +88,17 @@ func EditIssueComment(c *context.APIContext, form api.EditIssueCommentOption) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
issue, err := database.GetIssueByID(comment.IssueID)
|
||||||
|
if err != nil {
|
||||||
|
c.NotFoundOrError(err, "get issue by ID")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if issue.RepoID != c.Repo.Repository.ID {
|
||||||
|
c.NotFound()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if c.User.ID != comment.PosterID && !c.Repo.IsAdmin() {
|
if c.User.ID != comment.PosterID && !c.Repo.IsAdmin() {
|
||||||
c.Status(http.StatusForbidden)
|
c.Status(http.StatusForbidden)
|
||||||
return
|
return
|
||||||
@@ -112,6 +123,17 @@ func DeleteIssueComment(c *context.APIContext) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
issue, err := database.GetIssueByID(comment.IssueID)
|
||||||
|
if err != nil {
|
||||||
|
c.NotFoundOrError(err, "get issue by ID")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if issue.RepoID != c.Repo.Repository.ID {
|
||||||
|
c.NotFound()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if c.User.ID != comment.PosterID && !c.Repo.IsAdmin() {
|
if c.User.ID != comment.PosterID && !c.Repo.IsAdmin() {
|
||||||
c.Status(http.StatusForbidden)
|
c.Status(http.StatusForbidden)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -45,6 +45,11 @@ func GetDeployKey(c *context.APIContext) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if key.RepoID != c.Repo.Repository.ID {
|
||||||
|
c.NotFound()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if err = key.GetContent(); err != nil {
|
if err = key.GetContent(); err != nil {
|
||||||
c.Error(err, "get content")
|
c.Error(err, "get content")
|
||||||
return
|
return
|
||||||
@@ -94,7 +99,18 @@ func CreateDeployKey(c *context.APIContext, form api.CreateKeyOption) {
|
|||||||
|
|
||||||
// https://github.com/gogs/go-gogs-client/wiki/Repositories-Deploy-Keys#remove-a-deploy-key
|
// https://github.com/gogs/go-gogs-client/wiki/Repositories-Deploy-Keys#remove-a-deploy-key
|
||||||
func DeleteDeploykey(c *context.APIContext) {
|
func DeleteDeploykey(c *context.APIContext) {
|
||||||
if err := database.DeleteDeployKey(c.User, c.ParamsInt64(":id")); err != nil {
|
key, err := database.GetDeployKeyByID(c.ParamsInt64(":id"))
|
||||||
|
if err != nil {
|
||||||
|
c.NotFoundOrError(err, "get deploy key by ID")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if key.RepoID != c.Repo.Repository.ID {
|
||||||
|
c.NotFound()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := database.DeleteDeployKey(c.User, key.ID); err != nil {
|
||||||
if database.IsErrKeyAccessDenied(err) {
|
if database.IsErrKeyAccessDenied(err) {
|
||||||
c.ErrorStatus(http.StatusForbidden, errors.New("You do not have access to this key"))
|
c.ErrorStatus(http.StatusForbidden, errors.New("You do not have access to this key"))
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -17,13 +17,13 @@ func GetRepoGitTree(c *context.APIContext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sha := c.Params(":sha")
|
sha := c.Params(":sha")
|
||||||
tree, err := gitRepo.LsTree(sha)
|
tree, err := gitRepo.LsTree(sha, git.LsTreeOptions{Verbatim: true})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.NotFoundOrError(gitutil.NewError(err), "get tree")
|
c.NotFoundOrError(gitutil.NewError(err), "get tree")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
entries, err := tree.Entries()
|
entries, err := tree.Entries(git.LsTreeOptions{Verbatim: true})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Error(err, "list entries")
|
c.Error(err, "list entries")
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package route
|
package route
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
"net/mail"
|
"net/mail"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
@@ -194,13 +195,12 @@ func InstallPost(c *context.Context, f form.Install) {
|
|||||||
c.HasValue("Err_AdminEmail") {
|
c.HasValue("Err_AdminEmail") {
|
||||||
c.FormErr("Admin")
|
c.FormErr("Admin")
|
||||||
}
|
}
|
||||||
|
c.HTML(http.StatusBadRequest, INSTALL)
|
||||||
c.Success(INSTALL)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := exec.LookPath("git"); err != nil {
|
if _, err := exec.LookPath("git"); err != nil {
|
||||||
c.RenderWithErr(c.Tr("install.test_git_failed", err), INSTALL, &f)
|
c.RenderWithErr(c.Tr("install.test_git_failed", err), http.StatusInternalServerError, INSTALL, &f)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,7 +222,7 @@ func InstallPost(c *context.Context, f form.Install) {
|
|||||||
|
|
||||||
if conf.Database.Type == "sqlite3" && conf.Database.Path == "" {
|
if conf.Database.Type == "sqlite3" && conf.Database.Path == "" {
|
||||||
c.FormErr("DbPath")
|
c.FormErr("DbPath")
|
||||||
c.RenderWithErr(c.Tr("install.err_empty_db_path"), INSTALL, &f)
|
c.RenderWithErr(c.Tr("install.err_empty_db_path"), http.StatusBadRequest, INSTALL, &f)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,10 +230,10 @@ func InstallPost(c *context.Context, f form.Install) {
|
|||||||
if err := database.NewTestEngine(); err != nil {
|
if err := database.NewTestEngine(); err != nil {
|
||||||
if strings.Contains(err.Error(), `Unknown database type: sqlite3`) {
|
if strings.Contains(err.Error(), `Unknown database type: sqlite3`) {
|
||||||
c.FormErr("DbType")
|
c.FormErr("DbType")
|
||||||
c.RenderWithErr(c.Tr("install.sqlite3_not_available", "https://gogs.io/docs/installation/install_from_binary.html"), INSTALL, &f)
|
c.RenderWithErr(c.Tr("install.sqlite3_not_available", "https://gogs.io/docs/installation/install_from_binary.html"), http.StatusInternalServerError, INSTALL, &f)
|
||||||
} else {
|
} else {
|
||||||
c.FormErr("DbSetting")
|
c.FormErr("DbSetting")
|
||||||
c.RenderWithErr(c.Tr("install.invalid_db_setting", err), INSTALL, &f)
|
c.RenderWithErr(c.Tr("install.invalid_db_setting", err), http.StatusBadRequest, INSTALL, &f)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -242,7 +242,7 @@ func InstallPost(c *context.Context, f form.Install) {
|
|||||||
f.RepoRootPath = strings.ReplaceAll(f.RepoRootPath, "\\", "/")
|
f.RepoRootPath = strings.ReplaceAll(f.RepoRootPath, "\\", "/")
|
||||||
if err := os.MkdirAll(f.RepoRootPath, os.ModePerm); err != nil {
|
if err := os.MkdirAll(f.RepoRootPath, os.ModePerm); err != nil {
|
||||||
c.FormErr("RepoRootPath")
|
c.FormErr("RepoRootPath")
|
||||||
c.RenderWithErr(c.Tr("install.invalid_repo_path", err), INSTALL, &f)
|
c.RenderWithErr(c.Tr("install.invalid_repo_path", err), http.StatusBadRequest, INSTALL, &f)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,21 +250,21 @@ func InstallPost(c *context.Context, f form.Install) {
|
|||||||
f.LogRootPath = strings.ReplaceAll(f.LogRootPath, "\\", "/")
|
f.LogRootPath = strings.ReplaceAll(f.LogRootPath, "\\", "/")
|
||||||
if err := os.MkdirAll(f.LogRootPath, os.ModePerm); err != nil {
|
if err := os.MkdirAll(f.LogRootPath, os.ModePerm); err != nil {
|
||||||
c.FormErr("LogRootPath")
|
c.FormErr("LogRootPath")
|
||||||
c.RenderWithErr(c.Tr("install.invalid_log_root_path", err), INSTALL, &f)
|
c.RenderWithErr(c.Tr("install.invalid_log_root_path", err), http.StatusBadRequest, INSTALL, &f)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
currentUser, match := conf.CheckRunUser(f.RunUser)
|
currentUser, match := conf.CheckRunUser(f.RunUser)
|
||||||
if !match {
|
if !match {
|
||||||
c.FormErr("RunUser")
|
c.FormErr("RunUser")
|
||||||
c.RenderWithErr(c.Tr("install.run_user_not_match", f.RunUser, currentUser), INSTALL, &f)
|
c.RenderWithErr(c.Tr("install.run_user_not_match", f.RunUser, currentUser), http.StatusForbidden, INSTALL, &f)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check host address and port
|
// Check host address and port
|
||||||
if len(f.SMTPHost) > 0 && !strings.Contains(f.SMTPHost, ":") {
|
if len(f.SMTPHost) > 0 && !strings.Contains(f.SMTPHost, ":") {
|
||||||
c.FormErr("SMTP", "SMTPHost")
|
c.FormErr("SMTP", "SMTPHost")
|
||||||
c.RenderWithErr(c.Tr("install.smtp_host_missing_port"), INSTALL, &f)
|
c.RenderWithErr(c.Tr("install.smtp_host_missing_port"), http.StatusBadRequest, INSTALL, &f)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,7 +273,7 @@ func InstallPost(c *context.Context, f form.Install) {
|
|||||||
_, err := mail.ParseAddress(f.SMTPFrom)
|
_, err := mail.ParseAddress(f.SMTPFrom)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.FormErr("SMTP", "SMTPFrom")
|
c.FormErr("SMTP", "SMTPFrom")
|
||||||
c.RenderWithErr(c.Tr("install.invalid_smtp_from", err), INSTALL, &f)
|
c.RenderWithErr(c.Tr("install.invalid_smtp_from", err), http.StatusBadRequest, INSTALL, &f)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -281,19 +281,19 @@ func InstallPost(c *context.Context, f form.Install) {
|
|||||||
// Check logic loophole between disable self-registration and no admin account.
|
// Check logic loophole between disable self-registration and no admin account.
|
||||||
if f.DisableRegistration && f.AdminName == "" {
|
if f.DisableRegistration && f.AdminName == "" {
|
||||||
c.FormErr("Services", "Admin")
|
c.FormErr("Services", "Admin")
|
||||||
c.RenderWithErr(c.Tr("install.no_admin_and_disable_registration"), INSTALL, f)
|
c.RenderWithErr(c.Tr("install.no_admin_and_disable_registration"), http.StatusUnprocessableEntity, INSTALL, f)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check admin password.
|
// Check admin password.
|
||||||
if len(f.AdminName) > 0 && f.AdminPasswd == "" {
|
if len(f.AdminName) > 0 && f.AdminPasswd == "" {
|
||||||
c.FormErr("Admin", "AdminPasswd")
|
c.FormErr("Admin", "AdminPasswd")
|
||||||
c.RenderWithErr(c.Tr("install.err_empty_admin_password"), INSTALL, f)
|
c.RenderWithErr(c.Tr("install.err_empty_admin_password"), http.StatusBadRequest, INSTALL, f)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if f.AdminPasswd != f.AdminConfirmPasswd {
|
if f.AdminPasswd != f.AdminConfirmPasswd {
|
||||||
c.FormErr("Admin", "AdminPasswd")
|
c.FormErr("Admin", "AdminPasswd")
|
||||||
c.RenderWithErr(c.Tr("form.password_not_match"), INSTALL, f)
|
c.RenderWithErr(c.Tr("form.password_not_match"), http.StatusBadRequest, INSTALL, f)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -367,21 +367,21 @@ func InstallPost(c *context.Context, f form.Install) {
|
|||||||
cfg.Section("security").Key("INSTALL_LOCK").SetValue("true")
|
cfg.Section("security").Key("INSTALL_LOCK").SetValue("true")
|
||||||
secretKey, err := strutil.RandomChars(15)
|
secretKey, err := strutil.RandomChars(15)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.RenderWithErr(c.Tr("install.secret_key_failed", err), INSTALL, &f)
|
c.RenderWithErr(c.Tr("install.secret_key_failed", err), http.StatusInternalServerError, INSTALL, &f)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
cfg.Section("security").Key("SECRET_KEY").SetValue(secretKey)
|
cfg.Section("security").Key("SECRET_KEY").SetValue(secretKey)
|
||||||
|
|
||||||
_ = os.MkdirAll(filepath.Dir(conf.CustomConf), os.ModePerm)
|
_ = os.MkdirAll(filepath.Dir(conf.CustomConf), os.ModePerm)
|
||||||
if err := cfg.SaveTo(conf.CustomConf); err != nil {
|
if err := cfg.SaveTo(conf.CustomConf); err != nil {
|
||||||
c.RenderWithErr(c.Tr("install.save_config_failed", err), INSTALL, &f)
|
c.RenderWithErr(c.Tr("install.save_config_failed", err), http.StatusInternalServerError, INSTALL, &f)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: We reuse the current value because this handler does not have access to CLI flags.
|
// NOTE: We reuse the current value because this handler does not have access to CLI flags.
|
||||||
err = GlobalInit(conf.CustomConf)
|
err = GlobalInit(conf.CustomConf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.RenderWithErr(c.Tr("install.init_failed", err), INSTALL, &f)
|
c.RenderWithErr(c.Tr("install.init_failed", err), http.StatusInternalServerError, INSTALL, &f)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -401,7 +401,7 @@ func InstallPost(c *context.Context, f form.Install) {
|
|||||||
if !database.IsErrUserAlreadyExist(err) {
|
if !database.IsErrUserAlreadyExist(err) {
|
||||||
conf.Security.InstallLock = false
|
conf.Security.InstallLock = false
|
||||||
c.FormErr("AdminName", "AdminEmail")
|
c.FormErr("AdminName", "AdminEmail")
|
||||||
c.RenderWithErr(c.Tr("install.invalid_admin_setting", err), INSTALL, &f)
|
c.RenderWithErr(c.Tr("install.invalid_admin_setting", err), http.StatusBadRequest, INSTALL, &f)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ func (h *basicHandler) serveUpload(c *macaron.Context, repo *database.Repository
|
|||||||
s := h.DefaultStorager()
|
s := h.DefaultStorager()
|
||||||
written, err := s.Upload(oid, c.Req.Request.Body)
|
written, err := s.Upload(oid, c.Req.Request.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == lfsutil.ErrInvalidOID {
|
if err == lfsutil.ErrInvalidOID || err == lfsutil.ErrOIDMismatch {
|
||||||
responseJSON(c.Resp, http.StatusBadRequest, responseError{
|
responseJSON(c.Resp, http.StatusBadRequest, responseError{
|
||||||
Message: err.Error(),
|
Message: err.Error(),
|
||||||
})
|
})
|
||||||
@@ -105,8 +105,8 @@ func (h *basicHandler) serveUpload(c *macaron.Context, repo *database.Repository
|
|||||||
err = h.store.CreateLFSObject(c.Req.Context(), repo.ID, oid, written, s.Storage())
|
err = h.store.CreateLFSObject(c.Req.Context(), repo.ID, oid, written, s.Storage())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// NOTE: It is OK to leave the file when the whole operation failed
|
// NOTE: It is OK to leave the file when the whole operation failed
|
||||||
// with a DB error, a retry on client side can safely overwrite the
|
// with a DB error, a retry on client side will skip the upload as
|
||||||
// same file as OID is seen as unique to every file.
|
// the file already exists on disk.
|
||||||
internalServerError(c.Resp)
|
internalServerError(c.Resp)
|
||||||
log.Error("Failed to create object [repo_id: %d, oid: %s]: %v", repo.ID, oid, err)
|
log.Error("Failed to create object [repo_id: %d, oid: %s]: %v", repo.ID, oid, err)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ func RegisterRoutes(r *macaron.Router) {
|
|||||||
store: store,
|
store: store,
|
||||||
defaultStorage: lfsutil.Storage(conf.LFS.Storage),
|
defaultStorage: lfsutil.Storage(conf.LFS.Storage),
|
||||||
storagers: map[lfsutil.Storage]lfsutil.Storager{
|
storagers: map[lfsutil.Storage]lfsutil.Storager{
|
||||||
lfsutil.StorageLocal: &lfsutil.LocalStorage{Root: conf.LFS.ObjectsPath},
|
lfsutil.StorageLocal: &lfsutil.LocalStorage{Root: conf.LFS.ObjectsPath, TempDir: conf.LFS.ObjectsTempPath},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
r.Combo("/:oid", verifyOID()).
|
r.Combo("/:oid", verifyOID()).
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package org
|
package org
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
log "unknwon.dev/clog/v2"
|
log "unknwon.dev/clog/v2"
|
||||||
|
|
||||||
"gogs.io/gogs/internal/context"
|
"gogs.io/gogs/internal/context"
|
||||||
@@ -21,7 +23,7 @@ func CreatePost(c *context.Context, f form.CreateOrg) {
|
|||||||
c.Title("new_org")
|
c.Title("new_org")
|
||||||
|
|
||||||
if c.HasError() {
|
if c.HasError() {
|
||||||
c.Success(CREATE)
|
c.HTML(http.StatusBadRequest, CREATE)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,9 +37,9 @@ func CreatePost(c *context.Context, f form.CreateOrg) {
|
|||||||
c.Data["Err_OrgName"] = true
|
c.Data["Err_OrgName"] = true
|
||||||
switch {
|
switch {
|
||||||
case database.IsErrUserAlreadyExist(err):
|
case database.IsErrUserAlreadyExist(err):
|
||||||
c.RenderWithErr(c.Tr("form.org_name_been_taken"), CREATE, &f)
|
c.RenderWithErr(c.Tr("form.org_name_been_taken"), http.StatusUnprocessableEntity, CREATE, &f)
|
||||||
case database.IsErrNameNotAllowed(err):
|
case database.IsErrNameNotAllowed(err):
|
||||||
c.RenderWithErr(c.Tr("org.form.name_not_allowed", err.(database.ErrNameNotAllowed).Value()), CREATE, &f)
|
c.RenderWithErr(c.Tr("org.form.name_not_allowed", err.(database.ErrNameNotAllowed).Value()), http.StatusBadRequest, CREATE, &f)
|
||||||
default:
|
default:
|
||||||
c.Error(err, "create organization")
|
c.Error(err, "create organization")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package org
|
package org
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
log "unknwon.dev/clog/v2"
|
log "unknwon.dev/clog/v2"
|
||||||
|
|
||||||
"gogs.io/gogs/internal/auth"
|
"gogs.io/gogs/internal/auth"
|
||||||
@@ -27,7 +29,7 @@ func SettingsPost(c *context.Context, f form.UpdateOrgSetting) {
|
|||||||
c.Data["PageIsSettingsOptions"] = true
|
c.Data["PageIsSettingsOptions"] = true
|
||||||
|
|
||||||
if c.HasError() {
|
if c.HasError() {
|
||||||
c.Success(tmplOrgSettingsOptions)
|
c.HTML(http.StatusBadRequest, tmplOrgSettingsOptions)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,18 +40,14 @@ func SettingsPost(c *context.Context, f form.UpdateOrgSetting) {
|
|||||||
err := database.Handle.Users().ChangeUsername(c.Req.Context(), c.Org.Organization.ID, f.Name)
|
err := database.Handle.Users().ChangeUsername(c.Req.Context(), c.Org.Organization.ID, f.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Data["OrgName"] = true
|
c.Data["OrgName"] = true
|
||||||
var msg string
|
|
||||||
switch {
|
switch {
|
||||||
case database.IsErrUserAlreadyExist(err):
|
case database.IsErrUserAlreadyExist(err):
|
||||||
msg = c.Tr("form.username_been_taken")
|
c.RenderWithErr(c.Tr("form.username_been_taken"), http.StatusUnprocessableEntity, tmplOrgSettingsOptions, &f)
|
||||||
case database.IsErrNameNotAllowed(err):
|
case database.IsErrNameNotAllowed(err):
|
||||||
msg = c.Tr("user.form.name_not_allowed", err.(database.ErrNameNotAllowed).Value())
|
c.RenderWithErr(c.Tr("user.form.name_not_allowed", err.(database.ErrNameNotAllowed).Value()), http.StatusBadRequest, tmplOrgSettingsOptions, &f)
|
||||||
default:
|
default:
|
||||||
c.Error(err, "change organization name")
|
c.Error(err, "change organization name")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c.RenderWithErr(msg, tmplOrgSettingsOptions, &f)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,7 +102,7 @@ func SettingsDelete(c *context.Context) {
|
|||||||
if c.Req.Method == "POST" {
|
if c.Req.Method == "POST" {
|
||||||
if _, err := database.Handle.Users().Authenticate(c.Req.Context(), c.User.Name, c.Query("password"), c.User.LoginSource); err != nil {
|
if _, err := database.Handle.Users().Authenticate(c.Req.Context(), c.User.Name, c.Query("password"), c.User.LoginSource); err != nil {
|
||||||
if auth.IsErrBadCredentials(err) {
|
if auth.IsErrBadCredentials(err) {
|
||||||
c.RenderWithErr(c.Tr("form.enterred_invalid_password"), tmplOrgSettingsDelete, nil)
|
c.RenderWithErr(c.Tr("form.enterred_invalid_password"), http.StatusUnauthorized, tmplOrgSettingsDelete, nil)
|
||||||
} else {
|
} else {
|
||||||
c.Error(err, "authenticate user")
|
c.Error(err, "authenticate user")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ func NewTeamPost(c *context.Context, f form.CreateTeam) {
|
|||||||
c.Data["Team"] = t
|
c.Data["Team"] = t
|
||||||
|
|
||||||
if c.HasError() {
|
if c.HasError() {
|
||||||
c.Success(tmplOrgTeamNew)
|
c.HTML(http.StatusBadRequest, tmplOrgTeamNew)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,9 +167,9 @@ func NewTeamPost(c *context.Context, f form.CreateTeam) {
|
|||||||
c.Data["Err_TeamName"] = true
|
c.Data["Err_TeamName"] = true
|
||||||
switch {
|
switch {
|
||||||
case database.IsErrTeamAlreadyExist(err):
|
case database.IsErrTeamAlreadyExist(err):
|
||||||
c.RenderWithErr(c.Tr("form.team_name_been_taken"), tmplOrgTeamNew, &f)
|
c.RenderWithErr(c.Tr("form.team_name_been_taken"), http.StatusUnprocessableEntity, tmplOrgTeamNew, &f)
|
||||||
case database.IsErrNameNotAllowed(err):
|
case database.IsErrNameNotAllowed(err):
|
||||||
c.RenderWithErr(c.Tr("org.form.team_name_not_allowed", err.(database.ErrNameNotAllowed).Value()), tmplOrgTeamNew, &f)
|
c.RenderWithErr(c.Tr("org.form.team_name_not_allowed", err.(database.ErrNameNotAllowed).Value()), http.StatusBadRequest, tmplOrgTeamNew, &f)
|
||||||
default:
|
default:
|
||||||
c.Error(err, "new team")
|
c.Error(err, "new team")
|
||||||
}
|
}
|
||||||
@@ -214,7 +214,7 @@ func EditTeamPost(c *context.Context, f form.CreateTeam) {
|
|||||||
c.Data["Team"] = t
|
c.Data["Team"] = t
|
||||||
|
|
||||||
if c.HasError() {
|
if c.HasError() {
|
||||||
c.Success(tmplOrgTeamNew)
|
c.HTML(http.StatusBadRequest, tmplOrgTeamNew)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,7 +245,7 @@ func EditTeamPost(c *context.Context, f form.CreateTeam) {
|
|||||||
c.Data["Err_TeamName"] = true
|
c.Data["Err_TeamName"] = true
|
||||||
switch {
|
switch {
|
||||||
case database.IsErrTeamAlreadyExist(err):
|
case database.IsErrTeamAlreadyExist(err):
|
||||||
c.RenderWithErr(c.Tr("form.team_name_been_taken"), tmplOrgTeamNew, &f)
|
c.RenderWithErr(c.Tr("form.team_name_been_taken"), http.StatusUnprocessableEntity, tmplOrgTeamNew, &f)
|
||||||
default:
|
default:
|
||||||
c.Error(err, "update team")
|
c.Error(err, "update team")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -118,6 +118,21 @@ func DeleteBranchPost(c *context.Context) {
|
|||||||
if !c.Repo.GitRepo.HasBranch(branchName) {
|
if !c.Repo.GitRepo.HasBranch(branchName) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if branchName == c.Repo.Repository.DefaultBranch {
|
||||||
|
c.Flash.Error(c.Tr("repo.branches.default_deletion_not_allowed"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
protectBranch, err := database.GetProtectBranchOfRepoByName(c.Repo.Repository.ID, branchName)
|
||||||
|
if err != nil && !database.IsErrBranchNotExist(err) {
|
||||||
|
log.Error("Failed to get protected branch %q: %v", branchName, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if protectBranch != nil && protectBranch.Protected {
|
||||||
|
c.Flash.Error(c.Tr("repo.branches.protected_deletion_not_allowed"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if len(commitID) > 0 {
|
if len(commitID) > 0 {
|
||||||
branchCommitID, err := c.Repo.GitRepo.BranchCommitID(branchName)
|
branchCommitID, err := c.Repo.GitRepo.BranchCommitID(branchName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -153,20 +153,20 @@ func editFilePost(c *context.Context, f form.EditRepoFile, isNewFile bool) {
|
|||||||
c.Data["PreviewableFileModes"] = strings.Join(conf.Repository.Editor.PreviewableFileModes, ",")
|
c.Data["PreviewableFileModes"] = strings.Join(conf.Repository.Editor.PreviewableFileModes, ",")
|
||||||
|
|
||||||
if c.HasError() {
|
if c.HasError() {
|
||||||
c.Success(tmplEditorEdit)
|
c.HTML(http.StatusBadRequest, tmplEditorEdit)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if f.TreePath == "" {
|
if f.TreePath == "" {
|
||||||
c.FormErr("TreePath")
|
c.FormErr("TreePath")
|
||||||
c.RenderWithErr(c.Tr("repo.editor.filename_cannot_be_empty"), tmplEditorEdit, &f)
|
c.RenderWithErr(c.Tr("repo.editor.filename_cannot_be_empty"), http.StatusBadRequest, tmplEditorEdit, &f)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if oldBranchName != branchName {
|
if oldBranchName != branchName {
|
||||||
if _, err := c.Repo.Repository.GetBranch(branchName); err == nil {
|
if _, err := c.Repo.Repository.GetBranch(branchName); err == nil {
|
||||||
c.FormErr("NewBranchName")
|
c.FormErr("NewBranchName")
|
||||||
c.RenderWithErr(c.Tr("repo.editor.branch_already_exists", branchName), tmplEditorEdit, &f)
|
c.RenderWithErr(c.Tr("repo.editor.branch_already_exists", branchName), http.StatusUnprocessableEntity, tmplEditorEdit, &f)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -187,18 +187,18 @@ func editFilePost(c *context.Context, f form.EditRepoFile, isNewFile bool) {
|
|||||||
if index != len(treeNames)-1 {
|
if index != len(treeNames)-1 {
|
||||||
if !entry.IsTree() {
|
if !entry.IsTree() {
|
||||||
c.FormErr("TreePath")
|
c.FormErr("TreePath")
|
||||||
c.RenderWithErr(c.Tr("repo.editor.directory_is_a_file", part), tmplEditorEdit, &f)
|
c.RenderWithErr(c.Tr("repo.editor.directory_is_a_file", part), http.StatusUnprocessableEntity, tmplEditorEdit, &f)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 🚨 SECURITY: Do not allow editing if the target file is a symlink.
|
// 🚨 SECURITY: Do not allow editing if the target file is a symlink.
|
||||||
if entry.IsSymlink() {
|
if entry.IsSymlink() {
|
||||||
c.FormErr("TreePath")
|
c.FormErr("TreePath")
|
||||||
c.RenderWithErr(c.Tr("repo.editor.file_is_a_symlink", part), tmplEditorEdit, &f)
|
c.RenderWithErr(c.Tr("repo.editor.file_is_a_symlink", part), http.StatusUnprocessableEntity, tmplEditorEdit, &f)
|
||||||
return
|
return
|
||||||
} else if entry.IsTree() {
|
} else if entry.IsTree() {
|
||||||
c.FormErr("TreePath")
|
c.FormErr("TreePath")
|
||||||
c.RenderWithErr(c.Tr("repo.editor.filename_is_a_directory", part), tmplEditorEdit, &f)
|
c.RenderWithErr(c.Tr("repo.editor.filename_is_a_directory", part), http.StatusUnprocessableEntity, tmplEditorEdit, &f)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -209,7 +209,7 @@ func editFilePost(c *context.Context, f form.EditRepoFile, isNewFile bool) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
if gitutil.IsErrRevisionNotExist(err) {
|
if gitutil.IsErrRevisionNotExist(err) {
|
||||||
c.FormErr("TreePath")
|
c.FormErr("TreePath")
|
||||||
c.RenderWithErr(c.Tr("repo.editor.file_editing_no_longer_exists", oldTreePath), tmplEditorEdit, &f)
|
c.RenderWithErr(c.Tr("repo.editor.file_editing_no_longer_exists", oldTreePath), http.StatusNotFound, tmplEditorEdit, &f)
|
||||||
} else {
|
} else {
|
||||||
c.Error(err, "get tree entry")
|
c.Error(err, "get tree entry")
|
||||||
}
|
}
|
||||||
@@ -219,7 +219,7 @@ func editFilePost(c *context.Context, f form.EditRepoFile, isNewFile bool) {
|
|||||||
// 🚨 SECURITY: Do not allow editing if the old file is a symlink.
|
// 🚨 SECURITY: Do not allow editing if the old file is a symlink.
|
||||||
if entry.IsSymlink() {
|
if entry.IsSymlink() {
|
||||||
c.FormErr("TreePath")
|
c.FormErr("TreePath")
|
||||||
c.RenderWithErr(c.Tr("repo.editor.file_is_a_symlink", oldTreePath), tmplEditorEdit, &f)
|
c.RenderWithErr(c.Tr("repo.editor.file_is_a_symlink", oldTreePath), http.StatusUnprocessableEntity, tmplEditorEdit, &f)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,7 +232,7 @@ func editFilePost(c *context.Context, f form.EditRepoFile, isNewFile bool) {
|
|||||||
|
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
if file == f.TreePath {
|
if file == f.TreePath {
|
||||||
c.RenderWithErr(c.Tr("repo.editor.file_changed_while_editing", c.Repo.RepoLink+"/compare/"+lastCommit+"..."+c.Repo.CommitID), tmplEditorEdit, &f)
|
c.RenderWithErr(c.Tr("repo.editor.file_changed_while_editing", c.Repo.RepoLink+"/compare/"+lastCommit+"..."+c.Repo.CommitID), http.StatusConflict, tmplEditorEdit, &f)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -250,7 +250,7 @@ func editFilePost(c *context.Context, f form.EditRepoFile, isNewFile bool) {
|
|||||||
}
|
}
|
||||||
if entry != nil {
|
if entry != nil {
|
||||||
c.FormErr("TreePath")
|
c.FormErr("TreePath")
|
||||||
c.RenderWithErr(c.Tr("repo.editor.file_already_exists", f.TreePath), tmplEditorEdit, &f)
|
c.RenderWithErr(c.Tr("repo.editor.file_already_exists", f.TreePath), http.StatusUnprocessableEntity, tmplEditorEdit, &f)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -280,7 +280,7 @@ func editFilePost(c *context.Context, f form.EditRepoFile, isNewFile bool) {
|
|||||||
}); err != nil {
|
}); err != nil {
|
||||||
log.Error("Failed to update repo file: %v", err)
|
log.Error("Failed to update repo file: %v", err)
|
||||||
c.FormErr("TreePath")
|
c.FormErr("TreePath")
|
||||||
c.RenderWithErr(c.Tr("repo.editor.fail_to_update_file", f.TreePath, errInternalServerError), tmplEditorEdit, &f)
|
c.RenderWithErr(c.Tr("repo.editor.fail_to_update_file", f.TreePath, errInternalServerError), http.StatusInternalServerError, tmplEditorEdit, &f)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -358,14 +358,14 @@ func DeleteFilePost(c *context.Context, f form.DeleteRepoFile) {
|
|||||||
c.Data["new_branch_name"] = branchName
|
c.Data["new_branch_name"] = branchName
|
||||||
|
|
||||||
if c.HasError() {
|
if c.HasError() {
|
||||||
c.Success(tmplEditorDelete)
|
c.HTML(http.StatusBadRequest, tmplEditorDelete)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if oldBranchName != branchName {
|
if oldBranchName != branchName {
|
||||||
if _, err := c.Repo.Repository.GetBranch(branchName); err == nil {
|
if _, err := c.Repo.Repository.GetBranch(branchName); err == nil {
|
||||||
c.FormErr("NewBranchName")
|
c.FormErr("NewBranchName")
|
||||||
c.RenderWithErr(c.Tr("repo.editor.branch_already_exists", branchName), tmplEditorDelete, &f)
|
c.RenderWithErr(c.Tr("repo.editor.branch_already_exists", branchName), http.StatusUnprocessableEntity, tmplEditorDelete, &f)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -388,7 +388,7 @@ func DeleteFilePost(c *context.Context, f form.DeleteRepoFile) {
|
|||||||
Message: message,
|
Message: message,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
log.Error("Failed to delete repo file: %v", err)
|
log.Error("Failed to delete repo file: %v", err)
|
||||||
c.RenderWithErr(c.Tr("repo.editor.fail_to_delete_file", c.Repo.TreePath, errInternalServerError), tmplEditorDelete, &f)
|
c.RenderWithErr(c.Tr("repo.editor.fail_to_delete_file", c.Repo.TreePath, errInternalServerError), http.StatusInternalServerError, tmplEditorDelete, &f)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -456,14 +456,14 @@ func UploadFilePost(c *context.Context, f form.UploadRepoFile) {
|
|||||||
c.Data["new_branch_name"] = branchName
|
c.Data["new_branch_name"] = branchName
|
||||||
|
|
||||||
if c.HasError() {
|
if c.HasError() {
|
||||||
c.Success(tmplEditorUpload)
|
c.HTML(http.StatusBadRequest, tmplEditorUpload)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if oldBranchName != branchName {
|
if oldBranchName != branchName {
|
||||||
if _, err := c.Repo.Repository.GetBranch(branchName); err == nil {
|
if _, err := c.Repo.Repository.GetBranch(branchName); err == nil {
|
||||||
c.FormErr("NewBranchName")
|
c.FormErr("NewBranchName")
|
||||||
c.RenderWithErr(c.Tr("repo.editor.branch_already_exists", branchName), tmplEditorUpload, &f)
|
c.RenderWithErr(c.Tr("repo.editor.branch_already_exists", branchName), http.StatusUnprocessableEntity, tmplEditorUpload, &f)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -485,7 +485,7 @@ func UploadFilePost(c *context.Context, f form.UploadRepoFile) {
|
|||||||
// User can only upload files to a directory.
|
// User can only upload files to a directory.
|
||||||
if !entry.IsTree() {
|
if !entry.IsTree() {
|
||||||
c.FormErr("TreePath")
|
c.FormErr("TreePath")
|
||||||
c.RenderWithErr(c.Tr("repo.editor.directory_is_a_file", part), tmplEditorUpload, &f)
|
c.RenderWithErr(c.Tr("repo.editor.directory_is_a_file", part), http.StatusUnprocessableEntity, tmplEditorUpload, &f)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -510,7 +510,7 @@ func UploadFilePost(c *context.Context, f form.UploadRepoFile) {
|
|||||||
}); err != nil {
|
}); err != nil {
|
||||||
log.Error("Failed to upload files: %v", err)
|
log.Error("Failed to upload files: %v", err)
|
||||||
c.FormErr("TreePath")
|
c.FormErr("TreePath")
|
||||||
c.RenderWithErr(c.Tr("repo.editor.unable_to_upload_files", f.TreePath, errInternalServerError), tmplEditorUpload, &f)
|
c.RenderWithErr(c.Tr("repo.editor.unable_to_upload_files", f.TreePath, errInternalServerError), http.StatusInternalServerError, tmplEditorUpload, &f)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -409,7 +409,7 @@ func NewIssuePost(c *context.Context, f form.NewIssue) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if c.HasError() {
|
if c.HasError() {
|
||||||
c.Success(tmplRepoIssueNew)
|
c.HTML(http.StatusBadRequest, tmplRepoIssueNew)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -926,6 +926,17 @@ func UpdateCommentContent(c *context.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
issue, err := database.GetIssueByID(comment.IssueID)
|
||||||
|
if err != nil {
|
||||||
|
c.NotFoundOrError(err, "get issue by ID")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if issue.RepoID != c.Repo.Repository.ID {
|
||||||
|
c.NotFound()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if c.UserID() != comment.PosterID && !c.Repo.IsAdmin() {
|
if c.UserID() != comment.PosterID && !c.Repo.IsAdmin() {
|
||||||
c.NotFound()
|
c.NotFound()
|
||||||
return
|
return
|
||||||
@@ -959,6 +970,17 @@ func DeleteComment(c *context.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
issue, err := database.GetIssueByID(comment.IssueID)
|
||||||
|
if err != nil {
|
||||||
|
c.NotFoundOrError(err, "get issue by ID")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if issue.RepoID != c.Repo.Repository.ID {
|
||||||
|
c.NotFound()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if c.UserID() != comment.PosterID && !c.Repo.IsAdmin() {
|
if c.UserID() != comment.PosterID && !c.Repo.IsAdmin() {
|
||||||
c.NotFound()
|
c.NotFound()
|
||||||
return
|
return
|
||||||
@@ -1034,9 +1056,9 @@ func NewLabel(c *context.Context, f form.CreateLabel) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func UpdateLabel(c *context.Context, f form.CreateLabel) {
|
func UpdateLabel(c *context.Context, f form.CreateLabel) {
|
||||||
l, err := database.GetLabelByID(f.ID)
|
l, err := database.GetLabelOfRepoByID(c.Repo.Repository.ID, f.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.NotFoundOrError(err, "get label by ID")
|
c.NotFoundOrError(err, "get label of repository by ID")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1126,7 +1148,7 @@ func NewMilestonePost(c *context.Context, f form.CreateMilestone) {
|
|||||||
c.Data["DateLang"] = conf.I18n.DateLang(c.Language())
|
c.Data["DateLang"] = conf.I18n.DateLang(c.Language())
|
||||||
|
|
||||||
if c.HasError() {
|
if c.HasError() {
|
||||||
c.Success(tmplRepoIssueMilestoneNew)
|
c.HTML(http.StatusBadRequest, tmplRepoIssueMilestoneNew)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1136,7 +1158,7 @@ func NewMilestonePost(c *context.Context, f form.CreateMilestone) {
|
|||||||
deadline, err := time.ParseInLocation("2006-01-02", f.Deadline, time.Local)
|
deadline, err := time.ParseInLocation("2006-01-02", f.Deadline, time.Local)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Data["Err_Deadline"] = true
|
c.Data["Err_Deadline"] = true
|
||||||
c.RenderWithErr(c.Tr("repo.milestones.invalid_due_date_format"), tmplRepoIssueMilestoneNew, &f)
|
c.RenderWithErr(c.Tr("repo.milestones.invalid_due_date_format"), http.StatusBadRequest, tmplRepoIssueMilestoneNew, &f)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1182,7 +1204,7 @@ func EditMilestonePost(c *context.Context, f form.CreateMilestone) {
|
|||||||
c.Data["DateLang"] = conf.I18n.DateLang(c.Language())
|
c.Data["DateLang"] = conf.I18n.DateLang(c.Language())
|
||||||
|
|
||||||
if c.HasError() {
|
if c.HasError() {
|
||||||
c.Success(tmplRepoIssueMilestoneNew)
|
c.HTML(http.StatusBadRequest, tmplRepoIssueMilestoneNew)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1192,7 +1214,7 @@ func EditMilestonePost(c *context.Context, f form.CreateMilestone) {
|
|||||||
deadline, err := time.ParseInLocation("2006-01-02", f.Deadline, time.Local)
|
deadline, err := time.ParseInLocation("2006-01-02", f.Deadline, time.Local)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Data["Err_Deadline"] = true
|
c.Data["Err_Deadline"] = true
|
||||||
c.RenderWithErr(c.Tr("repo.milestones.invalid_due_date_format"), tmplRepoIssueMilestoneNew, &f)
|
c.RenderWithErr(c.Tr("repo.milestones.invalid_due_date_format"), http.StatusBadRequest, tmplRepoIssueMilestoneNew, &f)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ func ForkPost(c *context.Context, f form.CreateRepo) {
|
|||||||
c.Data["ContextUser"] = ctxUser
|
c.Data["ContextUser"] = ctxUser
|
||||||
|
|
||||||
if c.HasError() {
|
if c.HasError() {
|
||||||
c.Success(tmplRepoPullsFork)
|
c.HTML(http.StatusBadRequest, tmplRepoPullsFork)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,7 +129,7 @@ func ForkPost(c *context.Context, f form.CreateRepo) {
|
|||||||
|
|
||||||
// Cannot fork to same owner
|
// Cannot fork to same owner
|
||||||
if ctxUser.ID == baseRepo.OwnerID {
|
if ctxUser.ID == baseRepo.OwnerID {
|
||||||
c.RenderWithErr(c.Tr("repo.settings.cannot_fork_to_same_owner"), tmplRepoPullsFork, &f)
|
c.RenderWithErr(c.Tr("repo.settings.cannot_fork_to_same_owner"), http.StatusUnprocessableEntity, tmplRepoPullsFork, &f)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,11 +138,11 @@ func ForkPost(c *context.Context, f form.CreateRepo) {
|
|||||||
c.Data["Err_RepoName"] = true
|
c.Data["Err_RepoName"] = true
|
||||||
switch {
|
switch {
|
||||||
case database.IsErrReachLimitOfRepo(err):
|
case database.IsErrReachLimitOfRepo(err):
|
||||||
c.RenderWithErr(c.Tr("repo.form.reach_limit_of_creation", err.(database.ErrReachLimitOfRepo).Limit), tmplRepoPullsFork, &f)
|
c.RenderWithErr(c.Tr("repo.form.reach_limit_of_creation", err.(database.ErrReachLimitOfRepo).Limit), http.StatusForbidden, tmplRepoPullsFork, &f)
|
||||||
case database.IsErrRepoAlreadyExist(err):
|
case database.IsErrRepoAlreadyExist(err):
|
||||||
c.RenderWithErr(c.Tr("repo.settings.new_owner_has_same_repo"), tmplRepoPullsFork, &f)
|
c.RenderWithErr(c.Tr("repo.settings.new_owner_has_same_repo"), http.StatusUnprocessableEntity, tmplRepoPullsFork, &f)
|
||||||
case database.IsErrNameNotAllowed(err):
|
case database.IsErrNameNotAllowed(err):
|
||||||
c.RenderWithErr(c.Tr("repo.form.name_not_allowed", err.(database.ErrNameNotAllowed).Value()), tmplRepoPullsFork, &f)
|
c.RenderWithErr(c.Tr("repo.form.name_not_allowed", err.(database.ErrNameNotAllowed).Value()), http.StatusBadRequest, tmplRepoPullsFork, &f)
|
||||||
default:
|
default:
|
||||||
c.Error(err, "fork repository")
|
c.Error(err, "fork repository")
|
||||||
}
|
}
|
||||||
@@ -433,23 +433,23 @@ func MergePullRequest(c *context.Context) {
|
|||||||
func ParseCompareInfo(c *context.Context) (*database.User, *database.Repository, *git.Repository, *gitutil.PullRequestMeta, string, string) {
|
func ParseCompareInfo(c *context.Context) (*database.User, *database.Repository, *git.Repository, *gitutil.PullRequestMeta, string, string) {
|
||||||
baseRepo := c.Repo.Repository
|
baseRepo := c.Repo.Repository
|
||||||
|
|
||||||
// Get compared branches information
|
// Get compared refs information
|
||||||
// format: <base branch>...[<head repo>:]<head branch>
|
// format: <base ref>...[<head repo>:]<head ref>
|
||||||
// base<-head: master...head:feature
|
// base<-head: master...head:feature
|
||||||
// same repo: master...feature
|
// same repo: master...feature
|
||||||
infos := strings.Split(c.Params("*"), "...")
|
infos := strings.Split(c.Params("*"), "...")
|
||||||
if len(infos) != 2 {
|
if len(infos) != 2 {
|
||||||
log.Trace("ParseCompareInfo[%d]: not enough compared branches information %s", baseRepo.ID, infos)
|
log.Trace("ParseCompareInfo[%d]: not enough compared refs information %s", baseRepo.ID, infos)
|
||||||
c.NotFound()
|
c.NotFound()
|
||||||
return nil, nil, nil, nil, "", ""
|
return nil, nil, nil, nil, "", ""
|
||||||
}
|
}
|
||||||
|
|
||||||
baseBranch := infos[0]
|
baseRef := infos[0]
|
||||||
c.Data["BaseBranch"] = baseBranch
|
c.Data["BaseBranch"] = baseRef
|
||||||
|
|
||||||
var (
|
var (
|
||||||
headUser *database.User
|
headUser *database.User
|
||||||
headBranch string
|
headRef string
|
||||||
isSameRepo bool
|
isSameRepo bool
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
@@ -459,7 +459,7 @@ func ParseCompareInfo(c *context.Context) (*database.User, *database.Repository,
|
|||||||
if len(headInfos) == 1 {
|
if len(headInfos) == 1 {
|
||||||
isSameRepo = true
|
isSameRepo = true
|
||||||
headUser = c.Repo.Owner
|
headUser = c.Repo.Owner
|
||||||
headBranch = headInfos[0]
|
headRef = headInfos[0]
|
||||||
|
|
||||||
} else if len(headInfos) == 2 {
|
} else if len(headInfos) == 2 {
|
||||||
headUser, err = database.Handle.Users().GetByUsername(c.Req.Context(), headInfos[0])
|
headUser, err = database.Handle.Users().GetByUsername(c.Req.Context(), headInfos[0])
|
||||||
@@ -467,7 +467,7 @@ func ParseCompareInfo(c *context.Context) (*database.User, *database.Repository,
|
|||||||
c.NotFoundOrError(err, "get user by name")
|
c.NotFoundOrError(err, "get user by name")
|
||||||
return nil, nil, nil, nil, "", ""
|
return nil, nil, nil, nil, "", ""
|
||||||
}
|
}
|
||||||
headBranch = headInfos[1]
|
headRef = headInfos[1]
|
||||||
isSameRepo = headUser.ID == baseRepo.OwnerID
|
isSameRepo = headUser.ID == baseRepo.OwnerID
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@@ -475,11 +475,11 @@ func ParseCompareInfo(c *context.Context) (*database.User, *database.Repository,
|
|||||||
return nil, nil, nil, nil, "", ""
|
return nil, nil, nil, nil, "", ""
|
||||||
}
|
}
|
||||||
c.Data["HeadUser"] = headUser
|
c.Data["HeadUser"] = headUser
|
||||||
c.Data["HeadBranch"] = headBranch
|
c.Data["HeadBranch"] = headRef
|
||||||
c.Repo.PullRequest.SameRepo = isSameRepo
|
c.Repo.PullRequest.SameRepo = isSameRepo
|
||||||
|
|
||||||
// Check if base branch is valid.
|
// Check if base ref is valid.
|
||||||
if !c.Repo.GitRepo.HasBranch(baseBranch) {
|
if _, err := c.Repo.GitRepo.RevParse(baseRef); err != nil {
|
||||||
c.NotFound()
|
c.NotFound()
|
||||||
return nil, nil, nil, nil, "", ""
|
return nil, nil, nil, nil, "", ""
|
||||||
}
|
}
|
||||||
@@ -528,8 +528,8 @@ func ParseCompareInfo(c *context.Context) (*database.User, *database.Repository,
|
|||||||
return nil, nil, nil, nil, "", ""
|
return nil, nil, nil, nil, "", ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if head branch is valid.
|
// Check if head ref is valid.
|
||||||
if !headGitRepo.HasBranch(headBranch) {
|
if _, err := headGitRepo.RevParse(headRef); err != nil {
|
||||||
c.NotFound()
|
c.NotFound()
|
||||||
return nil, nil, nil, nil, "", ""
|
return nil, nil, nil, nil, "", ""
|
||||||
}
|
}
|
||||||
@@ -542,7 +542,7 @@ func ParseCompareInfo(c *context.Context) (*database.User, *database.Repository,
|
|||||||
c.Data["HeadBranches"] = headBranches
|
c.Data["HeadBranches"] = headBranches
|
||||||
|
|
||||||
baseRepoPath := database.RepoPath(baseRepo.Owner.Name, baseRepo.Name)
|
baseRepoPath := database.RepoPath(baseRepo.Owner.Name, baseRepo.Name)
|
||||||
meta, err := gitutil.Module.PullRequestMeta(headGitRepo.Path(), baseRepoPath, headBranch, baseBranch)
|
meta, err := gitutil.Module.PullRequestMeta(headGitRepo.Path(), baseRepoPath, headRef, baseRef)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if gitutil.IsErrNoMergeBase(err) {
|
if gitutil.IsErrNoMergeBase(err) {
|
||||||
c.Data["IsNoMergeBase"] = true
|
c.Data["IsNoMergeBase"] = true
|
||||||
@@ -554,7 +554,7 @@ func ParseCompareInfo(c *context.Context) (*database.User, *database.Repository,
|
|||||||
}
|
}
|
||||||
c.Data["BeforeCommitID"] = meta.MergeBase
|
c.Data["BeforeCommitID"] = meta.MergeBase
|
||||||
|
|
||||||
return headUser, headRepo, headGitRepo, meta, baseBranch, headBranch
|
return headUser, headRepo, headGitRepo, meta, baseRef, headRef
|
||||||
}
|
}
|
||||||
|
|
||||||
func PrepareCompareDiff(
|
func PrepareCompareDiff(
|
||||||
@@ -563,7 +563,7 @@ func PrepareCompareDiff(
|
|||||||
headRepo *database.Repository,
|
headRepo *database.Repository,
|
||||||
headGitRepo *git.Repository,
|
headGitRepo *git.Repository,
|
||||||
meta *gitutil.PullRequestMeta,
|
meta *gitutil.PullRequestMeta,
|
||||||
headBranch string,
|
headRef string,
|
||||||
) bool {
|
) bool {
|
||||||
var (
|
var (
|
||||||
repo = c.Repo.Repository
|
repo = c.Repo.Repository
|
||||||
@@ -573,9 +573,9 @@ func PrepareCompareDiff(
|
|||||||
// Get diff information.
|
// Get diff information.
|
||||||
c.Data["CommitRepoLink"] = headRepo.Link()
|
c.Data["CommitRepoLink"] = headRepo.Link()
|
||||||
|
|
||||||
headCommitID, err := headGitRepo.BranchCommitID(headBranch)
|
headCommitID, err := headGitRepo.RevParse(headRef)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Error(err, "get head branch commit ID")
|
c.Error(err, "get head commit ID")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
c.Data["AfterCommitID"] = headCommitID
|
c.Data["AfterCommitID"] = headCommitID
|
||||||
@@ -625,12 +625,12 @@ func CompareAndPullRequest(c *context.Context) {
|
|||||||
setTemplateIfExists(c, PullRequestTemplateKey, PullRequestTemplateCandidates)
|
setTemplateIfExists(c, PullRequestTemplateKey, PullRequestTemplateCandidates)
|
||||||
renderAttachmentSettings(c)
|
renderAttachmentSettings(c)
|
||||||
|
|
||||||
headUser, headRepo, headGitRepo, prInfo, baseBranch, headBranch := ParseCompareInfo(c)
|
headUser, headRepo, headGitRepo, prInfo, baseRef, headRef := ParseCompareInfo(c)
|
||||||
if c.Written() {
|
if c.Written() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
pr, err := database.GetUnmergedPullRequest(headRepo.ID, c.Repo.Repository.ID, headBranch, baseBranch)
|
pr, err := database.GetUnmergedPullRequest(headRepo.ID, c.Repo.Repository.ID, headRef, baseRef)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !database.IsErrPullRequestNotExist(err) {
|
if !database.IsErrPullRequestNotExist(err) {
|
||||||
c.Error(err, "get unmerged pull request")
|
c.Error(err, "get unmerged pull request")
|
||||||
@@ -643,7 +643,7 @@ func CompareAndPullRequest(c *context.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
nothingToCompare := PrepareCompareDiff(c, headUser, headRepo, headGitRepo, prInfo, headBranch)
|
nothingToCompare := PrepareCompareDiff(c, headUser, headRepo, headGitRepo, prInfo, headRef)
|
||||||
if c.Written() {
|
if c.Written() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -666,7 +666,7 @@ func CompareAndPullRequest(c *context.Context) {
|
|||||||
|
|
||||||
if c.Data[PullRequestTitleTemplateKey] != nil {
|
if c.Data[PullRequestTitleTemplateKey] != nil {
|
||||||
customTitle := c.Data[PullRequestTitleTemplateKey].(string)
|
customTitle := c.Data[PullRequestTitleTemplateKey].(string)
|
||||||
r := strings.NewReplacer("{{headBranch}}", headBranch, "{{baseBranch}}", baseBranch)
|
r := strings.NewReplacer("{{headBranch}}", headRef, "{{baseBranch}}", baseRef)
|
||||||
c.Data["title"] = r.Replace(customTitle)
|
c.Data["title"] = r.Replace(customTitle)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -685,7 +685,7 @@ func CompareAndPullRequestPost(c *context.Context, f form.NewIssue) {
|
|||||||
attachments []string
|
attachments []string
|
||||||
)
|
)
|
||||||
|
|
||||||
headUser, headRepo, headGitRepo, meta, baseBranch, headBranch := ParseCompareInfo(c)
|
headUser, headRepo, headGitRepo, meta, baseRef, headRef := ParseCompareInfo(c)
|
||||||
if c.Written() {
|
if c.Written() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -704,16 +704,16 @@ func CompareAndPullRequestPost(c *context.Context, f form.NewIssue) {
|
|||||||
|
|
||||||
// This stage is already stop creating new pull request, so it does not matter if it has
|
// This stage is already stop creating new pull request, so it does not matter if it has
|
||||||
// something to compare or not.
|
// something to compare or not.
|
||||||
PrepareCompareDiff(c, headUser, headRepo, headGitRepo, meta, headBranch)
|
PrepareCompareDiff(c, headUser, headRepo, headGitRepo, meta, headRef)
|
||||||
if c.Written() {
|
if c.Written() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Success(tmplRepoPullsCompare)
|
c.HTML(http.StatusBadRequest, tmplRepoPullsCompare)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
patch, err := headGitRepo.DiffBinary(meta.MergeBase, headBranch)
|
patch, err := headGitRepo.DiffBinary(meta.MergeBase, headRef)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Error(err, "get patch")
|
c.Error(err, "get patch")
|
||||||
return
|
return
|
||||||
@@ -734,8 +734,8 @@ func CompareAndPullRequestPost(c *context.Context, f form.NewIssue) {
|
|||||||
HeadRepoID: headRepo.ID,
|
HeadRepoID: headRepo.ID,
|
||||||
BaseRepoID: repo.ID,
|
BaseRepoID: repo.ID,
|
||||||
HeadUserName: headUser.Name,
|
HeadUserName: headUser.Name,
|
||||||
HeadBranch: headBranch,
|
HeadBranch: headRef,
|
||||||
BaseBranch: baseBranch,
|
BaseBranch: baseRef,
|
||||||
HeadRepo: headRepo,
|
HeadRepo: headRepo,
|
||||||
BaseRepo: repo,
|
BaseRepo: repo,
|
||||||
MergeBase: meta.MergeBase,
|
MergeBase: meta.MergeBase,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package repo
|
package repo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/cockroachdb/errors"
|
"github.com/cockroachdb/errors"
|
||||||
@@ -169,12 +170,12 @@ func NewReleasePost(c *context.Context, f form.NewRelease) {
|
|||||||
renderReleaseAttachmentSettings(c)
|
renderReleaseAttachmentSettings(c)
|
||||||
|
|
||||||
if c.HasError() {
|
if c.HasError() {
|
||||||
c.Success(tmplRepoReleaseNew)
|
c.HTML(http.StatusBadRequest, tmplRepoReleaseNew)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !c.Repo.GitRepo.HasBranch(f.Target) {
|
if !c.Repo.GitRepo.HasBranch(f.Target) {
|
||||||
c.RenderWithErr(c.Tr("form.target_branch_not_exist"), tmplRepoReleaseNew, &f)
|
c.RenderWithErr(c.Tr("form.target_branch_not_exist"), http.StatusUnprocessableEntity, tmplRepoReleaseNew, &f)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,9 +223,9 @@ func NewReleasePost(c *context.Context, f form.NewRelease) {
|
|||||||
c.Data["Err_TagName"] = true
|
c.Data["Err_TagName"] = true
|
||||||
switch {
|
switch {
|
||||||
case database.IsErrReleaseAlreadyExist(err):
|
case database.IsErrReleaseAlreadyExist(err):
|
||||||
c.RenderWithErr(c.Tr("repo.release.tag_name_already_exist"), tmplRepoReleaseNew, &f)
|
c.RenderWithErr(c.Tr("repo.release.tag_name_already_exist"), http.StatusUnprocessableEntity, tmplRepoReleaseNew, &f)
|
||||||
case database.IsErrInvalidTagName(err):
|
case database.IsErrInvalidTagName(err):
|
||||||
c.RenderWithErr(c.Tr("repo.release.tag_name_invalid"), tmplRepoReleaseNew, &f)
|
c.RenderWithErr(c.Tr("repo.release.tag_name_invalid"), http.StatusBadRequest, tmplRepoReleaseNew, &f)
|
||||||
default:
|
default:
|
||||||
c.Error(err, "new release")
|
c.Error(err, "new release")
|
||||||
}
|
}
|
||||||
@@ -280,7 +281,7 @@ func EditReleasePost(c *context.Context, f form.EditRelease) {
|
|||||||
c.Data["IsDraft"] = rel.IsDraft
|
c.Data["IsDraft"] = rel.IsDraft
|
||||||
|
|
||||||
if c.HasError() {
|
if c.HasError() {
|
||||||
c.Success(tmplRepoReleaseNew)
|
c.HTML(http.StatusBadRequest, tmplRepoReleaseNew)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -86,13 +86,13 @@ func Create(c *context.Context) {
|
|||||||
func handleCreateError(c *context.Context, err error, name, tpl string, form any) {
|
func handleCreateError(c *context.Context, err error, name, tpl string, form any) {
|
||||||
switch {
|
switch {
|
||||||
case database.IsErrReachLimitOfRepo(err):
|
case database.IsErrReachLimitOfRepo(err):
|
||||||
c.RenderWithErr(c.Tr("repo.form.reach_limit_of_creation", err.(database.ErrReachLimitOfRepo).Limit), tpl, form)
|
c.RenderWithErr(c.Tr("repo.form.reach_limit_of_creation", err.(database.ErrReachLimitOfRepo).Limit), http.StatusForbidden, tpl, form)
|
||||||
case database.IsErrRepoAlreadyExist(err):
|
case database.IsErrRepoAlreadyExist(err):
|
||||||
c.Data["Err_RepoName"] = true
|
c.Data["Err_RepoName"] = true
|
||||||
c.RenderWithErr(c.Tr("form.repo_name_been_taken"), tpl, form)
|
c.RenderWithErr(c.Tr("form.repo_name_been_taken"), http.StatusUnprocessableEntity, tpl, form)
|
||||||
case database.IsErrNameNotAllowed(err):
|
case database.IsErrNameNotAllowed(err):
|
||||||
c.Data["Err_RepoName"] = true
|
c.Data["Err_RepoName"] = true
|
||||||
c.RenderWithErr(c.Tr("repo.form.name_not_allowed", err.(database.ErrNameNotAllowed).Value()), tpl, form)
|
c.RenderWithErr(c.Tr("repo.form.name_not_allowed", err.(database.ErrNameNotAllowed).Value()), http.StatusBadRequest, tpl, form)
|
||||||
default:
|
default:
|
||||||
c.Error(err, name)
|
c.Error(err, name)
|
||||||
}
|
}
|
||||||
@@ -112,7 +112,7 @@ func CreatePost(c *context.Context, f form.CreateRepo) {
|
|||||||
c.Data["ContextUser"] = ctxUser
|
c.Data["ContextUser"] = ctxUser
|
||||||
|
|
||||||
if c.HasError() {
|
if c.HasError() {
|
||||||
c.Success(CREATE)
|
c.HTML(http.StatusBadRequest, CREATE)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,7 +166,7 @@ func MigratePost(c *context.Context, f form.MigrateRepo) {
|
|||||||
c.Data["ContextUser"] = ctxUser
|
c.Data["ContextUser"] = ctxUser
|
||||||
|
|
||||||
if c.HasError() {
|
if c.HasError() {
|
||||||
c.Success(MIGRATE)
|
c.HTML(http.StatusBadRequest, MIGRATE)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,13 +177,13 @@ func MigratePost(c *context.Context, f form.MigrateRepo) {
|
|||||||
addrErr := err.(database.ErrInvalidCloneAddr)
|
addrErr := err.(database.ErrInvalidCloneAddr)
|
||||||
switch {
|
switch {
|
||||||
case addrErr.IsURLError:
|
case addrErr.IsURLError:
|
||||||
c.RenderWithErr(c.Tr("repo.migrate.clone_address")+c.Tr("form.url_error"), MIGRATE, &f)
|
c.RenderWithErr(c.Tr("repo.migrate.clone_address")+c.Tr("form.url_error"), http.StatusBadRequest, MIGRATE, &f)
|
||||||
case addrErr.IsPermissionDenied:
|
case addrErr.IsPermissionDenied:
|
||||||
c.RenderWithErr(c.Tr("repo.migrate.permission_denied"), MIGRATE, &f)
|
c.RenderWithErr(c.Tr("repo.migrate.permission_denied"), http.StatusForbidden, MIGRATE, &f)
|
||||||
case addrErr.IsInvalidPath:
|
case addrErr.IsInvalidPath:
|
||||||
c.RenderWithErr(c.Tr("repo.migrate.invalid_local_path"), MIGRATE, &f)
|
c.RenderWithErr(c.Tr("repo.migrate.invalid_local_path"), http.StatusBadRequest, MIGRATE, &f)
|
||||||
case addrErr.IsBlockedLocalAddress:
|
case addrErr.IsBlockedLocalAddress:
|
||||||
c.RenderWithErr(c.Tr("repo.migrate.clone_address_resolved_to_blocked_local_address"), MIGRATE, &f)
|
c.RenderWithErr(c.Tr("repo.migrate.clone_address_resolved_to_blocked_local_address"), http.StatusForbidden, MIGRATE, &f)
|
||||||
default:
|
default:
|
||||||
c.Error(err, "unexpected error")
|
c.Error(err, "unexpected error")
|
||||||
}
|
}
|
||||||
@@ -216,11 +216,11 @@ func MigratePost(c *context.Context, f form.MigrateRepo) {
|
|||||||
if strings.Contains(err.Error(), "Authentication failed") ||
|
if strings.Contains(err.Error(), "Authentication failed") ||
|
||||||
strings.Contains(err.Error(), "could not read Username") {
|
strings.Contains(err.Error(), "could not read Username") {
|
||||||
c.Data["Err_Auth"] = true
|
c.Data["Err_Auth"] = true
|
||||||
c.RenderWithErr(c.Tr("form.auth_failed", database.HandleMirrorCredentials(err.Error(), true)), MIGRATE, &f)
|
c.RenderWithErr(c.Tr("form.auth_failed", database.HandleMirrorCredentials(err.Error(), true)), http.StatusUnauthorized, MIGRATE, &f)
|
||||||
return
|
return
|
||||||
} else if strings.Contains(err.Error(), "fatal:") {
|
} else if strings.Contains(err.Error(), "fatal:") {
|
||||||
c.Data["Err_CloneAddr"] = true
|
c.Data["Err_CloneAddr"] = true
|
||||||
c.RenderWithErr(c.Tr("repo.migrate.failed", database.HandleMirrorCredentials(err.Error(), true)), MIGRATE, &f)
|
c.RenderWithErr(c.Tr("repo.migrate.failed", database.HandleMirrorCredentials(err.Error(), true)), http.StatusInternalServerError, MIGRATE, &f)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package repo
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -49,7 +50,7 @@ func SettingsPost(c *context.Context, f form.RepoSetting) {
|
|||||||
switch c.Query("action") {
|
switch c.Query("action") {
|
||||||
case "update":
|
case "update":
|
||||||
if c.HasError() {
|
if c.HasError() {
|
||||||
c.Success(tmplRepoSettingsOptions)
|
c.HTML(http.StatusBadRequest, tmplRepoSettingsOptions)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,9 +64,9 @@ func SettingsPost(c *context.Context, f form.RepoSetting) {
|
|||||||
c.FormErr("RepoName")
|
c.FormErr("RepoName")
|
||||||
switch {
|
switch {
|
||||||
case database.IsErrRepoAlreadyExist(err):
|
case database.IsErrRepoAlreadyExist(err):
|
||||||
c.RenderWithErr(c.Tr("form.repo_name_been_taken"), tmplRepoSettingsOptions, &f)
|
c.RenderWithErr(c.Tr("form.repo_name_been_taken"), http.StatusUnprocessableEntity, tmplRepoSettingsOptions, &f)
|
||||||
case database.IsErrNameNotAllowed(err):
|
case database.IsErrNameNotAllowed(err):
|
||||||
c.RenderWithErr(c.Tr("repo.form.name_not_allowed", err.(database.ErrNameNotAllowed).Value()), tmplRepoSettingsOptions, &f)
|
c.RenderWithErr(c.Tr("repo.form.name_not_allowed", err.(database.ErrNameNotAllowed).Value()), http.StatusBadRequest, tmplRepoSettingsOptions, &f)
|
||||||
default:
|
default:
|
||||||
c.Error(err, "change repository name")
|
c.Error(err, "change repository name")
|
||||||
}
|
}
|
||||||
@@ -175,7 +176,7 @@ func SettingsPost(c *context.Context, f form.RepoSetting) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if repo.Name != f.RepoName {
|
if repo.Name != f.RepoName {
|
||||||
c.RenderWithErr(c.Tr("form.enterred_invalid_repo_name"), tmplRepoSettingsOptions, nil)
|
c.RenderWithErr(c.Tr("form.enterred_invalid_repo_name"), http.StatusBadRequest, tmplRepoSettingsOptions, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,7 +210,7 @@ func SettingsPost(c *context.Context, f form.RepoSetting) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if repo.Name != f.RepoName {
|
if repo.Name != f.RepoName {
|
||||||
c.RenderWithErr(c.Tr("form.enterred_invalid_repo_name"), tmplRepoSettingsOptions, nil)
|
c.RenderWithErr(c.Tr("form.enterred_invalid_repo_name"), http.StatusBadRequest, tmplRepoSettingsOptions, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,13 +223,13 @@ func SettingsPost(c *context.Context, f form.RepoSetting) {
|
|||||||
|
|
||||||
newOwner := c.Query("new_owner_name")
|
newOwner := c.Query("new_owner_name")
|
||||||
if !database.Handle.Users().IsUsernameUsed(c.Req.Context(), newOwner, c.Repo.Owner.ID) {
|
if !database.Handle.Users().IsUsernameUsed(c.Req.Context(), newOwner, c.Repo.Owner.ID) {
|
||||||
c.RenderWithErr(c.Tr("form.enterred_invalid_owner_name"), tmplRepoSettingsOptions, nil)
|
c.RenderWithErr(c.Tr("form.enterred_invalid_owner_name"), http.StatusBadRequest, tmplRepoSettingsOptions, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := database.TransferOwnership(c.User, newOwner, repo); err != nil {
|
if err := database.TransferOwnership(c.User, newOwner, repo); err != nil {
|
||||||
if database.IsErrRepoAlreadyExist(err) {
|
if database.IsErrRepoAlreadyExist(err) {
|
||||||
c.RenderWithErr(c.Tr("repo.settings.new_owner_has_same_repo"), tmplRepoSettingsOptions, nil)
|
c.RenderWithErr(c.Tr("repo.settings.new_owner_has_same_repo"), http.StatusUnprocessableEntity, tmplRepoSettingsOptions, nil)
|
||||||
} else {
|
} else {
|
||||||
c.Error(err, "transfer ownership")
|
c.Error(err, "transfer ownership")
|
||||||
}
|
}
|
||||||
@@ -244,7 +245,7 @@ func SettingsPost(c *context.Context, f form.RepoSetting) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if repo.Name != f.RepoName {
|
if repo.Name != f.RepoName {
|
||||||
c.RenderWithErr(c.Tr("form.enterred_invalid_repo_name"), tmplRepoSettingsOptions, nil)
|
c.RenderWithErr(c.Tr("form.enterred_invalid_repo_name"), http.StatusBadRequest, tmplRepoSettingsOptions, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,7 +271,7 @@ func SettingsPost(c *context.Context, f form.RepoSetting) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if repo.Name != f.RepoName {
|
if repo.Name != f.RepoName {
|
||||||
c.RenderWithErr(c.Tr("form.enterred_invalid_repo_name"), tmplRepoSettingsOptions, nil)
|
c.RenderWithErr(c.Tr("form.enterred_invalid_repo_name"), http.StatusBadRequest, tmplRepoSettingsOptions, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -436,7 +437,7 @@ func SettingsBranches(c *context.Context) {
|
|||||||
|
|
||||||
if c.Repo.Repository.IsBare {
|
if c.Repo.Repository.IsBare {
|
||||||
c.Flash.Info(c.Tr("repo.settings.branches_bare"), true)
|
c.Flash.Info(c.Tr("repo.settings.branches_bare"), true)
|
||||||
c.Success(tmplRepoSettingsBranches)
|
c.HTML(http.StatusUnprocessableEntity, tmplRepoSettingsBranches)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -652,7 +653,7 @@ func SettingsDeployKeysPost(c *context.Context, f form.AddSSHKey) {
|
|||||||
c.Data["Deploykeys"] = keys
|
c.Data["Deploykeys"] = keys
|
||||||
|
|
||||||
if c.HasError() {
|
if c.HasError() {
|
||||||
c.Success(tmplRepoSettingsDeployKeys)
|
c.HTML(http.StatusBadRequest, tmplRepoSettingsDeployKeys)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -675,10 +676,10 @@ func SettingsDeployKeysPost(c *context.Context, f form.AddSSHKey) {
|
|||||||
switch {
|
switch {
|
||||||
case database.IsErrKeyAlreadyExist(err):
|
case database.IsErrKeyAlreadyExist(err):
|
||||||
c.Data["Err_Content"] = true
|
c.Data["Err_Content"] = true
|
||||||
c.RenderWithErr(c.Tr("repo.settings.key_been_used"), tmplRepoSettingsDeployKeys, &f)
|
c.RenderWithErr(c.Tr("repo.settings.key_been_used"), http.StatusUnprocessableEntity, tmplRepoSettingsDeployKeys, &f)
|
||||||
case database.IsErrKeyNameAlreadyUsed(err):
|
case database.IsErrKeyNameAlreadyUsed(err):
|
||||||
c.Data["Err_Title"] = true
|
c.Data["Err_Title"] = true
|
||||||
c.RenderWithErr(c.Tr("repo.settings.key_name_used"), tmplRepoSettingsDeployKeys, &f)
|
c.RenderWithErr(c.Tr("repo.settings.key_name_used"), http.StatusUnprocessableEntity, tmplRepoSettingsDeployKeys, &f)
|
||||||
default:
|
default:
|
||||||
c.Error(err, "add deploy key")
|
c.Error(err, "add deploy key")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ func renderDirectory(c *context.Context, treeLink string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
entries, err := tree.Entries()
|
entries, err := tree.Entries(git.LsTreeOptions{Verbatim: true})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Error(err, "list entries")
|
c.Error(err, "list entries")
|
||||||
return
|
return
|
||||||
@@ -125,6 +125,7 @@ func renderFile(c *context.Context, entry *git.TreeEntry, treeLink, rawLink stri
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.Data["Title"] = blob.Name() + " - " + c.Data["Title"].(string)
|
||||||
c.Data["FileSize"] = blob.Size()
|
c.Data["FileSize"] = blob.Size()
|
||||||
c.Data["FileName"] = blob.Name()
|
c.Data["FileName"] = blob.Name()
|
||||||
c.Data["HighlightClass"] = highlight.FileNameToHighlightClass(blob.Name())
|
c.Data["HighlightClass"] = highlight.FileNameToHighlightClass(blob.Name())
|
||||||
@@ -180,7 +181,7 @@ func renderFile(c *context.Context, entry *git.TreeEntry, treeLink, rawLink stri
|
|||||||
|
|
||||||
output.Reset()
|
output.Reset()
|
||||||
for i := 0; i < len(lines); i++ {
|
for i := 0; i < len(lines); i++ {
|
||||||
output.WriteString(fmt.Sprintf(`<span id="L%d">%d</span>`, i+1, i+1))
|
fmt.Fprintf(&output, `<span id="L%d">%d</span>`, i+1, i+1)
|
||||||
}
|
}
|
||||||
c.Data["LineNums"] = gotemplate.HTML(output.String())
|
c.Data["LineNums"] = gotemplate.HTML(output.String())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,32 +116,32 @@ func WebhooksNew(c *context.Context, orCtx *orgRepoContext) {
|
|||||||
c.Success(orCtx.TmplNew)
|
c.Success(orCtx.TmplNew)
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateWebhook(l macaron.Locale, w *database.Webhook) (field, msg string, ok bool) {
|
func validateWebhook(l macaron.Locale, w *database.Webhook) (field, msg string, status int) {
|
||||||
// 🚨 SECURITY: Local addresses must not be allowed by non-admins to prevent SSRF,
|
// 🚨 SECURITY: Local addresses must not be allowed by non-admins to prevent SSRF,
|
||||||
// see https://github.com/gogs/gogs/issues/5366 for details.
|
// see https://github.com/gogs/gogs/issues/5366 for details.
|
||||||
payloadURL, err := url.Parse(w.URL)
|
payloadURL, err := url.Parse(w.URL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "PayloadURL", l.Tr("repo.settings.webhook.err_cannot_parse_payload_url", err), false
|
return "PayloadURL", l.Tr("repo.settings.webhook.err_cannot_parse_payload_url", err), http.StatusBadRequest
|
||||||
}
|
}
|
||||||
|
|
||||||
if netutil.IsBlockedLocalHostname(payloadURL.Hostname(), conf.Security.LocalNetworkAllowlist) {
|
if netutil.IsBlockedLocalHostname(payloadURL.Hostname(), conf.Security.LocalNetworkAllowlist) {
|
||||||
return "PayloadURL", l.Tr("repo.settings.webhook.url_resolved_to_blocked_local_address"), false
|
return "PayloadURL", l.Tr("repo.settings.webhook.url_resolved_to_blocked_local_address"), http.StatusForbidden
|
||||||
}
|
}
|
||||||
return "", "", true
|
return "", "", http.StatusOK
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateAndCreateWebhook(c *context.Context, orCtx *orgRepoContext, w *database.Webhook) {
|
func validateAndCreateWebhook(c *context.Context, orCtx *orgRepoContext, w *database.Webhook) {
|
||||||
c.Data["Webhook"] = w
|
c.Data["Webhook"] = w
|
||||||
|
|
||||||
if c.HasError() {
|
if c.HasError() {
|
||||||
c.Success(orCtx.TmplNew)
|
c.HTML(http.StatusBadRequest, orCtx.TmplNew)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
field, msg, ok := validateWebhook(c.Locale, w)
|
field, msg, status := validateWebhook(c.Locale, w)
|
||||||
if !ok {
|
if status != http.StatusOK {
|
||||||
c.FormErr(field)
|
c.FormErr(field)
|
||||||
c.RenderWithErr(msg, orCtx.TmplNew, nil)
|
c.RenderWithErr(msg, status, orCtx.TmplNew, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -338,14 +338,14 @@ func validateAndUpdateWebhook(c *context.Context, orCtx *orgRepoContext, w *data
|
|||||||
c.Data["Webhook"] = w
|
c.Data["Webhook"] = w
|
||||||
|
|
||||||
if c.HasError() {
|
if c.HasError() {
|
||||||
c.Success(orCtx.TmplNew)
|
c.HTML(http.StatusBadRequest, orCtx.TmplNew)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
field, msg, ok := validateWebhook(c.Locale, w)
|
field, msg, status := validateWebhook(c.Locale, w)
|
||||||
if !ok {
|
if status != http.StatusOK {
|
||||||
c.FormErr(field)
|
c.FormErr(field)
|
||||||
c.RenderWithErr(msg, orCtx.TmplNew, nil)
|
c.RenderWithErr(msg, status, orCtx.TmplNew, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package repo
|
package repo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@@ -9,7 +10,7 @@ import (
|
|||||||
"gogs.io/gogs/internal/mocks"
|
"gogs.io/gogs/internal/mocks"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_validateWebhook(t *testing.T) {
|
func TestValidateWebhook(t *testing.T) {
|
||||||
l := &mocks.Locale{
|
l := &mocks.Locale{
|
||||||
MockLang: "en",
|
MockLang: "en",
|
||||||
MockTr: func(s string, _ ...any) string {
|
MockTr: func(s string, _ ...any) string {
|
||||||
@@ -18,33 +19,33 @@ func Test_validateWebhook(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
actor *database.User
|
actor *database.User
|
||||||
webhook *database.Webhook
|
webhook *database.Webhook
|
||||||
expField string
|
wantField string
|
||||||
expMsg string
|
wantMsg string
|
||||||
expOK bool
|
wantStatus int
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "admin bypass local address check",
|
name: "admin bypass local address check",
|
||||||
webhook: &database.Webhook{URL: "https://www.google.com"},
|
webhook: &database.Webhook{URL: "https://www.google.com"},
|
||||||
expOK: true,
|
wantStatus: http.StatusOK,
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
name: "local address not allowed",
|
name: "local address not allowed",
|
||||||
webhook: &database.Webhook{URL: "http://localhost:3306"},
|
webhook: &database.Webhook{URL: "http://localhost:3306"},
|
||||||
expField: "PayloadURL",
|
wantField: "PayloadURL",
|
||||||
expMsg: "repo.settings.webhook.url_resolved_to_blocked_local_address",
|
wantMsg: "repo.settings.webhook.url_resolved_to_blocked_local_address",
|
||||||
expOK: false,
|
wantStatus: http.StatusForbidden,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
field, msg, ok := validateWebhook(l, test.webhook)
|
field, msg, status := validateWebhook(l, test.webhook)
|
||||||
assert.Equal(t, test.expOK, ok)
|
assert.Equal(t, test.wantStatus, status)
|
||||||
assert.Equal(t, test.expMsg, msg)
|
assert.Equal(t, test.wantMsg, msg)
|
||||||
assert.Equal(t, test.expField, field)
|
assert.Equal(t, test.wantField, field)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user