Merge branch 'main' into item

This commit is contained in:
wxiaoguang 2025-12-13 21:01:12 +08:00 committed by GitHub
commit 38040b7924
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 167 additions and 65 deletions

View File

@ -9,8 +9,10 @@ jobs:
cron-licenses: cron-licenses:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: github.repository == 'go-gitea/gitea' if: github.repository == 'go-gitea/gitea'
permissions:
contents: write
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- uses: actions/setup-go@v6 - uses: actions/setup-go@v6
with: with:
go-version-file: go.mod go-version-file: go.mod

View File

@ -9,8 +9,10 @@ jobs:
crowdin-pull: crowdin-pull:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: github.repository == 'go-gitea/gitea' if: github.repository == 'go-gitea/gitea'
permissions:
contents: write
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- uses: crowdin/github-action@v1 - uses: crowdin/github-action@v1
with: with:
upload_sources: true upload_sources: true

View File

@ -24,6 +24,8 @@ jobs:
detect: detect:
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 3 timeout-minutes: 3
permissions:
contents: read
outputs: outputs:
backend: ${{ steps.changes.outputs.backend }} backend: ${{ steps.changes.outputs.backend }}
frontend: ${{ steps.changes.outputs.frontend }} frontend: ${{ steps.changes.outputs.frontend }}
@ -34,7 +36,7 @@ jobs:
swagger: ${{ steps.changes.outputs.swagger }} swagger: ${{ steps.changes.outputs.swagger }}
yaml: ${{ steps.changes.outputs.yaml }} yaml: ${{ steps.changes.outputs.yaml }}
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- uses: dorny/paths-filter@v3 - uses: dorny/paths-filter@v3
id: changes id: changes
with: with:

View File

@ -10,13 +10,17 @@ concurrency:
jobs: jobs:
files-changed: files-changed:
uses: ./.github/workflows/files-changed.yml uses: ./.github/workflows/files-changed.yml
permissions:
contents: read
lint-backend: lint-backend:
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true' if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
needs: files-changed needs: files-changed
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions:
contents: read
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- uses: actions/setup-go@v6 - uses: actions/setup-go@v6
with: with:
go-version-file: go.mod go-version-file: go.mod
@ -30,8 +34,10 @@ jobs:
if: needs.files-changed.outputs.templates == 'true' if: needs.files-changed.outputs.templates == 'true'
needs: files-changed needs: files-changed
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions:
contents: read
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- uses: astral-sh/setup-uv@v6 - uses: astral-sh/setup-uv@v6
- run: uv python install 3.12 - run: uv python install 3.12
- uses: pnpm/action-setup@v4 - uses: pnpm/action-setup@v4
@ -46,8 +52,10 @@ jobs:
if: needs.files-changed.outputs.yaml == 'true' if: needs.files-changed.outputs.yaml == 'true'
needs: files-changed needs: files-changed
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions:
contents: read
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- uses: astral-sh/setup-uv@v6 - uses: astral-sh/setup-uv@v6
- run: uv python install 3.12 - run: uv python install 3.12
- run: make deps-py - run: make deps-py
@ -57,8 +65,10 @@ jobs:
if: needs.files-changed.outputs.swagger == 'true' if: needs.files-changed.outputs.swagger == 'true'
needs: files-changed needs: files-changed
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions:
contents: read
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- uses: pnpm/action-setup@v4 - uses: pnpm/action-setup@v4
- uses: actions/setup-node@v5 - uses: actions/setup-node@v5
with: with:
@ -70,8 +80,10 @@ jobs:
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.frontend == 'true' || needs.files-changed.outputs.actions == 'true' || needs.files-changed.outputs.docs == 'true' || needs.files-changed.outputs.templates == 'true' if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.frontend == 'true' || needs.files-changed.outputs.actions == 'true' || needs.files-changed.outputs.docs == 'true' || needs.files-changed.outputs.templates == 'true'
needs: files-changed needs: files-changed
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions:
contents: read
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- uses: actions/setup-go@v6 - uses: actions/setup-go@v6
with: with:
go-version-file: go.mod go-version-file: go.mod
@ -82,8 +94,10 @@ jobs:
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true' if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
needs: files-changed needs: files-changed
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions:
contents: read
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- uses: actions/setup-go@v6 - uses: actions/setup-go@v6
with: with:
go-version-file: go.mod go-version-file: go.mod
@ -99,8 +113,10 @@ jobs:
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true' if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
needs: files-changed needs: files-changed
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions:
contents: read
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- uses: actions/setup-go@v6 - uses: actions/setup-go@v6
with: with:
go-version-file: go.mod go-version-file: go.mod
@ -114,8 +130,10 @@ jobs:
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true' if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
needs: files-changed needs: files-changed
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions:
contents: read
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- uses: actions/setup-go@v6 - uses: actions/setup-go@v6
with: with:
go-version-file: go.mod go-version-file: go.mod
@ -127,8 +145,10 @@ jobs:
if: needs.files-changed.outputs.frontend == 'true' || needs.files-changed.outputs.actions == 'true' if: needs.files-changed.outputs.frontend == 'true' || needs.files-changed.outputs.actions == 'true'
needs: files-changed needs: files-changed
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions:
contents: read
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- uses: pnpm/action-setup@v4 - uses: pnpm/action-setup@v4
- uses: actions/setup-node@v5 - uses: actions/setup-node@v5
with: with:
@ -143,8 +163,10 @@ jobs:
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true' if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
needs: files-changed needs: files-changed
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions:
contents: read
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- uses: actions/setup-go@v6 - uses: actions/setup-go@v6
with: with:
go-version-file: go.mod go-version-file: go.mod
@ -175,8 +197,10 @@ jobs:
if: needs.files-changed.outputs.docs == 'true' || needs.files-changed.outputs.actions == 'true' if: needs.files-changed.outputs.docs == 'true' || needs.files-changed.outputs.actions == 'true'
needs: files-changed needs: files-changed
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions:
contents: read
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- uses: pnpm/action-setup@v4 - uses: pnpm/action-setup@v4
- uses: actions/setup-node@v5 - uses: actions/setup-node@v5
with: with:
@ -188,8 +212,10 @@ jobs:
if: needs.files-changed.outputs.actions == 'true' || needs.files-changed.outputs.actions == 'true' if: needs.files-changed.outputs.actions == 'true' || needs.files-changed.outputs.actions == 'true'
needs: files-changed needs: files-changed
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions:
contents: read
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- uses: actions/setup-go@v6 - uses: actions/setup-go@v6
with: with:
go-version-file: go.mod go-version-file: go.mod

View File

@ -10,11 +10,15 @@ concurrency:
jobs: jobs:
files-changed: files-changed:
uses: ./.github/workflows/files-changed.yml uses: ./.github/workflows/files-changed.yml
permissions:
contents: read
test-pgsql: test-pgsql:
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true' if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
needs: files-changed needs: files-changed
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions:
contents: read
services: services:
pgsql: pgsql:
image: postgres:14 image: postgres:14
@ -38,7 +42,7 @@ jobs:
ports: ports:
- "9000:9000" - "9000:9000"
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- uses: actions/setup-go@v6 - uses: actions/setup-go@v6
with: with:
go-version-file: go.mod go-version-file: go.mod
@ -65,8 +69,10 @@ jobs:
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true' if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
needs: files-changed needs: files-changed
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions:
contents: read
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- uses: actions/setup-go@v6 - uses: actions/setup-go@v6
with: with:
go-version-file: go.mod go-version-file: go.mod
@ -90,6 +96,8 @@ jobs:
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true' if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
needs: files-changed needs: files-changed
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions:
contents: read
services: services:
elasticsearch: elasticsearch:
image: elasticsearch:7.5.0 image: elasticsearch:7.5.0
@ -124,7 +132,7 @@ jobs:
ports: ports:
- 10000:10000 - 10000:10000
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- uses: actions/setup-go@v6 - uses: actions/setup-go@v6
with: with:
go-version-file: go.mod go-version-file: go.mod
@ -152,6 +160,8 @@ jobs:
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true' if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
needs: files-changed needs: files-changed
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions:
contents: read
services: services:
mysql: mysql:
# the bitnami mysql image has more options than the official one, it's easier to customize # the bitnami mysql image has more options than the official one, it's easier to customize
@ -177,7 +187,7 @@ jobs:
- "587:587" - "587:587"
- "993:993" - "993:993"
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- uses: actions/setup-go@v6 - uses: actions/setup-go@v6
with: with:
go-version-file: go.mod go-version-file: go.mod
@ -203,6 +213,8 @@ jobs:
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true' if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
needs: files-changed needs: files-changed
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions:
contents: read
services: services:
mssql: mssql:
image: mcr.microsoft.com/mssql/server:2019-latest image: mcr.microsoft.com/mssql/server:2019-latest
@ -217,7 +229,7 @@ jobs:
ports: ports:
- 10000:10000 - 10000:10000
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- uses: actions/setup-go@v6 - uses: actions/setup-go@v6
with: with:
go-version-file: go.mod go-version-file: go.mod

View File

@ -10,13 +10,17 @@ concurrency:
jobs: jobs:
files-changed: files-changed:
uses: ./.github/workflows/files-changed.yml uses: ./.github/workflows/files-changed.yml
permissions:
contents: read
container: container:
if: needs.files-changed.outputs.docker == 'true' || needs.files-changed.outputs.actions == 'true' if: needs.files-changed.outputs.docker == 'true' || needs.files-changed.outputs.actions == 'true'
needs: files-changed needs: files-changed
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions:
contents: read
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- uses: docker/setup-buildx-action@v3 - uses: docker/setup-buildx-action@v3
- name: Build regular container image - name: Build regular container image
uses: docker/build-push-action@v5 uses: docker/build-push-action@v5

View File

@ -11,8 +11,10 @@ concurrency:
jobs: jobs:
nightly-binary: nightly-binary:
runs-on: namespace-profile-gitea-release-binary runs-on: namespace-profile-gitea-release-binary
permissions:
contents: read
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
# fetch all commits instead of only the last as some branches are long lived and could have many between versions # fetch all commits instead of only the last as some branches are long lived and could have many between versions
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567 # fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
- run: git fetch --unshallow --quiet --tags --force - run: git fetch --unshallow --quiet --tags --force
@ -56,12 +58,14 @@ jobs:
- name: upload binaries to s3 - name: upload binaries to s3
run: | run: |
aws s3 sync dist/release s3://${{ secrets.AWS_S3_BUCKET }}/gitea/${{ steps.clean_name.outputs.branch }} --no-progress aws s3 sync dist/release s3://${{ secrets.AWS_S3_BUCKET }}/gitea/${{ steps.clean_name.outputs.branch }} --no-progress
nightly-container: nightly-container:
runs-on: namespace-profile-gitea-release-docker runs-on: namespace-profile-gitea-release-docker
permissions: permissions:
contents: read
packages: write # to publish to ghcr.io packages: write # to publish to ghcr.io
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
# fetch all commits instead of only the last as some branches are long lived and could have many between versions # fetch all commits instead of only the last as some branches are long lived and could have many between versions
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567 # fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
- run: git fetch --unshallow --quiet --tags --force - run: git fetch --unshallow --quiet --tags --force

View File

@ -12,8 +12,10 @@ concurrency:
jobs: jobs:
binary: binary:
runs-on: namespace-profile-gitea-release-binary runs-on: namespace-profile-gitea-release-binary
permissions:
contents: read
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
# fetch all commits instead of only the last as some branches are long lived and could have many between versions # fetch all commits instead of only the last as some branches are long lived and could have many between versions
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567 # fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
- run: git fetch --unshallow --quiet --tags --force - run: git fetch --unshallow --quiet --tags --force
@ -66,12 +68,14 @@ jobs:
gh release create ${{ github.ref_name }} --title ${{ github.ref_name }} --draft --notes-from-tag dist/release/* gh release create ${{ github.ref_name }} --title ${{ github.ref_name }} --draft --notes-from-tag dist/release/*
env: env:
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
container: container:
runs-on: namespace-profile-gitea-release-docker runs-on: namespace-profile-gitea-release-docker
permissions: permissions:
contents: read
packages: write # to publish to ghcr.io packages: write # to publish to ghcr.io
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
# fetch all commits instead of only the last as some branches are long lived and could have many between versions # fetch all commits instead of only the last as some branches are long lived and could have many between versions
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567 # fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
- run: git fetch --unshallow --quiet --tags --force - run: git fetch --unshallow --quiet --tags --force

View File

@ -15,9 +15,10 @@ jobs:
binary: binary:
runs-on: namespace-profile-gitea-release-binary runs-on: namespace-profile-gitea-release-binary
permissions: permissions:
contents: read
packages: write # to publish to ghcr.io packages: write # to publish to ghcr.io
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
# fetch all commits instead of only the last as some branches are long lived and could have many between versions # fetch all commits instead of only the last as some branches are long lived and could have many between versions
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567 # fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
- run: git fetch --unshallow --quiet --tags --force - run: git fetch --unshallow --quiet --tags --force
@ -70,12 +71,14 @@ jobs:
gh release create ${{ github.ref_name }} --title ${{ github.ref_name }} --notes-from-tag dist/release/* gh release create ${{ github.ref_name }} --title ${{ github.ref_name }} --notes-from-tag dist/release/*
env: env:
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
container: container:
runs-on: namespace-profile-gitea-release-docker runs-on: namespace-profile-gitea-release-docker
permissions: permissions:
contents: read
packages: write # to publish to ghcr.io packages: write # to publish to ghcr.io
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
# fetch all commits instead of only the last as some branches are long lived and could have many between versions # fetch all commits instead of only the last as some branches are long lived and could have many between versions
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567 # fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
- run: git fetch --unshallow --quiet --tags --force - run: git fetch --unshallow --quiet --tags --force

View File

@ -62,7 +62,28 @@ type PackageMetadata struct {
Author User `json:"author"` Author User `json:"author"`
ReadmeFilename string `json:"readmeFilename,omitempty"` ReadmeFilename string `json:"readmeFilename,omitempty"`
Users map[string]bool `json:"users,omitempty"` Users map[string]bool `json:"users,omitempty"`
License string `json:"license,omitempty"` License License `json:"license,omitempty"`
}
type License string
func (l *License) UnmarshalJSON(data []byte) error {
switch data[0] {
case '"':
var value string
if err := json.Unmarshal(data, &value); err != nil {
return err
}
*l = License(value)
case '{':
var values map[string]any
if err := json.Unmarshal(data, &values); err != nil {
return err
}
value, _ := values["type"].(string)
*l = License(value)
}
return nil
} }
// PackageMetadataVersion documentation: https://github.com/npm/registry/blob/master/docs/REGISTRY-API.md#version // PackageMetadataVersion documentation: https://github.com/npm/registry/blob/master/docs/REGISTRY-API.md#version
@ -74,7 +95,7 @@ type PackageMetadataVersion struct {
Description string `json:"description"` Description string `json:"description"`
Author User `json:"author"` Author User `json:"author"`
Homepage string `json:"homepage,omitempty"` Homepage string `json:"homepage,omitempty"`
License string `json:"license,omitempty"` License License `json:"license,omitempty"`
Repository Repository `json:"repository"` Repository Repository `json:"repository"`
Keywords []string `json:"keywords,omitempty"` Keywords []string `json:"keywords,omitempty"`
Dependencies map[string]string `json:"dependencies,omitempty"` Dependencies map[string]string `json:"dependencies,omitempty"`

View File

@ -13,6 +13,7 @@ import (
"code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/json"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
func TestParsePackage(t *testing.T) { func TestParsePackage(t *testing.T) {
@ -291,11 +292,36 @@ func TestParsePackage(t *testing.T) {
assert.Equal(t, packageDescription, p.Metadata.Readme) assert.Equal(t, packageDescription, p.Metadata.Readme)
assert.Equal(t, packageAuthor, p.Metadata.Author) assert.Equal(t, packageAuthor, p.Metadata.Author)
assert.Equal(t, packageBin, p.Metadata.Bin["bin"]) assert.Equal(t, packageBin, p.Metadata.Bin["bin"])
assert.Equal(t, "MIT", p.Metadata.License) assert.Equal(t, "MIT", string(p.Metadata.License))
assert.Equal(t, "https://gitea.io/", p.Metadata.ProjectURL) assert.Equal(t, "https://gitea.io/", p.Metadata.ProjectURL)
assert.Contains(t, p.Metadata.Dependencies, "package") assert.Contains(t, p.Metadata.Dependencies, "package")
assert.Equal(t, "1.2.0", p.Metadata.Dependencies["package"]) assert.Equal(t, "1.2.0", p.Metadata.Dependencies["package"])
assert.Equal(t, repository.Type, p.Metadata.Repository.Type) assert.Equal(t, repository.Type, p.Metadata.Repository.Type)
assert.Equal(t, repository.URL, p.Metadata.Repository.URL) assert.Equal(t, repository.URL, p.Metadata.Repository.URL)
}) })
t.Run("ValidLicenseMap", func(t *testing.T) {
packageJSON := `{
"versions": {
"0.1.1": {
"name": "dev-null",
"version": "0.1.1",
"license": {
"type": "MIT"
},
"dist": {
"integrity": "sha256-"
}
}
},
"_attachments": {
"foo": {
"data": "AAAA"
}
}
}`
p, err := ParsePackage(strings.NewReader(packageJSON))
require.NoError(t, err)
require.Equal(t, "MIT", string(p.Metadata.License))
})
} }

View File

@ -12,7 +12,7 @@ type Metadata struct {
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"` Description string `json:"description,omitempty"`
Author string `json:"author,omitempty"` Author string `json:"author,omitempty"`
License string `json:"license,omitempty"` License License `json:"license,omitempty"`
ProjectURL string `json:"project_url,omitempty"` ProjectURL string `json:"project_url,omitempty"`
Keywords []string `json:"keywords,omitempty"` Keywords []string `json:"keywords,omitempty"`
Dependencies map[string]string `json:"dependencies,omitempty"` Dependencies map[string]string `json:"dependencies,omitempty"`

View File

@ -36,9 +36,7 @@ func CherryPick(ctx *context.Context) {
ctx.Data["commit_message"] = "revert " + cherryPickCommit.Message() ctx.Data["commit_message"] = "revert " + cherryPickCommit.Message()
} else { } else {
ctx.Data["CherryPickType"] = "cherry-pick" ctx.Data["CherryPickType"] = "cherry-pick"
splits := strings.SplitN(cherryPickCommit.Message(), "\n", 2) ctx.Data["commit_summary"], ctx.Data["commit_message"], _ = strings.Cut(cherryPickCommit.Message(), "\n")
ctx.Data["commit_summary"] = splits[0]
ctx.Data["commit_message"] = splits[1]
} }
ctx.HTML(http.StatusOK, tplCherryPick) ctx.HTML(http.StatusOK, tplCherryPick)

View File

@ -4,10 +4,8 @@
package sender package sender
import ( import (
"errors"
"io" "io"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
) )
type Sender interface { type Sender interface {
@ -16,23 +14,18 @@ type Sender interface {
var Send = send var Send = send
func send(sender Sender, msgs ...*Message) error { func send(sender Sender, msg *Message) error {
if setting.MailService == nil { m := msg.ToMessage()
log.Error("Mailer: Send is being invoked but mail service hasn't been initialized") froms := m.GetFrom()
return nil to, err := m.GetRecipients()
if err != nil {
return err
} }
for _, msg := range msgs {
m := msg.ToMessage()
froms := m.GetFrom()
to, err := m.GetRecipients()
if err != nil {
return err
}
// TODO: implement sending from multiple addresses // TODO: implement sending from multiple addresses
if err := sender.Send(froms[0].Address, to, m); err != nil { if len(froms) == 0 {
return err // FIXME: no idea why sometimes the "froms" can be empty, need to figure out the root problem
} return errors.New("no FROM specified")
} }
return nil return sender.Send(froms[0].Address, to, m)
} }

View File

@ -177,7 +177,7 @@ func substGiteaTemplateFile(ctx context.Context, tmpDir, tmpDirSubPath string, t
} }
generatedContent := generateExpansion(ctx, string(content), templateRepo, generateRepo) generatedContent := generateExpansion(ctx, string(content), templateRepo, generateRepo)
substSubPath := filepath.Clean(filePathSanitize(generateExpansion(ctx, tmpDirSubPath, templateRepo, generateRepo))) substSubPath := filePathSanitize(generateExpansion(ctx, tmpDirSubPath, templateRepo, generateRepo))
newLocalPath := filepath.Join(tmpDir, substSubPath) newLocalPath := filepath.Join(tmpDir, substSubPath)
regular, err := util.IsRegularFile(newLocalPath) regular, err := util.IsRegularFile(newLocalPath)
if canWrite := regular || errors.Is(err, fs.ErrNotExist); !canWrite { if canWrite := regular || errors.Is(err, fs.ErrNotExist); !canWrite {
@ -358,5 +358,5 @@ func filePathSanitize(s string) string {
} }
fields[i] = field fields[i] = field
} }
return filepath.FromSlash(strings.Join(fields, "/")) return filepath.Clean(filepath.FromSlash(strings.Trim(strings.Join(fields, "/"), "/")))
} }

View File

@ -54,19 +54,24 @@ text/*.txt
} }
func TestFilePathSanitize(t *testing.T) { func TestFilePathSanitize(t *testing.T) {
assert.Equal(t, "test_CON", filePathSanitize("test_CON")) // path clean
assert.Equal(t, "test CON", filePathSanitize("test CON ")) assert.Equal(t, "a", filePathSanitize("//a/"))
assert.Equal(t, "__/traverse/__", filePathSanitize(".. /traverse/ ..")) assert.Equal(t, "_a", filePathSanitize(`\a`))
assert.Equal(t, "./__/a/_git/b_", filePathSanitize("./../a/.git/ b: ")) assert.Equal(t, "__/a/__", filePathSanitize(".. /a/ .."))
assert.Equal(t, "__/a/_git/b_", filePathSanitize("./../a/.git/ b: "))
// Windows reserved names
assert.Equal(t, "_", filePathSanitize("CoN")) assert.Equal(t, "_", filePathSanitize("CoN"))
assert.Equal(t, "_", filePathSanitize("LpT1")) assert.Equal(t, "_", filePathSanitize("LpT1"))
assert.Equal(t, "_", filePathSanitize("CoM1")) assert.Equal(t, "_", filePathSanitize("CoM1"))
assert.Equal(t, "test_CON", filePathSanitize("test_CON"))
assert.Equal(t, "test CON", filePathSanitize("test CON "))
// special chars
assert.Equal(t, "_", filePathSanitize("\u0000")) assert.Equal(t, "_", filePathSanitize("\u0000"))
assert.Equal(t, "目标", filePathSanitize("目标")) assert.Equal(t, ".", filePathSanitize(""))
// unlike filepath.Clean, it only sanitizes, doesn't change the separator layout
assert.Equal(t, "", filePathSanitize("")) //nolint:testifylint // for easy reading
assert.Equal(t, ".", filePathSanitize(".")) assert.Equal(t, ".", filePathSanitize("."))
assert.Equal(t, "/", filePathSanitize("/")) assert.Equal(t, ".", filePathSanitize("/"))
} }
func TestProcessGiteaTemplateFile(t *testing.T) { func TestProcessGiteaTemplateFile(t *testing.T) {

View File

@ -4,7 +4,7 @@
<div class="ui form"> <div class="ui form">
<div class="field"> <div class="field">
<label>{{svg "octicon-terminal"}} {{ctx.Locale.Tr "packages.pypi.install"}}</label> <label>{{svg "octicon-terminal"}} {{ctx.Locale.Tr "packages.pypi.install"}}</label>
<div class="markup"><pre class="code-block"><code>pip install --index-url <origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/pypi/simple/"></origin-url> --extra-index-url https://pypi.org/ {{.PackageDescriptor.Package.Name}}</code></pre></div> <div class="markup"><pre class="code-block"><code>pip install --index-url <origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/pypi/simple/"></origin-url> --extra-index-url https://pypi.org/simple {{.PackageDescriptor.Package.Name}}</code></pre></div>
</div> </div>
<div class="field"> <div class="field">
<label>{{ctx.Locale.Tr "packages.registry.documentation" "PyPI" "https://docs.gitea.com/usage/packages/pypi/"}}</label> <label>{{ctx.Locale.Tr "packages.registry.documentation" "PyPI" "https://docs.gitea.com/usage/packages/pypi/"}}</label>

View File

@ -96,7 +96,7 @@
</div> </div>
{{else if eq .Type 2}} {{else if eq .Type 2}}
<div class="timeline-item event" id="{{.HashTag}}"> <div class="timeline-item event" id="{{.HashTag}}">
<span class="badge tw-bg-red tw-text-white">{{svg "octicon-circle-slash"}}</span> <span class="badge tw-bg-red tw-text-white">{{svg "octicon-issue-closed"}}</span>
{{if not .OriginalAuthor}} {{if not .OriginalAuthor}}
{{template "shared/user/avatarlink" dict "user" .Poster}} {{template "shared/user/avatarlink" dict "user" .Poster}}
{{end}} {{end}}