Compare commits

...

16 Commits

Author SHA1 Message Date
Earl Warren
021c8fe15a Merge pull request '[gitea] week 2024-50 cherry pick (gitea/main -> forgejo)' (#6200) from earl-warren/wcp/2024-50 into forgejo
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6200
Reviewed-by: Otto <otto@codeberg.org>
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
2024-12-10 06:23:36 +00:00
Earl Warren
e6629fa5d1 Merge pull request 'chore(ci): set the milestone when a pull request is closed (take 4)' (#6223) from earl-warren/forgejo:wip-milestones into forgejo
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6223
Reviewed-by: Michael Kriese <michael.kriese@gmx.de>
2024-12-10 06:20:39 +00:00
Earl Warren
a900775ada Merge pull request 'Add - as reserved user' (#6221) from fnetx/- into forgejo
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6221
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
2024-12-10 06:19:26 +00:00
Earl Warren
6f53f7d007
chore(ci): set the milestone when a pull request is closed (take 4)
The milestone can only be determined to be final when a pull request
is merged.

It is possible that a pull request is opened during the development of
v10 and merged after it is published.

It is also possible that it is permanently closed without being merged.
2024-12-09 23:49:33 +01:00
Earl Warren
5cb542e0d9 Merge pull request 'chore(ci): set the milestone when a pull request is open (take 3)' (#6219) from earl-warren/forgejo:wip-milestones into forgejo
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6219
Reviewed-by: Michael Kriese <michael.kriese@gmx.de>
2024-12-09 22:49:05 +00:00
0ko
4fbdd1fc8c ui: add copy path button to file view (#6079)
Port of d11f8d24b0.
Followup to 187e10d8c9.

* removed `aria-label` in the diff template
* changed `Copy to clipboard` to `Copy path`
* left `copy_generic` for now, but it's unused
* ported the addition of this button to the file view template

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6079
Reviewed-by: Otto <otto@codeberg.org>
Co-authored-by: silverwind <me@silverwind.io>
2024-12-09 19:32:16 +00:00
Otto Richter
5f173bdab3 Add - as reserved user
used in routers such as
af640ac4d4/routers/init.go (L185)
2024-12-09 18:45:05 +01:00
Earl Warren
bf9e19cc21
chore(ci): set the milestone when a pull request is open (take 3)
pull_request_target runs from the target branch, not the default branch
2024-12-09 17:49:54 +01:00
Earl Warren
af640ac4d4 Merge pull request 'chore(ci): set the milestone when a pull request is open (take 2)' (#6213) from earl-warren/forgejo:wip-milestones into forgejo
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6213
2024-12-09 16:07:39 +00:00
Earl Warren
ebfe702df6
chore(ci): set the milestone when a pull request is open (take 2)
Use the oci:ci image to get jq
2024-12-09 17:01:35 +01:00
Earl Warren
c325e29200
chore(release-notes): notes for the week 2024-50 weekly cherry pick 2024-12-08 09:10:58 +01:00
metiftikci
b811574d47
fix: render job title as commit message (#32748)
resolves #32724

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
(cherry picked from commit ad994780af48e2aff27b0e7e496fd559dc0fe3fc)

Conflicts:
	routers/web/repo/actions/view.go
  because of RenderCommitMessage context
2024-12-08 08:32:20 +01:00
metiftikci
63faaaeda4
fix(project): add title to project view page (#32747)
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
(cherry picked from commit 6bd70d4d87262e3bf423aa8a598cfdcf3aae4008)
2024-12-08 08:14:25 +01:00
Lunny Xiao
6f2875d3c6
Make wiki pages visit fast (#32732)
(cherry picked from commit b32f0cdfa05c3a0e34425e1b8a5dfa8b63914a01)

Conflicts:
   tests/integration/wiki_test.go
   "Long-Page" is missing as well as the tests package
2024-12-08 08:06:01 +01:00
Lunny Xiao
6ac88eab0f
Remove outdated code about fixture generation (#32708)
(cherry picked from commit e45ffc530f482a46de25d28f18b039f296750414)

Conflicts:
	models/fixture_test.go
  trivial context conflict and remove one line in deadcode
2024-12-08 07:31:32 +01:00
KN4CK3R
0786ddc5de
Add Swift login endpoint (#32693)
Fix #32683

This PR adds the login endpoint and fixes the documentation links.

(cherry picked from commit 136408307c6de7aac2ab5476f8cddf90f39355dc)

Conflicts:
	routers/api/packages/api.go
  trivial context conflicts
2024-12-08 07:19:58 +01:00
19 changed files with 182 additions and 241 deletions

View File

@ -15,7 +15,6 @@ code.gitea.io/gitea/models
ErrUpdateTaskNotExist.Unwrap ErrUpdateTaskNotExist.Unwrap
IsErrSHANotFound IsErrSHANotFound
IsErrMergeDivergingFastForwardOnly IsErrMergeDivergingFastForwardOnly
GetYamlFixturesAccess
code.gitea.io/gitea/models/actions code.gitea.io/gitea/models/actions
ScheduleList.GetUserIDs ScheduleList.GetUserIDs

View File

@ -1,24 +1,19 @@
# Copyright 2024 The Forgejo Authors # Copyright 2024 The Forgejo Authors
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
# #
# This workflow is triggered on pull_request_target and runs from
# the forgejo branch so that it does not need to be backported.
# If it was triggered on pull_request, it would have to exist on all
# stable branches.
#
name: milestone name: milestone
on: on:
pull_request_target: pull_request_target:
types: types:
- opened - closed
jobs: jobs:
set: set:
if: vars.ROLE == 'forgejo-coding' if: vars.ROLE == 'forgejo-coding' && github.event.pull_request.merged
runs-on: docker runs-on: docker
container: container:
image: 'code.forgejo.org/oci/node:20-bookworm' image: 'code.forgejo.org/oci/ci:1'
steps: steps:
- uses: https://code.forgejo.org/forgejo/set-milestone@v1.0.0 - uses: https://code.forgejo.org/forgejo/set-milestone@v1.0.0
with: with:

View File

@ -1,80 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
//nolint:forbidigo
package main
import (
"context"
"fmt"
"os"
"path/filepath"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/unittest"
)
// To generate derivative fixtures, execute the following from Gitea's repository base dir:
// go run -tags 'sqlite sqlite_unlock_notify' contrib/fixtures/fixture_generation.go [fixture...]
var (
generators = []struct {
gen func(ctx context.Context) (string, error)
name string
}{
{
models.GetYamlFixturesAccess, "access",
},
}
fixturesDir string
)
func main() {
pathToGiteaRoot := "."
fixturesDir = filepath.Join(pathToGiteaRoot, "models", "fixtures")
if err := unittest.CreateTestEngine(unittest.FixturesOptions{
Dir: fixturesDir,
}); err != nil {
fmt.Printf("CreateTestEngine: %+v", err)
os.Exit(1)
}
if err := unittest.PrepareTestDatabase(); err != nil {
fmt.Printf("PrepareTestDatabase: %+v\n", err)
os.Exit(1)
}
ctx := context.Background()
if len(os.Args) == 0 {
for _, r := range os.Args {
if err := generate(ctx, r); err != nil {
fmt.Printf("generate '%s': %+v\n", r, err)
os.Exit(1)
}
}
} else {
for _, g := range generators {
if err := generate(ctx, g.name); err != nil {
fmt.Printf("generate '%s': %+v\n", g.name, err)
os.Exit(1)
}
}
}
}
func generate(ctx context.Context, name string) error {
for _, g := range generators {
if g.name == name {
data, err := g.gen(ctx)
if err != nil {
return err
}
path := filepath.Join(fixturesDir, name+".yml")
if err := os.WriteFile(path, []byte(data), 0o644); err != nil {
return fmt.Errorf("%s: %+v", path, err)
}
fmt.Printf("%s created.\n", path)
return nil
}
}
return fmt.Errorf("generator not found")
}

View File

@ -1,50 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package models
import (
"context"
"fmt"
"strings"
"code.gitea.io/gitea/models/db"
access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo"
)
// GetYamlFixturesAccess returns a string containing the contents
// for the access table, as recalculated using repo.RecalculateAccesses()
func GetYamlFixturesAccess(ctx context.Context) (string, error) {
repos := make([]*repo_model.Repository, 0, 50)
if err := db.GetEngine(ctx).Find(&repos); err != nil {
return "", err
}
for _, repo := range repos {
repo.MustOwner(ctx)
if err := access_model.RecalculateAccesses(ctx, repo); err != nil {
return "", err
}
}
var b strings.Builder
accesses := make([]*access_model.Access, 0, 200)
if err := db.GetEngine(ctx).OrderBy("user_id, repo_id").Find(&accesses); err != nil {
return "", err
}
for i, a := range accesses {
fmt.Fprintf(&b, "-\n")
fmt.Fprintf(&b, " id: %d\n", i+1)
fmt.Fprintf(&b, " user_id: %d\n", a.UserID)
fmt.Fprintf(&b, " repo_id: %d\n", a.RepoID)
fmt.Fprintf(&b, " mode: %d\n", a.Mode)
if i < len(accesses)-1 {
fmt.Fprintf(&b, "\n")
}
}
return b.String(), nil
}

View File

@ -1,36 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package models
import (
"context"
"os"
"path/filepath"
"testing"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/util"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestFixtureGeneration(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
test := func(ctx context.Context, gen func(ctx context.Context) (string, error), name string) {
expected, err := gen(ctx)
require.NoError(t, err)
p := filepath.Join(unittest.FixturesDir(), name+".yml")
bytes, err := os.ReadFile(p)
require.NoError(t, err)
data := string(util.NormalizeEOL(bytes))
assert.EqualValues(t, expected, data, "Differences detected for %s", p)
}
test(db.DefaultContext, GetYamlFixturesAccess, "access")
}

View File

@ -586,6 +586,7 @@ var (
reservedUsernames = []string{ reservedUsernames = []string{
".", ".",
"..", "..",
"-", // used by certain web routes
".well-known", ".well-known",
"api", // gitea api "api", // gitea api

View File

@ -107,6 +107,7 @@ copy = Copy
copy_generic = Copy to clipboard copy_generic = Copy to clipboard
copy_url = Copy URL copy_url = Copy URL
copy_hash = Copy hash copy_hash = Copy hash
copy_path = Copy path
copy_content = Copy content copy_content = Copy content
copy_branch = Copy branch name copy_branch = Copy branch name
copy_success = Copied! copy_success = Copied!

1
release-notes/6200.md Normal file
View File

@ -0,0 +1 @@
feat: [commit](https://codeberg.org/forgejo/forgejo/commit/0786ddc5de37a01d1c3e3bf99b794665341b3c12) Add Swift login endpoint

View File

@ -637,6 +637,11 @@ func CommonRoutes() *web.Route {
}, reqPackageAccess(perm.AccessModeWrite)) }, reqPackageAccess(perm.AccessModeWrite))
}, reqPackageAccess(perm.AccessModeRead)) }, reqPackageAccess(perm.AccessModeRead))
r.Group("/swift", func() { r.Group("/swift", func() {
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("/{scope}/{name}", func() {
r.Group("", func() { r.Group("", func() {
r.Get("", swift.EnumeratePackageVersions) r.Get("", swift.EnumeratePackageVersions)
@ -671,6 +676,7 @@ func CommonRoutes() *web.Route {
}) })
r.Get("/identifiers", swift.CheckAcceptMediaType(swift.AcceptJSON), swift.LookupPackageIdentifiers) r.Get("/identifiers", swift.CheckAcceptMediaType(swift.AcceptJSON), swift.LookupPackageIdentifiers)
}, reqPackageAccess(perm.AccessModeRead)) }, reqPackageAccess(perm.AccessModeRead))
})
r.Group("/vagrant", func() { r.Group("/vagrant", func() {
r.Group("/authenticate", func() { r.Group("/authenticate", func() {
r.Get("", vagrant.CheckAuthenticate) r.Get("", vagrant.CheckAuthenticate)

View File

@ -27,7 +27,7 @@ import (
"github.com/hashicorp/go-version" "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 ( const (
AcceptJSON = "application/vnd.swift.registry.v1+json" AcceptJSON = "application/vnd.swift.registry.v1+json"
AcceptSwift = "application/vnd.swift.registry.v1+swift" AcceptSwift = "application/vnd.swift.registry.v1+swift"
@ -35,9 +35,9 @@ const (
) )
var ( 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`) 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`) namePattern = regexp.MustCompile(`\A[a-zA-Z0-9][a-zA-Z0-9-_]{0,99}\z`)
) )
@ -49,7 +49,7 @@ type headers struct {
Link string 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) { func setResponseHeaders(resp http.ResponseWriter, h *headers) {
if h.ContentType != "" { if h.ContentType != "" {
resp.Header().Set("Content-Type", 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) { func apiError(ctx *context.Context, status int, obj any) {
// https://www.rfc-editor.org/rfc/rfc7807 // https://www.rfc-editor.org/rfc/rfc7807
type Problem struct { 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) { func CheckAcceptMediaType(requiredAcceptHeader string) func(ctx *context.Context) {
return func(ctx *context.Context) { return func(ctx *context.Context) {
accept := ctx.Req.Header.Get("Accept") 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 { func buildPackageID(scope, name string) string {
return scope + "." + name return scope + "." + name
} }
@ -113,7 +123,7 @@ type EnumeratePackageVersionsResponse struct {
Releases map[string]Release `json:"releases"` 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) { func EnumeratePackageVersions(ctx *context.Context) {
packageScope := ctx.Params("scope") packageScope := ctx.Params("scope")
packageName := ctx.Params("name") packageName := ctx.Params("name")
@ -170,7 +180,7 @@ type PackageVersionMetadataResponse struct {
Metadata *swift_module.SoftwareSourceCode `json:"metadata"` 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) { func PackageVersionMetadata(ctx *context.Context) {
id := buildPackageID(ctx.Params("scope"), ctx.Params("name")) 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) { func DownloadManifest(ctx *context.Context) {
packageScope := ctx.Params("scope") packageScope := ctx.Params("scope")
packageName := ctx.Params("name") 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) { func UploadPackageFile(ctx *context.Context) {
packageScope := ctx.Params("scope") packageScope := ctx.Params("scope")
packageName := ctx.Params("name") packageName := ctx.Params("name")
@ -379,7 +389,7 @@ func UploadPackageFile(ctx *context.Context) {
ctx.Status(http.StatusCreated) 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) { 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")) 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 { if err != nil {
@ -420,7 +430,7 @@ type LookupPackageIdentifiersResponse struct {
Identifiers []string `json:"identifiers"` 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) { func LookupPackageIdentifiers(ctx *context.Context) {
url := ctx.FormTrim("url") url := ctx.FormTrim("url")
if url == "" { if url == "" {

View File

@ -10,6 +10,7 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"html/template"
"io" "io"
"net/http" "net/http"
"net/url" "net/url"
@ -25,6 +26,7 @@ import (
"code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/storage"
"code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/modules/web"
@ -110,6 +112,7 @@ type ViewResponse struct {
Run struct { Run struct {
Link string `json:"link"` Link string `json:"link"`
Title string `json:"title"` Title string `json:"title"`
TitleHTML template.HTML `json:"titleHTML"`
Status string `json:"status"` Status string `json:"status"`
CanCancel bool `json:"canCancel"` CanCancel bool `json:"canCancel"`
CanApprove bool `json:"canApprove"` // the run needs an approval and the doer has permission to approve CanApprove bool `json:"canApprove"` // the run needs an approval and the doer has permission to approve
@ -194,7 +197,10 @@ func ViewPost(ctx *context_module.Context) {
resp := &ViewResponse{} resp := &ViewResponse{}
metas := ctx.Repo.Repository.ComposeMetas(ctx)
resp.State.Run.Title = run.Title resp.State.Run.Title = run.Title
resp.State.Run.TitleHTML = templates.RenderCommitMessage(ctx, run.Title, metas)
resp.State.Run.Link = run.Link() resp.State.Run.Link = run.Link()
resp.State.Run.CanCancel = !run.Status.IsDone() && ctx.Repo.CanWrite(unit.TypeActions) resp.State.Run.CanCancel = !run.Status.IsDone() && ctx.Repo.CanWrite(unit.TypeActions)
resp.State.Run.CanApprove = run.NeedApproval && ctx.Repo.CanWrite(unit.TypeActions) resp.State.Run.CanApprove = run.NeedApproval && ctx.Repo.CanWrite(unit.TypeActions)

View File

@ -363,6 +363,7 @@ func ViewProject(ctx *context.Context) {
return return
} }
ctx.Data["Title"] = project.Title
ctx.Data["IsProjectsPage"] = true ctx.Data["IsProjectsPage"] = true
ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects) ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects)
ctx.Data["Project"] = project ctx.Data["Project"] = project

View File

@ -6,6 +6,7 @@ package repo
import ( import (
"bytes" "bytes"
gocontext "context"
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
@ -598,22 +599,32 @@ func WikiPages(ctx *context.Context) {
} }
}() }()
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 { if err != nil {
ctx.ServerError("ListEntries", err) ctx.ServerError("ListEntries", err)
return return
} }
pages := make([]PageMeta, 0, len(entries)) allEntries.CustomSort(base.NaturalSortLess)
for _, entry := range entries {
if !entry.IsRegular() { entries, _, err := allEntries.GetCommitsInfo(gocontext.Context(ctx), commit, treePath)
continue
}
c, err := wikiRepo.GetCommitByPath(entry.Name())
if err != nil { if err != nil {
ctx.ServerError("GetCommit", err) ctx.ServerError("GetCommitsInfo", err)
return return
} }
wikiName, err := wiki_service.GitPathToWebPath(entry.Name())
pages := make([]PageMeta, 0, len(entries))
for _, entry := range entries {
if !entry.Entry.IsRegular() {
continue
}
wikiName, err := wiki_service.GitPathToWebPath(entry.Entry.Name())
if err != nil { if err != nil {
if repo_model.IsErrWikiInvalidFileName(err) { if repo_model.IsErrWikiInvalidFileName(err) {
continue continue
@ -625,8 +636,8 @@ func WikiPages(ctx *context.Context) {
pages = append(pages, PageMeta{ pages = append(pages, PageMeta{
Name: displayName, Name: displayName,
SubURL: wiki_service.WebPathToURLPath(wikiName), SubURL: wiki_service.WebPathToURLPath(wikiName),
GitEntryName: entry.Name(), GitEntryName: entry.Entry.Name(),
UpdatedUnix: timeutil.TimeStamp(c.Author.When.Unix()), UpdatedUnix: timeutil.TimeStamp(entry.Commit.Author.When.Unix()),
}) })
} }
ctx.Data["Pages"] = pages ctx.Data["Pages"] = pages

View File

@ -130,7 +130,7 @@
</div> </div>
<span class="file tw-flex tw-items-center tw-font-mono tw-flex-1"><a class="muted file-link" title="{{if $file.IsRenamed}}{{$file.OldName}}{{end}}{{$file.Name}}" href="#diff-{{$file.NameHash}}">{{if $file.IsRenamed}}{{$file.OldName}}{{end}}{{$file.Name}}</a> <span class="file tw-flex tw-items-center tw-font-mono tw-flex-1"><a class="muted file-link" title="{{if $file.IsRenamed}}{{$file.OldName}}{{end}}{{$file.Name}}" href="#diff-{{$file.NameHash}}">{{if $file.IsRenamed}}{{$file.OldName}}{{end}}{{$file.Name}}</a>
{{if .IsLFSFile}} ({{ctx.Locale.Tr "repo.stored_lfs"}}){{end}} {{if .IsLFSFile}} ({{ctx.Locale.Tr "repo.stored_lfs"}}){{end}}
<button class="btn interact-fg tw-p-2" data-clipboard-text="{{$file.Name}}" data-tooltip-content="{{ctx.Locale.Tr "copy_generic"}}" aria-label="{{ctx.Locale.Tr "copy_generic"}}">{{svg "octicon-copy" 14}}</button> <button class="btn interact-fg tw-p-2" data-clipboard-text="{{$file.Name}}" data-tooltip-content="{{ctx.Locale.Tr "copy_path"}}">{{svg "octicon-copy" 14}}</button>
{{if $file.IsGenerated}} {{if $file.IsGenerated}}
<span class="ui label">{{ctx.Locale.Tr "repo.diff.generated"}}</span> <span class="ui label">{{ctx.Locale.Tr "repo.diff.generated"}}</span>
{{end}} {{end}}

View File

@ -113,6 +113,7 @@
<span class="breadcrumb-divider">/</span> <span class="breadcrumb-divider">/</span>
{{- if eq $i $l -}} {{- if eq $i $l -}}
<span class="active section" title="{{$v}}">{{$v}}</span> <span class="active section" title="{{$v}}">{{$v}}</span>
<button class="btn interact-fg tw-p-2" data-clipboard-text="{{$.TreePath}}" data-tooltip-content="{{ctx.Locale.Tr "copy_path"}}">{{svg "octicon-copy" 14}}</button>
{{- else -}} {{- else -}}
{{$p := index $.Paths $i}}<span class="section"><a href="{{$.BranchLink}}/{{PathEscapeSegments $p}}" title="{{$v}}">{{$v}}</a></span> {{$p := index $.Paths $i}}<span class="section"><a href="{{$.BranchLink}}/{{PathEscapeSegments $p}}" title="{{$v}}">{{$v}}</a></span>
{{- end -}} {{- end -}}

View File

@ -0,0 +1,33 @@
// Copyright 2024 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: GPL-3.0-or-later
// @watch start
// templates/repo/home.tmpl
// templates/repo/diff/box.tmpl
// web_src/js/features/clipboard.js
// @watch end
import {expect} from '@playwright/test';
import {test} from './utils_e2e.ts';
test.use({
permissions: ['clipboard-write'],
});
test('copy src file path to clipboard', async ({page}) => {
const response = await page.goto('/user2/repo1/src/branch/master/README.md');
expect(response?.status()).toBe(200);
await page.click('[data-clipboard-text]');
const clipboardText = await page.evaluate(() => navigator.clipboard.readText());
expect(clipboardText).toContain('README.md');
});
test('copy diff file path to clipboard', async ({page}) => {
const response = await page.goto('/user2/repo1/src/commit/65f1bf27bc3bf70f64657658635e66094edbcb4d/README.md');
expect(response?.status()).toBe(200);
await page.click('[data-clipboard-text]');
const clipboardText = await page.evaluate(() => navigator.clipboard.readText());
expect(clipboardText).toContain('README.md');
});

View File

@ -43,6 +43,24 @@ func TestPackageSwift(t *testing.T) {
url := fmt.Sprintf("/api/packages/%s/swift", user.Name) 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) { t.Run("CheckAcceptMediaType", func(t *testing.T) {
defer tests.PrintCurrentTest(t)() defer tests.PrintCurrentTest(t)()

View File

@ -6,14 +6,18 @@ package integration
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
"net/url" "net/url"
"os" "os"
"path/filepath" "path/filepath"
"strings"
"testing" "testing"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/tests"
"github.com/PuerkitoBio/goquery"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -47,3 +51,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", "Long-Page", "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

@ -42,6 +42,7 @@ const sfc = {
run: { run: {
link: '', link: '',
title: '', title: '',
titleHTML: '',
status: '', status: '',
canCancel: false, canCancel: false,
canApprove: false, canApprove: false,
@ -424,9 +425,8 @@ export function initRepositoryActionView() {
<div class="action-info-summary"> <div class="action-info-summary">
<div class="action-info-summary-title"> <div class="action-info-summary-title">
<ActionRunStatus :locale-status="locale.status[run.status]" :status="run.status" :size="20"/> <ActionRunStatus :locale-status="locale.status[run.status]" :status="run.status" :size="20"/>
<h2 class="action-info-summary-title-text"> <!-- eslint-disable-next-line vue/no-v-html -->
{{ run.title }} <h2 class="action-info-summary-title-text" v-html="run.titleHTML"/>
</h2>
</div> </div>
<button class="ui basic small compact button primary" @click="approveRun()" v-if="run.canApprove"> <button class="ui basic small compact button primary" @click="approveRun()" v-if="run.canApprove">
{{ locale.approve }} {{ locale.approve }}