+
{{template "base/footer" .}}
diff --git a/templates/repo/editor/edit.tmpl b/templates/repo/editor/edit.tmpl
index 0911d02e1f..e6b9c55770 100644
--- a/templates/repo/editor/edit.tmpl
+++ b/templates/repo/editor/edit.tmpl
@@ -1,53 +1,59 @@
{{template "base/head" .}}
{{template "base/alert" .}}
-
+
+ {{template "repo/view_file_tree" .}}
+
+
+
+
{{template "repo/header" .}}
-
+
{{template "base/footer" .}}
diff --git a/templates/repo/editor/upload.tmpl b/templates/repo/editor/upload.tmpl
index 3e36c77b3b..847d6df88d 100644
--- a/templates/repo/editor/upload.tmpl
+++ b/templates/repo/editor/upload.tmpl
@@ -1,19 +1,25 @@
{{template "base/head" .}}
{{template "base/alert" .}}
-
{{template "repo/header" .}}
-
+
{{template "base/alert" .}}
-
- {{.CsrfTokenHtml}}
- {{template "repo/editor/common_top" .}}
-
{{template "base/footer" .}}
diff --git a/templates/repo/find/files.tmpl b/templates/repo/find/files.tmpl
deleted file mode 100644
index ce242796be..0000000000
--- a/templates/repo/find/files.tmpl
+++ /dev/null
@@ -1,21 +0,0 @@
-{{template "base/head" .}}
-
- {{template "repo/editor/common_breadcrumb" .}}
+
+ {{template "repo/view_file_tree" .}}
+
+
+ {{.CsrfTokenHtml}}
+ {{template "repo/editor/common_top" .}}
+
-
+ {{template "repo/view_file_tree_toggle_button" .}}
+ {{template "repo/editor/common_breadcrumb" .}}
+
+
+ {{template "repo/upload" .}}
+
+ {{template "repo/editor/commit_form" .}}
+
- {{template "repo/upload" .}}
-
- {{template "repo/editor/commit_form" .}}
-
+
- {{template "repo/header" .}}
-
-
-{{template "base/footer" .}}
diff --git a/templates/repo/view.tmpl b/templates/repo/view.tmpl
index f99fe2f57a..99f2a7da7e 100644
--- a/templates/repo/view.tmpl
+++ b/templates/repo/view.tmpl
@@ -17,9 +17,7 @@
{{template "repo/code/recently_pushed_new_branches" dict "RecentBranchesPromptData" .RecentBranchesPromptData}}
-
- {{template "repo/view_file_tree" .}}
-
+ {{template "repo/view_file_tree" .}}
{{template "repo/view_content" .}}
diff --git a/templates/repo/view_content.tmpl b/templates/repo/view_content.tmpl
index 66e4fffcb9..b31648fbbe 100644
--- a/templates/repo/view_content.tmpl
+++ b/templates/repo/view_content.tmpl
@@ -5,11 +5,7 @@
{{if not $isTreePathRoot}}
-
+ {{template "repo/view_file_tree_toggle_button" .}}
{{end}}
{{template "repo/branch_dropdown" dict
@@ -37,31 +33,6 @@
{{end}}
-
- {{if $isTreePathRoot}}
- {{ctx.Locale.Tr "repo.find_file.go_to_file"}}
- {{end}}
-
- {{if and .RefFullName.IsBranch (not .IsViewFile)}}
-
- {{end}}
-
{{if and $isTreePathRoot .Repository.IsTemplate}}
{{ctx.Locale.Tr "repo.use_template"}}
@@ -86,12 +57,65 @@
+
+
+ {{if .RefFullName.IsBranch}}
+ {{$addFilePath := .TreePath}}
+ {{if .IsViewFile}}
+ {{if gt (len .TreeNames) 1}}
+ {{$addFilePath = StringUtils.Join (slice .TreeNames 0 (Eval (len .TreeNames) "-" 1)) "/"}}
+ {{else}}
+ {{$addFilePath = ""}}
+ {{end}}
+ {{end}}
+
+
+ {{if and (not .IsViewFile) (not $isTreePathRoot)}}
+
+ {{end}}
+ {{end}}
{{if $isTreePathRoot}}
{{template "repo/clone_panel" .}}
{{end}}
{{if and (not $isTreePathRoot) (not .IsViewFile) (not .IsBlame)}}{{/* IsViewDirectory (not home), TODO: split the templates, avoid using "if" tricks */}}
-
+
{{svg "octicon-history" 16 "tw-mr-2"}}{{ctx.Locale.Tr "repo.file_history"}}
{{end}}
diff --git a/templates/repo/view_file_tree.tmpl b/templates/repo/view_file_tree.tmpl
index 8aed05f346..f79fcc22aa 100644
--- a/templates/repo/view_file_tree.tmpl
+++ b/templates/repo/view_file_tree.tmpl
@@ -1,15 +1,17 @@
-
-
-
- {{ctx.Locale.Tr "files"}}
-
+
+
diff --git a/templates/repo/view_file_tree_toggle_button.tmpl b/templates/repo/view_file_tree_toggle_button.tmpl
new file mode 100644
index 0000000000..3d6ea928ed
--- /dev/null
+++ b/templates/repo/view_file_tree_toggle_button.tmpl
@@ -0,0 +1,6 @@
+
diff --git a/templates/repo/view_list.tmpl b/templates/repo/view_list.tmpl
index 145494aa1a..61443ac465 100644
--- a/templates/repo/view_list.tmpl
+++ b/templates/repo/view_list.tmpl
@@ -47,7 +47,7 @@
{{end}}
{{end}}
+
+ {{ctx.Locale.Tr "files"}}
+
-{{/* TODO: Dynamically move components such as refSelector and createPR here */}}
-
+ {{/* TODO: Dynamically move components such as refSelector and createPR here */}}
+
+
+
{{if $commit}}
{{$commitLink := printf "%s/commit/%s" $.RepoLink (PathEscape $commit.ID.String)}}
{{ctx.RenderUtils.RenderCommitMessageLinkSubject $commit.Message $commitLink $.Repository}}
diff --git a/tests/integration/actions_trigger_test.go b/tests/integration/actions_trigger_test.go
index d5486b6a39..9dc0ddb9df 100644
--- a/tests/integration/actions_trigger_test.go
+++ b/tests/integration/actions_trigger_test.go
@@ -30,6 +30,7 @@ import (
"code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
+ webhook_module "code.gitea.io/gitea/modules/webhook"
issue_service "code.gitea.io/gitea/services/issue"
pull_service "code.gitea.io/gitea/services/pull"
release_service "code.gitea.io/gitea/services/release"
@@ -1595,3 +1596,56 @@ jobs:
assert.NotNil(t, run)
})
}
+
+func TestPullRequestWithPathsRebase(t *testing.T) {
+ onGiteaRun(t, func(t *testing.T, u *url.URL) {
+ user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ session := loginUser(t, user2.Name)
+ token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
+
+ repoName := "actions-pr-paths-rebase"
+ apiRepo := createActionsTestRepo(t, token, repoName, false)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: apiRepo.ID})
+ apiCtx := NewAPITestContext(t, "user2", repoName, auth_model.AccessTokenScopeWriteRepository)
+ runner := newMockRunner()
+ runner.registerAsRepoRunner(t, "user2", repoName, "mock-runner", []string{"ubuntu-latest"}, false)
+
+ // init files and dirs
+ testCreateFile(t, session, "user2", repoName, repo.DefaultBranch, "", "dir1/dir1.txt", "1")
+ testCreateFile(t, session, "user2", repoName, repo.DefaultBranch, "", "dir2/dir2.txt", "2")
+ wfFileContent := `name: ci
+on:
+ pull_request:
+ paths:
+ - 'dir1/**'
+jobs:
+ ci-job:
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo 'ci'
+`
+ testCreateFile(t, session, "user2", repoName, repo.DefaultBranch, "", ".gitea/workflows/ci.yml", wfFileContent)
+
+ // create a PR to modify "dir1/dir1.txt", the workflow will be triggered
+ testEditFileToNewBranch(t, session, "user2", repoName, repo.DefaultBranch, "update-dir1", "dir1/dir1.txt", "11")
+ _, err := doAPICreatePullRequest(apiCtx, "user2", repoName, repo.DefaultBranch, "update-dir1")(t)
+ assert.NoError(t, err)
+ pr1Task := runner.fetchTask(t)
+ _, _, pr1Run := getTaskAndJobAndRunByTaskID(t, pr1Task.Id)
+ assert.Equal(t, webhook_module.HookEventPullRequest, pr1Run.Event)
+
+ // create a PR to modify "dir2/dir2.txt" then update main branch and rebase, the workflow will not be triggered
+ testEditFileToNewBranch(t, session, "user2", repoName, repo.DefaultBranch, "update-dir2", "dir2/dir2.txt", "22")
+ apiPull, err := doAPICreatePullRequest(apiCtx, "user2", repoName, repo.DefaultBranch, "update-dir2")(t)
+ runner.fetchNoTask(t)
+ assert.NoError(t, err)
+ testEditFile(t, session, "user2", repoName, repo.DefaultBranch, "dir1/dir1.txt", "11") // change the file in "dir1"
+ req := NewRequestWithValues(t, "POST",
+ fmt.Sprintf("/%s/%s/pulls/%d/update?style=rebase", "user2", repoName, apiPull.Index), // update by rebase
+ map[string]string{
+ "_csrf": GetUserCSRFToken(t, session),
+ })
+ session.MakeRequest(t, req, http.StatusSeeOther)
+ runner.fetchNoTask(t)
+ })
+}
diff --git a/tests/integration/api_admin_test.go b/tests/integration/api_admin_test.go
index dbd62c4078..763d4d526b 100644
--- a/tests/integration/api_admin_test.go
+++ b/tests/integration/api_admin_test.go
@@ -382,10 +382,12 @@ func TestAPIEditUser_NotAllowedEmailDomain(t *testing.T) {
SourceID: 0,
Email: &newEmail,
}).AddTokenAuth(token)
- resp := MakeRequest(t, req, http.StatusOK)
- assert.Equal(t, "the domain of user email user2@example1.com conflicts with EMAIL_DOMAIN_ALLOWLIST or EMAIL_DOMAIN_BLOCKLIST", resp.Header().Get("X-Gitea-Warning"))
+ resp := MakeRequest(t, req, http.StatusBadRequest)
+ errMap := make(map[string]string)
+ assert.NoError(t, json.Unmarshal(resp.Body.Bytes(), &errMap))
+ assert.Equal(t, "the domain of user email user2@example1.com conflicts with EMAIL_DOMAIN_ALLOWLIST or EMAIL_DOMAIN_BLOCKLIST", errMap["message"])
- originalEmail := "user2@example.com"
+ originalEmail := "user2@example.org"
req = NewRequestWithJSON(t, "PATCH", urlStr, api.EditUserOption{
LoginName: "user2",
SourceID: 0,
diff --git a/tests/integration/api_repo_file_helpers.go b/tests/integration/api_repo_file_helpers.go
index f8d6fc803f..9a4c448664 100644
--- a/tests/integration/api_repo_file_helpers.go
+++ b/tests/integration/api_repo_file_helpers.go
@@ -5,6 +5,7 @@ package integration
import (
"context"
+ "errors"
"strings"
"testing"
@@ -72,7 +73,7 @@ func deleteFileInBranch(user *user_model.User, repo *repo_model.Repository, tree
func createOrReplaceFileInBranch(user *user_model.User, repo *repo_model.Repository, treePath, branchName, content string) error {
_, err := deleteFileInBranch(user, repo, treePath, branchName)
- if err != nil && !files_service.IsErrRepoFileDoesNotExist(err) {
+ if err != nil && !errors.Is(err, util.ErrNotExist) {
return err
}
diff --git a/tests/integration/git_ssh_redirect_test.go b/tests/integration/git_ssh_redirect_test.go
index 5e35ed2a74..3ae2652412 100644
--- a/tests/integration/git_ssh_redirect_test.go
+++ b/tests/integration/git_ssh_redirect_test.go
@@ -6,9 +6,13 @@ package integration
import (
"fmt"
"net/url"
+ "os"
"testing"
auth_model "code.gitea.io/gitea/models/auth"
+ "code.gitea.io/gitea/modules/structs"
+
+ "github.com/stretchr/testify/assert"
)
func TestGitSSHRedirect(t *testing.T) {
@@ -16,7 +20,8 @@ func TestGitSSHRedirect(t *testing.T) {
}
func testGitSSHRedirect(t *testing.T, u *url.URL) {
- apiTestContext := NewAPITestContext(t, "user2", "repo1", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
+ apiTestContext := NewAPITestContext(t, "user2", "repo1", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser, auth_model.AccessTokenScopeWriteOrganization)
+ session := loginUser(t, "user2")
withKeyFile(t, "my-testing-key", func(keyFile string) {
t.Run("CreateUserKey", doAPICreateUserKey(apiTestContext, "test-key", keyFile))
@@ -38,5 +43,39 @@ func testGitSSHRedirect(t *testing.T, u *url.URL) {
t.Run("Clone", doGitClone(t.TempDir(), cloneURL))
})
}
+
+ doAPICreateOrganization(apiTestContext, &structs.CreateOrgOption{
+ UserName: "olduser2",
+ FullName: "Old User2",
+ })(t)
+
+ cloneURL := createSSHUrl("olduser2/repo1.git", u)
+ t.Run("Clone Should Fail", doGitCloneFail(cloneURL))
+
+ doAPICreateOrganizationRepository(apiTestContext, "olduser2", &structs.CreateRepoOption{
+ Name: "repo1",
+ AutoInit: true,
+ })(t)
+ testEditFile(t, session, "olduser2", "repo1", "master", "README.md", "This is olduser2's repo1\n")
+
+ dstDir := t.TempDir()
+ t.Run("Clone", doGitClone(dstDir, cloneURL))
+ readMEContent, err := os.ReadFile(dstDir + "/README.md")
+ assert.NoError(t, err)
+ assert.Equal(t, "This is olduser2's repo1\n", string(readMEContent))
+
+ apiTestContext2 := NewAPITestContext(t, "user2", "oldrepo1", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser, auth_model.AccessTokenScopeWriteOrganization)
+ doAPICreateRepository(apiTestContext2, false)(t)
+ testEditFile(t, session, "user2", "oldrepo1", "master", "README.md", "This is user2's oldrepo1\n")
+
+ dstDir = t.TempDir()
+ cloneURL = createSSHUrl("user2/oldrepo1.git", u)
+ t.Run("Clone", doGitClone(dstDir, cloneURL))
+ readMEContent, err = os.ReadFile(dstDir + "/README.md")
+ assert.NoError(t, err)
+ assert.Equal(t, "This is user2's oldrepo1\n", string(readMEContent))
+
+ cloneURL = createSSHUrl("olduser2/oldrepo1.git", u)
+ t.Run("Clone Should Fail", doGitCloneFail(cloneURL))
})
}
diff --git a/tests/integration/repofiles_change_test.go b/tests/integration/repofiles_change_test.go
index 6821f8bf61..6fd42401c5 100644
--- a/tests/integration/repofiles_change_test.go
+++ b/tests/integration/repofiles_change_test.go
@@ -5,6 +5,7 @@ package integration
import (
"fmt"
+ "net/http"
"net/url"
"path"
"strings"
@@ -12,7 +13,6 @@ import (
"time"
repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/setting"
@@ -22,6 +22,7 @@ import (
files_service "code.gitea.io/gitea/services/repository/files"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func getCreateRepoFilesOptions(repo *repo_model.Repository) *files_service.ChangeRepoFilesOptions {
@@ -93,55 +94,6 @@ func getUpdateRepoFilesRenameOptions(repo *repo_model.Repository) *files_service
}
}
-func getDeleteRepoFilesOptions(repo *repo_model.Repository) *files_service.ChangeRepoFilesOptions {
- return &files_service.ChangeRepoFilesOptions{
- Files: []*files_service.ChangeRepoFile{
- {
- Operation: "delete",
- TreePath: "README.md",
- SHA: "4b4851ad51df6a7d9f25c979345979eaeb5b349f",
- },
- },
- LastCommitID: "",
- OldBranch: repo.DefaultBranch,
- NewBranch: repo.DefaultBranch,
- Message: "Deletes README.md",
- Author: &files_service.IdentityOptions{
- GitUserName: "Bob Smith",
- GitUserEmail: "bob@smith.com",
- },
- Committer: nil,
- }
-}
-
-func getExpectedFileResponseForRepoFilesDelete() *api.FileResponse {
- // Just returns fields that don't change, i.e. fields with commit SHAs and dates can't be determined
- return &api.FileResponse{
- Content: nil,
- Commit: &api.FileCommitResponse{
- Author: &api.CommitUser{
- Identity: api.Identity{
- Name: "Bob Smith",
- Email: "bob@smith.com",
- },
- },
- Committer: &api.CommitUser{
- Identity: api.Identity{
- Name: "Bob Smith",
- Email: "bob@smith.com",
- },
- },
- Message: "Deletes README.md\n",
- },
- Verification: &api.PayloadCommitVerification{
- Verified: false,
- Reason: "gpg.error.not_signed_commit",
- Signature: "",
- Payload: "",
- },
- }
-}
-
func getExpectedFileResponseForRepoFilesCreate(commitID string, lastCommit *git.Commit) *api.FileResponse {
treePath := "new/file.txt"
encoding := "base64"
@@ -578,75 +530,88 @@ func TestChangeRepoFilesWithoutBranchNames(t *testing.T) {
}
func TestChangeRepoFilesForDelete(t *testing.T) {
- onGiteaRun(t, testDeleteRepoFiles)
-}
+ onGiteaRun(t, func(t *testing.T, u *url.URL) {
+ ctx, _ := contexttest.MockContext(t, "user2/repo1")
+ ctx.SetPathParam("id", "1")
+ contexttest.LoadRepo(t, ctx, 1)
+ contexttest.LoadRepoCommit(t, ctx)
+ contexttest.LoadUser(t, ctx, 2)
+ contexttest.LoadGitRepo(t, ctx)
+ defer ctx.Repo.GitRepo.Close()
+ repo := ctx.Repo.Repository
+ doer := ctx.Doer
-func testDeleteRepoFiles(t *testing.T, u *url.URL) {
- // setup
- unittest.PrepareTestEnv(t)
- ctx, _ := contexttest.MockContext(t, "user2/repo1")
- ctx.SetPathParam("id", "1")
- contexttest.LoadRepo(t, ctx, 1)
- contexttest.LoadRepoCommit(t, ctx)
- contexttest.LoadUser(t, ctx, 2)
- contexttest.LoadGitRepo(t, ctx)
- defer ctx.Repo.GitRepo.Close()
- repo := ctx.Repo.Repository
- doer := ctx.Doer
- opts := getDeleteRepoFilesOptions(repo)
+ t.Run("Delete README.md by commit", func(t *testing.T) {
+ urlRaw := "/user2/repo1/raw/branch/branch2/README.md"
+ MakeRequest(t, NewRequest(t, "GET", urlRaw), http.StatusOK)
+ opts := &files_service.ChangeRepoFilesOptions{
+ OldBranch: "branch2",
+ LastCommitID: "985f0301dba5e7b34be866819cd15ad3d8f508ee",
+ Files: []*files_service.ChangeRepoFile{
+ {
+ Operation: "delete",
+ TreePath: "README.md",
+ },
+ },
+ Message: "test message",
+ }
+ filesResponse, err := files_service.ChangeRepoFiles(t.Context(), repo, doer, opts)
+ require.NoError(t, err)
+ assert.NotNil(t, filesResponse)
+ assert.Nil(t, filesResponse.Files[0])
+ assert.Equal(t, "test message\n", filesResponse.Commit.Message)
+ MakeRequest(t, NewRequest(t, "GET", urlRaw), http.StatusNotFound)
+ })
- t.Run("Delete README.md file", func(t *testing.T) {
- filesResponse, err := files_service.ChangeRepoFiles(t.Context(), repo, doer, opts)
- assert.NoError(t, err)
- expectedFileResponse := getExpectedFileResponseForRepoFilesDelete()
- assert.NotNil(t, filesResponse)
- assert.Nil(t, filesResponse.Files[0])
- assert.Equal(t, expectedFileResponse.Commit.Message, filesResponse.Commit.Message)
- assert.Equal(t, expectedFileResponse.Commit.Author.Identity, filesResponse.Commit.Author.Identity)
- assert.Equal(t, expectedFileResponse.Commit.Committer.Identity, filesResponse.Commit.Committer.Identity)
- assert.Equal(t, expectedFileResponse.Verification, filesResponse.Verification)
- })
+ t.Run("Delete README.md with options", func(t *testing.T) {
+ urlRaw := "/user2/repo1/raw/branch/master/README.md"
+ MakeRequest(t, NewRequest(t, "GET", urlRaw), http.StatusOK)
+ opts := &files_service.ChangeRepoFilesOptions{
+ Files: []*files_service.ChangeRepoFile{
+ {
+ Operation: "delete",
+ TreePath: "README.md",
+ SHA: "4b4851ad51df6a7d9f25c979345979eaeb5b349f",
+ },
+ },
+ OldBranch: repo.DefaultBranch,
+ NewBranch: repo.DefaultBranch,
+ Message: "Message for deleting README.md",
+ Author: &files_service.IdentityOptions{GitUserName: "Bob Smith", GitUserEmail: "bob@smith.com"},
+ }
+ filesResponse, err := files_service.ChangeRepoFiles(t.Context(), repo, doer, opts)
+ require.NoError(t, err)
+ require.NotNil(t, filesResponse)
+ assert.Nil(t, filesResponse.Files[0])
+ assert.Equal(t, "Message for deleting README.md\n", filesResponse.Commit.Message)
+ assert.Equal(t, api.Identity{Name: "Bob Smith", Email: "bob@smith.com"}, filesResponse.Commit.Author.Identity)
+ assert.Equal(t, api.Identity{Name: "Bob Smith", Email: "bob@smith.com"}, filesResponse.Commit.Committer.Identity)
+ assert.Equal(t, &api.PayloadCommitVerification{Reason: "gpg.error.not_signed_commit"}, filesResponse.Verification)
+ MakeRequest(t, NewRequest(t, "GET", urlRaw), http.StatusNotFound)
+ })
- t.Run("Verify README.md has been deleted", func(t *testing.T) {
- filesResponse, err := files_service.ChangeRepoFiles(t.Context(), repo, doer, opts)
- assert.Nil(t, filesResponse)
- expectedError := "repository file does not exist [path: " + opts.Files[0].TreePath + "]"
- assert.EqualError(t, err, expectedError)
- })
-}
-
-// Test opts with branch names removed, same results
-func TestChangeRepoFilesForDeleteWithoutBranchNames(t *testing.T) {
- onGiteaRun(t, testDeleteRepoFilesWithoutBranchNames)
-}
-
-func testDeleteRepoFilesWithoutBranchNames(t *testing.T, u *url.URL) {
- // setup
- unittest.PrepareTestEnv(t)
- ctx, _ := contexttest.MockContext(t, "user2/repo1")
- ctx.SetPathParam("id", "1")
- contexttest.LoadRepo(t, ctx, 1)
- contexttest.LoadRepoCommit(t, ctx)
- contexttest.LoadUser(t, ctx, 2)
- contexttest.LoadGitRepo(t, ctx)
- defer ctx.Repo.GitRepo.Close()
-
- repo := ctx.Repo.Repository
- doer := ctx.Doer
- opts := getDeleteRepoFilesOptions(repo)
- opts.OldBranch = ""
- opts.NewBranch = ""
-
- t.Run("Delete README.md without Branch Name", func(t *testing.T) {
- filesResponse, err := files_service.ChangeRepoFiles(t.Context(), repo, doer, opts)
- assert.NoError(t, err)
- expectedFileResponse := getExpectedFileResponseForRepoFilesDelete()
- assert.NotNil(t, filesResponse)
- assert.Nil(t, filesResponse.Files[0])
- assert.Equal(t, expectedFileResponse.Commit.Message, filesResponse.Commit.Message)
- assert.Equal(t, expectedFileResponse.Commit.Author.Identity, filesResponse.Commit.Author.Identity)
- assert.Equal(t, expectedFileResponse.Commit.Committer.Identity, filesResponse.Commit.Committer.Identity)
- assert.Equal(t, expectedFileResponse.Verification, filesResponse.Verification)
+ t.Run("Delete directory", func(t *testing.T) {
+ urlRaw := "/user2/repo1/raw/branch/sub-home-md-img-check/docs/README.md"
+ MakeRequest(t, NewRequest(t, "GET", urlRaw), http.StatusOK)
+ opts := &files_service.ChangeRepoFilesOptions{
+ OldBranch: "sub-home-md-img-check",
+ LastCommitID: "4649299398e4d39a5c09eb4f534df6f1e1eb87cc",
+ Files: []*files_service.ChangeRepoFile{
+ {
+ Operation: "delete",
+ TreePath: "docs",
+ DeleteRecursively: true,
+ },
+ },
+ Message: "test message",
+ }
+ filesResponse, err := files_service.ChangeRepoFiles(t.Context(), repo, doer, opts)
+ require.NoError(t, err)
+ assert.NotNil(t, filesResponse)
+ assert.Nil(t, filesResponse.Files[0])
+ assert.Equal(t, "test message\n", filesResponse.Commit.Message)
+ MakeRequest(t, NewRequest(t, "GET", urlRaw), http.StatusNotFound)
+ })
})
}
diff --git a/tools/lint-go-gopls.sh b/tools/lint-go-gopls.sh
deleted file mode 100755
index 2cd26ca6fe..0000000000
--- a/tools/lint-go-gopls.sh
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/bin/bash
-set -uo pipefail
-
-cd "$(dirname -- "${BASH_SOURCE[0]}")" && cd ..
-
-IGNORE_PATTERNS=(
- "is deprecated" # TODO: fix these
-)
-
-# lint all go files with 'gopls check' and look for lines starting with the
-# current absolute path, indicating a error was found. This is necessary
-# because the tool does not set non-zero exit code when errors are found.
-# ref: https://github.com/golang/go/issues/67078
-ERROR_LINES=$("$GO" run "$GOPLS_PACKAGE" check -severity=warning "$@" 2>/dev/null | grep -E "^$PWD" | grep -vFf <(printf '%s\n' "${IGNORE_PATTERNS[@]}"));
-NUM_ERRORS=$(echo -n "$ERROR_LINES" | wc -l)
-
-if [ "$NUM_ERRORS" -eq "0" ]; then
- exit 0;
-else
- echo "$ERROR_LINES"
- echo "Found $NUM_ERRORS 'gopls check' errors"
- exit 1;
-fi
diff --git a/tsconfig.json b/tsconfig.json
index 1daf4b7233..2466faf592 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -40,7 +40,7 @@
"strictBindCallApply": true,
"strictBuiltinIteratorReturn": true,
"strictFunctionTypes": true,
- "strictNullChecks": false,
+ "strictNullChecks": true,
"stripInternal": true,
"verbatimModuleSyntax": true,
"types": [
diff --git a/web_src/css/editor/fileeditor.css b/web_src/css/editor/fileeditor.css
index 698efffc99..12ae97a109 100644
--- a/web_src/css/editor/fileeditor.css
+++ b/web_src/css/editor/fileeditor.css
@@ -1,23 +1,3 @@
-.repository.file.editor .tab[data-tab="write"] {
- padding: 0 !important;
-}
-
-.repository.file.editor .tab[data-tab="write"] .editor-toolbar {
- border: 0 !important;
-}
-
-.repository.file.editor .tab[data-tab="write"] .CodeMirror {
- border-left: 0;
- border-right: 0;
- border-bottom: 0;
-}
-
-.repo-editor-header {
- display: flex;
- margin: 1rem 0;
- padding: 3px 0;
-}
-
.editor-toolbar {
border-color: var(--color-secondary);
}
diff --git a/web_src/css/modules/animations.css b/web_src/css/modules/animations.css
index 779339c46b..aedf53569a 100644
--- a/web_src/css/modules/animations.css
+++ b/web_src/css/modules/animations.css
@@ -28,7 +28,7 @@
aspect-ratio: 1;
transform: translate(-50%, -50%);
animation: isloadingspin 1000ms infinite linear;
- border-width: 4px;
+ border-width: 3px;
border-style: solid;
border-color: var(--color-secondary) var(--color-secondary) var(--color-secondary-dark-8) var(--color-secondary-dark-8);
border-radius: var(--border-radius-full);
diff --git a/web_src/css/modules/svg.css b/web_src/css/modules/svg.css
index 738ec22cd3..e32fa0911f 100644
--- a/web_src/css/modules/svg.css
+++ b/web_src/css/modules/svg.css
@@ -1,17 +1,24 @@
-.svg {
+/* some material icons have "fill=none" (e.g.: ".txt -> document"), so the CSS styles shouldn't overwrite it,
+ and material icons should have no "fill" set explicitly, otherwise some like ".editorconfig" won't render correctly */
+.svg:not(.git-entry-icon) {
display: inline-block;
vertical-align: text-top;
fill: currentcolor;
}
-.svg.git-entry-icon {
- fill: transparent; /* some material icons have dark background fill, so need to reset */
-}
-
.middle .svg {
vertical-align: middle;
}
+/* some browsers like Chrome have a bug: when a SVG is in a "display: none" container and referenced
+ somewhere else by `