Repository: go-gitea/gitea
Pull request #34068: Swift files can be passed either as file or as form value
Author: login: wgr1984
Labels: name: type/bug
name: lgtm/done
description: This PR has enough approvals to get merged. There are no important open reservations anymore.
name: backport/done
description: All backports for this PR have been created
name: topic/packages
Created: 2025-03-30T00:43:17Z | Merged: 2025-04-18T12:09:56Z

Fix #33990

Linked issue #33990: Swift Package Manager Publishing using commandline throws error
### Description

There seem some error using apples swift publish command on swift package manager registry

```
swift package-registry publish -v test.Test 1.1.13
info: archiving the source at '/Users/wolfgangreithmeier/spm_test_lib'
info: publishing test.Test archive at '/Users/wolfgangreithmeier/spm_test_lib/.build/registry/publish/test.Test-1.1.13.zip' to https://127.0.0.1:8080/
info: publishing test.Test 1.1.13 to https://127.0.0.1:8080/test/Test/1.1.13
Error: failed publishing: server error 409: upload failed, package exists:test.Test-1.1.13.zip
```

using curl works without problem

```
curl -k -v -X PUT --user ....:.... \
         -H "Accept: application/vnd.swift.registry.v1+json" \
         -F source-archive=@spm_test_lib.zip \
         https://127.0.0.1:3000/api/packages/swift-repo/swift/test/Test/1.2.1
*   Trying 127.0.0.1:3000...
* Connected to 127.0.0.1 (127.0.0.1) port 3000
* ALPN: curl offers h2,http/1.1
* (304) (OUT), TLS handshake, Client hello (1):
* (304) (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-ECDSA-CHACHA20-POLY1305 / [blank] / UNDEF
* ALPN: server accepted h2
* Server certificate:
*  subject: C=DE; ST=Some-State; L=Munich; O=Internet Widgits Pty Ltd
*  start date: Sep 29 16:43:04 2024 GMT
*  expire date: Sep 27 16:43:04 2034 GMT
*  issuer: C=DE; ST=Some-State; L=Munich; O=Internet Widgits Pty Ltd
*  SSL certificate verify ok.
* using HTTP/2
* Server auth using Basic with user 'admin'
* [HTTP/2] [1] OPENED stream for https://127.0.0.1:3000/api/packages/swift-repo/swift/test/Test/1.2.1
* [HTTP/2] [1] [:method: PUT]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: 127.0.0.1:3000]
* [HTTP/2] [1] [:path: /api/packages/swift-repo/swift/test/Test/1.2.1]
* [HTTP/2] [1] [authorization:  ......]
* [HTTP/2] [1] [user-agent: curl/8.6.0]
* [HTTP/2] [1] [accept: application/vnd.swift.registry.v1+json]
* [HTTP/2] [1] [content-length: 34891]
* [HTTP/2] [1] [content-type: multipart/form-data; boundary=------------------------V4sAMoUbKlnyqmUHIfSozi]
> PUT /api/packages/swift-repo/swift/test/Test/1.2.1 HTTP/2
> Host: 127.0.0.1:3000
> Authorization: Basic YWRtaW46YWRtaW5wYXNzd29yZDEyMw==
> User-Agent: curl/8.6.0
> Accept: application/vnd.swift.registry.v1+json
> Content-Length: 34891
> Content-Type: multipart/form-data; boundary=------------------------V4sAMoUbKlnyqmUHIfSozi
> 
* We are completely uploaded and fine
< HTTP/2 201 
< content-version: 1
< content-length: 0
< date: Sun, 23 Mar 2025 19:55:31 GMT
< 
* Connection #0 to host 127.0.0.1 left intact
```

### Gitea Version

1.23.5

### Can you reproduce the bug on the Gitea demo site?

No

### Log Gist

https://gist.github.com/wgr1984/edb15ebabadad1c4f18cf4c0b0d2eb31

### Screenshots

_No response_

### Git Version

_No response_

### Operating System

Docker:  gitea/gitea:latest

### How are you running Gitea?

Docker compose setup

```
version: '3'

networks:
  gitea:
    external: false

services:
  gitea:
    image: gitea/gitea:latest
    container_name: gitea
    environment:
      - GITEA__database__DB_TYPE=mysql
      - GITEA__database__HOST=db:3306
      - GITEA__database__NAME=gitea
      - GITEA__database__USER=gitea
      - GITEA__database__PASSWD=gitea_password
      # Server settings
      - GITEA__server__DOMAIN=localhost
      - GITEA__server__ROOT_URL=https://localhost:3000/
      - GITEA__server__SSH_DOMAIN=localhost
      - GITEA__server__SSH_PORT=222
      - GITEA__server__SSH_LISTEN_PORT=22
      - GITEA__server__APP_NAME=Gitea
      # HTTPS settings
      - GITEA__server__PROTOCOL=https
      - GITEA__server__CERT_FILE=/data/ssl/gitea.crt
      - GITEA__server__KEY_FILE=/data/ssl/gitea.key
      # Skip setup page
      - GITEA__security__INSTALL_LOCK=true
      # Language settings
      - GITEA__i18n__LANGS=en-US
      - GITEA__i18n__NAMES=English
      # Authentication settings
      - GITEA__service__REQUIRE_SIGNIN_VIEW=true
      - GITEA__service__REGISTER_EMAIL_CONFIRM=false
      - GITEA__openid__ENABLE_OPENID_SIGNIN=false
      - GITEA__openid__ENABLE_OPENID_SIGNUP=false
      - GITEA__oauth2_client__ENABLE_AUTO_REGISTRATION=false
      - GITEA__service__ENABLE_BASIC_AUTHENTICATION=true
      - GITEA__service__DISABLE_REGISTRATION=true
      # 2FA settings
      - GITEA__security__REQUIRE_SIGNIN_2FA=true
      - GITEA__security__DISABLE_2FA=false
      # Repository and package settings
      - GITEA__repository__DISABLE_REPOSITORIES=true
      - GITEA__repository__DISABLE_HTTP_GIT=true
      - GITEA__repository__DISABLE_SSH=true
      - GITEA__packages__ENABLED=true
      - GITEA__repository__DEFAULT_PRIVATE=true
      - GITEA__repository__DEFAULT_REPO_UNITS=packages
    restart: always
    networks:
      - gitea
    volumes:
      - ./gitea:/data
      - ./ssl:/data/ssl:ro
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    ports:
      - "3000:3000"
      - "222:22"
    depends_on:
      - db

  db:
    image: mysql:8.0
    container_name: gitea-db
    restart: always
    networks:
      - gitea
    environment:
      - MYSQL_ROOT_PASSWORD=gitea_root_password
      - MYSQL_DATABASE=gitea
      - MYSQL_USER=gitea
      - MYSQL_PASSWORD=gitea_password
    volumes:
      - ./mysql:/var/lib/mysql
    ports:
      - "3306:3306" 
```

### Database

MSSQL
Linked issue comments: user: login: hiifong
created_at: 2025-03-29T05:20:53Z
body: What's your version of swift cli?
author_association: MEMBER
user: login: wgr1984
created_at: 2025-03-29T21:32:54Z
body: ```
> swift package-registry --version  
Swift Package Manager - Swift 6.0.3
```

aka XCode 16.2

```
> xcrun swift -version
swift-driver version: 1.115.1 Apple Swift version 6.0.3 (swiftlang-6.0.3.1.10 clang-1600.0.30.1)
Target: arm64-apple-macosx14.0
```
author_association: CONTRIBUTOR
user: login: wgr1984
created_at: 2025-03-29T21:49:40Z
body: btw sorry for confusion guess i catched the wrong output:


```
swift package-registry publish -v test.TestLib 1.1.13
info: archiving the source at '/Users/wolfgangreithmeier/spm_test_lib'
info: publishing test.TestLib archive at '/Users/wolfgangreithmeier/spm_test_lib/.build/registry/publish/test.TestLib-1.1.13.zip' to https://127.0.0.1:3000/api/packages/swift-repo/swift
info: publishing test.TestLib 1.1.13 to https://127.0.0.1:3000/api/packages/swift-repo/swift/test/TestLib/1.1.13
Error: failed publishing: server error 400: http: no such file
```

it throughs 400 not 409
author_association: CONTRIBUTOR
user: login: wgr1984
created_at: 2025-03-29T23:04:48Z
body: ```
gitea     | 2025/03/30 00:03:44 ...eb/routing/logger.go:102:func1() [I] router: completed PUT /api/packages/swift-repo/swift/test/TestLib/1.3.18 for 192.168.107.1:20869, 400 Bad Request in 34.1ms @ swift/swift.go:294(swift.UploadPackageFile)
```

somehow the multipart upload not detected correctly

need to debug bit deeper with a proxy bit deeper to get correct curl call to reproduce ...

author_association: CONTRIBUTOR
user: login: wgr1984
created_at: 2025-03-30T00:47:19Z
body: guess problem can be found in the way the multi-parts are sent ... let me verify the proposed solution workes.
At least it did for https://github.com/wgr1984/openspmregistry/blob/feature/11-write-unit-tests/controller/publish.go 
(to be fair never read manifest from forms but archive only, so better be safe https://github.com/go-gitea/gitea/pull/34068)
author_association: CONTRIBUTOR
user: login: hiifong
created_at: 2025-03-30T06:25:26Z
body: ~~gitea does not support receiving files from `multipart/form-data`~~

![Image](https://github.com/user-attachments/assets/9e640508-ff34-4ec3-9cef-94fb329b99d5)

![Image](https://github.com/user-attachments/assets/a9361955-f7bb-4152-ad13-f1f25030b4e9)

![Image](https://github.com/user-attachments/assets/6f258931-e8c5-49a3-b6b0-ca4285c057f7)
author_association: MEMBER
user: login: hiifong
created_at: 2025-03-31T12:49:43Z
body: ```
0000   50 55 54 20 2f 70 6b 67 2f 74 65 73 74 2f 30 2e   PUT /pkg/test/0.
0010   31 2e 30 20 48 54 54 50 2f 31 2e 31 0d 0a 48 6f   1.0 HTTP/1.1..Ho
0020   73 74 3a 20 31 39 32 2e 31 36 38 2e 32 2e 32 32   st: 192.168.2.22
0030   32 3a 38 32 38 32 0d 0a 41 63 63 65 70 74 2d 45   2:8282..Accept-E
0040   6e 63 6f 64 69 6e 67 3a 20 64 65 66 6c 61 74 65   ncoding: deflate
0050   2c 20 67 7a 69 70 2c 20 62 72 2c 20 7a 73 74 64   , gzip, br, zstd
0060   0d 0a 41 63 63 65 70 74 3a 20 61 70 70 6c 69 63   ..Accept: applic
0070   61 74 69 6f 6e 2f 76 6e 64 2e 73 77 69 66 74 2e   ation/vnd.swift.
0080   72 65 67 69 73 74 72 79 2e 76 31 2b 6a 73 6f 6e   registry.v1+json
0090   0d 0a 45 78 70 65 63 74 3a 20 31 30 30 2d 63 6f   ..Expect: 100-co
00a0   6e 74 69 6e 75 65 0d 0a 43 6f 6e 74 65 6e 74 2d   ntinue..Content-
00b0   54 79 70 65 3a 20 6d 75 6c 74 69 70 61 72 74 2f   Type: multipart/
00c0   66 6f 72 6d 2d 64 61 74 61 3b 62 6f 75 6e 64 61   form-data;bounda
00d0   72 79 3d 22 33 38 30 42 41 39 35 41 2d 32 32 43   ry="380BA95A-22C
00e0   45 2d 34 37 37 39 2d 39 44 41 38 2d 42 31 46 33   E-4779-9DA8-B1F3
00f0   43 31 43 43 46 45 45 38 22 0d 0a 50 72 65 66 65   C1CCFEE8"..Prefe
0100   72 3a 20 72 65 73 70 6f 6e 64 2d 61 73 79 6e 63   r: respond-async
0110   0d 0a 55 73 65 72 2d 41 67 65 6e 74 3a 20 53 77   ..User-Agent: Sw
0120   69 66 74 50 61 63 6b 61 67 65 4d 61 6e 61 67 65   iftPackageManage
0130   72 2f 36 2e 30 2e 33 0d 0a 43 6f 6e 6e 65 63 74   r/6.0.3..Connect
0140   69 6f 6e 3a 20 6b 65 65 70 2d 61 6c 69 76 65 0d   ion: keep-alive.
0150   0a 41 63 63 65 70 74 2d 4c 61 6e 67 75 61 67 65   .Accept-Language
0160   3a 20 65 6e 0d 0a 43 6f 6e 74 65 6e 74 2d 4c 65   : en..Content-Le
0170   6e 67 74 68 3a 20 32 31 35 33 0d 0a 0d 0a 2d 2d   ngth: 2153....--
0180   33 38 30 42 41 39 35 41 2d 32 32 43 45 2d 34 37   380BA95A-22CE-47
0190   37 39 2d 39 44 41 38 2d 42 31 46 33 43 31 43 43   79-9DA8-B1F3C1CC
01a0   46 45 45 38 0d 0a 43 6f 6e 74 65 6e 74 2d 44 69   FEE8..Content-Di
01b0   73 70 6f 73 69 74 69 6f 6e 3a 20 66 6f 72 6d 2d   sposition: form-
01c0   64 61 74 61 3b 20 6e 61 6d 65 3d 22 73 6f 75 72   data; name="sour
01d0   63 65 2d 61 72 63 68 69 76 65 22 0d 0a 43 6f 6e   ce-archive"..Con
01e0   74 65 6e 74 2d 54 79 70 65 3a 20 61 70 70 6c 69   tent-Type: appli
01f0   63 61 74 69 6f 6e 2f 7a 69 70 0d 0a 43 6f 6e 74   cation/zip..Cont
0200   65 6e 74 2d 54 72 61 6e 73 66 65 72 2d 45 6e 63   ent-Transfer-Enc
0210   6f 64 69 6e 67 3a 20 62 69 6e 61 72 79 0d 0a 0d   oding: binary...
0220   0a 50 4b 03 04 0a 00 00 00 00 00 d7 a4 7f 5a 00   .PK...........Z.
0230   00 00 00 00 00 00 00 00 00 00 00 09 00 1c 00 70   ...............p
0240   6b 67 2e 74 65 73 74 2f 55 54 09 00 03 d6 8c ea   kg.test/UT......
0250   67 d6 8c ea 67 75 78 0b 00 01 04 e8 03 00 00 04   g...gux.........
0260   e8 03 00 00 50 4b 03 04 0a 00 00 00 00 00 c9 a2   ....PK..........
0270   7f 5a 00 00 00 00 00 00 00 00 00 00 00 00 11 00   .Z..............
0280   1c 00 70 6b 67 2e 74 65 73 74 2f 53 6f 75 72 63   ..pkg.test/Sourc
0290   65 73 2f 55 54 09 00 03 fa 88 ea 67 d6 8c ea 67   es/UT......g...g
02a0   75 78 0b 00 01 04 e8 03 00 00 04 e8 03 00 00 50   ux.............P
02b0   4b 03 04 0a 00 00 00 00 00 c9 a2 7f 5a 00 00 00   K...........Z...
02c0   00 00 00 00 00 00 00 00 00 16 00 1c 00 70 6b 67   .............pkg
02d0   2e 74 65 73 74 2f 53 6f 75 72 63 65 73 2f 74 65   .test/Sources/te
02e0   73 74 2f 55 54 09 00 03 fa 88 ea 67 d6 8c ea 67   st/UT......g...g
02f0   75 78 0b 00 01 04 e8 03 00 00 04 e8 03 00 00 50   ux.............P
0300   4b 03 04 14 00 00 00 08 00 d7 a4 7f 5a a3 f2 2a   K...........Z..*
0310   fa 43 00 00 00 47 00 00 00 20 00 1c 00 70 6b 67   .C...G... ...pkg
0320   2e 74 65 73 74 2f 53 6f 75 72 63 65 73 2f 74 65   .test/Sources/te
0330   73 74 2f 74 65 73 74 2e 73 77 69 66 74 55 54 09   st/test.swiftUT.
0340   00 03 d6 8c ea 67 d6 8c ea 67 75 78 0b 00 01 04   .....g...gux....
0350   e8 03 00 00 04 e8 03 00 00 d3 d7 57 08 c9 48 55   ...........W..HU
0360   08 2e cf 4c 2b 51 08 28 ca 4f 2f 4a cc cd cd cc   ...L+Q.(.O/J....
0370   4b 57 f0 49 cc 4b 2f 4d 4c 4f e5 d2 d7 57 c8 28   KW.I.K/MLO...W.(
0380   29 29 28 b6 d2 d7 4f c9 4f 2e d6 2b 06 29 d5 cb   ))(...O.O..+.)..
0390   2f 4a d7 07 b3 74 93 f2 f3 b3 b9 00 50 4b 03 04   /J...t......PK..
03a0   14 00 00 00 08 00 d7 a4 7f 5a e0 c8 a8 4d 51 01   .........Z...MQ.
03b0   00 00 ff 02 00 00 16 00 1c 00 70 6b 67 2e 74 65   ..........pkg.te
03c0   73 74 2f 50 61 63 6b 61 67 65 2e 73 77 69 66 74   st/Package.swift
03d0   55 54 09 00 03 d6 8c ea 67 d6 8c ea 67 75 78 0b   UT......g...gux.
03e0   00 01 04 e8 03 00 00 04 e8 03 00 00 7d 51 bb 6e   ............}Q.n
03f0   c3 30 0c dc f5 15 44 a6 04 70 ec 4e 1d 02 74 eb   .0....D..p.N..t.
0400   07 14 a8 b7 22 83 2c d3 09 11 4b 72 45 b9 8f bf   ....".,...KrE...
0410   2f 65 c5 4e 52 a4 15 60 58 22 8f e4 dd b1 aa 80   /e.NR..`X"......
0420   3f a9 8b db e8 7d cf db 0f 0c 4c de ed e0 b1 7c   ?....}....L....|
0430   50 55 05 f5 11 ef e5 a1 45 d3 eb 80 0c 51 00 96   PU......E....Q..
0440   1c d9 d1 c2 9c f4 1d bc a6 1a 08 f8 3e 52 c0 16   ............>R..
0450   a2 87 66 a4 5e 2e 47 62 18 b4 39 e9 03 96 4a 91   ..f.^.Gb..9...J.
0460   1d 7c 88 f0 92 03 cf c8 26 d0 10 a5 87 52 3d c6   .|......&....R=.
0470   19 08 4f 33 62 ad 40 8e d3 16 77 b0 8a c8 71 55   ..O3b.@...w...qU
0480   4c 91 21 f8 76 34 91 77 f0 36 bd d3 11 f6 2f e7   L.!.v4.w.6..../.
0490   b0 d0 ed c8 e1 44 16 bf d0 8c 51 37 bd 90 d7 ae   .....D....Q7....
04a0   85 9e 9a a0 03 a5 d7 32 2f b7 43 2e c0 ea 13 b9   .......2/.C.....
04b0   43 2a 14 79 c4 24 65 49 8c 97 40 98 e1 5c 2e 33   C*.y.$eI..@..\.3
04c0   cb dc ed 7b bd 44 ee f3 9d 4f d4 e1 80 13 ed 9c   ...{.D...O......
04d0   dd 6f 72 7e 9f 7f 97 f4 b5 aa 3a 47 41 fc 9f 14   .or~......:GA...
04e0   35 9a c9 64 7f 13 d7 a6 f7 e6 c4 69 09 8b a0 22   5..d.......i..."
04f0   1b 90 b2 1a ac 68 13 15 3e c8 3d 0d 05 1e 29 62   .....h..>.=...)b
0500   79 6f 82 d1 69 d3 03 8a 4d 69 ad 93 e8 33 27 20   yo..i...Mi...3' 
0510   77 b3 cc c9 cb 79 0d d0 05 6f cf 95 e8 0c dd 58   w....y...o.....X
0520   94 1b fc ed d0 a6 b8 c2 4a a0 fe 1f 5f cb c7 bf   ........J..._...
0530   6c bd 9e 7c f1 76 81 cc 26 ab 8d fa 01 50 4b 03   l..|.v..&....PK.
0540   04 0a 00 00 00 00 00 c9 a2 7f 5a 00 00 00 00 00   ..........Z.....
0550   00 00 00 00 00 00 00 0f 00 1c 00 70 6b 67 2e 74   ...........pkg.t
0560   65 73 74 2f 54 65 73 74 73 2f 55 54 09 00 03 fa   est/Tests/UT....
0570   88 ea 67 d6 8c ea 67 75 78 0b 00 01 04 e8 03 00   ..g...gux.......
0580   00 04 e8 03 00 00 50 4b 03 04 0a 00 00 00 00 00   ......PK........
0590   c9 a2 7f 5a 00 00 00 00 00 00 00 00 00 00 00 00   ...Z............
05a0   19 00 1c 00 70 6b 67 2e 74 65 73 74 2f 54 65 73   ....pkg.test/Tes
05b0   74 73 2f 74 65 73 74 54 65 73 74 73 2f 55 54 09   ts/testTests/UT.
05c0   00 03 fa 88 ea 67 d6 8c ea 67 75 78 0b 00 01 04   .....g...gux....
05d0   e8 03 00 00 04 e8 03 00 00 50 4b 03 04 14 00 00   .........PK.....
05e0   00 08 00 d7 a4 7f 5a 34 a0 00 90 84 00 00 00 a7   ......Z4........
05f0   00 00 00 28 00 1c 00 70 6b 67 2e 74 65 73 74 2f   ...(...pkg.test/
0600   54 65 73 74 73 2f 74 65 73 74 54 65 73 74 73 2f   Tests/testTests/
0610   74 65 73 74 54 65 73 74 73 2e 73 77 69 66 74 55   testTests.swiftU
0620   54 09 00 03 d6 8c ea 67 d6 8c ea 67 75 78 0b 00   T......g...gux..
0630   01 04 e8 03 00 00 04 e8 03 00 00 25 8b b1 0e 82   ...........%....
0640   30 18 84 f7 3e c5 25 2e b0 94 57 c0 d1 cd c1 c4   0...>.%...W.....
0650   95 5a 4e f9 03 b4 a4 fd 89 10 e3 bb 0b 72 d3 7d   .ZN..........r.}
0660   f7 e5 64 9c 62 52 dc 98 55 c2 cb d4 ba 15 f7 18   ..d.bR..U.......
0670   08 39 c4 ce c6 d4 bb c7 73 0e 1e 5c dc 38 0d 2c   .9......s..\.8.,
0680   4a b8 bc 6e ac 5d 8a ef 8c 8f c1 96 aa c2 3d 89   J..n.]........=.
0690   12 6b 9c d3 ff 8b 8e 89 70 a1 c5 9c 89 f3 f5 92   .k......p.......
06a0   31 48 4f 34 27 2e 13 bd 16 d6 da b2 81 46 f8 8e   1HO4'........F..
06b0   be c7 b1 b2 85 8f a1 15 95 18 b2 35 5f f3 03 50   ...........5_..P
06c0   4b 01 02 1e 03 0a 00 00 00 00 00 d7 a4 7f 5a 00   K.............Z.
06d0   00 00 00 00 00 00 00 00 00 00 00 09 00 18 00 00   ................
06e0   00 00 00 00 00 10 00 fd 41 00 00 00 00 70 6b 67   ........A....pkg
06f0   2e 74 65 73 74 2f 55 54 05 00 03 d6 8c ea 67 75   .test/UT......gu
0700   78 0b 00 01 04 e8 03 00 00 04 e8 03 00 00 50 4b   x.............PK
0710   01 02 1e 03 0a 00 00 00 00 00 c9 a2 7f 5a 00 00   .............Z..
0720   00 00 00 00 00 00 00 00 00 00 11 00 18 00 00 00   ................
0730   00 00 00 00 10 00 fd 41 43 00 00 00 70 6b 67 2e   .......AC...pkg.
0740   74 65 73 74 2f 53 6f 75 72 63 65 73 2f 55 54 05   test/Sources/UT.
0750   00 03 fa 88 ea 67 75 78 0b 00 01 04 e8 03 00 00   .....gux........
0760   04 e8 03 00 00 50 4b 01 02 1e 03 0a 00 00 00 00   .....PK.........
0770   00 c9 a2 7f 5a 00 00 00 00 00 00 00 00 00 00 00   ....Z...........
0780   00 16 00 18 00 00 00 00 00 00 00 10 00 fd 41 8e   ..............A.
0790   00 00 00 70 6b 67 2e 74 65 73 74 2f 53 6f 75 72   ...pkg.test/Sour
07a0   63 65 73 2f 74 65 73 74 2f 55 54 05 00 03 fa 88   ces/test/UT.....
07b0   ea 67 75 78 0b 00 01 04 e8 03 00 00 04 e8 03 00   .gux............
07c0   00 50 4b 01 02 1e 03 14 00 00 00 08 00 d7 a4 7f   .PK.............
07d0   5a a3 f2 2a fa 43 00 00 00 47 00 00 00 20 00 18   Z..*.C...G... ..
07e0   00 00 00 00 00 01 00 00 00 b4 81 de 00 00 00 70   ...............p
07f0   6b 67 2e 74 65 73 74 2f 53 6f 75 72 63 65 73 2f   kg.test/Sources/
0800   74 65 73 74 2f 74 65 73 74 2e 73 77 69 66 74 55   test/test.swiftU
0810   54 05 00 03 d6 8c ea 67 75 78 0b 00 01 04 e8 03   T......gux......
0820   00 00 04 e8 03 00 00 50 4b 01 02 1e 03 14 00 00   .......PK.......
0830   00 08 00 d7 a4 7f 5a e0 c8 a8 4d 51 01 00 00 ff   ......Z...MQ....
0840   02 00 00 16 00 18 00 00 00 00 00 01 00 00 00 b4   ................
0850   81 7b 01 00 00 70 6b 67 2e 74 65 73 74 2f 50 61   .{...pkg.test/Pa
0860   63 6b 61 67 65 2e 73 77 69 66 74 55 54 05 00 03   ckage.swiftUT...
0870   d6 8c ea 67 75 78 0b 00 01 04 e8 03 00 00 04 e8   ...gux..........
0880   03 00 00 50 4b 01 02 1e 03 0a 00 00 00 00 00 c9   ...PK...........
0890   a2 7f 5a 00 00 00 00 00 00 00 00 00 00 00 00 0f   ..Z.............
08a0   00 18 00 00 00 00 00 00 00 10 00 fd 41 1c 03 00   ............A...
08b0   00 70 6b 67 2e 74 65 73 74 2f 54 65 73 74 73 2f   .pkg.test/Tests/
08c0   55 54 05 00 03 fa 88 ea 67 75 78 0b 00 01 04 e8   UT......gux.....
08d0   03 00 00 04 e8 03 00 00 50 4b 01 02 1e 03 0a 00   ........PK......
08e0   00 00 00 00 c9 a2 7f 5a 00 00 00 00 00 00 00 00   .......Z........
08f0   00 00 00 00 19 00 18 00 00 00 00 00 00 00 10 00   ................
0900   fd 41 65 03 00 00 70 6b 67 2e 74 65 73 74 2f 54   .Ae...pkg.test/T
0910   65 73 74 73 2f 74 65 73 74 54 65 73 74 73 2f 55   ests/testTests/U
0920   54 05 00 03 fa 88 ea 67 75 78 0b 00 01 04 e8 03   T......gux......
0930   00 00 04 e8 03 00 00 50 4b 01 02 1e 03 14 00 00   .......PK.......
0940   00 08 00 d7 a4 7f 5a 34 a0 00 90 84 00 00 00 a7   ......Z4........
0950   00 00 00 28 00 18 00 00 00 00 00 01 00 00 00 b4   ...(............
0960   81 b8 03 00 00 70 6b 67 2e 74 65 73 74 2f 54 65   .....pkg.test/Te
0970   73 74 73 2f 74 65 73 74 54 65 73 74 73 2f 74 65   sts/testTests/te
0980   73 74 54 65 73 74 73 2e 73 77 69 66 74 55 54 05   stTests.swiftUT.
0990   00 03 d6 8c ea 67 75 78 0b 00 01 04 e8 03 00 00   .....gux........
09a0   04 e8 03 00 00 50 4b 05 06 00 00 00 00 08 00 08   .....PK.........
09b0   00 e6 02 00 00 9e 04 00 00 00 00 0d 0a 2d 2d 33   .............--3
09c0   38 30 42 41 39 35 41 2d 32 32 43 45 2d 34 37 37   80BA95A-22CE-477
09d0   39 2d 39 44 41 38 2d 42 31 46 33 43 31 43 43 46   9-9DA8-B1F3C1CCF
09e0   45 45 38 2d 2d 0d 0a                              EE8--..
```
author_association: MEMBER
user: login: wxiaoguang
created_at: 2025-03-31T13:23:42Z
body: > gitea does not support receiving files from `multipart/form-data`

Is it standard? If yes, it should.
author_association: CONTRIBUTOR
user: login: hiifong
created_at: 2025-03-31T13:30:36Z
body: > > gitea does not support receiving files from `multipart/form-data`
> 
> Is it standard? If yes, it should.

~~According to the Swift registry spec, the Content-Transfer-Encoding of the source-archive zip file uploaded by Swift CLI is base64, while that uploaded by curl is binary. Currently, gitea can only process binary, not base64.~~

[Swift Package Registry Doc](https://github.com/swiftlang/swift-package-manager/blob/main/Documentation/PackageRegistry/Registry.md#46-create-a-package-release)

```curl
PUT /mona/LinkedList/1.1.1 HTTP/1.1
Host: packages.example.com
Accept: application/vnd.swift.registry.v1+json
Content-Type: multipart/form-data;boundary="boundary"
Content-Length: 336
Expect: 100-continue
X-Swift-Package-Signature-Format: cms-1.0.0

--boundary
Content-Disposition: form-data; name="source-archive"
Content-Type: application/zip
Content-Length: 32
Content-Transfer-Encoding: base64

gHUFBgAAAAAAAAAAAAAAAAAAAAAAAA==

--boundary
Content-Disposition: form-data; name="source-archive-signature"
Content-Type: application/octet-stream
Content-Length: 88
Content-Transfer-Encoding: base64

l1TdTeIuGdNsO1FQ0ptD64F5nSSOsQ5WzhM6/7KsHRuLHfTsggnyIWr0DxMcBj5F40zfplwntXAgS0ynlqvlFw==

--boundary
Content-Disposition: form-data; name="metadata"
Content-Type: application/json
Content-Transfer-Encoding: quoted-printable
Content-Length: 3

{ "repositoryURLs": [] }

--boundary
Content-Disposition: form-data; name="metadata-signature"
Content-Type: application/octet-stream
Content-Length: 88
Content-Transfer-Encoding: base64

M6TdTeIuGdNsO1FQ0ptD64F5nSSOsQ5WzhM6/7KsHRuLHfTsggnyIWr0DxMcBj5F40zfplwntXAgS0ynlqvlFw==

```

author_association: MEMBER
user: login: hiifong
created_at: 2025-03-31T13:33:41Z
body: ~~A simple repair idea~~

```go
package main

import (
	"net/http"
	"os"

	"github.com/hiifong/log"
)

func main() {
	// /{scope}/{name}/{version}
	http.HandleFunc("/pkg/test/0.1.0", func(w http.ResponseWriter, r *http.Request) {
		log.Infof("Method: %s, URL: %s", r.Method, r.URL)
		log.Infof("Header: %v", r.Header)
		log.Infof("Body: %v", r.Body)
		w.Write([]byte("Hello, World!"))

		// ====================
		err := r.ParseMultipartForm(32 << 20)
		if err != nil {
			log.Errorf("ParseMultipartForm error: %v", err)
		}
		str := r.MultipartForm.Value["source-archive"][0]
		//log.Infof("strs: %v", []byte(str))

		//dec, err := base64.StdEncoding.DecodeString(str)
		//if err != nil {
		//	log.Errorf("base64 decode error: %v", err)
		//}

		f, err := os.Create("test.zip")
		if err != nil {
			log.Errorf("create file error: %v", err)
		}
		defer f.Close()

		if _, err = f.Write([]byte(str)); err != nil {
			log.Errorf("write file error: %v", err)
		}

		if err := f.Sync(); err != nil {
			log.Errorf("sync file error: %v", err)
		}

		w.Write([]byte("Created!"))
	})

	log.Fatalf("server error: %v", http.ListenAndServe(":8282", nil))
}
```
author_association: MEMBER
user: login: hiifong
created_at: 2025-03-31T13:34:48Z
body: > A simple repair idea
> 
> package main
> 
> import (
> 	"net/http"
> 	"os"
> 
> 	"github.com/hiifong/log"
> )
> 
> func main() {
> 	// /{scope}/{name}/{version}
> 	http.HandleFunc("/pkg/test/0.1.0", func(w http.ResponseWriter, r *http.Request) {
> 		log.Infof("Method: %s, URL: %s", r.Method, r.URL)
> 		log.Infof("Header: %v", r.Header)
> 		log.Infof("Body: %v", r.Body)
> 		w.Write([]byte("Hello, World!"))
> 
> 		// ====================
> 		err := r.ParseMultipartForm(32 << 20)
> 		if err != nil {
> 			log.Errorf("ParseMultipartForm error: %v", err)
> 		}
> 		str := r.MultipartForm.Value["source-archive"][0]
> 		//log.Infof("strs: %v", []byte(str))
> 
> 		//dec, err := base64.StdEncoding.DecodeString(str)
> 		//if err != nil {
> 		//	log.Errorf("base64 decode error: %v", err)
> 		//}
> 
> 		f, err := os.Create("test.zip")
> 		if err != nil {
> 			log.Errorf("create file error: %v", err)
> 		}
> 		defer f.Close()
> 
> 		if _, err = f.Write([]byte(str)); err != nil {
> 			log.Errorf("write file error: %v", err)
> 		}
> 
> 		if err := f.Sync(); err != nil {
> 			log.Errorf("sync file error: %v", err)
> 		}
> 
> 		w.Write([]byte("Created!"))
> 	})
> 
> 	log.Fatalf("server error: %v", http.ListenAndServe(":8282", nil))
> }


~~After my test, this is feasible.~~

![Image](https://github.com/user-attachments/assets/fbd086de-a9e5-478f-89d3-2227396417b6)
author_association: MEMBER
user: login: hiifong
created_at: 2025-04-01T03:18:38Z
body: Source-archive uploaded via swift cli does not have filename.
![Image](https://github.com/user-attachments/assets/ef59872c-2f29-485f-8f90-f10fa3ee5384)

The ones uploaded via curl do, and I think that's where the problem lies.
![Image](https://github.com/user-attachments/assets/8295c61d-b12f-4f87-bda7-47e5e38069a0)
author_association: MEMBER

Diff:
diff --git a/models/packages/package_version.go b/models/packages/package_version.go
index 278e8e3a86b0e..bb7fd895f81da 100644
--- a/models/packages/package_version.go
+++ b/models/packages/package_version.go
@@ -279,9 +279,7 @@ func (opts *PackageSearchOptions) configureOrderBy(e db.Engine) {
 	default:
 		e.Desc("package_version.created_unix")
 	}
-
-	// Sort by id for stable order with duplicates in the other field
-	e.Asc("package_version.id")
+	e.Desc("package_version.id") // Sort by id for stable order with duplicates in the other field
 }
 
 // SearchVersions gets all versions of packages matching the search options
diff --git a/routers/api/packages/swift/swift.go b/routers/api/packages/swift/swift.go
index 4d7fb8b1a6992..9c909d49186f0 100644
--- a/routers/api/packages/swift/swift.go
+++ b/routers/api/packages/swift/swift.go
@@ -290,7 +290,24 @@ func DownloadManifest(ctx *context.Context) {
 	})
 }
 
-// https://github.com/swiftlang/swift-package-manager/blob/main/Documentation/PackageRegistry/Registry.md#endpoint-6
+// formFileOptionalReadCloser returns (nil, nil) if the formKey is not present.
+func formFileOptionalReadCloser(ctx *context.Context, formKey string) (io.ReadCloser, error) {
+	multipartFile, _, err := ctx.Req.FormFile(formKey)
+	if err != nil && !errors.Is(err, http.ErrMissingFile) {
+		return nil, err
+	}
+	if multipartFile != nil {
+		return multipartFile, nil
+	}
+
+	content := ctx.Req.FormValue(formKey)
+	if content == "" {
+		return nil, nil
+	}
+	return io.NopCloser(strings.NewReader(ctx.Req.FormValue(formKey))), nil
+}
+
+// UploadPackageFile refers to https://github.com/swiftlang/swift-package-manager/blob/main/Documentation/PackageRegistry/Registry.md#endpoint-6
 func UploadPackageFile(ctx *context.Context) {
 	packageScope := ctx.PathParam("scope")
 	packageName := ctx.PathParam("name")
@@ -304,9 +321,9 @@ func UploadPackageFile(ctx *context.Context) {
 
 	packageVersion := v.Core().String()
 
-	file, _, err := ctx.Req.FormFile("source-archive")
-	if err != nil {
-		apiError(ctx, http.StatusBadRequest, err)
+	file, err := formFileOptionalReadCloser(ctx, "source-archive")
+	if file == nil || err != nil {
+		apiError(ctx, http.StatusBadRequest, "unable to read source-archive file")
 		return
 	}
 	defer file.Close()
@@ -318,10 +335,13 @@ func UploadPackageFile(ctx *context.Context) {
 	}
 	defer buf.Close()
 
-	var mr io.Reader
-	metadata := ctx.Req.FormValue("metadata")
-	if metadata != "" {
-		mr = strings.NewReader(metadata)
+	mr, err := formFileOptionalReadCloser(ctx, "metadata")
+	if err != nil {
+		apiError(ctx, http.StatusBadRequest, "unable to read metadata file")
+		return
+	}
+	if mr != nil {
+		defer mr.Close()
 	}
 
 	pck, err := swift_module.ParsePackage(buf, buf.Size(), mr)
diff --git a/tests/integration/api_packages_swift_test.go b/tests/integration/api_packages_swift_test.go
index c0e0dccfab11b..b29e8459ffb2b 100644
--- a/tests/integration/api_packages_swift_test.go
+++ b/tests/integration/api_packages_swift_test.go
@@ -23,6 +23,7 @@ import (
 	"code.gitea.io/gitea/tests"
 
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 )
 
 func TestPackageSwift(t *testing.T) {
@@ -34,6 +35,7 @@ func TestPackageSwift(t *testing.T) {
 	packageName := "test_package"
 	packageID := packageScope + "." + packageName
 	packageVersion := "1.0.3"
+	packageVersion2 := "1.0.4"
 	packageAuthor := "KN4CK3R"
 	packageDescription := "Gitea Test Package"
 	packageRepositoryURL := "https://gitea.io/gitea/gitea"
@@ -183,6 +185,94 @@ func TestPackageSwift(t *testing.T) {
 		)
 	})
 
+	t.Run("UploadMultipart", func(t *testing.T) {
+		defer tests.PrintCurrentTest(t)()
+
+		uploadPackage := func(t *testing.T, url string, expectedStatus int, sr io.Reader, metadata string) {
+			var body bytes.Buffer
+			mpw := multipart.NewWriter(&body)
+
+			// Read the source archive content
+			sourceContent, err := io.ReadAll(sr)
+			assert.NoError(t, err)
+			mpw.WriteField("source-archive", string(sourceContent))
+
+			if metadata != "" {
+				mpw.WriteField("metadata", metadata)
+			}
+
+			mpw.Close()
+
+			req := NewRequestWithBody(t, "PUT", url, &body).
+				SetHeader("Content-Type", mpw.FormDataContentType()).
+				SetHeader("Accept", swift_router.AcceptJSON).
+				AddBasicAuth(user.Name)
+			MakeRequest(t, req, expectedStatus)
+		}
+
+		createArchive := func(files map[string]string) *bytes.Buffer {
+			var buf bytes.Buffer
+			zw := zip.NewWriter(&buf)
+			for filename, content := range files {
+				w, _ := zw.Create(filename)
+				w.Write([]byte(content))
+			}
+			zw.Close()
+			return &buf
+		}
+
+		uploadURL := fmt.Sprintf("%s/%s/%s/%s", url, packageScope, packageName, packageVersion2)
+
+		req := NewRequestWithBody(t, "PUT", uploadURL, bytes.NewReader([]byte{}))
+		MakeRequest(t, req, http.StatusUnauthorized)
+
+		// Test with metadata as form field
+		uploadPackage(
+			t,
+			uploadURL,
+			http.StatusCreated,
+			createArchive(map[string]string{
+				"Package.swift":           contentManifest1,
+				"Package@swift-5.6.swift": contentManifest2,
+			}),
+			`{"name":"`+packageName+`","version":"`+packageVersion2+`","description":"`+packageDescription+`","codeRepository":"`+packageRepositoryURL+`","author":{"givenName":"`+packageAuthor+`"},"repositoryURLs":["`+packageRepositoryURL+`"]}`,
+		)
+
+		pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeSwift)
+		assert.NoError(t, err)
+		require.Len(t, pvs, 2) // ATTENTION: many subtests are unable to run separately, they depend on the results of previous tests
+		thisPackageVersion := pvs[0]
+		pd, err := packages.GetPackageDescriptor(db.DefaultContext, thisPackageVersion)
+		assert.NoError(t, err)
+		assert.NotNil(t, pd.SemVer)
+		assert.Equal(t, packageID, pd.Package.Name)
+		assert.Equal(t, packageVersion2, pd.Version.Version)
+		assert.IsType(t, &swift_module.Metadata{}, pd.Metadata)
+		metadata := pd.Metadata.(*swift_module.Metadata)
+		assert.Equal(t, packageDescription, metadata.Description)
+		assert.Len(t, metadata.Manifests, 2)
+		assert.Equal(t, contentManifest1, metadata.Manifests[""].Content)
+		assert.Equal(t, contentManifest2, metadata.Manifests["5.6"].Content)
+		assert.Len(t, pd.VersionProperties, 1)
+		assert.Equal(t, packageRepositoryURL, pd.VersionProperties.GetByName(swift_module.PropertyRepositoryURL))
+
+		pfs, err := packages.GetFilesByVersionID(db.DefaultContext, thisPackageVersion.ID)
+		assert.NoError(t, err)
+		assert.Len(t, pfs, 1)
+		assert.Equal(t, fmt.Sprintf("%s-%s.zip", packageName, packageVersion2), pfs[0].Name)
+		assert.True(t, pfs[0].IsLead)
+
+		uploadPackage(
+			t,
+			uploadURL,
+			http.StatusConflict,
+			createArchive(map[string]string{
+				"Package.swift": contentManifest1,
+			}),
+			"",
+		)
+	})
+
 	t.Run("Download", func(t *testing.T) {
 		defer tests.PrintCurrentTest(t)()
 
@@ -211,7 +301,7 @@ func TestPackageSwift(t *testing.T) {
 			SetHeader("Accept", swift_router.AcceptJSON)
 		resp := MakeRequest(t, req, http.StatusOK)
 
-		versionURL := setting.AppURL + url[1:] + fmt.Sprintf("/%s/%s/%s", packageScope, packageName, packageVersion)
+		versionURL := setting.AppURL + url[1:] + fmt.Sprintf("/%s/%s/%s", packageScope, packageName, packageVersion2)
 
 		assert.Equal(t, "1", resp.Header().Get("Content-Version"))
 		assert.Equal(t, fmt.Sprintf(`<%s>; rel="latest-version"`, versionURL), resp.Header().Get("Link"))
@@ -221,9 +311,9 @@ func TestPackageSwift(t *testing.T) {
 		var result *swift_router.EnumeratePackageVersionsResponse
 		DecodeJSON(t, resp, &result)
 
-		assert.Len(t, result.Releases, 1)
-		assert.Contains(t, result.Releases, packageVersion)
-		assert.Equal(t, versionURL, result.Releases[packageVersion].URL)
+		assert.Len(t, result.Releases, 2)
+		assert.Contains(t, result.Releases, packageVersion2)
+		assert.Equal(t, versionURL, result.Releases[packageVersion2].URL)
 
 		req = NewRequest(t, "GET", fmt.Sprintf("%s/%s/%s.json", url, packageScope, packageName)).
 			AddBasicAuth(user.Name)


Reviews:
user: login: hiifong
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-07T01:38:39Z
user: login: wxiaoguang
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-13T12:25:10Z
user: login: hiifong
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-13T15:14:17Z
user: login: delvh
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-13T17:35:17Z
user: login: wxiaoguang
body: CI fails, need to figure out the root problem and fix.
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-04-14T00:18:18Z
user: login: wxiaoguang
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T12:45:07Z

Review comments:
diff_hunk: @@ -304,10 +304,18 @@ func UploadPackageFile(ctx *context.Context) {
 
 	packageVersion := v.Core().String()
 
-	file, _, err := ctx.Req.FormFile("source-archive")
+	var file io.ReadCloser
+	multipartFile, _, err := ctx.Req.FormFile("source-archive")
 	if err != nil {
path: routers/api/packages/swift/swift.go
user: login: hiifong
body: The error here I think may need to be handled separately, if it's a http.ErrMissingFile error then we need to get the file content from FromValue, otherwise it just returns an error.
created_at: 2025-04-07T01:38:34Z
author_association: MEMBER
diff_hunk: @@ -290,7 +290,24 @@ func DownloadManifest(ctx *context.Context) {
 	})
 }
 
-// https://github.com/swiftlang/swift-package-manager/blob/main/Documentation/PackageRegistry/Registry.md#endpoint-6
+// formFileOptionalReadCloser returns (nil, nil) if the formKey is not present.
+func formFileOptionalReadCloser(ctx *context.Context, formKey string) (io.ReadCloser, error) {
+	multipartFile, _, err := ctx.Req.FormFile(formKey)
+	if err != nil && !errors.Is(err, http.ErrMissingFile) {
+		return nil, err
+	}
+	if multipartFile != nil {
+		return multipartFile, nil
+	}
+
+	content := ctx.Req.FormValue(formKey)
+	if content == "" {
+		return nil, nil
+	}
+	return io.NopCloser(strings.NewReader(ctx.Req.FormValue(formKey))), nil
path: routers/api/packages/swift/swift.go
user: login: wxiaoguang
body: I made a copy-paste mistake, ->  Fix some trivial problems #34237 
created_at: 2025-04-18T12:45:07Z
author_association: CONTRIBUTOR

PR comments:
user: login: KN4CK3R
created_at: 2025-04-02T13:05:27Z
body: Alternative fix with minimal changes:
```diff
@@ -304,10 +304,18 @@ func UploadPackageFile(ctx *context.Context) {
 
        packageVersion := v.Core().String()
 
-       file, _, err := ctx.Req.FormFile("source-archive")
+       var file io.ReadCloser
+       multipartFile, _, err := ctx.Req.FormFile("source-archive")
        if err != nil {
-               apiError(ctx, http.StatusBadRequest, err)
-               return
+               content := ctx.Req.FormValue("source-archive")
+               if content != "" {
+                       file = io.NopCloser(strings.NewReader(content))
+               } else {
+                       apiError(ctx, http.StatusBadRequest, err)
+                       return
+               }
+       } else {
+               file = multipartFile
        }
        defer file.Close()
```
\+ tests
author_association: MEMBER
user: login: hiifong
created_at: 2025-04-03T10:03:50Z
body: hi @wgr1984, would you be interested in continuing to complete the pr?
author_association: MEMBER
user: login: wgr1984
created_at: 2025-04-03T13:59:43Z
body: sure, will finish it once i find a minute to get local build running, in
order to confirm impl. and update test (in case needed)

Am Do., 3. Apr. 2025 um 12:04 Uhr schrieb hiifong ***@***.***
>:

> hi @wgr1984 <https://github.com/wgr1984>, would you be interested in
> continuing to complete the pr?
>
> —
> Reply to this email directly, view it on GitHub
> <https://github.com/go-gitea/gitea/pull/34068#issuecomment-2775168782>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AADEBZPEB6PIYRVWM734MXD2XUBRZAVCNFSM6AAAAAB2B5CUJCVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDONZVGE3DQNZYGI>
> .
> You are receiving this because you were mentioned.Message ID:
> ***@***.***>
> [image: hiifong]*hiifong* left a comment (go-gitea/gitea#34068)
> <https://github.com/go-gitea/gitea/pull/34068#issuecomment-2775168782>
>
> hi @wgr1984 <https://github.com/wgr1984>, would you be interested in
> continuing to complete the pr?
>
> —
> Reply to this email directly, view it on GitHub
> <https://github.com/go-gitea/gitea/pull/34068#issuecomment-2775168782>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AADEBZPEB6PIYRVWM734MXD2XUBRZAVCNFSM6AAAAAB2B5CUJCVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDONZVGE3DQNZYGI>
> .
> You are receiving this because you were mentioned.Message ID:
> ***@***.***>
>

author_association: CONTRIBUTOR
user: login: wgr1984
created_at: 2025-04-06T19:36:09Z
body: updated + test. Metadata looks good, too 👍 

```
curl -k --location 'https://127.0.0.1:3000/api/packages/tet/swift/test/TestLib/1.3.19' \
--header 'Accept: application/vnd.swift.registry.v1+json' \
--header 'Authorization: Basic dGV0OmRlZmF1bHQ=' \
--header 'Cookie: _csrf=BuZq7gKCTbJRLox5fIJSVlw_BNI6MTc0Mzk2NDIwODc2MTI2OTAwMA; i_like_gitea=497d7722fdfa170e'

{
   "id":"test.TestLib",
   "version":"1.3.19",
   "resources":[
      {
         "name":"source-archive",
         "type":"application/zip",
         "checksum":"3d4247adb875f627f5afd3ac860ff027ede1bb45f883059d4ceb5a8a025676df"
      }
   ],
   "metadata":{
      "@context":[
         "http://schema.org/"
      ],
      "@type":"SoftwareSourceCode",
      "name":"TestLib",
      "version":"1.3.19",
      "description":"A test Swift package",
      "keywords":[
         "test",
         "swift"
      ],
      "license":"MIT",
      "author":{
         "@type":"Person"
      },
      "programmingLanguage":{
         "@type":"ComputerLanguage",
         "name":"Swift",
         "url":"https://swift.org"
      }
   }
}

```

<img width="1436" alt="image" src="https://github.com/user-attachments/assets/6e0664ed-9cfd-4e59-90e0-74c6c8c0fe68" />

author_association: CONTRIBUTOR
user: login: wxiaoguang
created_at: 2025-04-13T12:28:23Z
body: CI failure seems related.
author_association: CONTRIBUTOR
user: login: hiifong
created_at: 2025-04-13T15:40:33Z
body: I was going to run the MySQL tests locally, but very unfortunately I can't.

![image](https://github.com/user-attachments/assets/557c113d-9323-4a6f-a641-c588634209bf)

author_association: MEMBER
user: login: wxiaoguang
created_at: 2025-04-14T00:18:04Z
body: Need to figure out the root problem and fix.

```
=== TestPackageSwift/LookupPackageIdentifiers (tests/integration/api_packages_swift_test.go:411)
--- FAIL: TestPackageSwift (0.76s)
    testlogger.go:61: 2025/04/13 23:02:49 modules/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /home/runner/work/gitea/gitea/tests/gitea-lfs-meta
    --- FAIL: TestPackageSwift/UploadMultipart (0.04s)
        testlogger.go:61: 2025/04/13 23:02:50 HTTPRequest [I] router: completed PUT /api/packages/user2/swift/test-scope/test_package/1.0.4 for test-mock:12345, 401 Unauthorized in 1.4ms @ packages/api.go:43(packages.reqPackageAccess)
        testlogger.go:61: 2025/04/13 23:02:50 HTTPRequest [I] router: completed PUT /api/packages/user2/swift/test-scope/test_package/1.0.4 for test-mock:12345, 201 Created in 16.8ms @ swift/swift.go:294(swift.UploadPackageFile)
        api_packages_swift_test.go:248: 
            	Error Trace:	/home/runner/work/gitea/gitea/tests/integration/api_packages_swift_test.go:248
            	Error:      	Not equal: 
            	            	expected: "1.0.4"
            	            	actual  : "1.0.3"
            	            	
            	            	Diff:
            	            	--- Expected
            	            	+++ Actual
            	            	@@ -1 +1 @@
            	            	-1.0.4
            	            	+1.0.3
            	Test:       	TestPackageSwift/UploadMultipart
        api_packages_swift_test.go:261: 
            	Error Trace:	/home/runner/work/gitea/gitea/tests/integration/api_packages_swift_test.go:261
            	Error:      	Not equal: 
            	            	expected: "test_package-1.0.4.zip"
            	            	actual  : "test_package-1.0.3.zip"
            	            	
            	            	Diff:
            	            	--- Expected
            	            	+++ Actual
            	            	@@ -1 +1 @@
            	            	-test_package-1.0.4.zip
            	            	+test_package-1.0.3.zip
            	Test:       	TestPackageSwift/UploadMultipart
        testlogger.go:61: 2025/04/13 23:02:50 .../packages/packages.go:168:createPackageAndVersion() [E] Error inserting package: package version already exists
        testlogger.go:61: 2025/04/13 23:02:50 HTTPRequest [I] router: completed PUT /api/packages/user2/swift/test-scope/test_package/1.0.4 for test-mock:12345, 409 Conflict in 5.3ms @ swift/swift.go:294(swift.UploadPackageFile)
```
author_association: CONTRIBUTOR
user: login: wgr1984
created_at: 2025-04-14T08:30:44Z
body: I know why because the test cases rely on each other. To test that went
need to get the. Independent, but it's a bigger change ...

wxiaoguang ***@***.***> schrieb am Mo. 14. Apr. 2025 um 02:18:

> ***@***.**** requested changes on this pull request.
>
> CI fails, need to figure out the root problem and fix.
>
> —
> Reply to this email directly, view it on GitHub
> <https://github.com/go-gitea/gitea/pull/34068#pullrequestreview-2762898274>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AADEBZIP6ZS4Q3KTGOEZBT32ZL5F7AVCNFSM6AAAAAB2B5CUJCVHI2DSMVQWIX3LMV43YUDVNRWFEZLROVSXG5CSMV3GSZLXHMZDONRSHA4TQMRXGQ>
> .
> You are receiving this because you were mentioned.Message ID:
> ***@***.***>
>

author_association: CONTRIBUTOR
user: login: KN4CK3R
created_at: 2025-04-14T08:37:32Z
body: Just move the upload test to the bottom or remove the package after uploading.
author_association: MEMBER
user: login: wgr1984
created_at: 2025-04-14T09:43:39Z
body: more fixing the symptom than the issue but sure, will do
author_association: CONTRIBUTOR
user: login: wxiaoguang
created_at: 2025-04-14T10:59:41Z
body: Strange, it doesn't explain why only MySQL fails, other databases succeed ..... anyway we need to make the CI pass to merge .......
author_association: CONTRIBUTOR
user: login: wxiaoguang
created_at: 2025-04-18T12:09:35Z
body: OK, I fixed the tests. Will merge.
author_association: CONTRIBUTOR