Compare commits

...

21 Commits

Author SHA1 Message Date
Lunny Xiao
8eefa1f6de Add missing two sync feed for refs/pull (#32815) (#32822)
Some checks are pending
release-tag-version / binary (push) Waiting to run
release-tag-version / docker-rootful (push) Waiting to run
release-tag-version / docker-rootless (push) Waiting to run
Fowllow #32659
Backport #32815
2024-12-13 08:25:22 +00:00
Lunny Xiao
d172c6d2b0 Add changelog for 1.22.6 (#32825) 2024-12-13 15:55:25 +08:00
Lunny Xiao
c630b88f35 Fix misuse of PublicKeyCallback(#32810) (#32824)
Backport #32810
2024-12-13 07:34:52 +00:00
Giteabot
e7de2fc136 Fix lfs migration (#32812) (#32818)
Backport #32812 by @hiifong

Fix: #32803

![image](https://github.com/user-attachments/assets/3ea1f4e0-e26f-4a15-957e-dd6caf91deb1)

![image](https://github.com/user-attachments/assets/44b99624-c347-4f2d-a11c-13ec1276eea2)

Co-authored-by: hiifong <i@hiif.ong>
2024-12-13 11:41:12 +08:00
Giteabot
4fe19fc722 Avoid MacOS keychain dialog in integration tests (#32813) (#32816)
Backport #32813 by @bohde

Mac's git installation ships with a system wide config that configures
the credential helper `osxkeychain`, which will prompt the user with a
dialog.

```
$ git config list --system 
credential.helper=osxkeychain
```
By setting the environment variable
[`GIT_CONFIG_NOSYSTEM=true`](https://git-scm.com/docs/git-config#ENVIRONMENT),
Git will not load the system wide config, preventing the dialog from
populating.

Closes #26717

Co-authored-by: Rowan Bohde <rowan.bohde@gmail.com>
2024-12-13 05:41:02 +08:00
techknowlogick
b54b6d103f use specific namespace labels 2024-12-12 15:45:55 -05:00
Giteabot
84ce417312 use dedicated runners for release artifacts (#32811) (#32814)
Backport #32811 by @techknowlogick

GH runners are having trouble, so switch the remaining release jobs to
use dedicated runners.

Co-authored-by: techknowlogick <techknowlogick@gitea.com>
2024-12-12 15:44:58 -05:00
Lunny Xiao
c0092af2e0 Add changelog for 1.22.5 (#32794)
Some checks failed
release-tag-version / binary (push) Waiting to run
release-tag-version / docker-rootful (push) Failing after 12m48s
release-tag-version / docker-rootless (push) Failing after 12m36s
2024-12-12 04:56:35 +08:00
Lunny Xiao
6092bbac4d 🐛 Fixe a keystring misuse and refactor duplicates keystrings (#32668) (#32792)
Backport #32668 

- Fixes a translation keystring misuse where the string 'open
milestones' is used in place of 'closed milestones'.
- De-duplicates the use of 'open milesones' and 'closed milestones'
keystrings on the sidebar of an issue, reusing the ones on the issues
filter and action bars.
- Closes #32667

Co-authored-by: Simon Pistache <105607989+SimonPistache@users.noreply.github.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-12-11 13:32:10 -05:00
Lunny Xiao
e4ca557fd0 Upgrade crypto library (#32791)
backport #32750
2024-12-11 13:18:58 -05:00
Giteabot
ef9d1e9002 Add standard-compliant route to serve outdated R packages (#32783) (#32789)
Backport #32783 by Sebastian-T-T

The R package repository currently does not have support for older
versions of packages which should be stored in a separate /Archive
router. This PR remedies that by adding a new path router.

Fixes #32782

Co-authored-by: Sebastian T. T. <109338575+Sebastian-T-T@users.noreply.github.com>
2024-12-11 16:49:06 +00:00
Giteabot
0c7e44fcf7 Fix internal server error when updating labels without write permission (#32776) (#32785) 2024-12-10 17:22:03 -08:00
Giteabot
3a9039bc95 Make wiki pages visit fast (#32732) (#32745)
Backport #32732 by @lunny

Fix #20156

We reuse the code from the repository code view instead of the current
code.
Previously it took `5653ms` for
https://gitea.com/henri/wiki/wiki/?action=_pages in my local machine,
now it's about `300ms` .

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-12-07 02:54:29 +08:00
silverwind
063655c391 Bump relative-time-element to v4.4.4 (#32739)
Backport https://github.com/go-gitea/gitea/pull/32730 to v1.22
2024-12-06 15:39:45 +01:00
Giteabot
eee16e433c Fix fork page branch selection (#32711) (#32725)
Backport #32711 by @lunny

Fix #32709

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-12-05 18:25:14 +00:00
KN4CK3R
0c4c28bc29 Add Swift login endpoint (#32693) (#32701)
Backport of #32693

Fix #32683

This PR adds the login endpoint and fixes the documentation links.
2024-12-06 01:53:55 +08:00
Giteabot
d8ad9228ca Fix gogit GetRefCommitID (#32705) (#32712)
Backport #32705 by @Zettat123

Fix #32335

When we call `GetRefCommitID` and the reference is already a commit ID,
the `GetRefCommitID` with go-git will return a `NotExist` error. This PR
improves the `GetRefCommitID` for go-git. If the input is already a
commit ID, it will be returned directly.

Co-authored-by: Zettat123 <zettat123@gmail.com>
2024-12-04 07:59:48 +00:00
Giteabot
0d1fc2b2e9 Fix delete branch perm checking (#32654) (#32707)
Backport #32654 by @lunny

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-12-04 13:25:35 +08:00
Giteabot
a332805f6e Fix word overflow in file search page (#32695) (#32699)
Backport #32695 by yp05327

Co-authored-by: yp05327 <576951401@qq.com>
2024-12-04 08:19:43 +08:00
Giteabot
4b73e92264 Fix race condition in mermaid observer (#32599) (#32673)
Backport #32599 by william-allspice
2024-11-29 19:44:41 +08:00
Giteabot
27489f2e0b Don't create action when syncing mirror pull refs (#32659) (#32664)
Backport #32659 by @lunny

Fix #27961

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-11-29 02:13:16 +08:00
35 changed files with 474 additions and 205 deletions

View File

@@ -3,3 +3,5 @@ self-hosted-runner:
- actuated-4cpu-8gb
- actuated-4cpu-16gb
- nscloud
- namespace-profile-gitea-release-docker
- namespace-profile-gitea-release-binary

View File

@@ -12,7 +12,7 @@ jobs:
disk-clean:
uses: ./.github/workflows/disk-clean.yml
nightly-binary:
runs-on: nscloud
runs-on: namespace-profile-gitea-release-binary
steps:
- uses: actions/checkout@v4
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
@@ -60,7 +60,7 @@ jobs:
run: |
aws s3 sync dist/release s3://${{ secrets.AWS_S3_BUCKET }}/gitea/${{ steps.clean_name.outputs.branch }} --no-progress
nightly-docker-rootful:
runs-on: ubuntu-latest
runs-on: namespace-profile-gitea-release-docker
steps:
- uses: actions/checkout@v4
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
@@ -97,7 +97,7 @@ jobs:
push: true
tags: gitea/gitea:${{ steps.clean_name.outputs.branch }}
nightly-docker-rootless:
runs-on: ubuntu-latest
runs-on: namespace-profile-gitea-release-docker
steps:
- uses: actions/checkout@v4
# fetch all commits instead of only the last as some branches are long lived and could have many between versions

View File

@@ -11,7 +11,7 @@ concurrency:
jobs:
binary:
runs-on: nscloud
runs-on: namespace-profile-gitea-release-binary
steps:
- uses: actions/checkout@v4
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
@@ -68,7 +68,7 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
docker-rootful:
runs-on: ubuntu-latest
runs-on: namespace-profile-gitea-release-docker
steps:
- uses: actions/checkout@v4
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
@@ -99,7 +99,7 @@ jobs:
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
docker-rootless:
runs-on: ubuntu-latest
runs-on: namespace-profile-gitea-release-docker
steps:
- uses: actions/checkout@v4
# fetch all commits instead of only the last as some branches are long lived and could have many between versions

View File

@@ -13,7 +13,7 @@ concurrency:
jobs:
binary:
runs-on: nscloud
runs-on: namespace-profile-gitea-release-binary
steps:
- uses: actions/checkout@v4
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
@@ -70,7 +70,7 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
docker-rootful:
runs-on: ubuntu-latest
runs-on: namespace-profile-gitea-release-docker
steps:
- uses: actions/checkout@v4
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
@@ -105,7 +105,7 @@ jobs:
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
docker-rootless:
runs-on: ubuntu-latest
runs-on: namespace-profile-gitea-release-docker
steps:
- uses: actions/checkout@v4
# fetch all commits instead of only the last as some branches are long lived and could have many between versions

View File

@@ -4,7 +4,37 @@ This changelog goes through the changes that have been made in each release
without substantial changes to our git log; to see the highlights of what has
been added to each release, please refer to the [blog](https://blog.gitea.com).
## [1.22.4](https://github.com/go-gitea/gitea/releases/tag/1.22.4) - 2024-11-14
## [1.22.6](https://github.com/go-gitea/gitea/releases/tag/v1.22.6) - 2024-12-12
* SECURITY
* Fix misuse of PublicKeyCallback(#32810)
* BUGFIXES
* Fix lfs migration (#32812) (#32818)
* Add missing two sync feed for refs/pull (#32815)
* TESTING
* Avoid MacOS keychain dialog in integration tests (#32813) (#32816)
## [1.22.5](https://github.com/go-gitea/gitea/releases/tag/v1.22.5) - 2024-12-11
* SECURITY
* Upgrade crypto library (#32791)
* Fix delete branch perm checking (#32654) (#32707)
* BUGFIXES
* Add standard-compliant route to serve outdated R packages (#32783) (#32789)
* Fix internal server error when updating labels without write permission (#32776) (#32785)
* Add Swift login endpoint (#32693) (#32701)
* Fix fork page branch selection (#32711) (#32725)
* Fix word overflow in file search page (#32695) (#32699)
* Fix gogit `GetRefCommitID` (#32705) (#32712)
* Fix race condition in mermaid observer (#32599) (#32673)
* Fixe a keystring misuse and refactor duplicates keystrings (#32668) (#32792)
* Bump relative-time-element to v4.4.4 (#32739)
* PERFORMANCE
* Make wiki pages visit fast (#32732) (#32745)
* MISC
* Don't create action when syncing mirror pull refs (#32659) (#32664)
## [1.22.4](https://github.com/go-gitea/gitea/releases/tag/v1.22.4) - 2024-11-14
* SECURITY
* Fix basic auth with webauthn (#32531) (#32536)
@@ -61,7 +91,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.com).
* Update github.com/go-enry/go-enry to v2.9.1 (#32295) (#32296)
* Warn users when they try to use a non-root-url to sign in/up (#32272) (#32273)
## [1.22.3](https://github.com/go-gitea/gitea/releases/tag/1.22.3) - 2024-10-08
## [1.22.3](https://github.com/go-gitea/gitea/releases/tag/v1.22.3) - 2024-10-08
* SECURITY
* Fix bug when a token is given public only (#32204) (#32218)
@@ -102,7 +132,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.com).
* Lazy load avatar images (#32051) (#32063)
* Upgrade cache to v0.2.1 (#32003) (#32009)
## [1.22.2](https://github.com/go-gitea/gitea/releases/tag/1.22.2) - 2024-08-28
## [1.22.2](https://github.com/go-gitea/gitea/releases/tag/v1.22.2) - 2024-08-28
* Security
* Replace v-html with v-text in search inputbox (#31966) (#31973)
@@ -158,7 +188,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.com).
* Upgrade micromatch to 4.0.8 (#31944)
* Update webpack to 5.94.0 (#31941)
## [1.22.1](https://github.com/go-gitea/gitea/releases/tag/1.22.1) - 2024-07-04
## [1.22.1](https://github.com/go-gitea/gitea/releases/tag/v1.22.1) - 2024-07-04
* SECURITY
* Add replacement module for `mholt/archiver` (#31267) (#31270)

10
go.mod
View File

@@ -34,7 +34,7 @@ require (
github.com/ethantkoenig/rupture v1.0.1
github.com/felixge/fgprof v0.9.4
github.com/fsnotify/fsnotify v1.7.0
github.com/gliderlabs/ssh v0.3.6
github.com/gliderlabs/ssh v0.3.8
github.com/go-ap/activitypub v0.0.0-20240316125321-b61fd6a83225
github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73
github.com/go-chi/chi/v5 v5.0.12
@@ -104,12 +104,12 @@ require (
github.com/yuin/goldmark v1.7.0
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
github.com/yuin/goldmark-meta v1.1.0
golang.org/x/crypto v0.26.0
golang.org/x/crypto v0.31.0
golang.org/x/image v0.18.0
golang.org/x/net v0.28.0
golang.org/x/oauth2 v0.21.0
golang.org/x/sys v0.24.0
golang.org/x/text v0.17.0
golang.org/x/sys v0.28.0
golang.org/x/text v0.21.0
golang.org/x/tools v0.24.0
google.golang.org/grpc v1.62.1
google.golang.org/protobuf v1.33.0
@@ -291,7 +291,7 @@ require (
go.uber.org/zap v1.27.0 // indirect
golang.org/x/exp v0.0.0-20240314144324-c7f7c6466f7f // indirect
golang.org/x/mod v0.20.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/time v0.5.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240314234333-6e1732d8331c // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect

24
go.sum
View File

@@ -269,8 +269,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/fxamacker/cbor/v2 v2.6.0 h1:sU6J2usfADwWlYDAFhZBQ6TnLFBHxgesMrQfQgk1tWA=
github.com/fxamacker/cbor/v2 v2.6.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/gliderlabs/ssh v0.3.6 h1:ZzjlDa05TcFRICb3anf/dSPN3ewz1Zx6CMLPWgkm3b8=
github.com/gliderlabs/ssh v0.3.6/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8=
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
github.com/go-ap/activitypub v0.0.0-20240316125321-b61fd6a83225 h1:OoM81OclgRX7CUch4M7MmsH0NcmLWpFiSn7rhs6Y5ZU=
@@ -835,8 +835,8 @@ golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2Uz
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/exp v0.0.0-20240314144324-c7f7c6466f7f h1:3CW0unweImhOzd5FmYuRsD4Y4oQFKZIjAnKbjV4WIrw=
golang.org/x/exp v0.0.0-20240314144324-c7f7c6466f7f/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
@@ -878,8 +878,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181221143128-b4a75ba826a6/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -915,8 +915,8 @@ golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
@@ -926,8 +926,8 @@ golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU=
golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@@ -938,8 +938,8 @@ golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

View File

@@ -14,9 +14,16 @@ import (
"github.com/go-git/go-git/v5/plumbing/object"
)
// GetRefCommitID returns the last commit ID string of given reference (branch or tag).
// GetRefCommitID returns the last commit ID string of given reference.
func (repo *Repository) GetRefCommitID(name string) (string, error) {
ref, err := repo.gogitRepo.Reference(plumbing.ReferenceName(name), true)
if plumbing.IsHash(name) {
return name, nil
}
refName := plumbing.ReferenceName(name)
if err := refName.Validate(); err != nil {
return "", err
}
ref, err := repo.gogitRepo.Reference(refName, true)
if err != nil {
if err == plumbing.ErrReferenceNotFound {
return "", ErrNotExist{

View File

@@ -101,3 +101,28 @@ func TestRepository_CommitsBetweenIDs(t *testing.T) {
assert.Len(t, commits, c.ExpectedCommits, "case %d", i)
}
}
func TestGetRefCommitID(t *testing.T) {
bareRepo1Path := filepath.Join(testReposDir, "repo1_bare")
bareRepo1, err := openRepositoryWithDefaultContext(bareRepo1Path)
assert.NoError(t, err)
defer bareRepo1.Close()
// these test case are specific to the repo1_bare test repo
testCases := []struct {
Ref string
ExpectedCommitID string
}{
{RefNameFromBranch("master").String(), "ce064814f4a0d337b333e646ece456cd39fab612"},
{RefNameFromBranch("branch1").String(), "2839944139e0de9737a044f78b0e4b40d989a9e3"},
{RefNameFromTag("test").String(), "3ad28a9149a2864384548f3d17ed7f38014c9e8a"},
{"ce064814f4a0d337b333e646ece456cd39fab612", "ce064814f4a0d337b333e646ece456cd39fab612"},
}
for _, testCase := range testCases {
commitID, err := bareRepo1.GetRefCommitID(testCase.Ref)
if assert.NoError(t, err) {
assert.Equal(t, testCase.ExpectedCommitID, commitID)
}
}
}

View File

@@ -211,6 +211,7 @@ func createRequest(ctx context.Context, method, url string, headers map[string]s
req.Header.Set(key, value)
}
req.Header.Set("Accept", AcceptHeader)
req.Header.Set("User-Agent", UserAgentHeader)
return req, nil
}

View File

@@ -15,7 +15,8 @@ const (
// MediaType contains the media type for LFS server requests
MediaType = "application/vnd.git-lfs+json"
// Some LFS servers offer content with other types, so fallback to '*/*' if application/vnd.git-lfs+json cannot be served
AcceptHeader = "application/vnd.git-lfs+json;q=0.9, */*;q=0.8"
AcceptHeader = "application/vnd.git-lfs+json;q=0.9, */*;q=0.8"
UserAgentHeader = "git-lfs"
)
// BatchRequest contains multiple requests processed in one batch operation.

View File

@@ -13,10 +13,12 @@ import (
"errors"
"fmt"
"io"
"maps"
"net"
"os"
"os/exec"
"path/filepath"
"reflect"
"strconv"
"strings"
"sync"
@@ -33,9 +35,22 @@ import (
gossh "golang.org/x/crypto/ssh"
)
type contextKey string
// The ssh auth overall works like this:
// NewServerConn:
// serverHandshake+serverAuthenticate:
// PublicKeyCallback:
// PublicKeyHandler (our code):
// reset(ctx.Permissions) and set ctx.Permissions.giteaKeyID = keyID
// pubKey.Verify
// return ctx.Permissions // only reaches here, the pub key is really authenticated
// set conn.Permissions from serverAuthenticate
// sessionHandler(conn)
//
// Then sessionHandler should only use the "verified keyID" from the original ssh conn, but not the ctx one.
// Otherwise, if a user provides 2 keys A (a correct one) and B (public key matches but no private key),
// then only A succeeds to authenticate, sessionHandler will see B's keyID
const giteaKeyID = contextKey("gitea-key-id")
const giteaPermissionExtensionKeyID = "gitea-perm-ext-key-id"
func getExitStatusFromError(err error) int {
if err == nil {
@@ -61,8 +76,32 @@ func getExitStatusFromError(err error) int {
return waitStatus.ExitStatus()
}
// sessionPartial is the private struct from "gliderlabs/ssh/session.go"
// We need to read the original "conn" field from "ssh.Session interface" which contains the "*session pointer"
// https://github.com/gliderlabs/ssh/blob/d137aad99cd6f2d9495bfd98c755bec4e5dffb8c/session.go#L109-L113
// If upstream fixes the problem and/or changes the struct, we need to follow.
// If the struct mismatches, the builtin ssh server will fail during integration tests.
type sessionPartial struct {
sync.Mutex
gossh.Channel
conn *gossh.ServerConn
}
func ptr[T any](intf any) *T {
// https://pkg.go.dev/unsafe#Pointer
// (1) Conversion of a *T1 to Pointer to *T2.
// Provided that T2 is no larger than T1 and that the two share an equivalent memory layout,
// this conversion allows reinterpreting data of one type as data of another type.
v := reflect.ValueOf(intf)
p := v.UnsafePointer()
return (*T)(p)
}
func sessionHandler(session ssh.Session) {
keyID := fmt.Sprintf("%d", session.Context().Value(giteaKeyID).(int64))
// here can't use session.Permissions() because it only uses the value from ctx, which might not be the authenticated one.
// so we must use the original ssh conn, which always contains the correct (verified) keyID.
sshConn := ptr[sessionPartial](session)
keyID := sshConn.conn.Permissions.Extensions[giteaPermissionExtensionKeyID]
command := session.RawCommand()
@@ -164,6 +203,23 @@ func sessionHandler(session ssh.Session) {
}
func publicKeyHandler(ctx ssh.Context, key ssh.PublicKey) bool {
// The publicKeyHandler (PublicKeyCallback) only helps to provide the candidate keys to authenticate,
// It does NOT really verify here, so we could only record the related information here.
// After authentication (Verify), the "Permissions" will be assigned to the ssh conn,
// then we can use it in the "session handler"
// first, reset the ctx permissions (just like https://github.com/gliderlabs/ssh/pull/243 does)
// it shouldn't be reused across different ssh conn (sessions), each pub key should have its own "Permissions"
oldCtxPerm := ctx.Permissions().Permissions
ctx.Permissions().Permissions = &gossh.Permissions{}
ctx.Permissions().Permissions.CriticalOptions = maps.Clone(oldCtxPerm.CriticalOptions)
setPermExt := func(keyID int64) {
ctx.Permissions().Permissions.Extensions = map[string]string{
giteaPermissionExtensionKeyID: fmt.Sprint(keyID),
}
}
if log.IsDebug() { // <- FingerprintSHA256 is kinda expensive so only calculate it if necessary
log.Debug("Handle Public Key: Fingerprint: %s from %s", gossh.FingerprintSHA256(key), ctx.RemoteAddr())
}
@@ -238,7 +294,7 @@ func publicKeyHandler(ctx ssh.Context, key ssh.PublicKey) bool {
if log.IsDebug() { // <- FingerprintSHA256 is kinda expensive so only calculate it if necessary
log.Debug("Successfully authenticated: %s Certificate Fingerprint: %s Principal: %s", ctx.RemoteAddr(), gossh.FingerprintSHA256(key), principal)
}
ctx.SetValue(giteaKeyID, pkey.ID)
setPermExt(pkey.ID)
return true
}
@@ -266,7 +322,7 @@ func publicKeyHandler(ctx ssh.Context, key ssh.PublicKey) bool {
if log.IsDebug() { // <- FingerprintSHA256 is kinda expensive so only calculate it if necessary
log.Debug("Successfully authenticated: %s Public Key Fingerprint: %s", ctx.RemoteAddr(), gossh.FingerprintSHA256(key))
}
ctx.SetValue(giteaKeyID, pkey.ID)
setPermExt(pkey.ID)
return true
}

View File

@@ -1434,8 +1434,6 @@ issues.new.no_items = No items
issues.new.milestone = Milestone
issues.new.no_milestone = No Milestone
issues.new.clear_milestone = Clear milestone
issues.new.open_milestone = Open Milestones
issues.new.closed_milestone = Closed Milestones
issues.new.assignees = Assignees
issues.new.clear_assignees = Clear assignees
issues.new.no_assignees = No Assignees

8
package-lock.json generated
View File

@@ -10,7 +10,7 @@
"@citation-js/plugin-csl": "0.7.11",
"@citation-js/plugin-software-formats": "0.6.1",
"@github/markdown-toolbar-element": "2.2.3",
"@github/relative-time-element": "4.4.2",
"@github/relative-time-element": "4.4.4",
"@github/text-expander-element": "2.6.1",
"@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
"@primer/octicons": "19.9.0",
@@ -1020,9 +1020,9 @@
"integrity": "sha512-AlquKGee+IWiAMYVB0xyHFZRMnu4n3X4HTvJHu79GiVJ1ojTukCWyxMlF5NMsecoLcBKsuBhx3QPv2vkE/zQ0A=="
},
"node_modules/@github/relative-time-element": {
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/@github/relative-time-element/-/relative-time-element-4.4.2.tgz",
"integrity": "sha512-wTXunu3hmuGljA5CHaaoUIKV0oI35wno0FKJl2yqKplTRnsCA5bPNj4bDeVIubkuskql6jwionWLlGM1Y6QLaw==",
"version": "4.4.4",
"resolved": "https://registry.npmjs.org/@github/relative-time-element/-/relative-time-element-4.4.4.tgz",
"integrity": "sha512-Oi8uOL8O+ZWLD7dHRWCkm2cudcTYtB3VyOYf9BtzCgDGm+OKomyOREtItNMtWl1dxvec62BTKErq36uy+RYxQg==",
"license": "MIT"
},
"node_modules/@github/text-expander-element": {

View File

@@ -9,7 +9,7 @@
"@citation-js/plugin-csl": "0.7.11",
"@citation-js/plugin-software-formats": "0.6.1",
"@github/markdown-toolbar-element": "2.2.3",
"@github/relative-time-element": "4.4.2",
"@github/relative-time-element": "4.4.4",
"@github/text-expander-element": "2.6.1",
"@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
"@primer/octicons": "19.9.0",

View File

@@ -314,6 +314,7 @@ func CommonRoutes() *web.Route {
r.Get("/PACKAGES", cran.EnumerateSourcePackages)
r.Get("/PACKAGES{format}", cran.EnumerateSourcePackages)
r.Get("/{filename}", cran.DownloadSourcePackageFile)
r.Get("/Archive/{packagename}/{filename}", cran.DownloadSourcePackageFile)
})
r.Put("", reqPackageAccess(perm.AccessModeWrite), cran.UploadSourcePackageFile)
})
@@ -608,40 +609,46 @@ func CommonRoutes() *web.Route {
}, reqPackageAccess(perm.AccessModeWrite))
}, reqPackageAccess(perm.AccessModeRead))
r.Group("/swift", func() {
r.Group("/{scope}/{name}", func() {
r.Group("", func() {
r.Get("", swift.EnumeratePackageVersions)
r.Get(".json", swift.EnumeratePackageVersions)
}, swift.CheckAcceptMediaType(swift.AcceptJSON))
r.Group("/{version}", func() {
r.Get("/Package.swift", swift.CheckAcceptMediaType(swift.AcceptSwift), swift.DownloadManifest)
r.Put("", reqPackageAccess(perm.AccessModeWrite), swift.CheckAcceptMediaType(swift.AcceptJSON), swift.UploadPackageFile)
r.Get("", func(ctx *context.Context) {
// Can't use normal routes here: https://github.com/go-chi/chi/issues/781
r.Group("", func() { // Needs to be unauthenticated.
r.Post("", swift.CheckAuthenticate)
r.Post("/login", swift.CheckAuthenticate)
})
r.Group("", func() {
r.Group("/{scope}/{name}", func() {
r.Group("", func() {
r.Get("", swift.EnumeratePackageVersions)
r.Get(".json", swift.EnumeratePackageVersions)
}, swift.CheckAcceptMediaType(swift.AcceptJSON))
r.Group("/{version}", func() {
r.Get("/Package.swift", swift.CheckAcceptMediaType(swift.AcceptSwift), swift.DownloadManifest)
r.Put("", reqPackageAccess(perm.AccessModeWrite), swift.CheckAcceptMediaType(swift.AcceptJSON), swift.UploadPackageFile)
r.Get("", func(ctx *context.Context) {
// Can't use normal routes here: https://github.com/go-chi/chi/issues/781
version := ctx.Params("version")
if strings.HasSuffix(version, ".zip") {
swift.CheckAcceptMediaType(swift.AcceptZip)(ctx)
if ctx.Written() {
return
version := ctx.Params("version")
if strings.HasSuffix(version, ".zip") {
swift.CheckAcceptMediaType(swift.AcceptZip)(ctx)
if ctx.Written() {
return
}
ctx.SetParams("version", version[:len(version)-4])
swift.DownloadPackageFile(ctx)
} else {
swift.CheckAcceptMediaType(swift.AcceptJSON)(ctx)
if ctx.Written() {
return
}
if strings.HasSuffix(version, ".json") {
ctx.SetParams("version", version[:len(version)-5])
}
swift.PackageVersionMetadata(ctx)
}
ctx.SetParams("version", version[:len(version)-4])
swift.DownloadPackageFile(ctx)
} else {
swift.CheckAcceptMediaType(swift.AcceptJSON)(ctx)
if ctx.Written() {
return
}
if strings.HasSuffix(version, ".json") {
ctx.SetParams("version", version[:len(version)-5])
}
swift.PackageVersionMetadata(ctx)
}
})
})
})
})
r.Get("/identifiers", swift.CheckAcceptMediaType(swift.AcceptJSON), swift.LookupPackageIdentifiers)
}, reqPackageAccess(perm.AccessModeRead))
r.Get("/identifiers", swift.CheckAcceptMediaType(swift.AcceptJSON), swift.LookupPackageIdentifiers)
}, reqPackageAccess(perm.AccessModeRead))
})
r.Group("/vagrant", func() {
r.Group("/authenticate", func() {
r.Get("", vagrant.CheckAuthenticate)

View File

@@ -27,7 +27,7 @@ import (
"github.com/hashicorp/go-version"
)
// https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md#35-api-versioning
// https://github.com/swiftlang/swift-package-manager/blob/main/Documentation/PackageRegistry/Registry.md#35-api-versioning
const (
AcceptJSON = "application/vnd.swift.registry.v1+json"
AcceptSwift = "application/vnd.swift.registry.v1+swift"
@@ -35,9 +35,9 @@ const (
)
var (
// https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md#361-package-scope
// https://github.com/swiftlang/swift-package-manager/blob/main/Documentation/PackageRegistry/Registry.md#361-package-scope
scopePattern = regexp.MustCompile(`\A[a-zA-Z0-9][a-zA-Z0-9-]{0,38}\z`)
// https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md#362-package-name
// https://github.com/swiftlang/swift-package-manager/blob/main/Documentation/PackageRegistry/Registry.md#362-package-name
namePattern = regexp.MustCompile(`\A[a-zA-Z0-9][a-zA-Z0-9-_]{0,99}\z`)
)
@@ -49,7 +49,7 @@ type headers struct {
Link string
}
// https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md#35-api-versioning
// https://github.com/swiftlang/swift-package-manager/blob/main/Documentation/PackageRegistry/Registry.md#35-api-versioning
func setResponseHeaders(resp http.ResponseWriter, h *headers) {
if h.ContentType != "" {
resp.Header().Set("Content-Type", h.ContentType)
@@ -69,7 +69,7 @@ func setResponseHeaders(resp http.ResponseWriter, h *headers) {
}
}
// https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md#33-error-handling
// https://github.com/swiftlang/swift-package-manager/blob/main/Documentation/PackageRegistry/Registry.md#33-error-handling
func apiError(ctx *context.Context, status int, obj any) {
// https://www.rfc-editor.org/rfc/rfc7807
type Problem struct {
@@ -91,7 +91,7 @@ func apiError(ctx *context.Context, status int, obj any) {
})
}
// https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md#35-api-versioning
// https://github.com/swiftlang/swift-package-manager/blob/main/Documentation/PackageRegistry/Registry.md#35-api-versioning
func CheckAcceptMediaType(requiredAcceptHeader string) func(ctx *context.Context) {
return func(ctx *context.Context) {
accept := ctx.Req.Header.Get("Accept")
@@ -101,6 +101,16 @@ func CheckAcceptMediaType(requiredAcceptHeader string) func(ctx *context.Context
}
}
// https://github.com/swiftlang/swift-package-manager/blob/main/Documentation/PackageRegistry/PackageRegistryUsage.md#registry-authentication
func CheckAuthenticate(ctx *context.Context) {
if ctx.Doer == nil {
apiError(ctx, http.StatusUnauthorized, nil)
return
}
ctx.Status(http.StatusOK)
}
func buildPackageID(scope, name string) string {
return scope + "." + name
}
@@ -113,7 +123,7 @@ type EnumeratePackageVersionsResponse struct {
Releases map[string]Release `json:"releases"`
}
// https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md#41-list-package-releases
// https://github.com/swiftlang/swift-package-manager/blob/main/Documentation/PackageRegistry/Registry.md#41-list-package-releases
func EnumeratePackageVersions(ctx *context.Context) {
packageScope := ctx.Params("scope")
packageName := ctx.Params("name")
@@ -170,7 +180,7 @@ type PackageVersionMetadataResponse struct {
Metadata *swift_module.SoftwareSourceCode `json:"metadata"`
}
// https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md#endpoint-2
// https://github.com/swiftlang/swift-package-manager/blob/main/Documentation/PackageRegistry/Registry.md#endpoint-2
func PackageVersionMetadata(ctx *context.Context) {
id := buildPackageID(ctx.Params("scope"), ctx.Params("name"))
@@ -228,7 +238,7 @@ func PackageVersionMetadata(ctx *context.Context) {
})
}
// https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md#43-fetch-manifest-for-a-package-release
// https://github.com/swiftlang/swift-package-manager/blob/main/Documentation/PackageRegistry/Registry.md#43-fetch-manifest-for-a-package-release
func DownloadManifest(ctx *context.Context) {
packageScope := ctx.Params("scope")
packageName := ctx.Params("name")
@@ -280,7 +290,7 @@ func DownloadManifest(ctx *context.Context) {
})
}
// https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md#endpoint-6
// https://github.com/swiftlang/swift-package-manager/blob/main/Documentation/PackageRegistry/Registry.md#endpoint-6
func UploadPackageFile(ctx *context.Context) {
packageScope := ctx.Params("scope")
packageName := ctx.Params("name")
@@ -379,7 +389,7 @@ func UploadPackageFile(ctx *context.Context) {
ctx.Status(http.StatusCreated)
}
// https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md#endpoint-4
// https://github.com/swiftlang/swift-package-manager/blob/main/Documentation/PackageRegistry/Registry.md#endpoint-4
func DownloadPackageFile(ctx *context.Context) {
pv, err := packages_model.GetVersionByNameAndVersion(ctx, ctx.Package.Owner.ID, packages_model.TypeSwift, buildPackageID(ctx.Params("scope"), ctx.Params("name")), ctx.Params("version"))
if err != nil {
@@ -420,7 +430,7 @@ type LookupPackageIdentifiersResponse struct {
Identifiers []string `json:"identifiers"`
}
// https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md#endpoint-5
// https://github.com/swiftlang/swift-package-manager/blob/main/Documentation/PackageRegistry/Registry.md#endpoint-5
func LookupPackageIdentifiers(ctx *context.Context) {
url := ctx.FormTrim("url")
if url == "" {

View File

@@ -155,11 +155,6 @@ func DeleteBranch(ctx *context.APIContext) {
}
}
if ctx.Repo.Repository.IsMirror {
ctx.Error(http.StatusForbidden, "IsMirrored", fmt.Errorf("can not delete branch of an mirror repository"))
return
}
if err := repo_service.DeleteBranch(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, branchName); err != nil {
switch {
case git.IsErrBranchNotExist(err):

View File

@@ -319,6 +319,11 @@ func prepareForReplaceOrAdd(ctx *context.APIContext, form api.IssueLabelsOption)
return nil, nil, err
}
if !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) {
ctx.Error(http.StatusForbidden, "CanWriteIssuesOrPulls", "write permission is required")
return nil, nil, fmt.Errorf("permission denied")
}
var (
labelIDs []int64
labelNames []string
@@ -350,10 +355,5 @@ func prepareForReplaceOrAdd(ctx *context.APIContext, form api.IssueLabelsOption)
return nil, nil, err
}
if !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) {
ctx.Status(http.StatusForbidden)
return nil, nil, nil
}
return issue, labels, err
}

View File

@@ -1000,49 +1000,54 @@ func MergePullRequest(ctx *context.APIContext) {
}
log.Trace("Pull request merged: %d", pr.ID)
if form.DeleteBranchAfterMerge {
// Don't cleanup when there are other PR's that use this branch as head branch.
exist, err := issues_model.HasUnmergedPullRequestsByHeadInfo(ctx, pr.HeadRepoID, pr.HeadBranch)
if err != nil {
ctx.ServerError("HasUnmergedPullRequestsByHeadInfo", err)
return
}
if exist {
ctx.Status(http.StatusOK)
return
}
var headRepo *git.Repository
if ctx.Repo != nil && ctx.Repo.Repository != nil && ctx.Repo.Repository.ID == pr.HeadRepoID && ctx.Repo.GitRepo != nil {
headRepo = ctx.Repo.GitRepo
} else {
headRepo, err = gitrepo.OpenRepository(ctx, pr.HeadRepo)
// for agit flow, we should not delete the agit reference after merge
if form.DeleteBranchAfterMerge && pr.Flow == issues_model.PullRequestFlowGithub {
// check permission even it has been checked in repo_service.DeleteBranch so that we don't need to
// do RetargetChildrenOnMerge
if err := repo_service.CanDeleteBranch(ctx, pr.HeadRepo, pr.HeadBranch, ctx.Doer); err == nil {
// Don't cleanup when there are other PR's that use this branch as head branch.
exist, err := issues_model.HasUnmergedPullRequestsByHeadInfo(ctx, pr.HeadRepoID, pr.HeadBranch)
if err != nil {
ctx.ServerError(fmt.Sprintf("OpenRepository[%s]", pr.HeadRepo.FullName()), err)
ctx.ServerError("HasUnmergedPullRequestsByHeadInfo", err)
return
}
defer headRepo.Close()
}
if err := pull_service.RetargetChildrenOnMerge(ctx, ctx.Doer, pr); err != nil {
ctx.Error(http.StatusInternalServerError, "RetargetChildrenOnMerge", err)
return
}
if err := repo_service.DeleteBranch(ctx, ctx.Doer, pr.HeadRepo, headRepo, pr.HeadBranch); err != nil {
switch {
case git.IsErrBranchNotExist(err):
ctx.NotFound(err)
case errors.Is(err, repo_service.ErrBranchIsDefault):
ctx.Error(http.StatusForbidden, "DefaultBranch", fmt.Errorf("can not delete default branch"))
case errors.Is(err, git_model.ErrBranchIsProtected):
ctx.Error(http.StatusForbidden, "IsProtectedBranch", fmt.Errorf("branch protected"))
default:
ctx.Error(http.StatusInternalServerError, "DeleteBranch", err)
if exist {
ctx.Status(http.StatusOK)
return
}
var headRepo *git.Repository
if ctx.Repo != nil && ctx.Repo.Repository != nil && ctx.Repo.Repository.ID == pr.HeadRepoID && ctx.Repo.GitRepo != nil {
headRepo = ctx.Repo.GitRepo
} else {
headRepo, err = gitrepo.OpenRepository(ctx, pr.HeadRepo)
if err != nil {
ctx.ServerError(fmt.Sprintf("OpenRepository[%s]", pr.HeadRepo.FullName()), err)
return
}
defer headRepo.Close()
}
if err := pull_service.RetargetChildrenOnMerge(ctx, ctx.Doer, pr); err != nil {
ctx.Error(http.StatusInternalServerError, "RetargetChildrenOnMerge", err)
return
}
if err := repo_service.DeleteBranch(ctx, ctx.Doer, pr.HeadRepo, headRepo, pr.HeadBranch); err != nil {
switch {
case git.IsErrBranchNotExist(err):
ctx.NotFound(err)
case errors.Is(err, repo_service.ErrBranchIsDefault):
ctx.Error(http.StatusForbidden, "DefaultBranch", fmt.Errorf("can not delete default branch"))
case errors.Is(err, git_model.ErrBranchIsProtected):
ctx.Error(http.StatusForbidden, "IsProtectedBranch", fmt.Errorf("branch protected"))
default:
ctx.Error(http.StatusInternalServerError, "DeleteBranch", err)
}
return
}
if err := issues_model.AddDeletePRBranchComment(ctx, ctx.Doer, pr.BaseRepo, pr.Issue.ID, pr.HeadBranch); err != nil {
// Do not fail here as branch has already been deleted
log.Error("DeleteBranch: %v", err)
}
return
}
if err := issues_model.AddDeletePRBranchComment(ctx, ctx.Doer, pr.BaseRepo, pr.Issue.ID, pr.HeadBranch); err != nil {
// Do not fail here as branch has already been deleted
log.Error("DeleteBranch: %v", err)
}
}

View File

@@ -1160,32 +1160,34 @@ func MergePullRequest(ctx *context.Context) {
log.Trace("Pull request merged: %d", pr.ID)
if form.DeleteBranchAfterMerge {
// Don't cleanup when other pr use this branch as head branch
exist, err := issues_model.HasUnmergedPullRequestsByHeadInfo(ctx, pr.HeadRepoID, pr.HeadBranch)
if err != nil {
ctx.ServerError("HasUnmergedPullRequestsByHeadInfo", err)
return
}
if exist {
ctx.JSONRedirect(issue.Link())
return
}
var headRepo *git.Repository
if ctx.Repo != nil && ctx.Repo.Repository != nil && pr.HeadRepoID == ctx.Repo.Repository.ID && ctx.Repo.GitRepo != nil {
headRepo = ctx.Repo.GitRepo
} else {
headRepo, err = gitrepo.OpenRepository(ctx, pr.HeadRepo)
if err != nil {
ctx.ServerError(fmt.Sprintf("OpenRepository[%s]", pr.HeadRepo.FullName()), err)
return
}
defer headRepo.Close()
}
deleteBranch(ctx, pr, headRepo)
if !form.DeleteBranchAfterMerge {
ctx.JSONRedirect(issue.Link())
return
}
// Don't cleanup when other pr use this branch as head branch
exist, err := issues_model.HasUnmergedPullRequestsByHeadInfo(ctx, pr.HeadRepoID, pr.HeadBranch)
if err != nil {
ctx.ServerError("HasUnmergedPullRequestsByHeadInfo", err)
return
}
if exist {
ctx.JSONRedirect(issue.Link())
return
}
var headRepo *git.Repository
if ctx.Repo != nil && ctx.Repo.Repository != nil && pr.HeadRepoID == ctx.Repo.Repository.ID && ctx.Repo.GitRepo != nil {
headRepo = ctx.Repo.GitRepo
} else {
headRepo, err = gitrepo.OpenRepository(ctx, pr.HeadRepo)
if err != nil {
ctx.ServerError(fmt.Sprintf("OpenRepository[%s]", pr.HeadRepo.FullName()), err)
return
}
defer headRepo.Close()
}
deleteBranch(ctx, pr, headRepo)
ctx.JSONRedirect(issue.Link())
}
@@ -1367,8 +1369,8 @@ func CleanUpPullRequest(ctx *context.Context) {
pr := issue.PullRequest
// Don't cleanup unmerged and unclosed PRs
if !pr.HasMerged && !issue.IsClosed {
// Don't cleanup unmerged and unclosed PRs and agit PRs
if !pr.HasMerged && !issue.IsClosed && pr.Flow != issues_model.PullRequestFlowGithub {
ctx.NotFound("CleanUpPullRequest", nil)
return
}
@@ -1399,13 +1401,12 @@ func CleanUpPullRequest(ctx *context.Context) {
return
}
perm, err := access_model.GetUserRepoPermission(ctx, pr.HeadRepo, ctx.Doer)
if err != nil {
ctx.ServerError("GetUserRepoPermission", err)
return
}
if !perm.CanWrite(unit.TypeCode) {
ctx.NotFound("CleanUpPullRequest", nil)
if err := repo_service.CanDeleteBranch(ctx, pr.HeadRepo, pr.HeadBranch, ctx.Doer); err != nil {
if errors.Is(err, util.ErrPermissionDenied) {
ctx.NotFound("CanDeleteBranch", nil)
} else {
ctx.ServerError("CanDeleteBranch", err)
}
return
}

View File

@@ -6,6 +6,7 @@ package repo
import (
"bytes"
gocontext "context"
"fmt"
"io"
"net/http"
@@ -651,22 +652,32 @@ func WikiPages(ctx *context.Context) {
return
}
entries, err := commit.ListEntries()
treePath := "" // To support list sub folders' pages in the future
tree, err := commit.SubTree(treePath)
if err != nil {
ctx.ServerError("SubTree", err)
return
}
allEntries, err := tree.ListEntries()
if err != nil {
ctx.ServerError("ListEntries", err)
return
}
allEntries.CustomSort(base.NaturalSortLess)
entries, _, err := allEntries.GetCommitsInfo(gocontext.Context(ctx), commit, treePath)
if err != nil {
ctx.ServerError("GetCommitsInfo", err)
return
}
pages := make([]PageMeta, 0, len(entries))
for _, entry := range entries {
if !entry.IsRegular() {
if !entry.Entry.IsRegular() {
continue
}
c, err := wikiRepo.GetCommitByPath(entry.Name())
if err != nil {
ctx.ServerError("GetCommit", err)
return
}
wikiName, err := wiki_service.GitPathToWebPath(entry.Name())
wikiName, err := wiki_service.GitPathToWebPath(entry.Entry.Name())
if err != nil {
if repo_model.IsErrWikiInvalidFileName(err) {
continue
@@ -678,8 +689,8 @@ func WikiPages(ctx *context.Context) {
pages = append(pages, PageMeta{
Name: displayName,
SubURL: wiki_service.WebPathToURLPath(wikiName),
GitEntryName: entry.Name(),
UpdatedUnix: timeutil.TimeStamp(c.Author.When.Unix()),
GitEntryName: entry.Entry.Name(),
UpdatedUnix: timeutil.TimeStamp(entry.Commit.Author.When.Unix()),
})
}
ctx.Data["Pages"] = pages

View File

@@ -390,6 +390,12 @@ func (a *actionNotifier) DeleteRef(ctx context.Context, doer *user_model.User, r
}
func (a *actionNotifier) SyncPushCommits(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) {
// ignore pull sync message for pull requests refs
// TODO: it's better to have a UI to let users chose
if opts.RefFullName.IsPull() {
return
}
data, err := json.Marshal(commits)
if err != nil {
log.Error("json.Marshal: %v", err)
@@ -411,6 +417,12 @@ func (a *actionNotifier) SyncPushCommits(ctx context.Context, pusher *user_model
}
func (a *actionNotifier) SyncCreateRef(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, refFullName git.RefName, refID string) {
// ignore pull sync message for pull requests refs
// TODO: it's better to have a UI to let users chose
if refFullName.IsPull() {
return
}
if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{
ActUserID: repo.OwnerID,
ActUser: repo.MustOwner(ctx),
@@ -425,6 +437,12 @@ func (a *actionNotifier) SyncCreateRef(ctx context.Context, doer *user_model.Use
}
func (a *actionNotifier) SyncDeleteRef(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, refFullName git.RefName) {
// ignore pull sync message for pull requests refs
// TODO: it's better to have a UI to let users chose
if refFullName.IsPull() {
return
}
if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{
ActUserID: repo.OwnerID,
ActUser: repo.MustOwner(ctx),

View File

@@ -14,7 +14,9 @@ import (
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
issues_model "code.gitea.io/gitea/models/issues"
access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/git"
@@ -463,15 +465,17 @@ var (
ErrBranchIsDefault = errors.New("branch is default")
)
// DeleteBranch delete branch
func DeleteBranch(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, gitRepo *git.Repository, branchName string) error {
err := repo.MustNotBeArchived()
func CanDeleteBranch(ctx context.Context, repo *repo_model.Repository, branchName string, doer *user_model.User) error {
if branchName == repo.DefaultBranch {
return ErrBranchIsDefault
}
perm, err := access_model.GetUserRepoPermission(ctx, repo, doer)
if err != nil {
return err
}
if branchName == repo.DefaultBranch {
return ErrBranchIsDefault
if !perm.CanWrite(unit.TypeCode) {
return util.NewPermissionDeniedErrorf("permission denied to access repo %d unit %s", repo.ID, unit.TypeCode.LogString())
}
isProtected, err := git_model.IsBranchProtected(ctx, repo.ID, branchName)
@@ -481,6 +485,19 @@ func DeleteBranch(ctx context.Context, doer *user_model.User, repo *repo_model.R
if isProtected {
return git_model.ErrBranchIsProtected
}
return nil
}
// DeleteBranch delete branch
func DeleteBranch(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, gitRepo *git.Repository, branchName string) error {
err := repo.MustNotBeArchived()
if err != nil {
return err
}
if err := CanDeleteBranch(ctx, repo, branchName, doer); err != nil {
return err
}
rawBranch, err := git_model.GetBranch(ctx, repo.ID, branchName)
if err != nil && !git_model.IsErrBranchNotExist(err) {

View File

@@ -9,7 +9,7 @@
<input id="repo-file-find-input" type="text" autofocus data-url-data-link="{{.DataLink}}" data-url-tree-link="{{.TreeLink}}">
</div>
</div>
<table id="repo-find-file-table" class="ui single line table">
<table id="repo-find-file-table" class="ui single line fixed table">
<tbody>
</tbody>
</table>

View File

@@ -58,7 +58,7 @@
{{end}}
{{if .ClosedMilestones}}
<div class="divider"></div>
<div class="header">{{ctx.Locale.Tr "repo.issues.filter_milestone_open"}}</div>
<div class="header">{{ctx.Locale.Tr "repo.issues.filter_milestone_closed"}}</div>
{{range .ClosedMilestones}}
<div class="item issue-action" data-element-id="{{.ID}}" data-url="{{$.RepoLink}}/issues/milestone">
{{.Name}}

View File

@@ -14,7 +14,7 @@
{{if .OpenMilestones}}
<div class="divider"></div>
<div class="header">
{{ctx.Locale.Tr "repo.issues.new.open_milestone"}}
{{ctx.Locale.Tr "repo.issues.filter_milestone_open"}}
</div>
{{range .OpenMilestones}}
<a class="item" data-id="{{.ID}}" data-href="{{$.RepoLink}}/issues?milestone={{.ID}}">
@@ -26,7 +26,7 @@
{{if .ClosedMilestones}}
<div class="divider"></div>
<div class="header">
{{ctx.Locale.Tr "repo.issues.new.closed_milestone"}}
{{ctx.Locale.Tr "repo.issues.filter_milestone_closed"}}
</div>
{{range .ClosedMilestones}}
<a class="item" data-id="{{.ID}}" data-href="{{$.RepoLink}}/issues?milestone={{.ID}}">

View File

@@ -53,20 +53,18 @@
</div>
<div class="inline field">
<label>{{ctx.Locale.Tr "repo.fork_branch"}}</label>
<div class="ui selection dropdown">
<div class="ui selection dropdown ellipsis-items-nowrap">
<input type="hidden" id="fork_single_branch" name="fork_single_branch" value="" required>
<span class="text truncated-item-container" data-value="" title="{{ctx.Locale.Tr "repo.all_branches"}}">
<div class="text" title="{{ctx.Locale.Tr "repo.all_branches"}}">
<span class="truncated-item-name">{{ctx.Locale.Tr "repo.all_branches"}}</span>
</span>
</div>
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
<div class="menu">
<div class="item truncated-item-container" data-value="" title="{{ctx.Locale.Tr "repo.all_branches"}}">
<span class="truncated-item-name">{{ctx.Locale.Tr "repo.all_branches"}}</span>
<div class="item" data-value="" title="{{ctx.Locale.Tr "repo.all_branches"}}">
{{ctx.Locale.Tr "repo.all_branches"}}
</div>
{{range .Branches}}
<div class="item truncated-item-container" data-value="{{.}}" title="{{.}}">
<span class="truncated-item-name">{{.}}</span>
</div>
<div class="item" data-value="{{.}}" title="{{.}}">{{.}}</div>
{{end}}
</div>
</div>

View File

@@ -115,6 +115,14 @@ func TestPackageCran(t *testing.T) {
MakeRequest(t, req, http.StatusOK)
})
t.Run("DownloadArchived", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
req := NewRequest(t, "GET", fmt.Sprintf("%s/src/contrib/Archive/%s/%s_%s.tar.gz", url, packageName, packageName, packageVersion)).
AddBasicAuth(user.Name)
MakeRequest(t, req, http.StatusOK)
})
t.Run("Enumerate", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()

View File

@@ -42,6 +42,24 @@ func TestPackageSwift(t *testing.T) {
url := fmt.Sprintf("/api/packages/%s/swift", user.Name)
t.Run("CheckLogin", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
req := NewRequestWithBody(t, "POST", url, strings.NewReader(""))
MakeRequest(t, req, http.StatusUnauthorized)
req = NewRequestWithBody(t, "POST", url, strings.NewReader("")).
AddBasicAuth(user.Name)
MakeRequest(t, req, http.StatusOK)
req = NewRequestWithBody(t, "POST", url+"/login", strings.NewReader(""))
MakeRequest(t, req, http.StatusUnauthorized)
req = NewRequestWithBody(t, "POST", url+"/login", strings.NewReader("")).
AddBasicAuth(user.Name)
MakeRequest(t, req, http.StatusOK)
})
t.Run("CheckAcceptMediaType", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()

View File

@@ -118,6 +118,11 @@ func TestMain(m *testing.M) {
os.Unsetenv("GIT_COMMITTER_EMAIL")
os.Unsetenv("GIT_COMMITTER_DATE")
// Avoid loading the default system config. On MacOS, this config
// sets the osxkeychain credential helper, which will cause tests
// to freeze with a dialog.
os.Setenv("GIT_CONFIG_NOSYSTEM", "true")
err := unittest.InitFixtures(
unittest.FixturesOptions{
Dir: filepath.Join(filepath.Dir(setting.AppPath), "models/fixtures/"),

View File

@@ -553,6 +553,10 @@ func TestPullRetargetChildOnBranchDelete(t *testing.T) {
testPullMerge(t, session, elemBasePR[1], elemBasePR[2], elemBasePR[4], repo_model.MergeStyleMerge, true)
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user2", Name: "repo1"})
branchBasePR := unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "base-pr"})
assert.True(t, branchBasePR.IsDeleted)
// Check child PR
req := NewRequest(t, "GET", test.RedirectURL(respChildPR))
resp := session.MakeRequest(t, req, http.StatusOK)
@@ -583,6 +587,10 @@ func TestPullDontRetargetChildOnWrongRepo(t *testing.T) {
testPullMerge(t, session, elemBasePR[1], elemBasePR[2], elemBasePR[4], repo_model.MergeStyleMerge, true)
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user1", Name: "repo1"})
branchBasePR := unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "base-pr"})
assert.True(t, branchBasePR.IsDeleted)
// Check child PR
req := NewRequest(t, "GET", test.RedirectURL(respChildPR))
resp := session.MakeRequest(t, req, http.StatusOK)
@@ -596,6 +604,27 @@ func TestPullDontRetargetChildOnWrongRepo(t *testing.T) {
})
}
func TestPullRequestMergedWithNoPermissionDeleteBranch(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
session := loginUser(t, "user4")
testRepoFork(t, session, "user2", "repo1", "user4", "repo1", "")
testEditFileToNewBranch(t, session, "user4", "repo1", "master", "base-pr", "README.md", "Hello, World\n(Edited - TestPullDontRetargetChildOnWrongRepo - base PR)\n")
respBasePR := testPullCreate(t, session, "user4", "repo1", false, "master", "base-pr", "Base Pull Request")
elemBasePR := strings.Split(test.RedirectURL(respBasePR), "/")
assert.EqualValues(t, "pulls", elemBasePR[3])
// user2 has no permission to delete branch of repo user1/repo1
session2 := loginUser(t, "user2")
testPullMerge(t, session2, elemBasePR[1], elemBasePR[2], elemBasePR[4], repo_model.MergeStyleMerge, true)
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user4", Name: "repo1"})
branchBasePR := unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "base-pr"})
// branch has not been deleted
assert.False(t, branchBasePR.IsDeleted)
})
}
func TestPullMergeIndexerNotifier(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
// create a pull request

View File

@@ -6,15 +6,18 @@ package integration
import (
"context"
"fmt"
"net/http"
"net/url"
"os"
"path/filepath"
"strings"
"testing"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/tests"
"github.com/PuerkitoBio/goquery"
"github.com/stretchr/testify/assert"
)
@@ -50,3 +53,23 @@ func TestRepoCloneWiki(t *testing.T) {
})
})
}
func Test_RepoWikiPages(t *testing.T) {
defer tests.PrepareTestEnv(t)()
url := "/user2/repo1/wiki/?action=_pages"
req := NewRequest(t, "GET", url)
resp := MakeRequest(t, req, http.StatusOK)
doc := NewHTMLParser(t, resp.Body)
expectedPagePaths := []string{
"Home", "Page-With-Image", "Page-With-Spaced-Name", "Unescaped-File",
}
doc.Find("tr").Each(func(i int, s *goquery.Selection) {
firstAnchor := s.Find("a").First()
href, _ := firstAnchor.Attr("href")
pagePath := strings.TrimPrefix(href, "/user2/repo1/wiki/")
assert.EqualValues(t, expectedPagePaths[i], pagePath)
})
}

View File

@@ -90,6 +90,7 @@ function filterRepoFiles(filter) {
const span = document.createElement('span');
// safely escape by using textContent
span.textContent = part;
span.title = span.textContent;
// if the target file path is "abc/xyz", to search "bx", then the matchResult is ['a', 'b', 'c/', 'x', 'yz']
// the matchResult[odd] is matched and highlighted to red.
if (index % 2 === 1) span.classList.add('ui', 'text', 'red');

View File

@@ -57,16 +57,12 @@ export async function renderMermaid() {
mermaidBlock.append(btn);
const updateIframeHeight = () => {
iframe.style.height = `${iframe.contentWindow.document.body.clientHeight}px`;
const body = iframe.contentWindow?.document?.body;
if (body) {
iframe.style.height = `${body.clientHeight}px`;
}
};
// update height when element's visibility state changes, for example when the diagram is inside
// a <details> + <summary> block and the <details> block becomes visible upon user interaction, it
// would initially set a incorrect height and the correct height is set during this callback.
(new IntersectionObserver(() => {
updateIframeHeight();
}, {root: document.documentElement})).observe(iframe);
iframe.addEventListener('load', () => {
pre.replaceWith(mermaidBlock);
mermaidBlock.classList.remove('tw-hidden');
@@ -75,6 +71,13 @@ export async function renderMermaid() {
mermaidBlock.classList.remove('is-loading');
iframe.classList.remove('tw-invisible');
}, 0);
// update height when element's visibility state changes, for example when the diagram is inside
// a <details> + <summary> block and the <details> block becomes visible upon user interaction, it
// would initially set a incorrect height and the correct height is set during this callback.
(new IntersectionObserver(() => {
updateIframeHeight();
}, {root: document.documentElement})).observe(iframe);
});
document.body.append(mermaidBlock);