diff --git a/.forgejo/workflows-composite/apt-install-from/action.yaml b/.forgejo/workflows-composite/apt-install-from/action.yaml index ab55883..615e7cb 100644 --- a/.forgejo/workflows-composite/apt-install-from/action.yaml +++ b/.forgejo/workflows-composite/apt-install-from/action.yaml @@ -13,8 +13,6 @@ runs: run: | export DEBIAN_FRONTEND=noninteractive echo "deb http://deb.debian.org/debian/ ${RELEASE} main" > "/etc/apt/sources.list.d/${RELEASE}.list" - wget -O- http://neuro.debian.net/lists/bookworm.de-fzj.libre | tee /etc/apt/sources.list.d/neurodebian.sources.list - apt-key adv --recv-keys --keyserver hkps://keyserver.ubuntu.com 0xA5D32F012649A5A9 env: RELEASE: ${{inputs.release}} - name: install packages @@ -26,7 +24,6 @@ runs: - name: remove temporary package list to prevent using it in other steps run: | rm "/etc/apt/sources.list.d/${RELEASE}.list" - rm "/etc/apt/sources.list.d/neurodebian.sources.list" apt-get update -qq env: RELEASE: ${{inputs.release}} diff --git a/.forgejo/workflows/build-oci-image.yml b/.forgejo/workflows/build-oci-image.yml deleted file mode 100644 index 8e843b4..0000000 --- a/.forgejo/workflows/build-oci-image.yml +++ /dev/null @@ -1,41 +0,0 @@ -on: - push: - branches: - - 'forgejo' - tags: - - '*-git-annex*' - -jobs: - build-oci-image: - runs-on: docker - strategy: - matrix: - type: ["rootful", "rootless"] - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 # fetch the full history so that the Forgejo version is determined properly - - name: Determine registry and username - id: determine-registry-and-username - run: | - echo "registry=${GITHUB_SERVER_URL#https://}" >> "$GITHUB_OUTPUT" - echo "username=${GITHUB_REPOSITORY%/*}" >> "$GITHUB_OUTPUT" - - name: Install Docker - run: curl -fsSL https://get.docker.com | sh - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - name: Login to Docker Hub - uses: docker/login-action@v3 - with: - registry: ${{ steps.determine-registry-and-username.outputs.registry }} - username: ${{ steps.determine-registry-and-username.outputs.username }} - password: ${{ secrets.REGISTRY_TOKEN }} - - name: Build and push - uses: docker/build-push-action@v6 - with: - context: . - file: ${{ (matrix.type == 'rootful' && 'Dockerfile') || (matrix.type == 'rootless' && 'Dockerfile.rootless') }} - push: true - tags: ${{ steps.determine-registry-and-username.outputs.registry }}/${{ github.repository }}:${{ github.ref_name }}${{ (matrix.type == 'rootful' && ' ') || (matrix.type == 'rootless' && '-rootless') }} diff --git a/.forgejo/workflows/testing.yml b/.forgejo/workflows/testing.yml index a93ca78..784bc45 100644 --- a/.forgejo/workflows/testing.yml +++ b/.forgejo/workflows/testing.yml @@ -10,6 +10,7 @@ on: jobs: backend-checks: + if: vars.ROLE == 'forgejo-coding' || vars.ROLE == 'forgejo-testing' runs-on: docker container: image: 'data.forgejo.org/oci/node:20-bookworm' @@ -26,6 +27,7 @@ jobs: - run: su forgejo -c 'make --always-make -j$(nproc) lint-backend tidy-check swagger-check fmt-check swagger-validate' # ensure the "go-licenses" make target runs - uses: ./.forgejo/workflows-composite/build-backend frontend-checks: + if: vars.ROLE == 'forgejo-coding' || vars.ROLE == 'forgejo-testing' runs-on: docker container: image: 'data.forgejo.org/oci/node:20-bookworm' @@ -174,6 +176,7 @@ jobs: TAGS: bindata TEST_REDIS_SERVER: cacher:${{ matrix.cacher.port }} test-mysql: + if: vars.ROLE == 'forgejo-coding' || vars.ROLE == 'forgejo-testing' runs-on: docker needs: [backend-checks, frontend-checks] container: @@ -196,13 +199,15 @@ jobs: - name: install dependencies & git >= 2.42 uses: ./.forgejo/workflows-composite/apt-install-from with: - packages: git git-annex-standalone git-lfs + packages: git git-lfs - uses: ./.forgejo/workflows-composite/build-backend - run: | su forgejo -c 'make test-mysql-migration test-mysql' + timeout-minutes: 120 env: USE_REPO_TEST_DIR: 1 test-pgsql: + if: vars.ROLE == 'forgejo-coding' || vars.ROLE == 'forgejo-testing' runs-on: docker needs: [backend-checks, frontend-checks] container: @@ -231,15 +236,17 @@ jobs: - name: install dependencies & git >= 2.42 uses: ./.forgejo/workflows-composite/apt-install-from with: - packages: git git-annex-standalone git-lfs + packages: git git-lfs - uses: ./.forgejo/workflows-composite/build-backend - run: | su forgejo -c 'make test-pgsql-migration test-pgsql' + timeout-minutes: 120 env: RACE_ENABLED: true USE_REPO_TEST_DIR: 1 TEST_LDAP: 1 test-sqlite: + if: vars.ROLE == 'forgejo-coding' || vars.ROLE == 'forgejo-testing' runs-on: docker needs: [backend-checks, frontend-checks] container: @@ -251,21 +258,25 @@ jobs: - name: install dependencies & git >= 2.42 uses: ./.forgejo/workflows-composite/apt-install-from with: - packages: git git-annex-standalone git-lfs + packages: git git-lfs - uses: ./.forgejo/workflows-composite/build-backend - run: | su forgejo -c 'make test-sqlite-migration test-sqlite' + timeout-minutes: 120 env: TAGS: sqlite sqlite_unlock_notify RACE_ENABLED: true TEST_TAGS: sqlite sqlite_unlock_notify USE_REPO_TEST_DIR: 1 security-check: + if: vars.ROLE == 'forgejo-coding' || vars.ROLE == 'forgejo-testing' runs-on: docker needs: - test-sqlite - test-pgsql - test-mysql + - test-remote-cacher + - test-unit container: image: 'data.forgejo.org/oci/node:20-bookworm' options: --tmpfs /tmp:exec,noatime diff --git a/Dockerfile b/Dockerfile index 1f33f5d..af9269a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -78,7 +78,6 @@ RUN apk --no-cache add \ sqlite \ su-exec \ gnupg \ - git-annex \ && rm -rf /var/cache/apk/* RUN addgroup \ diff --git a/Dockerfile.rootless b/Dockerfile.rootless index 63fb88a..82d15e8 100644 --- a/Dockerfile.rootless +++ b/Dockerfile.rootless @@ -71,7 +71,6 @@ RUN apk --no-cache add \ git \ curl \ gnupg \ - git-annex \ && rm -rf /var/cache/apk/* RUN addgroup \ diff --git a/Makefile b/Makefile index 561d674..a9de57e 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ self := $(location) @tmpdir=`mktemp --tmpdir -d` ; \ echo Using temporary directory $$tmpdir for test repositories ; \ USE_REPO_TEST_DIR= $(MAKE) -f $(self) --no-print-directory REPO_TEST_DIR=$$tmpdir/ $@ ; \ - STATUS=$$? ; chmod -R +w "$$tmpdir" && rm -r "$$tmpdir" ; exit $$STATUS + STATUS=$$? ; rm -r "$$tmpdir" ; exit $$STATUS else @@ -104,7 +104,7 @@ else FORGEJO_VERSION_API ?= $(GITEA_VERSION)+${GITEA_COMPATIBILITY} else # drop the "g" prefix prepended by git describe to the commit hash - FORGEJO_VERSION ?= $(shell git describe --exclude '*-test' --tags --always | sed 's/^v//' | sed 's/\-g/-/2')+${GITEA_COMPATIBILITY} + FORGEJO_VERSION ?= $(shell git describe --exclude '*-test' --tags --always | sed 's/^v//' | sed 's/\-g/-/')+${GITEA_COMPATIBILITY} endif endif FORGEJO_VERSION_MAJOR=$(shell echo $(FORGEJO_VERSION) | sed -e 's/\..*//') diff --git a/cmd/serv.go b/cmd/serv.go index 5780403..db67e36 100644 --- a/cmd/serv.go +++ b/cmd/serv.go @@ -38,7 +38,6 @@ import ( const ( lfsAuthenticateVerb = "git-lfs-authenticate" - gitAnnexShellVerb = "git-annex-shell" ) // CmdServ represents the available serv sub-command. @@ -80,7 +79,6 @@ var ( "git-upload-archive": perm.AccessModeRead, "git-receive-pack": perm.AccessModeWrite, lfsAuthenticateVerb: perm.AccessModeNone, - gitAnnexShellVerb: perm.AccessModeNone, // annex permissions are enforced by GIT_ANNEX_SHELL_READONLY, rather than the Gitea API } alphaDashDotPattern = regexp.MustCompile(`[^\w-\.]`) ) @@ -214,28 +212,6 @@ func runServ(c *cli.Context) error { } } - if verb == gitAnnexShellVerb { - if !setting.Annex.Enabled { - return fail(ctx, "Unknown git command", "git-annex request over SSH denied, git-annex support is disabled") - } - - if len(words) < 3 { - return fail(ctx, "Too few arguments", "Too few arguments in cmd: %s", cmd) - } - - // git-annex always puts the repo in words[2], unlike most other - // git subcommands; and it sometimes names repos like /~/, as if - // $HOME should get expanded while also being rooted. e.g.: - // git-annex-shell 'configlist' '/~/user/repo' - // git-annex-shell 'sendkey' '/user/repo 'key' - repoPath = words[2] - repoPath = strings.TrimPrefix(repoPath, "/") - repoPath = strings.TrimPrefix(repoPath, "~/") - } - - // prevent directory traversal attacks - repoPath = filepath.Clean("/" + repoPath)[1:] - rr := strings.SplitN(repoPath, "/", 2) if len(rr) != 2 { return fail(ctx, "Invalid repository path", "Invalid repository path: %v", repoPath) @@ -249,18 +225,6 @@ func runServ(c *cli.Context) error { // so that username and reponame are not affected. repoPath = strings.ToLower(strings.TrimSpace(repoPath)) - // put the sanitized repoPath back into the argument list for later - if verb == gitAnnexShellVerb { - // git-annex-shell demands an absolute path - absRepoPath, err := filepath.Abs(filepath.Join(setting.RepoRootPath, repoPath)) - if err != nil { - return fail(ctx, "Error locating repoPath", "%v", err) - } - words[2] = absRepoPath - } else { - words[1] = repoPath - } - if alphaDashDotPattern.MatchString(reponame) { return fail(ctx, "Invalid repo name", "Invalid repo name: %s", reponame) } @@ -339,45 +303,21 @@ func runServ(c *cli.Context) error { return nil } - gitBinVerb, err := exec.LookPath(verb) - if err != nil { + var gitcmd *exec.Cmd + gitBinPath := filepath.Dir(git.GitExecutable) // e.g. /usr/bin + gitBinVerb := filepath.Join(gitBinPath, verb) // e.g. /usr/bin/git-upload-pack + if _, err := os.Stat(gitBinVerb); err != nil { // if the command "git-upload-pack" doesn't exist, try to split "git-upload-pack" to use the sub-command with git // ps: Windows only has "git.exe" in the bin path, so Windows always uses this way - // ps: git-annex-shell and other extensions may not necessarily be in gitBinPath, - // but '{gitBinPath}/git annex-shell' should be able to find them on $PATH. verbFields := strings.SplitN(verb, "-", 2) if len(verbFields) == 2 { // use git binary with the sub-command part: "C:\...\bin\git.exe", "upload-pack", ... - gitBinVerb = git.GitExecutable - words = append([]string{verbFields[1]}, words...) + gitcmd = exec.CommandContext(ctx, git.GitExecutable, verbFields[1], repoPath) } } - - // by default, use the verb (it has been checked above by allowedCommands) - gitcmd := exec.CommandContext(ctx, gitBinVerb, words[1:]...) - - if verb == gitAnnexShellVerb { - // This doesn't get its own isolated section like LFS does, because LFS - // is handled by internal Gitea routines, but git-annex has to be shelled out - // to like other git subcommands, so we need to build up gitcmd. - - // TODO: does this work on Windows? - gitcmd.Env = append(gitcmd.Env, - // "If set, disallows running git-shell to handle unknown commands." - // - git-annex-shell(1) - "GIT_ANNEX_SHELL_LIMITED=True", - // "If set, git-annex-shell will refuse to run commands - // that do not operate on the specified directory." - // - git-annex-shell(1) - fmt.Sprintf("GIT_ANNEX_SHELL_DIRECTORY=%s", words[2]), - ) - if results.UserMode < perm.AccessModeWrite { - // "If set, disallows any action that could modify the git-annex repository." - // - git-annex-shell(1) - // We set this when the backend API has told us that we don't have write permission to this repo. - log.Debug("Setting GIT_ANNEX_SHELL_READONLY=True") - gitcmd.Env = append(gitcmd.Env, "GIT_ANNEX_SHELL_READONLY=True") - } + if gitcmd == nil { + // by default, use the verb (it has been checked above by allowedCommands) + gitcmd = exec.CommandContext(ctx, gitBinVerb, repoPath) } process.SetSysProcAttribute(gitcmd) diff --git a/cmd/web.go b/cmd/web.go index 661e6d1..44babd5 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -9,7 +9,6 @@ import ( "net" "net/http" "os" - "os/exec" "path/filepath" "strconv" "strings" @@ -248,12 +247,6 @@ func runWeb(ctx *cli.Context) error { createPIDFile(ctx.String("pid")) } - if setting.Annex.Enabled { - if _, err := exec.LookPath("git-annex"); err != nil { - log.Fatal("You have enabled git-annex support but git-annex is not installed. Please make sure that Forgejo's PATH contains the git-annex executable.") - } - } - if !setting.InstallLock { if err := serveInstall(ctx); err != nil { return err @@ -318,10 +311,6 @@ func listen(m http.Handler, handleRedirector bool) error { log.Info("LFS server enabled") } - if setting.Annex.Enabled { - log.Info("git-annex enabled") - } - var err error switch setting.Protocol { case setting.HTTP: diff --git a/modules/annex/annex.go b/modules/annex/annex.go deleted file mode 100644 index dee24d2..0000000 --- a/modules/annex/annex.go +++ /dev/null @@ -1,192 +0,0 @@ -// Copyright 2022 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -// Unlike modules/lfs, which operates mainly on git.Blobs, this operates on git.TreeEntrys. -// The motivation for this is that TreeEntrys have an easy pointer to the on-disk repo path, -// while blobs do not (in fact, if building with TAGS=gogit, blobs might exist only in a mock -// filesystem, living only in process RAM). We must have the on-disk path to do anything -// useful with git-annex because all of its interesting data is on-disk under .git/annex/. - -package annex - -import ( - "errors" - "fmt" - "io/fs" - "os" - "path" - "path/filepath" - "regexp" - "strings" - - "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/typesniffer" -) - -// ErrBlobIsNotAnnexed occurs if a blob does not contain a valid annex key -var ErrBlobIsNotAnnexed = errors.New("not a git-annex pointer") - -func LookupKey(blob *git.Blob) (string, error) { - stdout, _, err := git.NewCommand(git.DefaultContext, "annex", "lookupkey", "--ref").AddDynamicArguments(blob.ID.String()).RunStdString(&git.RunOpts{Dir: blob.Repo().Path}) - if err != nil { - return "", ErrBlobIsNotAnnexed - } - key := strings.TrimSpace(stdout) - return key, nil -} - -func ContentLocationFromKey(repoPath, key string) (string, error) { - contentLocation, _, err := git.NewCommandContextNoGlobals(git.DefaultContext, "annex", "contentlocation").AddDynamicArguments(key).RunStdString(&git.RunOpts{Dir: repoPath}) - if err != nil { - return "", fmt.Errorf("in %s: %s does not seem to be a valid annexed file: %w", repoPath, key, err) - } - contentLocation = strings.TrimSpace(contentLocation) - contentLocation = path.Clean("/" + contentLocation)[1:] // prevent directory traversals - contentLocation = path.Join(repoPath, contentLocation) - - return contentLocation, nil -} - -// return the absolute path of the content pointed to by the annex pointer stored in the git object -// errors if the content is not found in this repo -func ContentLocation(blob *git.Blob) (string, error) { - key, err := LookupKey(blob) - if err != nil { - return "", err - } - return ContentLocationFromKey(blob.Repo().Path, key) -} - -// returns a stream open to the annex content -func Content(blob *git.Blob) (*os.File, error) { - contentLocation, err := ContentLocation(blob) - if err != nil { - return nil, err - } - - return os.Open(contentLocation) -} - -// whether the object appears to be a valid annex pointer -// does *not* verify if the content is actually in this repo; -// for that, use ContentLocation() -func IsAnnexed(blob *git.Blob) (bool, error) { - if !setting.Annex.Enabled { - return false, nil - } - - // LookupKey is written to only return well-formed keys - // so the test is just to see if it errors - _, err := LookupKey(blob) - if err != nil { - if errors.Is(err, ErrBlobIsNotAnnexed) { - return false, nil - } - return false, err - } - return true, nil -} - -// IsAnnexRepo determines if repo is a git-annex enabled repository -func IsAnnexRepo(repo *git.Repository) bool { - _, _, err := git.NewCommand(repo.Ctx, "config", "annex.uuid").RunStdString(&git.RunOpts{Dir: repo.Path}) - return err == nil -} - -var repoConfigFileRe = regexp.MustCompile("[^/]+/[^/]+.git/config$") - -var ( - uuid2repoPathCache = make(map[string]string) - repoPath2uuidCache = make(map[string]string) -) - -func Init() error { - if !setting.Annex.Enabled { - return nil - } - log.Info("Populating the git-annex UUID cache with existing repositories") - return updateUUID2RepoPathCache() -} - -func updateUUID2RepoPathCache() error { - return filepath.WalkDir(setting.RepoRootPath, func(path string, d fs.DirEntry, err error) error { - if err == nil && repoConfigFileRe.MatchString(path) { - thisRepoPath := strings.TrimSuffix(path, "/config") - _, ok := repoPath2uuidCache[thisRepoPath] - if ok { - return nil - } - stdout, _, err := git.NewCommand(git.DefaultContext, "config", "annex.uuid").RunStdString(&git.RunOpts{Dir: thisRepoPath}) - if err != nil { - return nil - } - repoUUID := strings.TrimSpace(stdout) - if repoUUID != "" { - uuid2repoPathCache[repoUUID] = thisRepoPath - repoPath2uuidCache[thisRepoPath] = repoUUID - } - } - return nil - }) -} - -func repoPathFromUUIDCache(uuid string) (string, error) { - if repoPath, ok := uuid2repoPathCache[uuid]; ok { - return repoPath, nil - } - // If the cache didn't contain an entry for the UUID then update the cache and try again - if err := updateUUID2RepoPathCache(); err != nil { - return "", err - } - if repoPath, ok := uuid2repoPathCache[uuid]; ok { - return repoPath, nil - } - return "", fmt.Errorf("no repository known for UUID '%s'", uuid) -} - -func checkValidity(uuid, repoPath string) (bool, error) { - stdout, _, err := git.NewCommand(git.DefaultContext, "config", "annex.uuid").RunStdString(&git.RunOpts{Dir: repoPath}) - if err != nil { - return false, err - } - repoUUID := strings.TrimSpace(stdout) - return uuid == repoUUID, nil -} - -func removeCachedEntries(uuid, repoPath string) { - delete(uuid2repoPathCache, uuid) - delete(repoPath2uuidCache, repoPath) -} - -func UUID2RepoPath(uuid string) (string, error) { - // Get the current cache entry for the UUID - repoPath, err := repoPathFromUUIDCache(uuid) - if err != nil { - return "", err - } - // Check if it is still up-to-date - valid, err := checkValidity(uuid, repoPath) - if err != nil { - return "", err - } - if !valid { - // If it isn't, remove the cache entry and try again - removeCachedEntries(uuid, repoPath) - return UUID2RepoPath(uuid) - } - // Otherwise just return the cached entry - return repoPath, nil -} - -// GuessContentType guesses the content type of the annexed blob. -func GuessContentType(blob *git.Blob) (typesniffer.SniffedType, error) { - r, err := Content(blob) - if err != nil { - return typesniffer.SniffedType{}, err - } - defer r.Close() - - return typesniffer.DetectContentTypeFromReader(r) -} diff --git a/modules/base/tool.go b/modules/base/tool.go index a885546..02f1db5 100644 --- a/modules/base/tool.go +++ b/modules/base/tool.go @@ -16,7 +16,6 @@ import ( "strings" "unicode/utf8" - "code.gitea.io/gitea/modules/annex" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" @@ -102,12 +101,6 @@ func Int64sToStrings(ints []int64) []string { // EntryIcon returns the octicon class for displaying files/directories func EntryIcon(entry *git.TreeEntry) string { - isAnnexed, _ := annex.IsAnnexed(entry.Blob()) - if isAnnexed { - // Show git-annex files as binary files to differentiate them from non-annexed files - // TODO: find a more suitable icon, maybe something related to git-annex - return "file-binary" - } switch { case entry.IsLink(): te, _, err := entry.FollowLink() diff --git a/modules/git/blob.go b/modules/git/blob.go index bbfab7d..2f02693 100644 --- a/modules/git/blob.go +++ b/modules/git/blob.go @@ -126,10 +126,6 @@ func (b *blobReader) Close() error { return nil } -func (b *Blob) Repo() *Repository { - return b.repo -} - // Name returns name of the tree entry this blob object was created from (or empty string) func (b *Blob) Name() string { return b.name diff --git a/modules/git/command.go b/modules/git/command.go index d3e6b7b..a3d43aa 100644 --- a/modules/git/command.go +++ b/modules/git/command.go @@ -457,13 +457,12 @@ func (c *Command) RunStdBytes(opts *RunOpts) (stdout, stderr []byte, runErr RunS } // AllowLFSFiltersArgs return globalCommandArgs with lfs filter, it should only be used for tests -// It also re-enables git-credential(1), which is used to test git-annex's HTTP support func AllowLFSFiltersArgs() TrustedCmdArgs { // Now here we should explicitly allow lfs filters to run filteredLFSGlobalArgs := make(TrustedCmdArgs, len(globalCommandArgs)) j := 0 for _, arg := range globalCommandArgs { - if strings.Contains(string(arg), "lfs") || strings.Contains(string(arg), "credential") { + if strings.Contains(string(arg), "lfs") { j-- } else { filteredLFSGlobalArgs[j] = arg diff --git a/modules/git/repo_index.go b/modules/git/repo_index.go index 37988c3..f45b6e6 100644 --- a/modules/git/repo_index.go +++ b/modules/git/repo_index.go @@ -6,14 +6,12 @@ package git import ( "bytes" "context" - "errors" "os" "path/filepath" "strings" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/util" - "code.gitea.io/gitea/modules/git/internal" //nolint:depguard // only this file can use the internal type CmdArg, other files and packages should use AddXxx functions ) // ReadTreeToIndex reads a treeish to the index @@ -104,32 +102,6 @@ func (repo *Repository) LsFiles(filenames ...string) ([]string, error) { return filelist, err } -// Gives a list of all files in a directory and below -func (repo *Repository) LsFilesFromDirectory(directory, branch string) ([]string, error) { - if branch == "" { - return nil, errors.New("branch not found in context URL") - } - - cmd := NewCommand(repo.Ctx, "ls-files", internal.CmdArg("--with-tree=" + branch)) - - if len(directory) > 0 { - cmd = NewCommand(repo.Ctx, "ls-files", internal.CmdArg("--with-tree=" + branch), internal.CmdArg("--directory"), internal.CmdArg(directory)) - } - - res, stderror, err := cmd.RunStdBytes(&RunOpts{Dir: repo.Path}) - if err != nil { - return nil, err - } - - if len(stderror) > 0 { - return nil, errors.New(string(stderror)) - } - - lines := strings.Split(string(res), "\n") - - return lines, nil -} - // RemoveFilesFromIndex removes given filenames from the index - it does not check whether they are present. func (repo *Repository) RemoveFilesFromIndex(filenames ...string) error { objectFormat, err := repo.GetObjectFormat() diff --git a/modules/markup/external/external.go b/modules/markup/external/external.go index b976077..122517e 100644 --- a/modules/markup/external/external.go +++ b/modules/markup/external/external.go @@ -12,7 +12,6 @@ import ( "runtime" "strings" - "code.gitea.io/gitea/modules/annex" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup" @@ -87,22 +86,8 @@ func (p *Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io. commands = strings.Fields(command) args = commands[1:] ) - isAnnexed, _ := annex.IsAnnexed(ctx.Blob) - // if a renderer wants to read a file, and we have annexed content, we can - // provide the annex key file location directly to the renderer. git-annex - // takes care of having that location be read-only, so no critical - // protection layer is needed. Moreover, the file readily exists, and - // expensive temporary files can be avoided, also allowing an operator - // to raise MAX_DISPLAY_FILE_SIZE without much negative impact. - if p.IsInputFile && isAnnexed { - // look for annexed content, will be empty, if there is none - annexContentLocation, _ := annex.ContentLocation(ctx.Blob) - // we call the renderer, even if there is no annex content present. - // showing the pointer file content is not much use, and a topical - // renderer might be able to produce something useful from the - // filename alone (present in ENV) - args = append(args, annexContentLocation) - } else if p.IsInputFile { + + if p.IsInputFile { // write to temp file f, err := os.CreateTemp("", "gitea_input") if err != nil { @@ -145,12 +130,6 @@ func (p *Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io. os.Environ(), "GITEA_PREFIX_SRC="+ctx.Links.SrcLink(), "GITEA_PREFIX_RAW="+ctx.Links.RawLink(), - // also communicate the relative path of the to-be-rendered item. - // this enables the renderer to make use of the original file name - // and path, e.g., to make rendering or dtype-detection decisions - // that go beyond the originally matched extension. Even if the - // content is directly streamed to STDIN - "GITEA_RELATIVE_PATH="+ctx.RelativePath, ) if !p.IsInputFile { cmd.Stdin = input diff --git a/modules/markup/renderer.go b/modules/markup/renderer.go index c00bd2b..2137302 100644 --- a/modules/markup/renderer.go +++ b/modules/markup/renderer.go @@ -67,18 +67,14 @@ type Header struct { // RenderContext represents a render context type RenderContext struct { - Ctx context.Context - RelativePath string // relative path from tree root of the branch - Type string - IsWiki bool - Links Links - Metas map[string]string - DefaultLink string - GitRepo *git.Repository - // reporting the target blob that is to-be-rendered enables - // deeper inspection in the handler for external renderer - // (i.e., more targeted handling of annexed files) - Blob *git.Blob + Ctx context.Context + RelativePath string // relative path from tree root of the branch + Type string + IsWiki bool + Links Links + Metas map[string]string + DefaultLink string + GitRepo *git.Repository ShaExistCache map[string]bool cancelFn func() SidebarTocNode ast.Node diff --git a/modules/private/serv.go b/modules/private/serv.go index 6c7c753..480a446 100644 --- a/modules/private/serv.go +++ b/modules/private/serv.go @@ -40,7 +40,6 @@ type ServCommandResults struct { UserName string UserEmail string UserID int64 - UserMode perm.AccessMode OwnerName string RepoName string RepoID int64 diff --git a/modules/setting/annex.go b/modules/setting/annex.go deleted file mode 100644 index 35e9e55..0000000 --- a/modules/setting/annex.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2023 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package setting - -import ( - "code.gitea.io/gitea/modules/log" -) - -// Annex represents the configuration for git-annex -var Annex = struct { - Enabled bool `ini:"ENABLED"` - DisableP2PHTTP bool `ini:"DISABLE_P2PHTTP"` -}{} - -func loadAnnexFrom(rootCfg ConfigProvider) { - sec := rootCfg.Section("annex") - if err := sec.MapTo(&Annex); err != nil { - log.Fatal("Failed to map Annex settings: %v", err) - } - if !sec.HasKey("DISABLE_P2PHTTP") { - // If DisableP2PHTTP is not explicitly set then use DisableHTTPGit as its default - Annex.DisableP2PHTTP = Repository.DisableHTTPGit - } -} diff --git a/modules/setting/service.go b/modules/setting/service.go index b877fbd..74ed5cd 100644 --- a/modules/setting/service.go +++ b/modules/setting/service.go @@ -85,8 +85,6 @@ var Service = struct { DefaultOrgMemberVisible bool UserDeleteWithCommentsMaxTime time.Duration ValidSiteURLSchemes []string - LandingPageInfoEnabled bool - SignInForgottenPasswordEnabled bool // OpenID settings EnableOpenIDSignIn bool @@ -215,8 +213,6 @@ func loadServiceFrom(rootCfg ConfigProvider) { if Service.EnableTimetracking { Service.DefaultEnableTimetracking = sec.Key("DEFAULT_ENABLE_TIMETRACKING").MustBool(true) } - Service.LandingPageInfoEnabled = sec.Key("ENABLE_LANDING_PAGE_INFO").MustBool(true) - Service.SignInForgottenPasswordEnabled = sec.Key("SIGNIN_FORGOTTEN_PASSWORD_ENABLED").MustBool(true) Service.DefaultEnableDependencies = sec.Key("DEFAULT_ENABLE_DEPENDENCIES").MustBool(true) Service.AllowCrossRepositoryDependencies = sec.Key("ALLOW_CROSS_REPOSITORY_DEPENDENCIES").MustBool(true) Service.DefaultAllowOnlyContributorsToTrackTime = sec.Key("DEFAULT_ALLOW_ONLY_CONTRIBUTORS_TO_TRACK_TIME").MustBool(true) diff --git a/modules/setting/setting.go b/modules/setting/setting.go index 9710fb2..c9d3083 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -153,7 +153,6 @@ func loadCommonSettingsFrom(cfg ConfigProvider) error { loadCamoFrom(cfg) loadI18nFrom(cfg) loadGitFrom(cfg) - loadAnnexFrom(cfg) loadMirrorFrom(cfg) loadMarkupFrom(cfg) loadQuotaFrom(cfg) diff --git a/modules/util/remove.go b/modules/util/remove.go index 39556e5..d1e38fa 100644 --- a/modules/util/remove.go +++ b/modules/util/remove.go @@ -4,9 +4,7 @@ package util import ( - "io/fs" "os" - "path/filepath" "runtime" "syscall" "time" @@ -43,48 +41,10 @@ func Remove(name string) error { return err } -// MakeWritable recursively makes the named directory writable. -func MakeWritable(name string) error { - return filepath.WalkDir(name, func(path string, d fs.DirEntry, err error) error { - // NB: this is called WalkDir but it works on a single file too - if err == nil { - info, err := d.Info() - if err != nil { - return err - } - - // Don't try chmod'ing symlinks (will fail with broken symlinks) - if info.Mode()&os.ModeSymlink != os.ModeSymlink { - // 0200 == u+w, in octal unix permission notation - err = os.Chmod(path, info.Mode()|0o200) - if err != nil { - return err - } - } - } - return nil - }) -} - -// RemoveAll removes the named file or directory with at most 5 attempts. +// RemoveAll removes the named file or (empty) directory with at most 5 attempts. func RemoveAll(name string) error { var err error - for i := 0; i < 5; i++ { - // Do chmod -R +w to help ensure the removal succeeds. - // In particular, in the git-annex case, this handles - // https://git-annex.branchable.com/internals/lockdown/ : - // - // > (The only bad consequence of this is that rm -rf .git - // > doesn't work unless you first run chmod -R +w .git) - - err = MakeWritable(name) - if err != nil { - // try again - <-time.After(100 * time.Millisecond) - continue - } - err = os.RemoveAll(name) if err == nil { break diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini index 1894aeb..0143a9a 100644 --- a/options/locale/locale_cs-CZ.ini +++ b/options/locale/locale_cs-CZ.ini @@ -1317,7 +1317,6 @@ view_git_blame=Zobrazit git blame video_not_supported_in_browser=Váš prohlížeč nepodporuje značku HTML5 „video“. audio_not_supported_in_browser=Váš prohlížeč nepodporuje značku HTML5 „audio“. stored_lfs=Uloženo pomocí Git LFS -stored_annex=Uloženo pomocí Git Annex symbolic_link=Symbolický odkaz executable_file=Spustitelný soubor vendored = Vendorováno @@ -1343,7 +1342,6 @@ editor.upload_file=Nahrát soubor editor.edit_file=Upravit soubor editor.preview_changes=Náhled změn editor.cannot_edit_lfs_files=LFS soubory nemohou být upravovány přes webové rozhraní. -editor.cannot_edit_annex_files=Annex soubory nemohou být upravovány přes webové rozhraní. editor.cannot_edit_non_text_files=Binární soubory nemohou být upravovány přes webové rozhraní. editor.edit_this_file=Upravit soubor editor.this_file_locked=Soubor je uzamčen diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini index a67390e..3064552 100644 --- a/options/locale/locale_de-DE.ini +++ b/options/locale/locale_de-DE.ini @@ -1317,8 +1317,6 @@ view_git_blame=„git blame“ ansehen video_not_supported_in_browser=Dein Browser unterstützt das HTML5-„video“-Tag nicht. audio_not_supported_in_browser=Dein Browser unterstützt das HTML5-„audio“-Tag nicht. stored_lfs=Gespeichert mit Git LFS -stored_annex=Gespeichert mit Git Annex -stored_annex_not_present = hier nicht vorhanden, versuche git annex whereis symbolic_link=Softlink executable_file=Ausführbare Datei commit_graph=Commit-Graph @@ -1342,7 +1340,6 @@ editor.upload_file=Datei hochladen editor.edit_file=Datei bearbeiten editor.preview_changes=Vorschau der Änderungen editor.cannot_edit_lfs_files=LFS-Dateien können im Webinterface nicht bearbeitet werden. -editor.cannot_edit_annex_files=Annex-Dateien können im Webinterface nicht bearbeitet werden. editor.cannot_edit_non_text_files=Binärdateien können nicht im Webinterface bearbeitet werden. editor.edit_this_file=Datei bearbeiten editor.this_file_locked=Datei ist gesperrt diff --git a/options/locale/locale_el-GR.ini b/options/locale/locale_el-GR.ini index 2f6d82b..e80b815 100644 --- a/options/locale/locale_el-GR.ini +++ b/options/locale/locale_el-GR.ini @@ -1314,7 +1314,6 @@ view_git_blame=Προβολή git blame video_not_supported_in_browser=Το πρόγραμμα περιήγησής σας δεν υποστηρίζει την ετικέτα HTML5 «video». audio_not_supported_in_browser=Το πρόγραμμα περιήγησής σας δεν υποστηρίζει την ετικέτα HTML5 «audio». stored_lfs=Αποθηκεύτηκε με το Git LFS -stored_annex=Αποθηκεύτηκε με το Git Annex symbolic_link=Symbolic link executable_file=Εκτελέσιμο αρχείο commit_graph=Γράφημα υποβολών @@ -1338,7 +1337,6 @@ editor.upload_file=Ανέβασμα αρχείου editor.edit_file=Επεξεργασία αρχείου editor.preview_changes=Προεπισκόπηση αλλαγών editor.cannot_edit_lfs_files=Τα αρχεία LFS δεν μπορούν να επεξεργαστούν στη διεπαφή web. -editor.cannot_edit_annex_files=Τα αρχεία Annex δεν μπορούν να επεξεργαστούν στη διεπαφή web. editor.cannot_edit_non_text_files=Τα δυαδικά αρχεία δεν μπορούν να επεξεργαστούν στη διεπαφή web. editor.edit_this_file=Επεξεργασία αρχείου editor.this_file_locked=Το αρχείο είναι κλειδωμένο diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index db58d50..53a47f0 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1337,8 +1337,6 @@ view_git_blame = View git blame video_not_supported_in_browser = Your browser does not support the HTML5 "video" tag. audio_not_supported_in_browser = Your browser does not support the HTML5 "audio" tag. stored_lfs = Stored with Git LFS -stored_annex = Stored with Git Annex -stored_annex_not_present = not present here, try using git annex whereis symbolic_link = Symbolic link executable_file = Executable file vendored = Vendored @@ -1366,7 +1364,6 @@ editor.upload_file = Upload file editor.edit_file = Edit file editor.preview_changes = Preview changes editor.cannot_edit_lfs_files = LFS files cannot be edited in the web interface. -editor.cannot_edit_annex_files = Annex files cannot be edited in the web interface. editor.cannot_edit_non_text_files = Binary files cannot be edited in the web interface. editor.edit_this_file = Edit file editor.this_file_locked = File is locked diff --git a/options/locale/locale_es-ES.ini b/options/locale/locale_es-ES.ini index 839d2d0..cc24e95 100644 --- a/options/locale/locale_es-ES.ini +++ b/options/locale/locale_es-ES.ini @@ -1312,7 +1312,6 @@ view_git_blame=Ver Git blame video_not_supported_in_browser=Su navegador no soporta el tag "video" de HTML5. audio_not_supported_in_browser=Su navegador no soporta el tag "audio" de HTML5. stored_lfs=Almacenados con Git LFS -stored_annex=Almacenados con Git Annex symbolic_link=Enlace simbólico executable_file=Archivo ejecutable commit_graph=Gráfico de commits @@ -1336,7 +1335,6 @@ editor.upload_file=Subir archivo editor.edit_file=Editar archivo editor.preview_changes=Vista previa de los cambios editor.cannot_edit_lfs_files=Los archivos LFS no se pueden editar en la interfaz web. -editor.cannot_edit_annex_files=Los archivos Annex no se pueden editar en la interfaz web. editor.cannot_edit_non_text_files=Los archivos binarios no se pueden editar en la interfaz web. editor.edit_this_file=Editar archivo editor.this_file_locked=El archivo está bloqueado diff --git a/options/locale/locale_fa-IR.ini b/options/locale/locale_fa-IR.ini index 01d931b..e2c9dc4 100644 --- a/options/locale/locale_fa-IR.ini +++ b/options/locale/locale_fa-IR.ini @@ -951,7 +951,6 @@ file_copy_permalink=پرمالینک را کپی کنید video_not_supported_in_browser=مرورگر شما از تگ video که در HTML5 تعریف شده است، پشتیبانی نمی کند. audio_not_supported_in_browser=مرورگر شما از تگ audio که در HTML5 تعریف شده است، پشتیبانی نمی کند. stored_lfs=ذخیره شده با GIT LFS -stored_annex=ذخیره شده با GIT Annex symbolic_link=پیوند نمادین commit_graph=نمودار کامیت commit_graph.select=انتخاب برنچها @@ -969,7 +968,6 @@ editor.upload_file=بارگذاری پرونده editor.edit_file=ویرایش پرونده editor.preview_changes=پیش نمایش تغییرات editor.cannot_edit_lfs_files=پرونده های LFS در صحفه وب قابل تغییر نیست. -editor.cannot_edit_annex_files=پرونده های Annex در صحفه وب قابل تغییر نیست. editor.cannot_edit_non_text_files=پرونده‎های دودویی در صفحه وب قابل تغییر نیست. editor.edit_this_file=ویرایش پرونده editor.this_file_locked=پرونده قفل شده است diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini index aaaa2e7..92da5ee 100644 --- a/options/locale/locale_fr-FR.ini +++ b/options/locale/locale_fr-FR.ini @@ -1318,7 +1318,6 @@ view_git_blame=Voir Git blame video_not_supported_in_browser=Votre navigateur ne supporte pas la balise « vidéo » HTML5. audio_not_supported_in_browser=Votre navigateur ne supporte pas la balise « audio » HTML5. stored_lfs=Stocké avec Git LFS -stored_annex=Stocké avec Git Annex symbolic_link=Lien symbolique executable_file=Fichier exécutable vendored = Vendored @@ -1344,7 +1343,6 @@ editor.upload_file=Téléverser un fichier editor.edit_file=Modifier le fichier editor.preview_changes=Aperçu des modifications editor.cannot_edit_lfs_files=Les fichiers LFS ne peuvent pas être modifiés dans l'interface web. -editor.cannot_edit_annex_files=Les fichiers Annex ne peuvent pas être modifiés dans l'interface web. editor.cannot_edit_non_text_files=Les fichiers binaires ne peuvent pas être édités dans l'interface web. editor.edit_this_file=Modifier le fichier editor.this_file_locked=Le fichier est verrouillé diff --git a/options/locale/locale_hu-HU.ini b/options/locale/locale_hu-HU.ini index ef94d2d..57555b9 100644 --- a/options/locale/locale_hu-HU.ini +++ b/options/locale/locale_hu-HU.ini @@ -787,7 +787,6 @@ file_too_large=Ez a fájl túl nagy ahhoz, hogy megjelenítsük. video_not_supported_in_browser=A böngésző nem támogatja a HTML5 video tag-et. audio_not_supported_in_browser=A böngésző nem támogatja a HTML5 audio tag-et. stored_lfs=Git LFS-el eltárolva -stored_annex=Git Annex-el eltárolva symbolic_link=Szimbolikus hivatkozás commit_graph=Commit gráf commit_graph.hide_pr_refs=Pull request-ek elrejtése @@ -800,7 +799,6 @@ editor.upload_file=Fájl feltöltése editor.edit_file=Fájl szerkesztése editor.preview_changes=Változások előnézete editor.cannot_edit_lfs_files=LFS fájlok nem szerkeszthetőek a webes felületen. -editor.cannot_edit_annex_files=Annex fájlok nem szerkeszthetőek a webes felületen. editor.cannot_edit_non_text_files=Bináris fájlok nem szerkeszthetőek a webes felületen. editor.edit_this_file=Fájl szerkesztése editor.this_file_locked=Zárolt állomány diff --git a/options/locale/locale_id-ID.ini b/options/locale/locale_id-ID.ini index 8cf3457..1e0044e 100644 --- a/options/locale/locale_id-ID.ini +++ b/options/locale/locale_id-ID.ini @@ -596,7 +596,6 @@ file_permalink=Permalink file_too_large=Berkas terlalu besar untuk ditampilkan. stored_lfs=Tersimpan dengan GIT LFS -stored_annex=Tersimpan dengan GIT Annex commit_graph=Grafik Komit blame=Salahkan normal_view=Pandangan Normal @@ -608,7 +607,6 @@ editor.upload_file=Unggah Berkas editor.edit_file=Sunting Berkas editor.preview_changes=Tinjau Perubahan editor.cannot_edit_lfs_files=Berkas LFS tidak dapat disunting dalam antarmuka web. -editor.cannot_edit_annex_files=Berkas Annex tidak dapat disunting dalam antarmuka web. editor.cannot_edit_non_text_files=Berkas biner tidak dapat disunting dalam antarmuka web. editor.edit_this_file=Sunting Berkas editor.this_file_locked=Berkas terkunci diff --git a/options/locale/locale_is-IS.ini b/options/locale/locale_is-IS.ini index b3cf17f..3a6e844 100644 --- a/options/locale/locale_is-IS.ini +++ b/options/locale/locale_is-IS.ini @@ -680,7 +680,6 @@ file_view_rendered=Skoða Unnið file_copy_permalink=Afrita Varanlega Slóð stored_lfs=Geymt með Git LFS -stored_annex=Geymt með Git Annex commit_graph.hide_pr_refs=Fela Sameiningarbeiðnir commit_graph.monochrome=Einlitað commit_graph.color=Litað diff --git a/options/locale/locale_it-IT.ini b/options/locale/locale_it-IT.ini index 4bfc676..61753a7 100644 --- a/options/locale/locale_it-IT.ini +++ b/options/locale/locale_it-IT.ini @@ -1267,7 +1267,6 @@ view_git_blame=Visualizza git incolpa video_not_supported_in_browser=Il tuo browser non supporta le etichette "video" di HTML5. audio_not_supported_in_browser=Il tuo browser non supporta le etichette "audio" di HTML5. stored_lfs=Memorizzati con Git LFS -stored_annex=Memorizzati con Git Annex symbolic_link=Link Simbolico commit_graph=Grafico dei commit commit_graph.select=Seleziona rami @@ -1286,7 +1285,6 @@ editor.upload_file=Carica file editor.edit_file=Modifica file editor.preview_changes=Anteprima modifiche editor.cannot_edit_lfs_files=I file LFS non possono essere modificati nell'interfaccia web. -editor.cannot_edit_annex_files=I file Annex non possono essere modificati nell'interfaccia web. editor.cannot_edit_non_text_files=I file binari non possono essere modificati tramite interfaccia web. editor.edit_this_file=Modifica file editor.this_file_locked=Il file è bloccato diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini index 2a4f075..890c2e8 100644 --- a/options/locale/locale_ja-JP.ini +++ b/options/locale/locale_ja-JP.ini @@ -1305,7 +1305,6 @@ view_git_blame=Git Blameを表示 video_not_supported_in_browser=このブラウザはHTML5のvideoタグをサポートしていません。 audio_not_supported_in_browser=このブラウザーはHTML5のaudioタグをサポートしていません。 stored_lfs=Git LFSで保管されています -stored_annex=Git Annexで保管されています symbolic_link=シンボリック リンク executable_file=実行ファイル commit_graph=コミットグラフ @@ -1329,7 +1328,6 @@ editor.upload_file=ファイルをアップロード editor.edit_file=ファイルを編集 editor.preview_changes=変更をプレビュー editor.cannot_edit_lfs_files=LFSのファイルはWebインターフェースで編集できません。 -editor.cannot_edit_annex_files=AnnexのファイルはWebインターフェースで編集できません。 editor.cannot_edit_non_text_files=バイナリファイルはWebインターフェースで編集できません。 editor.edit_this_file=ファイルを編集 editor.this_file_locked=ファイルはロックされています diff --git a/options/locale/locale_ko-KR.ini b/options/locale/locale_ko-KR.ini index 0573982..58e723d 100644 --- a/options/locale/locale_ko-KR.ini +++ b/options/locale/locale_ko-KR.ini @@ -821,7 +821,6 @@ file_too_large=보여주기에는 파일이 너무 큽니다. video_not_supported_in_browser=당신의 브라우저가 HTML5의 "video" 태그를 지원하지 않습니다. audio_not_supported_in_browser=당신의 브라우저가 HTML5의 "audio" 태그를 지원하지 않습니다. stored_lfs=Git LFS에 저장되어 있습니다 -stored_annex=Git Annex에 저장되어 있습니다 commit_graph=커밋 그래프 editor.new_file=새 파일 diff --git a/options/locale/locale_lv-LV.ini b/options/locale/locale_lv-LV.ini index 30b53cb..bd77588 100644 --- a/options/locale/locale_lv-LV.ini +++ b/options/locale/locale_lv-LV.ini @@ -1316,7 +1316,6 @@ view_git_blame=Apskatīt Git izmaiņu veicējus video_not_supported_in_browser=Pārlūks neatbalsta HTML5 tagu "video". audio_not_supported_in_browser=Pārlūks neatbalsta HTML5 tagu "audio". stored_lfs=Saglabāts Git LFS -stored_annex=Saglabāts Git Annex symbolic_link=Simboliska saite executable_file=Izpildāma datne commit_graph=Iesūtījumu karte @@ -1340,7 +1339,6 @@ editor.upload_file=Augšupielādēt datni editor.edit_file=Labot datni editor.preview_changes=Priekšskatīt izmaiņas editor.cannot_edit_lfs_files=LFS datnes tīmekļa saskarnē nevar labot. -editor.cannot_edit_annex_files=Annex datnes tīmekļa saskarnē nevar labot. editor.cannot_edit_non_text_files=Binārās datnes tīmekļa saskarnē nevar labot. editor.edit_this_file=Labot datni editor.this_file_locked=Datne ir slēgta @@ -4029,4 +4027,4 @@ filepreview.lines = %[1]d. līdz %[2]d. rinda %[3]s filepreview.truncated = Priekšskatījums tika saīsināts [translation_meta] -test = Šī ir pārbaudes virkne. Tā netiek attēlota Forgejo saskarnē, bet tiek izmantota pārbaudes nolūkiem. Droši var ievadīt "ok", lai ietaupītu laiku (vai kādu jautru faktu pēc izvēles), lai sasniegtu to saldo 100% pabeigšanas atzīmi. +test = Šī ir pārbaudes virkne. Tā netiek attēlota Forgejo saskarnē, bet tiek izmantota pārbaudes nolūkiem. Droši var ievadīt "ok", lai ietaupītu laiku (vai kādu jautru faktu pēc izvēles), lai sasniegtu to saldo 100% pabeigšanas atzīmi. \ No newline at end of file diff --git a/options/locale/locale_nl-NL.ini b/options/locale/locale_nl-NL.ini index de2c1bd..83acf18 100644 --- a/options/locale/locale_nl-NL.ini +++ b/options/locale/locale_nl-NL.ini @@ -1285,7 +1285,6 @@ view_git_blame=Bekijk git blame video_not_supported_in_browser=Uw browser ondersteunt de HTML5 "video" element niet. audio_not_supported_in_browser=Uw browser ondersteunt de HTML5 "audio" element niet. stored_lfs=Opgeslagen met Git LFS -stored_annex=Opgeslagen met Git Annex symbolic_link=Symbolische link commit_graph=Commit grafiek commit_graph.select=Selecteer branches @@ -1304,7 +1303,6 @@ editor.upload_file=Upload bestand editor.edit_file=Bewerk bestand editor.preview_changes=Voorbeeld tonen editor.cannot_edit_lfs_files=LFS-bestanden kunnen niet worden bewerkt in de webinterface. -editor.cannot_edit_annex_files=Annex-bestanden kunnen niet worden bewerkt in de webinterface. editor.cannot_edit_non_text_files=Binaire bestanden kunnen niet worden bewerkt in de webinterface. editor.edit_this_file=Bewerk bestand editor.this_file_locked=Bestand is vergrendeld diff --git a/options/locale/locale_pl-PL.ini b/options/locale/locale_pl-PL.ini index 97678ea..11ea38e 100644 --- a/options/locale/locale_pl-PL.ini +++ b/options/locale/locale_pl-PL.ini @@ -1249,7 +1249,6 @@ file_copy_permalink=Kopiuj bezpośredni odnośnik video_not_supported_in_browser=Twoja przeglądarka nie obsługuje znacznika HTML5 "video". audio_not_supported_in_browser=Twoja przeglądarka nie obsługuje znacznika HTML5 "audio". stored_lfs=Przechowane za pomocą Git LFS -stored_annex=Przechowane za pomocą Git Annex symbolic_link=Dowiązanie symboliczne commit_graph=Wykres commitów commit_graph.select=Wybierz gałęzie @@ -1267,7 +1266,6 @@ editor.upload_file=Wyślij plik editor.edit_file=Edytuj plik editor.preview_changes=Podgląd zmian editor.cannot_edit_lfs_files=Pliki LFS nie mogą być edytowane poprzez interfejs przeglądarkowy. -editor.cannot_edit_annex_files=Pliki Annex nie mogą być edytowane poprzez interfejs przeglądarkowy. editor.cannot_edit_non_text_files=Pliki binarne nie mogą być edytowane poprzez interfejs przeglądarkowy. editor.edit_this_file=Edytuj plik editor.this_file_locked=Plik jest zablokowany diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini index 78a871f..2280241 100644 --- a/options/locale/locale_pt-BR.ini +++ b/options/locale/locale_pt-BR.ini @@ -1310,7 +1310,6 @@ view_git_blame=Ver git blame video_not_supported_in_browser=Seu navegador não tem suporte para a tag "video" do HTML5. audio_not_supported_in_browser=Seu navegador não tem suporte para a tag "audio" do HTML5. stored_lfs=Armazenado com Git LFS -stored_annex=Armazenado com Git Annex symbolic_link=Link simbólico executable_file=Arquivo executável commit_graph=Gráfico de commits @@ -1334,7 +1333,6 @@ editor.upload_file=Enviar arquivo editor.edit_file=Editar arquivo editor.preview_changes=Pré-visualizar alterações editor.cannot_edit_lfs_files=Arquivos LFS não podem ser editados na interface web. -editor.cannot_edit_annex_files=Arquivos Annex não podem ser editados na interface web. editor.cannot_edit_non_text_files=Arquivos binários não podem ser editados na interface web. editor.edit_this_file=Editar arquivo editor.this_file_locked=Arquivo está bloqueado diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini index 994f967..8e30b89 100644 --- a/options/locale/locale_pt-PT.ini +++ b/options/locale/locale_pt-PT.ini @@ -1321,7 +1321,6 @@ view_git_blame=Ver git blame video_not_supported_in_browser=O seu navegador não suporta a etiqueta "video" do HTML5. audio_not_supported_in_browser=O seu navegador não suporta a etiqueta "audio" do HTML5. stored_lfs=Armazenado com Git LFS -stored_annex=Armazenado com Git Annex symbolic_link=Ligação simbólica executable_file=Ficheiro executável vendored=Externo @@ -1347,7 +1346,6 @@ editor.upload_file=Carregar ficheiro editor.edit_file=Editar ficheiro editor.preview_changes=Pré-visualizar modificações editor.cannot_edit_lfs_files=Ficheiros LFS não podem ser editados na interface web. -editor.cannot_edit_annex_files=Ficheiros Annex não podem ser editados na interface web. editor.cannot_edit_non_text_files=Ficheiros binários não podem ser editados na interface da web. editor.edit_this_file=Editar ficheiro editor.this_file_locked=Ficheiro bloqueado diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini index d45a251..83093fa 100644 --- a/options/locale/locale_ru-RU.ini +++ b/options/locale/locale_ru-RU.ini @@ -1304,7 +1304,6 @@ view_git_blame=Показать git blame video_not_supported_in_browser=Ваш браузер не поддерживает тэг HTML5 «video». audio_not_supported_in_browser=Ваш браузер не поддерживает тэг HTML5 «audio». stored_lfs=Хранится Git LFS -stored_annex=Хранится Git Annex symbolic_link=Символическая ссылка executable_file=Исполняемый файл commit_graph=Граф коммитов @@ -1328,7 +1327,6 @@ editor.upload_file=Загрузить файл editor.edit_file=Редактировать файл editor.preview_changes=Просмотр изменений editor.cannot_edit_lfs_files=LFS файлы невозможно редактировать в веб-интерфейсе. -editor.cannot_edit_annex_files=Annex файлы невозможно редактировать в веб-интерфейсе. editor.cannot_edit_non_text_files=Двоичные файлы нельзя редактировать в веб-интерфейсе. editor.edit_this_file=Редактировать файл editor.this_file_locked=Файл заблокирован diff --git a/options/locale/locale_si-LK.ini b/options/locale/locale_si-LK.ini index e26bf30..e52e5bc 100644 --- a/options/locale/locale_si-LK.ini +++ b/options/locale/locale_si-LK.ini @@ -889,7 +889,6 @@ file_copy_permalink=පිටපත් මාමලින්ක් video_not_supported_in_browser=ඔබගේ බ්රව්සරය HTML5 'වීඩියෝ' ටැගය සඳහා සහය නොදක්වයි. audio_not_supported_in_browser=ඔබගේ බ්රව්සරය HTML5 'ශ්රව්ය' ටැගය සඳහා සහය නොදක්වයි. stored_lfs=Git LFS සමඟ ගබඩා -stored_annex=Git Annex සමඟ ගබඩා symbolic_link=සංකේතාත්මක සබැඳිය commit_graph=ප්රස්තාරය කැප commit_graph.select=ශාඛා තෝරන්න @@ -907,7 +906,6 @@ editor.upload_file=ගොනුව උඩුගත කරන්න editor.edit_file=ගොනුව සංස්කරණය editor.preview_changes=වෙනස්කම් පෙරදසුන editor.cannot_edit_lfs_files=LFS ගොනු වෙබ් අතුරු මුහුණත තුළ සංස්කරණය කළ නොහැක. -editor.cannot_edit_annex_files=Annex ගොනු වෙබ් අතුරු මුහුණත තුළ සංස්කරණය කළ නොහැක. editor.cannot_edit_non_text_files=ද්විමය ගොනු වෙබ් අතුරු මුහුණත තුළ සංස්කරණය කළ නොහැක. editor.edit_this_file=ගොනුව සංස්කරණය editor.this_file_locked=ගොනුවට අගුළු ලා ඇත diff --git a/options/locale/locale_sk-SK.ini b/options/locale/locale_sk-SK.ini index f714bdf..3e5b4a6 100644 --- a/options/locale/locale_sk-SK.ini +++ b/options/locale/locale_sk-SK.ini @@ -1010,7 +1010,6 @@ view_git_blame=Zobraziť Git Blame video_not_supported_in_browser=Váš prehliadač nepodporuje HTML5 tag 'video'. audio_not_supported_in_browser=Váš prehliadač nepodporuje HTML5 tag 'audio'. stored_lfs=Uložené pomocou Git LFS -stored_annex=Uložené pomocou Git Annex symbolic_link=Symbolický odkaz commit_graph=Graf commitov line=riadok diff --git a/options/locale/locale_sv-SE.ini b/options/locale/locale_sv-SE.ini index 3e8924f..4d94211 100644 --- a/options/locale/locale_sv-SE.ini +++ b/options/locale/locale_sv-SE.ini @@ -910,7 +910,6 @@ file_too_large=Filen är för stor för att visas. video_not_supported_in_browser=Din webbläsare stödjer ej HTML5-taggen "video". audio_not_supported_in_browser=Din webbläsare stödjer ej HTML5-taggen "audio". stored_lfs=Sparad med Git LFS -stored_annex=Sparad med Git Annex symbolic_link=Symbolisk länk commit_graph=Commitgraf commit_graph.monochrome=Mono @@ -924,7 +923,6 @@ editor.upload_file=Ladda upp fil editor.edit_file=Redigera fil editor.preview_changes=Förhandsgranska ändringar editor.cannot_edit_lfs_files=LFS-filer kan inte redigeras i webbgränssnittet. -editor.cannot_edit_annex_files=Annex-filer kan inte redigeras i webbgränssnittet. editor.cannot_edit_non_text_files=Binära filer kan inte redigeras genom webbgränssnittet. editor.edit_this_file=Redigera fil editor.this_file_locked=Filen är låst diff --git a/options/locale/locale_tr-TR.ini b/options/locale/locale_tr-TR.ini index 58384d9..49b52c3 100644 --- a/options/locale/locale_tr-TR.ini +++ b/options/locale/locale_tr-TR.ini @@ -1290,7 +1290,6 @@ view_git_blame=Git Suç Görüntüle video_not_supported_in_browser=Tarayıcınız HTML5 'video' etiketini desteklemiyor. audio_not_supported_in_browser=Tarayıcınız HTML5 'audio' etiketini desteklemiyor. stored_lfs=Git LFS ile depolandı -stored_annex=Git Annex ile depolandı symbolic_link=Sembolik Bağlantı executable_file=Çalıştırılabilir Dosya commit_graph=İşleme Grafiği @@ -1314,7 +1313,6 @@ editor.upload_file=Dosya Yükle editor.edit_file=Dosyayı Düzenle editor.preview_changes=Değişiklikleri Önizle editor.cannot_edit_lfs_files=LFS dosyaları web arayüzünde düzenlenemez. -editor.cannot_edit_annex_files=Annex dosyaları web arayüzünde düzenlenemez. editor.cannot_edit_non_text_files=Bu tür dosyalar web arayüzünden düzenlenemez. editor.edit_this_file=Dosyayı Düzenle editor.this_file_locked=Dosya kilitlendi diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini index c306e7d..52303c2 100644 --- a/options/locale/locale_uk-UA.ini +++ b/options/locale/locale_uk-UA.ini @@ -1243,7 +1243,6 @@ file_copy_permalink=Копіювати постійне посилання video_not_supported_in_browser=Ваш браузер не підтримує тег HTML5 «video». audio_not_supported_in_browser=Ваш браузер не підтримує тег HTML5 «audio». stored_lfs=Збережено з Git LFS -stored_annex=Збережено з Git Annex symbolic_link=Символічне посилання commit_graph=Графік комітів commit_graph.select=Виберіть гілки @@ -1261,7 +1260,6 @@ editor.upload_file=Завантажити файл editor.edit_file=Редагувати файл editor.preview_changes=Попередній перегляд змін editor.cannot_edit_lfs_files=Файли LFS не можна редагувати в веб-інтерфейсі. -editor.cannot_edit_annex_files=Файли Annex не можна редагувати в веб-інтерфейсі. editor.cannot_edit_non_text_files=Бінарні файли не можливо редагувати у веб-інтерфейсі. editor.edit_this_file=Редагувати файл editor.this_file_locked=Файл заблоковано diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index 1c152e7..e809795 100644 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -1319,7 +1319,6 @@ view_git_blame=查看 Git Blame video_not_supported_in_browser=您的浏览器不支持 HTML5 “video” 标签。 audio_not_supported_in_browser=您的浏览器不支持 HTML5 “audio” 标签。 stored_lfs=存储到Git LFS -stored_annex=存储到Git Annex symbolic_link=符号链接 executable_file=可执行文件 vendored = Vendored @@ -1345,7 +1344,6 @@ editor.upload_file=上传文件 editor.edit_file=编辑文件 editor.preview_changes=预览变更 editor.cannot_edit_lfs_files=无法在 web 界面中编辑 lfs 文件。 -editor.cannot_edit_annex_files=无法在 web 界面中编辑 lfs 文件。 editor.cannot_edit_non_text_files=网页不能编辑二进制文件。 editor.edit_this_file=编辑文件 editor.this_file_locked=文件已锁定 diff --git a/options/locale/locale_zh-HK.ini b/options/locale/locale_zh-HK.ini index 25dfcdf..e5080e6 100644 --- a/options/locale/locale_zh-HK.ini +++ b/options/locale/locale_zh-HK.ini @@ -472,7 +472,6 @@ file_view_raw=查看原始文件 file_permalink=永久連結 stored_lfs=儲存到到 Git LFS -stored_annex=儲存到到 Git Annex editor.preview_changes=預覽更改 editor.or=或 @@ -1133,4 +1132,4 @@ runners.labels = 標籤 [projects] -[git.filemode] +[git.filemode] \ No newline at end of file diff --git a/options/locale/locale_zh-TW.ini b/options/locale/locale_zh-TW.ini index 6506b67..720267f 100644 --- a/options/locale/locale_zh-TW.ini +++ b/options/locale/locale_zh-TW.ini @@ -1293,7 +1293,6 @@ view_git_blame=檢視 Git Blame video_not_supported_in_browser=您的瀏覽器不支援 HTML5 的「video」標籤。 audio_not_supported_in_browser=您的瀏覽器不支援 HTML5 的「audio」標籤。 stored_lfs=已使用 Git LFS 儲存 -stored_annex=已使用 Git Annex 儲存 symbolic_link=符號連結 commit_graph=提交線圖 commit_graph.select=選擇分支 @@ -1313,7 +1312,6 @@ editor.upload_file=上傳檔案 editor.edit_file=編輯檔案 editor.preview_changes=預覽變更 editor.cannot_edit_lfs_files=無法在 web 介面中編輯 LFS 檔。 -editor.cannot_edit_annex_files=無法在 web 介面中編輯 Annex 檔。 editor.cannot_edit_non_text_files=網站介面不能編輯二進位檔案。 editor.edit_this_file=編輯檔案 editor.this_file_locked=檔案已被鎖定 diff --git a/package-lock.json b/package-lock.json index 9088ea0..7b1c1ce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "forgejo-aneksajo", + "name": "forgejo", "lockfileVersion": 3, "requires": true, "packages": { diff --git a/routers/init.go b/routers/init.go index d881edd..821a0ef 100644 --- a/routers/init.go +++ b/routers/init.go @@ -11,7 +11,6 @@ import ( "code.gitea.io/gitea/models" asymkey_model "code.gitea.io/gitea/models/asymkey" authmodel "code.gitea.io/gitea/models/auth" - "code.gitea.io/gitea/modules/annex" "code.gitea.io/gitea/modules/cache" "code.gitea.io/gitea/modules/eventsource" "code.gitea.io/gitea/modules/git" @@ -168,8 +167,6 @@ func InitWebInstalled(ctx context.Context) { actions_service.Init() - mustInit(annex.Init) - // Finally start up the cron cron.NewContext(ctx) } diff --git a/routers/private/serv.go b/routers/private/serv.go index a3a0c69..ef3920d 100644 --- a/routers/private/serv.go +++ b/routers/private/serv.go @@ -81,14 +81,12 @@ func ServCommand(ctx *context.PrivateContext) { ownerName := ctx.Params(":owner") repoName := ctx.Params(":repo") mode := perm.AccessMode(ctx.FormInt("mode")) - verbs := ctx.FormStrings("verb") // Set the basic parts of the results to return results := private.ServCommandResults{ RepoName: repoName, OwnerName: ownerName, KeyID: keyID, - UserMode: perm.AccessModeNone, } // Now because we're not translating things properly let's just default some English strings here @@ -289,10 +287,8 @@ func ServCommand(ctx *context.PrivateContext) { repo.IsPrivate || owner.Visibility.IsPrivate() || (user != nil && user.IsRestricted) || // user will be nil if the key is a deploykey - (setting.Annex.Enabled && len(verbs) > 0 && verbs[0] == "git-annex-shell") || // git-annex has its own permission enforcement, for which we expose results.UserMode setting.Service.RequireSignInView) { if key.Type == asymkey_model.KeyTypeDeploy { - results.UserMode = deployKey.Mode if deployKey.Mode < mode { ctx.JSON(http.StatusUnauthorized, private.Response{ UserMsg: fmt.Sprintf("Deploy Key: %d:%s is not authorized to %s %s/%s.", key.ID, key.Name, modeString, results.OwnerName, results.RepoName), @@ -314,9 +310,9 @@ func ServCommand(ctx *context.PrivateContext) { return } - results.UserMode = perm.UnitAccessMode(unitType) + userMode := perm.UnitAccessMode(unitType) - if results.UserMode < mode { + if userMode < mode { log.Warn("Failed authentication attempt for %s with key %s (not authorized to %s %s/%s) from %s", user.Name, key.Name, modeString, ownerName, repoName, ctx.RemoteAddr()) ctx.JSON(http.StatusUnauthorized, private.Response{ UserMsg: fmt.Sprintf("User: %d:%s with Key: %d:%s is not authorized to %s %s/%s.", user.ID, user.Name, key.ID, key.Name, modeString, ownerName, repoName), @@ -357,7 +353,6 @@ func ServCommand(ctx *context.PrivateContext) { }) return } - results.UserMode = perm.AccessModeWrite results.RepoID = repo.ID } @@ -386,14 +381,13 @@ func ServCommand(ctx *context.PrivateContext) { return } } - log.Debug("Serv Results:\nIsWiki: %t\nDeployKeyID: %d\nKeyID: %d\tKeyName: %s\nUserName: %s\nUserID: %d\nUserMode: %d\nOwnerName: %s\nRepoName: %s\nRepoID: %d", + log.Debug("Serv Results:\nIsWiki: %t\nDeployKeyID: %d\nKeyID: %d\tKeyName: %s\nUserName: %s\nUserID: %d\nOwnerName: %s\nRepoName: %s\nRepoID: %d", results.IsWiki, results.DeployKeyID, results.KeyID, results.KeyName, results.UserName, results.UserID, - results.UserMode, results.OwnerName, results.RepoName, results.RepoID) diff --git a/routers/web/auth/auth.go b/routers/web/auth/auth.go index 968f596..ccab47a 100644 --- a/routers/web/auth/auth.go +++ b/routers/web/auth/auth.go @@ -169,8 +169,6 @@ func SignIn(ctx *context.Context) { if setting.Service.EnableCaptcha && setting.Service.RequireCaptchaForLogin { context.SetCaptchaData(ctx) } - - ctx.Data["SignInForgottenPasswordEnabled"] = setting.Service.SignInForgottenPasswordEnabled ctx.HTML(http.StatusOK, tplSignIn) } @@ -191,7 +189,6 @@ func SignInPost(ctx *context.Context) { ctx.Data["PageIsLogin"] = true ctx.Data["EnableSSPI"] = auth.IsSSPIEnabled(ctx) ctx.Data["EnableInternalSignIn"] = setting.Service.EnableInternalSignIn - ctx.Data["SignInForgottenPasswordEnabled"] = setting.Service.SignInForgottenPasswordEnabled // Permission denied if EnableInternalSignIn is false if !setting.Service.EnableInternalSignIn { diff --git a/routers/web/home.go b/routers/web/home.go index 3fa2524..d4be093 100644 --- a/routers/web/home.go +++ b/routers/web/home.go @@ -59,8 +59,6 @@ func Home(ctx *context.Context) { return } - ctx.Data["LandingPageInfoEnabled"] = setting.Service.LandingPageInfoEnabled - ctx.Data["PageIsHome"] = true ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled ctx.HTML(http.StatusOK, tplHome) diff --git a/routers/web/repo/annex.go b/routers/web/repo/annex.go deleted file mode 100644 index 852b5a1..0000000 --- a/routers/web/repo/annex.go +++ /dev/null @@ -1,146 +0,0 @@ -package repo - -import ( - "context" - "net" - "net/http" - "net/http/httputil" - "net/url" - "os" - "os/exec" - "strings" - "syscall" - "time" - - "code.gitea.io/gitea/models/perm" - access_model "code.gitea.io/gitea/models/perm/access" - repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/models/unit" - "code.gitea.io/gitea/modules/annex" - "code.gitea.io/gitea/modules/graceful" - "code.gitea.io/gitea/modules/log" - services_context "code.gitea.io/gitea/services/context" -) - -type p2phttpRecordType struct { - CancelFunc func() - LastUsed time.Time - Port string -} - -var p2phttpRecords = make(map[string]*p2phttpRecordType) - -// AnnexP2PHTTP implements git-annex smart HTTP support by delegating to git annex p2phttp -func AnnexP2PHTTP(ctx *services_context.Context) { - uuid := ctx.Params(":uuid") - repoPath, err := annex.UUID2RepoPath(uuid) - if err != nil { - ctx.PlainText(http.StatusNotFound, "Repository not found") - return - } - - parts := strings.Split(repoPath, "/") - repoName := strings.TrimSuffix(parts[len(parts)-1], ".git") - owner := parts[len(parts)-2] - repo, err := repo_model.GetRepositoryByOwnerAndName(ctx, owner, repoName) - if err != nil { - ctx.PlainText(http.StatusNotFound, "Repository not found") - return - } - - p, err := access_model.GetUserRepoPermission(ctx, repo, ctx.Doer) - if err != nil { - ctx.ServerError("GetUserRepoPermission", err) - return - } - - if !(ctx.Req.Method == "GET" && p.CanAccess(perm.AccessModeRead, unit.TypeCode) || - ctx.Req.Method == "POST" && p.CanAccess(perm.AccessModeWrite, unit.TypeCode) || - ctx.Req.Method == "POST" && strings.HasSuffix(ctx.Req.URL.Path, "/checkpresent") && p.CanAccess(perm.AccessModeRead, unit.TypeCode) || - ctx.Req.Method == "POST" && strings.HasSuffix(ctx.Req.URL.Path, "/keeplocked") || - ctx.Req.Method == "POST" && strings.HasSuffix(ctx.Req.URL.Path, "/lockcontent")) { - // GET requests require at least read access; POST requests for - // anything but checkpresent, lockcontent, and keeplocked - // require write permissions; POST requests for checkpresent - // only require read permissions, as it really is just a read. - // POST requests for lockcontent and keeplocked require no - // authentication at all, as is also the case for the - // authentication in the git-annex-p2phttp server. See - // https://git-annex.branchable.com/bugs/p2phttp__58___drop_difference_wideopen_unauth-readonly/ - // for reasoning. - ctx.Resp.WriteHeader(http.StatusUnauthorized) - return - } - - p2phttpRecord, p2phttpProcessExists := p2phttpRecords[uuid] - if p2phttpProcessExists { - p2phttpRecord.LastUsed = time.Now() - } else { - // Start a new p2phttp process for the requested repository - // There is a race condition here with the port selection, ideally git annex p2phttp could just listen on a unix socket... - lis, err := net.Listen("tcp", "127.0.0.1:0") - if err != nil { - log.Error("Failed to listen on a free port: %v", err) - ctx.Resp.WriteHeader(http.StatusInternalServerError) - return - } - hopefullyFreePort := strings.SplitN(lis.Addr().String(), ":", 2)[1] - lis.Close() - p2phttpCtx, p2phttpCtxCancel := context.WithCancel(context.Background()) - go func(ctx context.Context) { - cmd := exec.CommandContext(ctx, "git", "-C", repoPath, "annex", "p2phttp", "-J2", "--bind", "127.0.0.1", "--wideopen", "--port", hopefullyFreePort) - cmd.SysProcAttr = &syscall.SysProcAttr{ - Pdeathsig: syscall.SIGINT, - } - cmd.Cancel = func() error { return cmd.Process.Signal(os.Interrupt) } - _ = cmd.Run() - }(p2phttpCtx) - graceful.GetManager().RunAtTerminate(p2phttpCtxCancel) - - // Wait for the p2phttp server to get ready - start := time.Now() - sleepDuration := 1 * time.Millisecond - for { - if time.Since(start) > 5*time.Second { - p2phttpCtxCancel() - log.Error("Failed to start the p2phttp server in a reasonable amount of time") - ctx.Resp.WriteHeader(http.StatusInternalServerError) - return - } - conn, err := net.Dial("tcp", "127.0.0.1:"+hopefullyFreePort) - if err == nil { - conn.Close() - break - } - time.Sleep(sleepDuration) - sleepDuration *= 2 - if sleepDuration > 1*time.Second { - sleepDuration = 1 * time.Second - } - } - - p2phttpRecord = &p2phttpRecordType{CancelFunc: p2phttpCtxCancel, LastUsed: time.Now(), Port: hopefullyFreePort} - p2phttpRecords[uuid] = p2phttpRecord - } - - // Cleanup p2phttp processes that haven't been used for a while - for uuid, record := range p2phttpRecords { - if time.Since(record.LastUsed) > 5*time.Minute { - record.CancelFunc() - delete(p2phttpRecords, uuid) - } - } - - url, err := url.Parse("http://127.0.0.1:" + p2phttpRecord.Port + strings.TrimPrefix(ctx.Req.RequestURI, "/git-annex-p2phttp")) - if err != nil { - log.Error("Failed to parse URL: %v", err) - ctx.Resp.WriteHeader(http.StatusInternalServerError) - return - } - proxy := httputil.ReverseProxy{ - Rewrite: func(r *httputil.ProxyRequest) { - r.Out.URL = url - }, - } - proxy.ServeHTTP(ctx.Resp, ctx.Req) -} diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index 104655e..03d49fa 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -23,7 +23,6 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/annex" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/charset" csv_module "code.gitea.io/gitea/modules/csv" @@ -73,21 +72,7 @@ func setCompareContext(ctx *context.Context, before, head *git.Commit, headOwner return st } - isAnnexed, err := annex.IsAnnexed(blob) - if err != nil { - log.Error("IsAnnexed failed: %v", err) - return st - } - if isAnnexed { - st, err = annex.GuessContentType(blob) - if err != nil { - log.Error("GuessContentType failed: %v", err) - return st - } - return st - } - - st, err = blob.GuessContentType() + st, err := blob.GuessContentType() if err != nil { log.Error("GuessContentType failed: %v", err) return st @@ -105,18 +90,18 @@ func SourceCommitURL(owner, name string, commit *git.Commit) string { return setting.AppSubURL + "/" + url.PathEscape(owner) + "/" + url.PathEscape(name) + "/src/commit/" + url.PathEscape(commit.ID.String()) } -// MediaCommitURL creates a relative URL for the commit media (plain git, LFS, or annex content) in the given repository -func MediaCommitURL(owner, name string, commit *git.Commit) string { - return setting.AppSubURL + "/" + url.PathEscape(owner) + "/" + url.PathEscape(name) + "/media/commit/" + url.PathEscape(commit.ID.String()) +// RawCommitURL creates a relative URL for the raw commit in the given repository +func RawCommitURL(owner, name string, commit *git.Commit) string { + return setting.AppSubURL + "/" + url.PathEscape(owner) + "/" + url.PathEscape(name) + "/raw/commit/" + url.PathEscape(commit.ID.String()) } // setPathsCompareContext sets context data for source and raw paths func setPathsCompareContext(ctx *context.Context, base, head *git.Commit, headOwner, headName string) { ctx.Data["SourcePath"] = SourceCommitURL(headOwner, headName, head) - ctx.Data["RawPath"] = MediaCommitURL(headOwner, headName, head) + ctx.Data["RawPath"] = RawCommitURL(headOwner, headName, head) if base != nil { ctx.Data["BeforeSourcePath"] = SourceCommitURL(headOwner, headName, base) - ctx.Data["BeforeRawPath"] = MediaCommitURL(headOwner, headName, base) + ctx.Data["BeforeRawPath"] = RawCommitURL(headOwner, headName, base) } } diff --git a/routers/web/repo/download.go b/routers/web/repo/download.go index aefaa79..1e87bbf 100644 --- a/routers/web/repo/download.go +++ b/routers/web/repo/download.go @@ -9,7 +9,6 @@ import ( "time" git_model "code.gitea.io/gitea/models/git" - "code.gitea.io/gitea/modules/annex" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/httpcache" "code.gitea.io/gitea/modules/lfs" @@ -80,26 +79,6 @@ func ServeBlobOrLFS(ctx *context.Context, blob *git.Blob, lastModified *time.Tim } closed = true - // check for git-annex files - // (this code is weirdly redundant because I'm trying not to delete any lines in order to make merges easier) - isAnnexed, err := annex.IsAnnexed(blob) - if err != nil { - ctx.ServerError("annex.IsAnnexed", err) - return err - } - if isAnnexed { - content, err := annex.Content(blob) - if err != nil { - // XXX are there any other possible failure cases here? - // there are, there could be unrelated io errors; those should be ctx.ServerError()s - ctx.NotFound("annex.Content", err) - return err - } - defer content.Close() - common.ServeContentByReadSeeker(ctx.Base, ctx.Repo.TreePath, lastModified, content) - return nil - } - return common.ServeBlob(ctx.Base, ctx.Repo.TreePath, blob, lastModified) } diff --git a/routers/web/repo/editor.go b/routers/web/repo/editor.go index 2a43039..f27ad62 100644 --- a/routers/web/repo/editor.go +++ b/routers/web/repo/editor.go @@ -489,23 +489,8 @@ func DeleteFile(ctx *context.Context) { ctx.HTML(http.StatusOK, tplDeleteFile) } -// DeletePath render delete file page -func DeletePath(ctx *context.Context) { - DeleteFile(ctx) -} - // DeleteFilePost response for deleting file func DeleteFilePost(ctx *context.Context) { - DeletePathOrFilePost(ctx, false) -} - -// DeletePathPost response for deleting path -func DeletePathPost(ctx *context.Context) { - DeletePathOrFilePost(ctx, true) -} - -// DeletePathOrFilePost response for deleting path or file -func DeletePathOrFilePost(ctx *context.Context, isdir bool) { form := web.GetForm(ctx).(*forms.DeleteRepoFileForm) canCommit := renderCommitRights(ctx) branchName := ctx.Repo.BranchName @@ -558,7 +543,6 @@ func DeletePathOrFilePost(ctx *context.Context, isdir bool) { TreePath: ctx.Repo.TreePath, }, }, - IsDir: isdir, // Add this flag to indicate directory deletion Message: message, Signoff: form.Signoff, Author: gitIdentity, @@ -774,7 +758,6 @@ func UploadFilePost(ctx *context.Context) { TreePath: form.TreePath, Message: message, Files: form.Files, - FullPaths: form.FullPaths, Signoff: form.Signoff, Author: gitIdentity, Committer: gitIdentity, diff --git a/routers/web/repo/githttp.go b/routers/web/repo/githttp.go index 53f18f9..c1adca1 100644 --- a/routers/web/repo/githttp.go +++ b/routers/web/repo/githttp.go @@ -10,7 +10,6 @@ import ( gocontext "context" "fmt" "net/http" - "net/url" "os" "path/filepath" "regexp" @@ -79,24 +78,7 @@ func httpBase(ctx *context.Context) *serviceHandler { strings.HasSuffix(ctx.Req.URL.Path, "git-upload-archive") { isPull = true } else { - // In addition to GET requests, HEAD requests are also "pull" - // operations (reads), so they should also not require - // authentication. This is necessary for git-annex to operate - // properly, as it emits HEAD requests to check for the - // existence of keys, e.g. before dropping locally, and asking - // for authentication would break unauthenticated http usage in - // this situation. - // It should be safe to make all HEAD requests require no - // authentication, but as it is only necessary for the - // annex/objects endpoints to fix git-annex' drop operations it - // is limited to those for now. - r, err := regexp.Compile("^/?" + username + "/" + reponame + "(.git)?/annex/objects") - if err != nil { - ctx.ServerError("failed to create URL path regex", err) - return nil - } - isPull = ctx.Req.Method == "GET" || - r.MatchString(ctx.Req.URL.Path) && ctx.Req.Method == "HEAD" + isPull = ctx.Req.Method == "GET" } var accessMode perm.AccessMode @@ -563,42 +545,6 @@ func GetInfoRefs(ctx *context.Context) { } } -// GetConfig implements fetching the git config of a repository -func GetConfig(ctx *context.Context) { - h := httpBase(ctx) - if h != nil { - setHeaderNoCache(ctx) - config, err := os.ReadFile(filepath.Join(h.getRepoDir(), "config")) - if err != nil { - log.Error("Failed to read git config file: %v", err) - ctx.Resp.WriteHeader(http.StatusInternalServerError) - return - } - if !setting.Annex.DisableP2PHTTP { - appURL, err := url.Parse(setting.AppURL) - if err != nil { - log.Error("Could not parse 'setting.AppURL': %v", err) - ctx.Resp.WriteHeader(http.StatusInternalServerError) - return - } - if appURL.Port() == "" { - // If there is no port set then set the http(s) default ports. - // Without this, git-annex would try its own default port (9417) and fail. - if appURL.Scheme == "http" { - appURL.Host += ":80" - } - if appURL.Scheme == "https" { - appURL.Host += ":443" - } - } - config = append(config, []byte("[annex]\n\turl = annex+"+appURL.String()+"git-annex-p2phttp\n")...) - } - ctx.Resp.Header().Set("Content-Type", "text/plain") - ctx.Resp.Header().Set("Content-Length", fmt.Sprintf("%d", len(config))) - http.ServeContent(ctx.Resp, ctx.Req, "config", time.Now(), bytes.NewReader(config)) - } -} - // GetTextFile implements Git dumb HTTP func GetTextFile(p string) func(*context.Context) { return func(ctx *context.Context) { @@ -651,34 +597,3 @@ func GetIdxFile(ctx *context.Context) { h.sendFile(ctx, "application/x-git-packed-objects-toc", "objects/pack/pack-"+ctx.Params("file")+".idx") } } - -// GetAnnexObject implements git-annex dumb HTTP -func GetAnnexObject(ctx *context.Context) { - h := httpBase(ctx) - if h != nil { - // git-annex objects are stored in .git/annex/objects/{hash1}/{hash2}/{key}/{key} - // where key is a string containing the size and (usually SHA256) checksum of the file, - // and hash1+hash2 are the first few bits of the md5sum of key itself. - // ({hash1}/{hash2}/ is just there to avoid putting too many files in one directory) - // ref: https://git-annex.branchable.com/internals/hashing/ - - // keyDir should = key, but we don't enforce that - object := filepath.Join(ctx.Params("hash1"), ctx.Params("hash2"), ctx.Params("keyDir"), ctx.Params("key")) - - // Sanitize the input against directory traversals. - // - // This works because at the filesystem root, "/.." = "/"; - // So if a path starts rooted ("/"), path.Clean(), which - // path.Join() calls internally, removes all '..' prefixes. - // After, this unroots the path unconditionally ([1:]), which - // works because we know the input is never supposed to be rooted. - // - // The router code probably also disallows "..", so this - // should be redundant, but it's defensive to keep it - // whenever touching filesystem paths with user input. - object = filepath.Join(string(filepath.Separator), object)[1:] - - setHeaderCacheForever(ctx) - h.sendFile(ctx, "application/octet-stream", "annex/objects/"+object) - } -} diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index a3976ec..fd8c1da 100644 --- a/routers/web/repo/view.go +++ b/routers/web/repo/view.go @@ -34,7 +34,6 @@ import ( unit_model "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/actions" - "code.gitea.io/gitea/modules/annex" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/charset" "code.gitea.io/gitea/modules/git" @@ -210,59 +209,14 @@ func localizedExtensions(ext, languageCode string) (localizedExts []string) { } type fileInfo struct { - isTextFile bool - isLFSFile bool - isAnnexFile bool - isAnnexFilePresent bool - fileSize int64 - lfsMeta *lfs.Pointer - st typesniffer.SniffedType + isTextFile bool + isLFSFile bool + fileSize int64 + lfsMeta *lfs.Pointer + st typesniffer.SniffedType } func getFileReader(ctx gocontext.Context, repoID int64, blob *git.Blob) ([]byte, io.ReadCloser, *fileInfo, error) { - isAnnexed, err := annex.IsAnnexed(blob) - if err != nil { - return nil, nil, nil, err - } - if isAnnexed { - // TODO: this code could be merged with the LFS case, especially the redundant type sniffer, - // but it is *currently* written this way to make merging with the non-annex upstream easier: - // this way, the git-annex patch is (mostly) pure additions. - - annexContent, err := annex.Content(blob) - if err != nil { - // If annex.Content returns an error it can mean that the blob does not - // refer to an annexed file or that it is not present here. Since we already - // checked that it is annexed the latter must be the case. So we return the - // content of the blob instead and indicate that the file is indeed annexed, - // but not present here. The template can then communicate the situation. - dataRc, err := blob.DataAsync() - if err != nil { - return nil, nil, nil, err - } - - buf := make([]byte, 1024) - n, _ := util.ReadAtMost(dataRc, buf) - buf = buf[:n] - - st := typesniffer.DetectContentType(buf) - return buf, dataRc, &fileInfo{st.IsText(), false, true, false, blob.Size(), nil, st}, nil - } - - stat, err := annexContent.Stat() - if err != nil { - return nil, nil, nil, err - } - - buf := make([]byte, 1024) - n, _ := util.ReadAtMost(annexContent, buf) - buf = buf[:n] - - st := typesniffer.DetectContentType(buf) - - return buf, annexContent, &fileInfo{st.IsText(), false, true, true, stat.Size(), nil, st}, nil - } - dataRc, err := blob.DataAsync() if err != nil { return nil, nil, nil, err @@ -277,18 +231,18 @@ func getFileReader(ctx gocontext.Context, repoID int64, blob *git.Blob) ([]byte, // FIXME: what happens when README file is an image? if !isTextFile || !setting.LFS.StartServer { - return buf, dataRc, &fileInfo{isTextFile, false, false, false, blob.Size(), nil, st}, nil + return buf, dataRc, &fileInfo{isTextFile, false, blob.Size(), nil, st}, nil } pointer, _ := lfs.ReadPointerFromBuffer(buf) if !pointer.IsValid() { // fallback to plain file - return buf, dataRc, &fileInfo{isTextFile, false, false, false, blob.Size(), nil, st}, nil + return buf, dataRc, &fileInfo{isTextFile, false, blob.Size(), nil, st}, nil } meta, err := git_model.GetLFSMetaObjectByOid(ctx, repoID, pointer.Oid) if err != nil { // fallback to plain file log.Warn("Unable to access LFS pointer %s in repo %d: %v", pointer.Oid, repoID, err) - return buf, dataRc, &fileInfo{isTextFile, false, false, false, blob.Size(), nil, st}, nil + return buf, dataRc, &fileInfo{isTextFile, false, blob.Size(), nil, st}, nil } dataRc.Close() @@ -308,7 +262,7 @@ func getFileReader(ctx gocontext.Context, repoID int64, blob *git.Blob) ([]byte, st = typesniffer.DetectContentType(buf) - return buf, dataRc, &fileInfo{st.IsText(), true, false, false, meta.Size, &meta.Pointer, st}, nil + return buf, dataRc, &fileInfo{st.IsText(), true, meta.Size, &meta.Pointer, st}, nil } func renderReadmeFile(ctx *context.Context, subfolder string, readmeFile *git.TreeEntry) { @@ -371,7 +325,6 @@ func renderReadmeFile(ctx *context.Context, subfolder string, readmeFile *git.Tr }, Metas: ctx.Repo.Repository.ComposeDocumentMetas(ctx), GitRepo: ctx.Repo.GitRepo, - Blob: target.Blob(), }, rd) if err != nil { log.Error("Render failed for %s in %-v: %v Falling back to rendering source", readmeFile.Name(), ctx.Repo.Repository, err) @@ -494,17 +447,10 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry) { isDisplayingSource := ctx.FormString("display") == "source" isDisplayingRendered := !isDisplayingSource - if fInfo.isLFSFile || fInfo.isAnnexFile { + if fInfo.isLFSFile { ctx.Data["RawFileLink"] = ctx.Repo.RepoLink + "/media/" + ctx.Repo.BranchNameSubURL() + "/" + util.PathEscapeSegments(ctx.Repo.TreePath) } - if fInfo.isAnnexFile { - // pre-git-annex v7, all annexed files were represented in-repo as symlinks; - // but we pretend they aren't, since that's a distracting quirk of git-annex - // and not a meaningful choice on the user's part - ctx.Data["FileIsSymlink"] = false - } - isRepresentableAsText := fInfo.st.IsRepresentableAsText() if !isRepresentableAsText { // If we can't show plain text, always try to render. @@ -512,8 +458,6 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry) { isDisplayingRendered = true } ctx.Data["IsLFSFile"] = fInfo.isLFSFile - ctx.Data["IsAnnexFile"] = fInfo.isAnnexFile - ctx.Data["IsAnnexFilePresent"] = fInfo.isAnnexFilePresent ctx.Data["FileSize"] = fInfo.fileSize ctx.Data["IsTextFile"] = fInfo.isTextFile ctx.Data["IsRepresentableAsText"] = isRepresentableAsText @@ -548,8 +492,6 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry) { // Assume file is not editable first. if fInfo.isLFSFile { ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.cannot_edit_lfs_files") - } else if fInfo.isAnnexFile { - ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.cannot_edit_annex_files") } else if !isRepresentableAsText { ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.cannot_edit_non_text_files") } @@ -604,7 +546,6 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry) { }, Metas: metas, GitRepo: ctx.Repo.GitRepo, - Blob: entry.Blob(), }, rd) if err != nil { ctx.ServerError("Render", err) @@ -658,7 +599,7 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry) { ctx.Data["FileContent"] = fileContent ctx.Data["LineEscapeStatus"] = statuses } - if !fInfo.isLFSFile && !fInfo.isAnnexFile { + if !fInfo.isLFSFile { if ctx.Repo.CanEnableEditor(ctx, ctx.Doer) { if lfsLock != nil && lfsLock.OwnerID != ctx.Doer.ID { ctx.Data["CanEditFile"] = false @@ -703,7 +644,6 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry) { }, Metas: ctx.Repo.Repository.ComposeDocumentMetas(ctx), GitRepo: ctx.Repo.GitRepo, - Blob: entry.Blob(), }, rd) if err != nil { ctx.ServerError("Render", err) @@ -1219,36 +1159,6 @@ PostRecentBranchCheck: } else { ctx.Data["CodeSearchOptions"] = git.GrepSearchOptions } - - lfsLock, err := git_model.GetTreePathLock(ctx, ctx.Repo.Repository.ID, ctx.Repo.TreePath) - if err != nil { - ctx.ServerError("git_model.GetTreePathLock", err) - return - } - - if ctx.Repo.CanEnableEditor(ctx, ctx.Doer) { - if lfsLock != nil && lfsLock.OwnerID != ctx.Doer.ID { - ctx.Data["CanDeleteFile"] = false - ctx.Data["DeleteFileTooltip"] = ctx.Tr("repo.editor.this_file_locked") - } else { - ctx.Data["CanDeleteFile"] = true - ctx.Data["DeleteFileTooltip"] = ctx.Tr("repo.editor.delete_this_file") - } - } else if !ctx.Repo.IsViewBranch { - ctx.Data["DeleteFileTooltip"] = ctx.Tr("repo.editor.must_be_on_a_branch") - } else if !ctx.Repo.CanWriteToBranch(ctx, ctx.Doer, ctx.Repo.BranchName) { - ctx.Data["DeleteFileTooltip"] = ctx.Tr("repo.editor.must_have_write_access") - } - - isAnnexFile, okAnnexFile := ctx.Data["IsAnnexFile"] - isAnnexFilePresent, okAnnexFilePresent := ctx.Data["IsAnnexFilePresent"] - if okAnnexFile && okAnnexFilePresent && isAnnexFile.(bool) && !isAnnexFilePresent.(bool) { - // If the file to be viewed is annexed but not present then render it normally - // (which will show the plain git blob content, i.e. the symlink or pointer target) - // but make the status code a 404. - ctx.HTML(http.StatusNotFound, tplRepoHome) - return - } ctx.HTML(http.StatusOK, tplRepoHome) } diff --git a/routers/web/web.go b/routers/web/web.go index a276fa2..4d8d280 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -51,7 +51,6 @@ import ( _ "code.gitea.io/gitea/modules/session" // to registers all internal adapters - "code.forgejo.org/go-chi/binding" "code.forgejo.org/go-chi/captcha" chi_middleware "github.com/go-chi/chi/v5/middleware" "github.com/go-chi/cors" @@ -357,20 +356,6 @@ func registerRoutes(m *web.Route) { } } - annexEnabled := func(ctx *context.Context) { - if !setting.Annex.Enabled { - ctx.Error(http.StatusNotFound) - return - } - } - - annexP2PHTTPEnabled := func(ctx *context.Context) { - if setting.Annex.DisableP2PHTTP { - ctx.Error(http.StatusNotFound) - return - } - } - federationEnabled := func(ctx *context.Context) { if !setting.Federation.Enabled { ctx.Error(http.StatusNotFound) @@ -970,9 +955,6 @@ func registerRoutes(m *web.Route) { // ***** END: Organization ***** // ***** START: Repository ***** - m.Group("", func() { - m.Methods("GET,POST", "/git-annex-p2phttp/git-annex/{uuid}/*", repo.AnnexP2PHTTP) - }, ignSignInAndCsrf, annexEnabled, annexP2PHTTPEnabled) m.Group("/repo", func() { m.Get("/create", repo.Create) m.Post("/create", web.Bind(forms.CreateRepoForm{}), repo.CreatePost) @@ -1269,13 +1251,11 @@ func registerRoutes(m *web.Route) { m.Combo("/_new/*").Get(repo.NewFile). Post(web.Bind(forms.EditRepoFileForm{}), repo.NewFilePost) m.Post("/_preview/*", web.Bind(forms.EditPreviewDiffForm{}), repo.DiffPreviewPost) - m.Combo("/_delete_path/*").Get(repo.DeletePath). - Post(web.Bind(forms.DeleteRepoFileForm{}), repo.DeletePathPost) m.Combo("/_delete/*").Get(repo.DeleteFile). Post(web.Bind(forms.DeleteRepoFileForm{}), repo.DeleteFilePost) m.Combo("/_upload/*", repo.MustBeAbleToUpload). Get(repo.UploadFile). - Post(BindUpload(forms.UploadRepoFileForm{}), repo.UploadFilePost) + Post(web.Bind(forms.UploadRepoFileForm{}), repo.UploadFilePost) m.Combo("/_diffpatch/*").Get(repo.NewDiffPatch). Post(web.Bind(forms.EditRepoFileForm{}), repo.NewDiffPatchPost) m.Combo("/_cherrypick/{sha:([a-f0-9]{4,64})}/*").Get(repo.CherryPick). @@ -1655,12 +1635,6 @@ func registerRoutes(m *web.Route) { }) }, ignSignInAndCsrf, lfsServerEnabled) - m.Group("", func() { - // for git-annex - m.Methods("GET,OPTIONS", "/config", repo.GetConfig) // needed by clients reading annex.uuid during `git annex initremote` - m.Methods("GET,OPTIONS", "/annex/objects/{hash1}/{hash2}/{keyDir}/{key}", repo.GetAnnexObject) - }, ignSignInAndCsrf, annexEnabled, context.UserAssignmentWeb()) - gitHTTPRouters(m) }) }) @@ -1697,20 +1671,3 @@ func registerRoutes(m *web.Route) { ctx.NotFound("", nil) }) } - -func BindUpload(f forms.UploadRepoFileForm) http.HandlerFunc { - return func(resp http.ResponseWriter, req *http.Request) { - theObj := new(forms.UploadRepoFileForm) // create a new form obj for every request but not use obj directly - data := middleware.GetContextData(req.Context()) - binding.Bind(req, theObj) - files := theObj.Files - var fullpaths []string - for _, fileID := range files { - fullPath := req.Form.Get("files_fullpath[" + fileID + "]") - fullpaths = append(fullpaths, fullPath) - } - theObj.FullPaths = fullpaths - data.GetData()["__form"] = theObj - middleware.AssignForm(theObj, data) - } -} diff --git a/services/auth/auth.go b/services/auth/auth.go index ddd3191..c108723 100644 --- a/services/auth/auth.go +++ b/services/auth/auth.go @@ -61,17 +61,6 @@ func isArchivePath(req *http.Request) bool { return archivePathRe.MatchString(req.URL.Path) } -var annexPathRe = regexp.MustCompile(`^(/git-annex-p2phttp/|/[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+/annex/)`) - -func isAnnexPath(req *http.Request) bool { - if setting.Annex.Enabled { - // "/config" is git's config, not specifically git-annex's; but the only current - // user of it is when git-annex downloads the annex.uuid during 'git annex init'. - return strings.HasSuffix(req.URL.Path, "/config") || annexPathRe.MatchString(req.URL.Path) - } - return false -} - // handleSignIn clears existing session variables and stores new ones for the specified user object func handleSignIn(resp http.ResponseWriter, req *http.Request, sess SessionStore, user *user_model.User) { // We need to regenerate the session... diff --git a/services/auth/basic.go b/services/auth/basic.go index 8e8fbfc..d489164 100644 --- a/services/auth/basic.go +++ b/services/auth/basic.go @@ -43,8 +43,8 @@ func (b *Basic) Name() string { // name/token on successful validation. // Returns nil if header is empty or validation fails. func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) (*user_model.User, error) { - // Basic authentication should only fire on API, Download or on Git, LFSPaths or Git-Annex paths - if !middleware.IsAPIPath(req) && !isContainerPath(req) && !isAttachmentDownload(req) && !isGitRawOrAttachOrLFSPath(req) && !isAnnexPath(req) { + // Basic authentication should only fire on API, Download or on Git or LFSPaths + if !middleware.IsAPIPath(req) && !isContainerPath(req) && !isAttachmentDownload(req) && !isGitRawOrAttachOrLFSPath(req) { return nil, nil } diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index 75936f8..1ce9b29 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -671,7 +671,6 @@ type UploadRepoFileForm struct { CommitChoice string `binding:"Required;MaxSize(50)"` NewBranchName string `binding:"GitRefName;MaxSize(100)"` Files []string - FullPaths []string CommitMailID int64 `binding:"Required"` Signoff bool } diff --git a/services/repository/files/temp_repo.go b/services/repository/files/temp_repo.go index 566ae5f..6e7570b 100644 --- a/services/repository/files/temp_repo.go +++ b/services/repository/files/temp_repo.go @@ -202,26 +202,6 @@ func (t *TemporaryUploadRepository) AddObjectToIndex(mode, objectHash, objectPat return nil } -// InitPrivateAnnex initializes a private annex in the repository -func (t *TemporaryUploadRepository) InitPrivateAnnex() error { - if _, _, err := git.NewCommand(t.ctx, "config", "annex.private", "true").RunStdString(&git.RunOpts{Dir: t.basePath}); err != nil { - return err - } - if _, _, err := git.NewCommand(t.ctx, "annex", "init").RunStdString(&git.RunOpts{Dir: t.basePath}); err != nil { - return err - } - return nil -} - -// AddAnnex adds the file at path to the repository using git annex add -// This requires a non-bare repository -func (t *TemporaryUploadRepository) AddAnnex(path string) error { - if _, _, err := git.NewCommand(t.ctx, "annex", "add").AddDynamicArguments(path).RunStdString(&git.RunOpts{Dir: t.basePath}); err != nil { - return err - } - return nil -} - // WriteTree writes the current index as a tree to the object db and returns its hash func (t *TemporaryUploadRepository) WriteTree() (string, error) { stdout, _, err := git.NewCommand(t.ctx, "write-tree").RunStdString(&git.RunOpts{Dir: t.basePath}) diff --git a/services/repository/files/update.go b/services/repository/files/update.go index 5ef778c..d6025b6 100644 --- a/services/repository/files/update.go +++ b/services/repository/files/update.go @@ -10,7 +10,6 @@ import ( "path" "strings" "time" - "errors" "code.gitea.io/gitea/models" git_model "code.gitea.io/gitea/models/git" @@ -57,7 +56,6 @@ type ChangeRepoFilesOptions struct { Committer *IdentityOptions Dates *CommitDateOptions Signoff bool - IsDir bool `default:"false"` } type RepoFileOptions struct { @@ -92,30 +90,6 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use return nil, err } - if opts.IsDir { - var new_opts_files []*ChangeRepoFile - for _, file := range opts.Files { - if file.Operation != "delete" { - return nil, errors.New("invalid operation: only delete is allowed for directory paths") - } - treePath := CleanUploadFileName(file.TreePath) - filelist, err := gitRepo.LsFilesFromDirectory(treePath, opts.OldBranch) - if err != nil { - return nil, err - } - for _, filename := range filelist { - if len(filename) > 0{ - - new_opts_files = append(new_opts_files, &ChangeRepoFile{ - Operation: "delete", - TreePath: filename, - }) - } - } - } - opts.Files = new_opts_files - } - var treePaths []string for _, file := range opts.Files { // If FromTreePath is not set, set it to the opts.TreePath diff --git a/services/repository/files/upload.go b/services/repository/files/upload.go index a43e688..1330116 100644 --- a/services/repository/files/upload.go +++ b/services/repository/files/upload.go @@ -6,18 +6,13 @@ package files import ( "context" "fmt" - "html" - "io" "os" "path" - "path/filepath" - "regexp" "strings" git_model "code.gitea.io/gitea/models/git" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/annex" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/lfs" "code.gitea.io/gitea/modules/setting" @@ -32,8 +27,7 @@ type UploadRepoFileOptions struct { Message string Author *IdentityOptions Committer *IdentityOptions - Files []string // In UUID format - FullPaths []string + Files []string // In UUID format. Signoff bool } @@ -62,10 +56,6 @@ func UploadRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use return nil } - if len(opts.Files) != len(opts.FullPaths) { - return nil - } - uploads, err := repo_model.GetUploadsByUUIDs(ctx, opts.Files) if err != nil { return fmt.Errorf("GetUploadsByUUIDs [uuids: %v]: %w", opts.Files, err) @@ -75,7 +65,6 @@ func UploadRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use infos := make([]uploadInfo, len(uploads)) for i, upload := range uploads { // Check file is not lfs locked, will return nil if lock setting not enabled - upload.Name = fileNameSanitize(html.UnescapeString(opts.FullPaths[i])) filepath := path.Join(opts.TreePath, upload.Name) lfsLock, err := git_model.GetTreePathLock(ctx, repo.ID, filepath) if err != nil { @@ -100,7 +89,7 @@ func UploadRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use defer t.Close() hasOldBranch := true - if err = t.Clone(opts.OldBranch, false); err != nil { + if err = t.Clone(opts.OldBranch, true); err != nil { if !git.IsErrBranchNotExist(err) || !repo.IsEmpty { return err } @@ -116,30 +105,10 @@ func UploadRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use } } - r, err := git.OpenRepository(ctx, repo.RepoPath()) - if err != nil { + // Copy uploaded files into repository. + if err := copyUploadedLFSFilesIntoRepository(infos, t, opts.TreePath); err != nil { return err } - if annex.IsAnnexRepo(r) { - // Initialize annex privately in temporary clone - if err := t.InitPrivateAnnex(); err != nil { - return err - } - // Copy uploaded files into git-annex repository - if err := copyUploadedFilesIntoAnnexRepository(infos, t, opts.TreePath); err != nil { - return err - } - // Move all annexed content in the temporary repository, i.e. everything we have just added, to the origin - author, committer := GetAuthorAndCommitterUsers(opts.Author, opts.Committer, doer) - if err := moveAnnexedFilesToOrigin(t, author, committer); err != nil { - return err - } - } else { - // Copy uploaded files into repository. - if err := copyUploadedLFSFilesIntoRepository(infos, t, opts.TreePath); err != nil { - return err - } - } // Now write the tree treeHash, err := t.WriteTree() @@ -186,19 +155,6 @@ func UploadRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use return repo_model.DeleteUploads(ctx, uploads...) } -// From forgejo/services/repository/generate.go (but now allows /) -var fileNameSanitizeRegexp = regexp.MustCompile(`(?i)\.\.|[<>:\"\\|?*\x{0000}-\x{001F}]|^(con|prn|aux|nul|com\d|lpt\d)$`) - -// Sanitize user input to valid OS filenames -// -// Based on https://github.com/sindresorhus/filename-reserved-regex -// Adds ".." to prevent directory traversal -func fileNameSanitize(s string) string { - // Added this because I am not sure what Windows will deliver us \ or / but we need /. - s = strings.ReplaceAll(s, "\\", "/") - return strings.TrimSpace(fileNameSanitizeRegexp.ReplaceAllString(s, "_")) -} - func copyUploadedLFSFilesIntoRepository(infos []uploadInfo, t *TemporaryUploadRepository, treePath string) error { var storeInLFSFunc func(string) (bool, error) @@ -290,57 +246,3 @@ func uploadToLFSContentStore(info uploadInfo, contentStore *lfs.ContentStore) er } return nil } - -func copyUploadedFilesIntoAnnexRepository(infos []uploadInfo, t *TemporaryUploadRepository, treePath string) error { - for i := range len(infos) { - if err := copyUploadedFileIntoAnnexRepository(&infos[i], t, treePath); err != nil { - return err - } - } - return nil -} - -func copyUploadedFileIntoAnnexRepository(info *uploadInfo, t *TemporaryUploadRepository, treePath string) error { - pathInRepo := path.Join(t.basePath, treePath, info.upload.Name) - if err := os.MkdirAll(filepath.Dir(pathInRepo), 0o700); err != nil { - return err - } - if err := os.Rename(info.upload.LocalPath(), pathInRepo); err != nil { - // Rename didn't work, try copy and remove - inputFile, err := os.Open(info.upload.LocalPath()) - if err != nil { - return fmt.Errorf("could not open source file: %v", err) - } - defer inputFile.Close() - outputFile, err := os.Create(pathInRepo) - if err != nil { - return fmt.Errorf("could not open dest file: %v", err) - } - defer outputFile.Close() - _, err = io.Copy(outputFile, inputFile) - if err != nil { - return fmt.Errorf("could not copy to dest from source: %v", err) - } - inputFile.Close() - err = os.Remove(info.upload.LocalPath()) - if err != nil { - return fmt.Errorf("could not remove source file: %v", err) - } - } - return t.AddAnnex(pathInRepo) -} - -func moveAnnexedFilesToOrigin(t *TemporaryUploadRepository, author, committer *user_model.User) error { - authorSig := author.NewGitSig() - committerSig := committer.NewGitSig() - env := append(os.Environ(), - "GIT_AUTHOR_NAME="+authorSig.Name, - "GIT_AUTHOR_EMAIL="+authorSig.Email, - "GIT_COMMITTER_NAME="+committerSig.Name, - "GIT_COMMITTER_EMAIL="+committerSig.Email, - ) - if _, _, err := git.NewCommand(t.ctx, "annex", "move", "--to", "origin").RunStdString(&git.RunOpts{Dir: t.basePath, Env: env}); err != nil { - return err - } - return nil -} diff --git a/templates/home.tmpl b/templates/home.tmpl index 5f84fca..a974344 100644 --- a/templates/home.tmpl +++ b/templates/home.tmpl @@ -11,10 +11,41 @@ - - {{if .LandingPageInfoEnabled}} - {{template "landing-page" .}} - {{end}} - +
+
+

+ {{svg "octicon-flame"}} {{ctx.Locale.Tr "startpage.install"}} +

+

+ {{ctx.Locale.Tr "startpage.install_desc" "https://forgejo.org/download/#installation-from-binary" "https://forgejo.org/download/#container-image" "https://forgejo.org/download"}} +

+
+
+

+ {{svg "octicon-device-desktop"}} {{ctx.Locale.Tr "startpage.platform"}} +

+

+ {{ctx.Locale.Tr "startpage.platform_desc"}} +

+
+
+
+
+

+ {{svg "octicon-rocket"}} {{ctx.Locale.Tr "startpage.lightweight"}} +

+

+ {{ctx.Locale.Tr "startpage.lightweight_desc"}} +

+
+
+

+ {{svg "octicon-code"}} {{ctx.Locale.Tr "startpage.license"}} +

+

+ {{ctx.Locale.Tr "startpage.license_desc" "https://forgejo.org/download" "https://codeberg.org/forgejo/forgejo"}} +

+
+
{{template "base/footer" .}} diff --git a/templates/landing-page.tmpl b/templates/landing-page.tmpl deleted file mode 100644 index 5eb6822..0000000 --- a/templates/landing-page.tmpl +++ /dev/null @@ -1,36 +0,0 @@ -
-
-

- {{svg "octicon-flame"}} {{ctx.Locale.Tr "startpage.install"}} -

-

- {{ctx.Locale.Tr "startpage.install_desc" "https://forgejo.org/download/#installation-from-binary" "https://forgejo.org/download/#container-image" "https://forgejo.org/download"}} -

-
-
-

- {{svg "octicon-device-desktop"}} {{ctx.Locale.Tr "startpage.platform"}} -

-

- {{ctx.Locale.Tr "startpage.platform_desc"}} -

-
-
-
-
-

- {{svg "octicon-rocket"}} {{ctx.Locale.Tr "startpage.lightweight"}} -

-

- {{ctx.Locale.Tr "startpage.lightweight_desc"}} -

-
-
-

- {{svg "octicon-code"}} {{ctx.Locale.Tr "startpage.license"}} -

-

- {{ctx.Locale.Tr "startpage.license_desc" "https://forgejo.org/download" "https://codeberg.org/forgejo/forgejo"}} -

-
-
\ No newline at end of file diff --git a/templates/repo/file_info.tmpl b/templates/repo/file_info.tmpl index 8655404..6ae7c15 100644 --- a/templates/repo/file_info.tmpl +++ b/templates/repo/file_info.tmpl @@ -17,7 +17,6 @@ {{if .FileSize}}
{{ctx.Locale.TrSize .FileSize}}{{if .IsLFSFile}} ({{ctx.Locale.Tr "repo.stored_lfs"}}){{end}} - {{if .IsAnnexFile}} ({{ctx.Locale.Tr "repo.stored_annex"}}{{if not .IsAnnexFilePresent}} - {{ctx.Locale.Tr "repo.stored_annex_not_present"}}{{end}}){{end}}
{{end}} {{if .LFSLock}} diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl index 7b98665..7bf4ee4 100644 --- a/templates/repo/home.tmpl +++ b/templates/repo/home.tmpl @@ -114,15 +114,6 @@ {{- end -}} {{end}} - - {{if (not .IsViewFile)}} - {{if and .CanDeleteFile (not .IsBlame)}} - {{svg "octicon-trash"}} - {{else}} - {{svg "octicon-trash"}} - {{end}} - {{end}} -
diff --git a/templates/user/auth/signin_inner.tmpl b/templates/user/auth/signin_inner.tmpl index 6862f5c..ddef34f 100644 --- a/templates/user/auth/signin_inner.tmpl +++ b/templates/user/auth/signin_inner.tmpl @@ -50,21 +50,18 @@
-{{if .SignInForgottenPasswordEnabled}} -
- {{template "user/auth/webauthn_error" .}} +
+ {{template "user/auth/webauthn_error" .}} -
- {{if .ShowRegistrationButton}} -
- {{ctx.Locale.Tr "auth.hint_register" (printf "%s/user/sign_up" AppSubUrl)}} -
-
- {{end}} - +
+ {{if .ShowRegistrationButton}} +
+ {{ctx.Locale.Tr "auth.hint_register" (printf "%s/user/sign_up" AppSubUrl)}} +
-
-{{end}} - + {{end}} + +
+
diff --git a/tests/integration/api_helper_for_declarative_test.go b/tests/integration/api_helper_for_declarative_test.go index 5bd8dc5..dae71ca 100644 --- a/tests/integration/api_helper_for_declarative_test.go +++ b/tests/integration/api_helper_for_declarative_test.go @@ -22,7 +22,6 @@ import ( api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/services/forms" - "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -462,27 +461,3 @@ func doAPIAddRepoToOrganizationTeam(ctx APITestContext, teamID int64, orgName, r ctx.Session.MakeRequest(t, req, http.StatusNoContent) } } - -// generate and activate an ssh key for the user attached to the APITestContext -// TODO: pick a better name; golang doesn't do method overriding. -func withCtxKeyFile(t *testing.T, ctx APITestContext, callback func()) { - // we need to have write:public_key to do this step - // the easiest way is to create a throwaway ctx that is identical but only has that permission - ctxKeyWriter := ctx - ctxKeyWriter.Token = getTokenForLoggedInUser(t, ctx.Session, auth.AccessTokenScopeWriteUser) - - keyName := "One of " + ctx.Username + "'s keys: #" + uuid.New().String() - withKeyFile(t, keyName, func(keyFile string) { - var key api.PublicKey - - doAPICreateUserKey(ctxKeyWriter, keyName, keyFile, - func(t *testing.T, _key api.PublicKey) { - // save the key ID so we can delete it at the end - key = _key - })(t) - - defer doAPIDeleteUserKey(ctxKeyWriter, key.ID)(t) - - callback() - }) -} diff --git a/tests/integration/git_annex_test.go b/tests/integration/git_annex_test.go deleted file mode 100644 index efcb571..0000000 --- a/tests/integration/git_annex_test.go +++ /dev/null @@ -1,2925 +0,0 @@ -// Copyright 2022 The Gitea Authors. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. - -package integration - -import ( - "bytes" - "errors" - "fmt" - "io" - "math/rand" - "mime/multipart" - "net/http" - "net/url" - "os" - "path" - "path/filepath" - "regexp" - "strings" - "testing" - - auth_model "code.gitea.io/gitea/models/auth" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/models/perm" - repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/modules/annex" - "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/setting" - api "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/util" - "code.gitea.io/gitea/tests" - - "github.com/stretchr/testify/require" -) - -// Some guidelines: -// -// * a APITestContext is an awkward union of session credential + username + target repo -// which is assumed to be owned by that username; if you want to target a different -// repo, you need to edit its .Reponame or just ignore it and write "username/reponame.git" - -func doCreateRemoteAnnexRepository(t *testing.T, u *url.URL, ctx APITestContext, private bool, objectFormat git.ObjectFormat) (err error) { - // creating a repo counts as editing the user's profile (is done by POSTing - // to /api/v1/user/repos/) -- which means it needs a User-scoped token and - // both that and editing need a Repo-scoped token because they edit repositories. - rescopedCtx := ctx - rescopedCtx.Token = getTokenForLoggedInUser(t, ctx.Session, auth_model.AccessTokenScopeWriteUser, auth_model.AccessTokenScopeWriteRepository) - doAPICreateRepository(rescopedCtx, false, objectFormat)(t) - t.Cleanup(func() { util.MakeWritable(setting.RepoRootPath) }) - doAPIEditRepository(rescopedCtx, &api.EditRepoOption{Private: &private})(t) - - repoURL := createSSHUrl(ctx.GitPath(), u) - - // Fill in fixture data - withAnnexCtxKeyFile(t, ctx, func() { - err = doInitRemoteAnnexRepository(t, repoURL) - }) - if err != nil { - return fmt.Errorf("Unable to initialize remote repo with git-annex fixture: %w", err) - } - return nil -} - -func TestGitAnnexWebUpload(t *testing.T) { - if !setting.Annex.Enabled { - t.Skip("Skipping since annex support is disabled.") - } - - onGiteaRun(t, func(t *testing.T, u *url.URL) { - forEachObjectFormat(t, func(t *testing.T, objectFormat git.ObjectFormat) { - ctx := NewAPITestContext(t, "user2", "annex-web-upload-test"+objectFormat.Name(), auth_model.AccessTokenScopeWriteRepository) - require.NoError(t, doCreateRemoteAnnexRepository(t, u, ctx, false, objectFormat)) - - uploadFile := func(t *testing.T, path string) string { - t.Helper() - - body := &bytes.Buffer{} - mpForm := multipart.NewWriter(body) - err := mpForm.WriteField("_csrf", GetCSRF(t, ctx.Session, ctx.Username+"/"+ctx.Reponame+"/_upload/"+setting.Repository.DefaultBranch)) - require.NoError(t, err) - - file, err := mpForm.CreateFormFile("file", filepath.Base(path)) - require.NoError(t, err) - - srcFile, err := os.Open(path) - require.NoError(t, err) - - io.Copy(file, srcFile) - require.NoError(t, mpForm.Close()) - - req := NewRequestWithBody(t, "POST", "/"+ctx.Username+"/"+ctx.Reponame+"/upload-file", body) - req.Header.Add("Content-Type", mpForm.FormDataContentType()) - resp := ctx.Session.MakeRequest(t, req, http.StatusOK) - - respMap := map[string]string{} - DecodeJSON(t, resp, &respMap) - return respMap["uuid"] - } - - // Generate random file - tmpFile := path.Join(t.TempDir(), "web-upload-test-file.bin") - require.NoError(t, generateRandomFile(1024*1024/4, tmpFile)) - expectedContent, err := os.ReadFile(tmpFile) - require.NoError(t, err) - - // Upload generated file - fileUUID := uploadFile(t, tmpFile) - req := NewRequestWithValues(t, "POST", ctx.Username+"/"+ctx.Reponame+"/_upload/"+setting.Repository.DefaultBranch, map[string]string{ - "commit_choice": "direct", - "files": fileUUID, - "_csrf": GetCSRF(t, ctx.Session, ctx.Username+"/"+ctx.Reponame+"/_upload/"+setting.Repository.DefaultBranch), - "commit_mail_id": "-1", - }) - ctx.Session.MakeRequest(t, req, http.StatusSeeOther) - - // Get some handles on the target repository and file - remoteRepoPath := path.Join(setting.RepoRootPath, ctx.GitPath()) - repo, err := git.OpenRepository(git.DefaultContext, remoteRepoPath) - require.NoError(t, err) - defer repo.Close() - tree, err := repo.GetTree(setting.Repository.DefaultBranch) - require.NoError(t, err) - treeEntry, err := tree.GetTreeEntryByPath(filepath.Base(tmpFile)) - require.NoError(t, err) - blob := treeEntry.Blob() - - // Check that the uploaded file is annexed - isAnnexed, err := annex.IsAnnexed(blob) - require.NoError(t, err) - require.True(t, isAnnexed) - - // Check that the uploaded file has the correct content - annexedFile, err := annex.Content(blob) - require.NoError(t, err) - actualContent, err := io.ReadAll(annexedFile) - require.NoError(t, err) - require.Equal(t, expectedContent, actualContent) - }) - }) -} - -func TestGitAnnexMedia(t *testing.T) { - if !setting.Annex.Enabled { - t.Skip("Skipping since annex support is disabled.") - } - - onGiteaRun(t, func(t *testing.T, u *url.URL) { - forEachObjectFormat(t, func(t *testing.T, objectFormat git.ObjectFormat) { - ctx := NewAPITestContext(t, "user2", "annex-media-test"+objectFormat.Name(), auth_model.AccessTokenScopeWriteRepository) - - // create a public repo - require.NoError(t, doCreateRemoteAnnexRepository(t, u, ctx, false, objectFormat)) - - // the filenames here correspond to specific cases defined in doInitAnnexRepository() - t.Run("AnnexSymlink", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - doAnnexMediaTest(t, ctx, "annexed.tiff") - }) - t.Run("AnnexPointer", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - doAnnexMediaTest(t, ctx, "annexed.bin") - }) - }) - }) -} - -func doAnnexMediaTest(t *testing.T, ctx APITestContext, file string) { - // Make sure that downloading via /media on the website recognizes it should give the annexed content - - // TODO: - // - [ ] roll this into TestGitAnnexPermissions to ensure that permission enforcement works correctly even on /media? - - session := loginUser(t, ctx.Username) // logs in to the http:// site/API, storing a cookie; - // this is a different auth method than the git+ssh:// or git+http:// protocols TestGitAnnexPermissions uses! - - // compute server-side path of the annexed file - remoteRepoPath := path.Join(setting.RepoRootPath, ctx.GitPath()) - remoteObjectPath, err := contentLocation(remoteRepoPath, file) - require.NoError(t, err) - - // download annexed file - localObjectPath := path.Join(t.TempDir(), file) - fd, err := os.OpenFile(localObjectPath, os.O_CREATE|os.O_WRONLY, 0o777) - defer fd.Close() - require.NoError(t, err) - - mediaLink := path.Join("/", ctx.Username, ctx.Reponame, "/media/branch/master", file) - req := NewRequest(t, "GET", mediaLink) - resp := session.MakeRequest(t, req, http.StatusOK) - - _, err = io.Copy(fd, resp.Body) - require.NoError(t, err) - fd.Close() - - // verify the download - match, err := tests.FileCmp(localObjectPath, remoteObjectPath, 0) - require.NoError(t, err) - require.True(t, match, "Annexed files should be the same") -} - -func TestGitAnnexViews(t *testing.T) { - if !setting.Annex.Enabled { - t.Skip("Skipping since annex support is disabled.") - } - - onGiteaRun(t, func(t *testing.T, u *url.URL) { - forEachObjectFormat(t, func(t *testing.T, objectFormat git.ObjectFormat) { - ctx := NewAPITestContext(t, "user2", "annex-template-render-test"+objectFormat.Name(), auth_model.AccessTokenScopeWriteRepository) - - // create a public repo - require.NoError(t, doCreateRemoteAnnexRepository(t, u, ctx, false, objectFormat)) - - session := loginUser(t, ctx.Username) - - t.Run("Index", func(t *testing.T) { - // test that annexed files render with the binary file icon on the main list - defer tests.PrintCurrentTest(t)() - - repoLink := path.Join("/", ctx.Username, ctx.Reponame) - req := NewRequest(t, "GET", repoLink) - resp := session.MakeRequest(t, req, http.StatusOK) - - htmlDoc := NewHTMLParser(t, resp.Body) - isFileBinaryIconLocked := htmlDoc.Find("tr[data-entryname='annexed.tiff'] > td.name svg").HasClass("octicon-file-binary") - require.True(t, isFileBinaryIconLocked, "locked annexed files should render a binary file icon") - isFileBinaryIconUnlocked := htmlDoc.Find("tr[data-entryname='annexed.bin'] > td.name svg").HasClass("octicon-file-binary") - require.True(t, isFileBinaryIconUnlocked, "unlocked annexed files should render a binary file icon") - }) - - t.Run("View", func(t *testing.T) { - // test how routers/web/repo/view.go + templates/repo/view_file.tmpl handle annexed files - defer tests.PrintCurrentTest(t)() - - doViewTest := func(file string) (htmlDoc *HTMLDoc, viewLink, mediaLink string) { - viewLink = path.Join("/", ctx.Username, ctx.Reponame, "/src/branch/master", file) - // rawLink := strings.Replace(viewLink, "/src/", "/raw/", 1) // TODO: do something with this? - mediaLink = strings.Replace(viewLink, "/src/", "/media/", 1) - - req := NewRequest(t, "GET", viewLink) - resp := session.MakeRequest(t, req, http.StatusOK) - - htmlDoc = NewHTMLParser(t, resp.Body) - // the first button on the toolbar on the view template is the "Raw" button - // this CSS selector is the most precise I can think to use - buttonLink, exists := htmlDoc.Find(".file-header").Find("a[download]").Attr("href") - require.True(t, exists, "Download button should exist on the file header") - require.EqualValues(t, mediaLink, buttonLink, "Download link should use /media URL for annex files") - - return htmlDoc, viewLink, mediaLink - } - - t.Run("Binary", func(t *testing.T) { - // test that annexing a file renders the /media link in /src and NOT the /raw link - defer tests.PrintCurrentTest(t)() - - doBinaryViewTest := func(file string) { - htmlDoc, _, mediaLink := doViewTest(file) - - rawLink, exists := htmlDoc.Find("div.file-view > div.view-raw > a").Attr("href") - require.True(t, exists, "Download link should render instead of content because this is a binary file") - require.EqualValues(t, mediaLink, rawLink) - } - - t.Run("AnnexSymlink", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - doBinaryViewTest("annexed.tiff") - }) - t.Run("AnnexPointer", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - doBinaryViewTest("annexed.bin") - }) - }) - - t.Run("Text", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - doTextViewTest := func(file string) { - htmlDoc, _, _ := doViewTest(file) - require.True(t, htmlDoc.Find("div.file-view").Is(".code-view"), "should render as code") - } - - t.Run("AnnexSymlink", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - doTextViewTest("annexed.txt") - - t.Run("Markdown", func(t *testing.T) { - // special case: check that markdown can be pulled out of the annex and rendered, too - defer tests.PrintCurrentTest(t)() - htmlDoc, _, _ := doViewTest("annexed.md") - require.True(t, htmlDoc.Find("div.file-view").Is(".markdown"), "should render as markdown") - }) - }) - t.Run("AnnexPointer", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - doTextViewTest("annexed.rst") - - t.Run("Markdown", func(t *testing.T) { - // special case: check that markdown can be pulled out of the annex and rendered, too - defer tests.PrintCurrentTest(t)() - htmlDoc, _, _ := doViewTest("annexed.markdown") - require.True(t, htmlDoc.Find("div.file-view").Is(".markdown"), "should render as markdown") - }) - }) - }) - }) - }) - }) -} - -/* -Test that permissions are enforced on git-annex-shell commands. - - Along the way, this also tests that uploading, downloading, and deleting all work, - so we haven't written separate tests for those. -*/ -func TestGitAnnexPermissions(t *testing.T) { - if !setting.Annex.Enabled { - t.Skip("Skipping since annex support is disabled.") - } - - // Each case below is split so that 'clone' is done as - // the repo owner, but 'copy' as the user under test. - // - // Otherwise, in cases where permissions block the - // initial 'clone', the test would simply end there - // and never verify if permissions apply properly to - // 'annex copy' -- potentially leaving a security gap. - - onGiteaRun(t, func(t *testing.T, u *url.URL) { - // Tell git-annex to allow http://127.0.0.1, http://localhost and http://::1. Without - // this, all `git annex` commands will silently fail when run against http:// remotes - // without explaining what's wrong. - // - // Note: onGiteaRun() sets up an alternate HOME so this actually edits - // tests/integration/gitea-integration-*/data/home/.gitconfig and - // if you're debugging you need to remember to match that. - _, _, err := git.NewCommandContextNoGlobals(git.DefaultContext, "config").AddOptionValues("--global").AddArguments("annex.security.allowed-ip-addresses", "all").RunStdString(&git.RunOpts{}) - require.NoError(t, err) - - forEachObjectFormat(t, func(t *testing.T, objectFormat git.ObjectFormat) { - t.Run("Public", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - ownerCtx := NewAPITestContext(t, "user2", "annex-public"+objectFormat.Name(), auth_model.AccessTokenScopeWriteRepository) - - // create a public repo - require.NoError(t, doCreateRemoteAnnexRepository(t, u, ownerCtx, false, objectFormat)) - - // double-check it's public - repo, err := repo_model.GetRepositoryByOwnerAndName(db.DefaultContext, ownerCtx.Username, ownerCtx.Reponame) - require.NoError(t, err) - require.False(t, repo.IsPrivate) - - remoteRepoPath := path.Join(setting.RepoRootPath, ownerCtx.GitPath()) // path on disk -- which can be examined directly because we're testing from localhost - - // Different sessions, so we can test different permissions. - // We leave Reponame blank because we don't actually then later add it according to each case if needed - // - // NB: these usernames need to match appropriate entries in models/fixtures/user.yml - writerCtx := NewAPITestContext(t, "user5", "", auth_model.AccessTokenScopeWriteRepository) - readerCtx := NewAPITestContext(t, "user4", "", auth_model.AccessTokenScopeReadRepository) - outsiderCtx := NewAPITestContext(t, "user8", "", auth_model.AccessTokenScopeReadRepository) // a user with no specific access - - // set up collaborators - doAPIAddCollaborator(ownerCtx, readerCtx.Username, perm.AccessModeRead)(t) - doAPIAddCollaborator(ownerCtx, writerCtx.Username, perm.AccessModeWrite)(t) - - // tests - t.Run("Owner", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - t.Run("SSH", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - repoURL := createSSHUrl(ownerCtx.GitPath(), u) - - repoPath := path.Join(t.TempDir(), ownerCtx.Reponame) - defer util.RemoveAll(repoPath) // cleans out git-annex lockdown permissions - - withAnnexCtxKeyFile(t, ownerCtx, func() { - doGitClone(repoPath, repoURL)(t) - }) - - t.Run("Init", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, ownerCtx, func() { - require.NoError(t, doAnnexInitTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, ownerCtx, func() { - require.NoError(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("LocalDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, ownerCtx, func() { - require.NoError(t, doAnnexLocalDropTest(repoPath)) - }) - }) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, ownerCtx, func() { - require.NoError(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("RemoteDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, ownerCtx, func() { - require.NoError(t, doAnnexRemoteDropTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("Upload", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, ownerCtx, func() { - require.NoError(t, doAnnexUploadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("TestremoteReadOnly", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, ownerCtx, func() { - require.NoError(t, doAnnexTestremoteReadOnlyTest(repoPath)) - }) - }) - - t.Run("TestremoteReadWrite", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, ownerCtx, func() { - require.NoError(t, doAnnexTestremoteReadWriteTest(repoPath)) - }) - }) - }) - - t.Run("HTTP", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - repoURL := createHTTPUrl(ownerCtx.GitPath(), u) - - repoPath := path.Join(t.TempDir(), ownerCtx.Reponame) - defer util.RemoveAll(repoPath) // cleans out git-annex lockdown permissions - - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - doGitClone(repoPath, repoURL)(t) - }) - - t.Run("Init", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - require.NoError(t, doAnnexInitTest(remoteRepoPath, repoPath)) - }) - }) - - // Unset annexurl so that git-annex uses the dumb http support - _, _, err := git.NewCommand(git.DefaultContext, "config", "--unset", "remote.origin.annexurl").RunStdString(&git.RunOpts{Dir: repoPath}) - require.NoError(t, err) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - require.NoError(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("LocalDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - require.NoError(t, doAnnexLocalDropTest(repoPath)) - }) - }) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - require.NoError(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("RemoteDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - require.Error(t, doAnnexRemoteDropTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("Upload", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - require.Error(t, doAnnexUploadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("TestremoteReadOnly", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - require.NoError(t, doAnnexTestremoteReadOnlyTest(repoPath)) - }) - }) - - t.Run("TestremoteReadWrite", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - require.Error(t, doAnnexTestremoteReadWriteTest(repoPath)) - }) - }) - }) - - t.Run("P2PHTTP", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - repoURL := createHTTPUrl(ownerCtx.GitPath(), u) - - repoPath := path.Join(t.TempDir(), ownerCtx.Reponame) - defer util.RemoveAll(repoPath) // cleans out git-annex lockdown permissions - - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - doGitClone(repoPath, repoURL)(t) - }) - - t.Run("Init", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - require.NoError(t, doAnnexInitTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - require.NoError(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("LocalDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - require.NoError(t, doAnnexLocalDropTest(repoPath)) - }) - }) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - require.NoError(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("RemoteDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - require.NoError(t, doAnnexRemoteDropTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("Upload", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - require.NoError(t, doAnnexUploadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("TestremoteReadOnly", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - require.NoError(t, doAnnexTestremoteReadOnlyTest(repoPath)) - }) - }) - - t.Run("TestremoteReadWrite", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - require.NoError(t, doAnnexTestremoteReadWriteTest(repoPath)) - }) - }) - }) - }) - - t.Run("Writer", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - t.Run("SSH", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - repoURL := createSSHUrl(ownerCtx.GitPath(), u) - - repoPath := path.Join(t.TempDir(), ownerCtx.Reponame) - defer util.RemoveAll(repoPath) // cleans out git-annex lockdown permissions - - withAnnexCtxKeyFile(t, ownerCtx, func() { - doGitClone(repoPath, repoURL)(t) - }) - - t.Run("Init", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, writerCtx, func() { - require.NoError(t, doAnnexInitTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, writerCtx, func() { - require.NoError(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("LocalDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, writerCtx, func() { - require.NoError(t, doAnnexLocalDropTest(repoPath)) - }) - }) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, writerCtx, func() { - require.NoError(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("RemoteDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, writerCtx, func() { - require.NoError(t, doAnnexRemoteDropTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("Upload", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, writerCtx, func() { - require.NoError(t, doAnnexUploadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("TestremoteReadOnly", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, writerCtx, func() { - require.NoError(t, doAnnexTestremoteReadOnlyTest(repoPath)) - }) - }) - - t.Run("TestremoteReadWrite", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, writerCtx, func() { - require.NoError(t, doAnnexTestremoteReadWriteTest(repoPath)) - }) - }) - }) - - t.Run("HTTP", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - repoURL := createHTTPUrl(ownerCtx.GitPath(), u) - - repoPath := path.Join(t.TempDir(), ownerCtx.Reponame) - defer util.RemoveAll(repoPath) // cleans out git-annex lockdown permissions - - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - doGitClone(repoPath, repoURL)(t) - }) - - t.Run("Init", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, writerCtx, func() { - require.NoError(t, doAnnexInitTest(remoteRepoPath, repoPath)) - }) - }) - - // Unset annexurl so that git-annex uses the dumb http support - _, _, err := git.NewCommand(git.DefaultContext, "config", "--unset", "remote.origin.annexurl").RunStdString(&git.RunOpts{Dir: repoPath}) - require.NoError(t, err) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, writerCtx, func() { - require.NoError(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("LocalDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, writerCtx, func() { - require.NoError(t, doAnnexLocalDropTest(repoPath)) - }) - }) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, writerCtx, func() { - require.NoError(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("RemoteDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, writerCtx, func() { - require.Error(t, doAnnexRemoteDropTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("Upload", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, writerCtx, func() { - require.Error(t, doAnnexUploadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("TestremoteReadOnly", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, writerCtx, func() { - require.NoError(t, doAnnexTestremoteReadOnlyTest(repoPath)) - }) - }) - - t.Run("TestremoteReadWrite", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, writerCtx, func() { - require.Error(t, doAnnexTestremoteReadWriteTest(repoPath)) - }) - }) - }) - - t.Run("P2PHTTP", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - repoURL := createHTTPUrl(ownerCtx.GitPath(), u) - - repoPath := path.Join(t.TempDir(), ownerCtx.Reponame) - defer util.RemoveAll(repoPath) // cleans out git-annex lockdown permissions - - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - doGitClone(repoPath, repoURL)(t) - }) - - t.Run("Init", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, writerCtx, func() { - require.NoError(t, doAnnexInitTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, writerCtx, func() { - require.NoError(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("LocalDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, writerCtx, func() { - require.NoError(t, doAnnexLocalDropTest(repoPath)) - }) - }) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, writerCtx, func() { - require.NoError(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("RemoteDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, writerCtx, func() { - require.NoError(t, doAnnexRemoteDropTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("Upload", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, writerCtx, func() { - require.NoError(t, doAnnexUploadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("TestremoteReadOnly", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, writerCtx, func() { - require.NoError(t, doAnnexTestremoteReadOnlyTest(repoPath)) - }) - }) - - t.Run("TestremoteReadWrite", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, writerCtx, func() { - require.NoError(t, doAnnexTestremoteReadWriteTest(repoPath)) - }) - }) - }) - }) - - t.Run("Reader", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - t.Run("SSH", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - repoURL := createSSHUrl(ownerCtx.GitPath(), u) - - repoPath := path.Join(t.TempDir(), ownerCtx.Reponame) - defer util.RemoveAll(repoPath) // cleans out git-annex lockdown permissions - - withAnnexCtxKeyFile(t, ownerCtx, func() { - doGitClone(repoPath, repoURL)(t) - }) - - t.Run("Init", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, readerCtx, func() { - require.NoError(t, doAnnexInitTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, readerCtx, func() { - require.NoError(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("LocalDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, readerCtx, func() { - require.NoError(t, doAnnexLocalDropTest(repoPath)) - }) - }) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, readerCtx, func() { - require.NoError(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("RemoteDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, readerCtx, func() { - require.Error(t, doAnnexRemoteDropTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("Upload", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, readerCtx, func() { - require.Error(t, doAnnexUploadTest(remoteRepoPath, repoPath), "Uploading should fail due to permissions") - }) - }) - - t.Run("TestremoteReadOnly", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, readerCtx, func() { - require.NoError(t, doAnnexTestremoteReadOnlyTest(repoPath)) - }) - }) - - t.Run("TestremoteReadWrite", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, readerCtx, func() { - require.Error(t, doAnnexTestremoteReadWriteTest(repoPath)) - }) - }) - }) - - t.Run("HTTP", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - repoURL := createHTTPUrl(ownerCtx.GitPath(), u) - - repoPath := path.Join(t.TempDir(), ownerCtx.Reponame) - defer util.RemoveAll(repoPath) // cleans out git-annex lockdown permissions - - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - doGitClone(repoPath, repoURL)(t) - }) - - t.Run("Init", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, readerCtx, func() { - require.NoError(t, doAnnexInitTest(remoteRepoPath, repoPath)) - }) - }) - - // Unset annexurl so that git-annex uses the dumb http support - _, _, err := git.NewCommand(git.DefaultContext, "config", "--unset", "remote.origin.annexurl").RunStdString(&git.RunOpts{Dir: repoPath}) - require.NoError(t, err) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, readerCtx, func() { - require.NoError(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("LocalDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, readerCtx, func() { - require.NoError(t, doAnnexLocalDropTest(repoPath)) - }) - }) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, readerCtx, func() { - require.NoError(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("RemoteDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, readerCtx, func() { - require.Error(t, doAnnexRemoteDropTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("Upload", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, readerCtx, func() { - require.Error(t, doAnnexUploadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("TestremoteReadOnly", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, readerCtx, func() { - require.NoError(t, doAnnexTestremoteReadOnlyTest(repoPath)) - }) - }) - - t.Run("TestremoteReadWrite", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, readerCtx, func() { - require.Error(t, doAnnexTestremoteReadWriteTest(repoPath)) - }) - }) - }) - - t.Run("P2PHTTP", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - repoURL := createHTTPUrl(ownerCtx.GitPath(), u) - - repoPath := path.Join(t.TempDir(), ownerCtx.Reponame) - defer util.RemoveAll(repoPath) // cleans out git-annex lockdown permissions - - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - doGitClone(repoPath, repoURL)(t) - }) - - t.Run("Init", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, readerCtx, func() { - require.NoError(t, doAnnexInitTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, readerCtx, func() { - require.NoError(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("LocalDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, readerCtx, func() { - require.NoError(t, doAnnexLocalDropTest(repoPath)) - }) - }) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, readerCtx, func() { - require.NoError(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("RemoteDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, readerCtx, func() { - require.Error(t, doAnnexRemoteDropTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("Upload", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, readerCtx, func() { - require.Error(t, doAnnexUploadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("TestremoteReadOnly", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, readerCtx, func() { - require.NoError(t, doAnnexTestremoteReadOnlyTest(repoPath)) - }) - }) - - t.Run("TestremoteReadWrite", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, readerCtx, func() { - require.Error(t, doAnnexTestremoteReadWriteTest(repoPath)) - }) - }) - }) - }) - - t.Run("Outsider", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - t.Run("SSH", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - repoURL := createSSHUrl(ownerCtx.GitPath(), u) - - repoPath := path.Join(t.TempDir(), ownerCtx.Reponame) - defer util.RemoveAll(repoPath) // cleans out git-annex lockdown permissions - - withAnnexCtxKeyFile(t, ownerCtx, func() { - doGitClone(repoPath, repoURL)(t) - }) - - t.Run("Init", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, outsiderCtx, func() { - require.NoError(t, doAnnexInitTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, outsiderCtx, func() { - require.NoError(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("LocalDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, outsiderCtx, func() { - require.NoError(t, doAnnexLocalDropTest(repoPath)) - }) - }) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, outsiderCtx, func() { - require.NoError(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("RemoteDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, outsiderCtx, func() { - require.Error(t, doAnnexRemoteDropTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("Upload", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, outsiderCtx, func() { - require.Error(t, doAnnexUploadTest(remoteRepoPath, repoPath), "Uploading should fail due to permissions") - }) - }) - - t.Run("TestremoteReadOnly", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, outsiderCtx, func() { - require.NoError(t, doAnnexTestremoteReadOnlyTest(repoPath)) - }) - }) - - t.Run("TestremoteReadWrite", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, outsiderCtx, func() { - require.Error(t, doAnnexTestremoteReadWriteTest(repoPath)) - }) - }) - }) - - t.Run("HTTP", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - repoURL := createHTTPUrl(ownerCtx.GitPath(), u) - - repoPath := path.Join(t.TempDir(), ownerCtx.Reponame) - defer util.RemoveAll(repoPath) // cleans out git-annex lockdown permissions - - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - doGitClone(repoPath, repoURL)(t) - }) - - t.Run("Init", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, outsiderCtx, func() { - require.NoError(t, doAnnexInitTest(remoteRepoPath, repoPath)) - }) - }) - - // Unset annexurl so that git-annex uses the dumb http support - _, _, err = git.NewCommand(git.DefaultContext, "config", "--unset", "remote.origin.annexurl").RunStdString(&git.RunOpts{Dir: repoPath}) - require.NoError(t, err) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, outsiderCtx, func() { - require.NoError(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("LocalDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, outsiderCtx, func() { - require.NoError(t, doAnnexLocalDropTest(repoPath)) - }) - }) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, outsiderCtx, func() { - require.NoError(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("RemoteDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, outsiderCtx, func() { - require.Error(t, doAnnexRemoteDropTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("Upload", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, outsiderCtx, func() { - require.Error(t, doAnnexUploadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("TestremoteReadOnly", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, outsiderCtx, func() { - require.NoError(t, doAnnexTestremoteReadOnlyTest(repoPath)) - }) - }) - - t.Run("TestremoteReadWrite", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, outsiderCtx, func() { - require.Error(t, doAnnexTestremoteReadWriteTest(repoPath)) - }) - }) - }) - - t.Run("P2PHTTP", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - repoURL := createHTTPUrl(ownerCtx.GitPath(), u) - - repoPath := path.Join(t.TempDir(), ownerCtx.Reponame) - defer util.RemoveAll(repoPath) // cleans out git-annex lockdown permissions - - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - doGitClone(repoPath, repoURL)(t) - }) - - t.Run("Init", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, outsiderCtx, func() { - require.NoError(t, doAnnexInitTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, outsiderCtx, func() { - require.NoError(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("LocalDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, outsiderCtx, func() { - require.NoError(t, doAnnexLocalDropTest(repoPath)) - }) - }) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, outsiderCtx, func() { - require.NoError(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("RemoteDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, outsiderCtx, func() { - require.Error(t, doAnnexRemoteDropTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("Upload", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, outsiderCtx, func() { - require.Error(t, doAnnexUploadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("TestremoteReadOnly", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, outsiderCtx, func() { - require.NoError(t, doAnnexTestremoteReadOnlyTest(repoPath)) - }) - }) - - t.Run("TestremoteReadWrite", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, outsiderCtx, func() { - require.Error(t, doAnnexTestremoteReadWriteTest(repoPath)) - }) - }) - }) - }) - - t.Run("Anonymous", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - // Only HTTP and P2PHTTP have an anonymous mode - t.Run("HTTP", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - repoURL := createHTTPUrl(ownerCtx.GitPath(), u) - - repoPath := path.Join(t.TempDir(), ownerCtx.Reponame) - defer util.RemoveAll(repoPath) // cleans out git-annex lockdown permissions - - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - doGitClone(repoPath, repoURL)(t) - }) - - // unlike the other tests, at this step we *do not* define credentials: - - t.Run("Init", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - require.NoError(t, doAnnexInitTest(remoteRepoPath, repoPath)) - }) - - // Unset annexurl so that git-annex uses the dumb http support - _, _, err := git.NewCommand(git.DefaultContext, "config", "--unset", "remote.origin.annexurl").RunStdString(&git.RunOpts{Dir: repoPath}) - require.NoError(t, err) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - require.NoError(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - - t.Run("LocalDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - require.NoError(t, doAnnexLocalDropTest(repoPath)) - }) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - require.NoError(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - - t.Run("RemoteDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - require.Error(t, doAnnexRemoteDropTest(remoteRepoPath, repoPath)) - }) - - t.Run("Upload", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - require.Error(t, doAnnexUploadTest(remoteRepoPath, repoPath)) - }) - - t.Run("TestremoteReadOnly", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - require.NoError(t, doAnnexTestremoteReadOnlyTest(repoPath)) - }) - - t.Run("TestremoteReadWrite", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - require.Error(t, doAnnexTestremoteReadWriteTest(repoPath)) - }) - }) - - t.Run("P2PHTTP", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - repoURL := createHTTPUrl(ownerCtx.GitPath(), u) - - repoPath := path.Join(t.TempDir(), ownerCtx.Reponame) - defer util.RemoveAll(repoPath) // cleans out git-annex lockdown permissions - - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - doGitClone(repoPath, repoURL)(t) - }) - - // unlike the other tests, at this step we *do not* define credentials: - - t.Run("Init", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - require.NoError(t, doAnnexInitTest(remoteRepoPath, repoPath)) - }) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - require.NoError(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - - t.Run("LocalDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - require.NoError(t, doAnnexLocalDropTest(repoPath)) - }) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - require.NoError(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - - t.Run("RemoteDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - require.Error(t, doAnnexRemoteDropTest(remoteRepoPath, repoPath)) - }) - - t.Run("Upload", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - require.Error(t, doAnnexUploadTest(remoteRepoPath, repoPath)) - }) - - t.Run("TestremoteReadOnly", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - require.NoError(t, doAnnexTestremoteReadOnlyTest(repoPath)) - }) - - t.Run("TestremoteReadWrite", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - require.Error(t, doAnnexTestremoteReadWriteTest(repoPath)) - }) - }) - }) - - t.Run("Delete", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - // Delete the repo, make sure it's fully gone - doAPIDeleteRepository(ownerCtx)(t) - _, statErr := os.Stat(remoteRepoPath) - require.True(t, os.IsNotExist(statErr), "Remote annex repo should be removed from disk") - }) - }) - - t.Run("Private", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - ownerCtx := NewAPITestContext(t, "user2", "annex-private"+objectFormat.Name(), auth_model.AccessTokenScopeWriteRepository) - - // create a private repo - require.NoError(t, doCreateRemoteAnnexRepository(t, u, ownerCtx, true, objectFormat)) - - // double-check it's private - repo, err := repo_model.GetRepositoryByOwnerAndName(db.DefaultContext, ownerCtx.Username, ownerCtx.Reponame) - require.NoError(t, err) - require.True(t, repo.IsPrivate) - - remoteRepoPath := path.Join(setting.RepoRootPath, ownerCtx.GitPath()) // path on disk -- which can be examined directly because we're testing from localhost - - // Different sessions, so we can test different permissions. - // We leave Reponame blank because we don't actually then later add it according to each case if needed - // - // NB: these usernames need to match appropriate entries in models/fixtures/user.yml - writerCtx := NewAPITestContext(t, "user5", "", auth_model.AccessTokenScopeWriteRepository) - readerCtx := NewAPITestContext(t, "user4", "", auth_model.AccessTokenScopeReadRepository) - outsiderCtx := NewAPITestContext(t, "user8", "", auth_model.AccessTokenScopeReadRepository) // a user with no specific access - // Note: there's also full anonymous access, which is only available for public HTTP repos; - // it should behave the same as 'outsider' but we (will) test it separately below anyway - - // set up collaborators - doAPIAddCollaborator(ownerCtx, readerCtx.Username, perm.AccessModeRead)(t) - doAPIAddCollaborator(ownerCtx, writerCtx.Username, perm.AccessModeWrite)(t) - - // tests - t.Run("Owner", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - t.Run("SSH", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - repoURL := createSSHUrl(ownerCtx.GitPath(), u) - - repoPath := path.Join(t.TempDir(), ownerCtx.Reponame) - defer util.RemoveAll(repoPath) // cleans out git-annex lockdown permissions - - withAnnexCtxKeyFile(t, ownerCtx, func() { - doGitClone(repoPath, repoURL)(t) - }) - - t.Run("Init", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, ownerCtx, func() { - require.NoError(t, doAnnexInitTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, ownerCtx, func() { - require.NoError(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("LocalDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, ownerCtx, func() { - require.NoError(t, doAnnexLocalDropTest(repoPath)) - }) - }) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, ownerCtx, func() { - require.NoError(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("RemoteDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, ownerCtx, func() { - require.NoError(t, doAnnexRemoteDropTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("Upload", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, ownerCtx, func() { - require.NoError(t, doAnnexUploadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("TestremoteReadOnly", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, ownerCtx, func() { - require.NoError(t, doAnnexTestremoteReadOnlyTest(repoPath)) - }) - }) - - t.Run("TestremoteReadWrite", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, ownerCtx, func() { - require.NoError(t, doAnnexTestremoteReadWriteTest(repoPath)) - }) - }) - }) - - t.Run("HTTP", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - repoURL := createHTTPUrl(ownerCtx.GitPath(), u) - - repoPath := path.Join(t.TempDir(), ownerCtx.Reponame) - defer util.RemoveAll(repoPath) // cleans out git-annex lockdown permissions - - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - doGitClone(repoPath, repoURL)(t) - }) - - t.Run("Init", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - require.NoError(t, doAnnexInitTest(remoteRepoPath, repoPath)) - }) - }) - - // Unset annexurl so that git-annex uses the dumb http support - _, _, err := git.NewCommand(git.DefaultContext, "config", "--unset", "remote.origin.annexurl").RunStdString(&git.RunOpts{Dir: repoPath}) - require.NoError(t, err) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - require.NoError(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("LocalDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - require.NoError(t, doAnnexLocalDropTest(repoPath)) - }) - }) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - require.NoError(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("RemoteDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - require.Error(t, doAnnexRemoteDropTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("Upload", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - require.Error(t, doAnnexUploadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("TestremoteReadOnly", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - require.NoError(t, doAnnexTestremoteReadOnlyTest(repoPath)) - }) - }) - - t.Run("TestremoteReadWrite", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - require.Error(t, doAnnexTestremoteReadWriteTest(repoPath)) - }) - }) - }) - - t.Run("P2PHTTP", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - repoURL := createHTTPUrl(ownerCtx.GitPath(), u) - - repoPath := path.Join(t.TempDir(), ownerCtx.Reponame) - defer util.RemoveAll(repoPath) // cleans out git-annex lockdown permissions - - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - doGitClone(repoPath, repoURL)(t) - }) - - t.Run("Init", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - require.NoError(t, doAnnexInitTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - require.NoError(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("LocalDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - require.NoError(t, doAnnexLocalDropTest(repoPath)) - }) - }) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - require.NoError(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("RemoteDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - require.NoError(t, doAnnexRemoteDropTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("Upload", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - require.NoError(t, doAnnexUploadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("TestremoteReadOnly", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - require.NoError(t, doAnnexTestremoteReadOnlyTest(repoPath)) - }) - }) - - t.Run("TestremoteReadWrite", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - require.NoError(t, doAnnexTestremoteReadWriteTest(repoPath)) - }) - }) - }) - }) - - t.Run("Writer", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - t.Run("SSH", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - repoURL := createSSHUrl(ownerCtx.GitPath(), u) - - repoPath := path.Join(t.TempDir(), ownerCtx.Reponame) - defer util.RemoveAll(repoPath) // cleans out git-annex lockdown permissions - - withAnnexCtxKeyFile(t, ownerCtx, func() { - doGitClone(repoPath, repoURL)(t) - }) - - t.Run("Init", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, writerCtx, func() { - require.NoError(t, doAnnexInitTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, writerCtx, func() { - require.NoError(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("LocalDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, writerCtx, func() { - require.NoError(t, doAnnexLocalDropTest(repoPath)) - }) - }) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, writerCtx, func() { - require.NoError(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("RemoteDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, writerCtx, func() { - require.NoError(t, doAnnexRemoteDropTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("Upload", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, writerCtx, func() { - require.NoError(t, doAnnexUploadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("TestremoteReadOnly", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, writerCtx, func() { - require.NoError(t, doAnnexTestremoteReadOnlyTest(repoPath)) - }) - }) - - t.Run("TestremoteReadWrite", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, writerCtx, func() { - require.NoError(t, doAnnexTestremoteReadWriteTest(repoPath)) - }) - }) - }) - - t.Run("HTTP", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - repoURL := createHTTPUrl(ownerCtx.GitPath(), u) - - repoPath := path.Join(t.TempDir(), ownerCtx.Reponame) - defer util.RemoveAll(repoPath) // cleans out git-annex lockdown permissions - - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - doGitClone(repoPath, repoURL)(t) - }) - - t.Run("Init", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, writerCtx, func() { - require.NoError(t, doAnnexInitTest(remoteRepoPath, repoPath)) - }) - }) - - // Unset annexurl so that git-annex uses the dumb http support - _, _, err := git.NewCommand(git.DefaultContext, "config", "--unset", "remote.origin.annexurl").RunStdString(&git.RunOpts{Dir: repoPath}) - require.NoError(t, err) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, writerCtx, func() { - require.NoError(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("LocalDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, writerCtx, func() { - require.NoError(t, doAnnexLocalDropTest(repoPath)) - }) - }) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, writerCtx, func() { - require.NoError(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("RemoteDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, writerCtx, func() { - require.Error(t, doAnnexRemoteDropTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("Upload", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, writerCtx, func() { - require.Error(t, doAnnexUploadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("TestremoteReadOnly", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, writerCtx, func() { - require.NoError(t, doAnnexTestremoteReadOnlyTest(repoPath)) - }) - }) - - t.Run("TestremoteReadWrite", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, writerCtx, func() { - require.Error(t, doAnnexTestremoteReadWriteTest(repoPath)) - }) - }) - }) - - t.Run("P2PHTTP", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - repoURL := createHTTPUrl(ownerCtx.GitPath(), u) - - repoPath := path.Join(t.TempDir(), ownerCtx.Reponame) - defer util.RemoveAll(repoPath) // cleans out git-annex lockdown permissions - - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - doGitClone(repoPath, repoURL)(t) - }) - - t.Run("Init", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, writerCtx, func() { - require.NoError(t, doAnnexInitTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, writerCtx, func() { - require.NoError(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("LocalDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, writerCtx, func() { - require.NoError(t, doAnnexLocalDropTest(repoPath)) - }) - }) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, writerCtx, func() { - require.NoError(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("RemoteDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, writerCtx, func() { - require.NoError(t, doAnnexRemoteDropTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("Upload", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, writerCtx, func() { - require.NoError(t, doAnnexUploadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("TestremoteReadOnly", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, writerCtx, func() { - require.NoError(t, doAnnexTestremoteReadOnlyTest(repoPath)) - }) - }) - - t.Run("TestremoteReadWrite", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, writerCtx, func() { - require.NoError(t, doAnnexTestremoteReadWriteTest(repoPath)) - }) - }) - }) - }) - - t.Run("Reader", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - t.Run("SSH", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - repoURL := createSSHUrl(ownerCtx.GitPath(), u) - - repoPath := path.Join(t.TempDir(), ownerCtx.Reponame) - defer util.RemoveAll(repoPath) // cleans out git-annex lockdown permissions - - withAnnexCtxKeyFile(t, ownerCtx, func() { - doGitClone(repoPath, repoURL)(t) - }) - - t.Run("Init", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, readerCtx, func() { - require.NoError(t, doAnnexInitTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, readerCtx, func() { - require.NoError(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("LocalDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, readerCtx, func() { - require.NoError(t, doAnnexLocalDropTest(repoPath)) - }) - }) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, readerCtx, func() { - require.NoError(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("RemoteDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, readerCtx, func() { - require.Error(t, doAnnexRemoteDropTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("Upload", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, readerCtx, func() { - require.Error(t, doAnnexUploadTest(remoteRepoPath, repoPath), "Uploading should fail due to permissions") - }) - }) - - t.Run("TestremoteReadOnly", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, readerCtx, func() { - require.NoError(t, doAnnexTestremoteReadOnlyTest(repoPath)) - }) - }) - - t.Run("TestremoteReadWrite", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, readerCtx, func() { - require.Error(t, doAnnexTestremoteReadWriteTest(repoPath)) - }) - }) - }) - - t.Run("HTTP", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - repoURL := createHTTPUrl(ownerCtx.GitPath(), u) - - repoPath := path.Join(t.TempDir(), ownerCtx.Reponame) - defer util.RemoveAll(repoPath) // cleans out git-annex lockdown permissions - - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - doGitClone(repoPath, repoURL)(t) - }) - - t.Run("Init", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, readerCtx, func() { - require.NoError(t, doAnnexInitTest(remoteRepoPath, repoPath)) - }) - }) - - // Unset annexurl so that git-annex uses the dumb http support - _, _, err := git.NewCommand(git.DefaultContext, "config", "--unset", "remote.origin.annexurl").RunStdString(&git.RunOpts{Dir: repoPath}) - require.NoError(t, err) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, readerCtx, func() { - require.NoError(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("LocalDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, readerCtx, func() { - require.NoError(t, doAnnexLocalDropTest(repoPath)) - }) - }) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, readerCtx, func() { - require.NoError(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("RemoteDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, readerCtx, func() { - require.Error(t, doAnnexRemoteDropTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("Upload", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, readerCtx, func() { - require.Error(t, doAnnexUploadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("TestremoteReadOnly", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, readerCtx, func() { - require.NoError(t, doAnnexTestremoteReadOnlyTest(repoPath)) - }) - }) - - t.Run("TestremoteReadWrite", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, readerCtx, func() { - require.Error(t, doAnnexTestremoteReadWriteTest(repoPath)) - }) - }) - }) - - t.Run("P2PHTTP", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - repoURL := createHTTPUrl(ownerCtx.GitPath(), u) - - repoPath := path.Join(t.TempDir(), ownerCtx.Reponame) - defer util.RemoveAll(repoPath) // cleans out git-annex lockdown permissions - - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - doGitClone(repoPath, repoURL)(t) - }) - - t.Run("Init", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, readerCtx, func() { - require.NoError(t, doAnnexInitTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, readerCtx, func() { - require.NoError(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("LocalDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, readerCtx, func() { - require.NoError(t, doAnnexLocalDropTest(repoPath)) - }) - }) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, readerCtx, func() { - require.NoError(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("RemoteDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, readerCtx, func() { - require.Error(t, doAnnexRemoteDropTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("Upload", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, readerCtx, func() { - require.Error(t, doAnnexUploadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("TestremoteReadOnly", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, readerCtx, func() { - require.NoError(t, doAnnexTestremoteReadOnlyTest(repoPath)) - }) - }) - - t.Run("TestremoteReadWrite", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, readerCtx, func() { - require.Error(t, doAnnexTestremoteReadWriteTest(repoPath)) - }) - }) - }) - - t.Run("Outsider", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - t.Run("SSH", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - repoURL := createSSHUrl(ownerCtx.GitPath(), u) - - repoPath := path.Join(t.TempDir(), ownerCtx.Reponame) - defer util.RemoveAll(repoPath) // cleans out git-annex lockdown permissions - - withAnnexCtxKeyFile(t, ownerCtx, func() { - doGitClone(repoPath, repoURL)(t) - }) - - t.Run("Init", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, outsiderCtx, func() { - require.Error(t, doAnnexInitTest(remoteRepoPath, repoPath), "annex init should fail due to permissions") - }) - }) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, outsiderCtx, func() { - require.Error(t, doAnnexDownloadTest(remoteRepoPath, repoPath), "annex copy --from should fail due to permissions") - }) - }) - - t.Run("LocalDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, outsiderCtx, func() { - require.Error(t, doAnnexLocalDropTest(repoPath)) - }) - }) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, outsiderCtx, func() { - require.Error(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("RemoteDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, outsiderCtx, func() { - require.Error(t, doAnnexRemoteDropTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("Upload", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, outsiderCtx, func() { - require.Error(t, doAnnexUploadTest(remoteRepoPath, repoPath), "annex copy --to should fail due to permissions") - }) - }) - - t.Run("TestremoteReadOnly", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, outsiderCtx, func() { - require.Error(t, doAnnexTestremoteReadOnlyTest(repoPath)) - }) - }) - - t.Run("TestremoteReadWrite", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxKeyFile(t, outsiderCtx, func() { - require.Error(t, doAnnexTestremoteReadWriteTest(repoPath)) - }) - }) - }) - - t.Run("HTTP", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - repoURL := createHTTPUrl(ownerCtx.GitPath(), u) - - repoPath := path.Join(t.TempDir(), ownerCtx.Reponame) - defer util.RemoveAll(repoPath) // cleans out git-annex lockdown permissions - - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - doGitClone(repoPath, repoURL)(t) - }) - - t.Run("Init", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, outsiderCtx, func() { - require.Error(t, doAnnexInitTest(remoteRepoPath, repoPath)) - }) - }) - - // Try unsetting annexurl - _, _, err := git.NewCommand(git.DefaultContext, "config", "--unset", "remote.origin.annexurl").RunStdString(&git.RunOpts{Dir: repoPath}) - require.Error(t, err) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, outsiderCtx, func() { - require.Error(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("LocalDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, outsiderCtx, func() { - require.Error(t, doAnnexLocalDropTest(repoPath)) - }) - }) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, outsiderCtx, func() { - require.Error(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("RemoteDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, outsiderCtx, func() { - require.Error(t, doAnnexRemoteDropTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("Upload", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, outsiderCtx, func() { - require.Error(t, doAnnexUploadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("TestremoteReadOnly", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, outsiderCtx, func() { - require.Error(t, doAnnexTestremoteReadOnlyTest(repoPath)) - }) - }) - - t.Run("TestremoteReadWrite", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, outsiderCtx, func() { - require.Error(t, doAnnexTestremoteReadWriteTest(repoPath)) - }) - }) - }) - - t.Run("P2PHTTP", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - repoURL := createHTTPUrl(ownerCtx.GitPath(), u) - - repoPath := path.Join(t.TempDir(), ownerCtx.Reponame) - defer util.RemoveAll(repoPath) // cleans out git-annex lockdown permissions - - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - doGitClone(repoPath, repoURL)(t) - }) - - t.Run("Init", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, outsiderCtx, func() { - require.Error(t, doAnnexInitTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, outsiderCtx, func() { - require.Error(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("LocalDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, outsiderCtx, func() { - require.Error(t, doAnnexLocalDropTest(repoPath)) - }) - }) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, outsiderCtx, func() { - require.Error(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("RemoteDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, outsiderCtx, func() { - require.Error(t, doAnnexRemoteDropTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("Upload", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, outsiderCtx, func() { - require.Error(t, doAnnexUploadTest(remoteRepoPath, repoPath)) - }) - }) - - t.Run("TestremoteReadOnly", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, outsiderCtx, func() { - require.Error(t, doAnnexTestremoteReadOnlyTest(repoPath)) - }) - }) - - t.Run("TestremoteReadWrite", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - withAnnexCtxHTTPPassword(t, u, outsiderCtx, func() { - require.Error(t, doAnnexTestremoteReadWriteTest(repoPath)) - }) - }) - }) - }) - }) - - t.Run("Anonymous", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - // Only HTTP and P2PHTTP have an anonymous mode - t.Run("HTTP", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - repoURL := createHTTPUrl(ownerCtx.GitPath(), u) - - repoPath := path.Join(t.TempDir(), ownerCtx.Reponame) - defer util.RemoveAll(repoPath) // cleans out git-annex lockdown permissions - - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - doGitClone(repoPath, repoURL)(t) - }) - - // unlike the other tests, at this step we *do not* define credentials: - - t.Run("Init", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - require.Error(t, doAnnexInitTest(remoteRepoPath, repoPath)) - }) - - // Try unsetting annexurl - _, _, err := git.NewCommand(git.DefaultContext, "config", "--unset", "remote.origin.annexurl").RunStdString(&git.RunOpts{Dir: repoPath}) - require.Error(t, err) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - require.Error(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - - t.Run("LocalDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - require.Error(t, doAnnexLocalDropTest(repoPath)) - }) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - require.Error(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - - t.Run("RemoteDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - require.Error(t, doAnnexRemoteDropTest(remoteRepoPath, repoPath)) - }) - - t.Run("Upload", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - require.Error(t, doAnnexUploadTest(remoteRepoPath, repoPath)) - }) - - t.Run("TestremoteReadOnly", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - require.Error(t, doAnnexTestremoteReadOnlyTest(repoPath)) - }) - - t.Run("TestremoteReadWrite", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - require.Error(t, doAnnexTestremoteReadWriteTest(repoPath)) - }) - }) - - t.Run("P2PHTTP", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - repoURL := createHTTPUrl(ownerCtx.GitPath(), u) - - repoPath := path.Join(t.TempDir(), ownerCtx.Reponame) - defer util.RemoveAll(repoPath) // cleans out git-annex lockdown permissions - - withAnnexCtxHTTPPassword(t, u, ownerCtx, func() { - doGitClone(repoPath, repoURL)(t) - }) - - // unlike the other tests, at this step we *do not* define credentials: - - t.Run("Init", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - require.Error(t, doAnnexInitTest(remoteRepoPath, repoPath)) - }) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - require.Error(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - - t.Run("LocalDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - require.Error(t, doAnnexLocalDropTest(repoPath)) - }) - - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - require.Error(t, doAnnexDownloadTest(remoteRepoPath, repoPath)) - }) - - t.Run("RemoteDrop", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - require.Error(t, doAnnexRemoteDropTest(remoteRepoPath, repoPath)) - }) - - t.Run("Upload", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - require.Error(t, doAnnexUploadTest(remoteRepoPath, repoPath)) - }) - - t.Run("TestremoteReadOnly", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - require.Error(t, doAnnexTestremoteReadOnlyTest(repoPath)) - }) - - t.Run("TestremoteReadWrite", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - require.Error(t, doAnnexTestremoteReadWriteTest(repoPath)) - }) - }) - }) - - t.Run("Delete", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - // Delete the repo, make sure it's fully gone - doAPIDeleteRepository(ownerCtx)(t) - _, statErr := os.Stat(remoteRepoPath) - require.True(t, os.IsNotExist(statErr), "Remote annex repo should be removed from disk") - }) - }) - }) - }) -} - -/* -Test that 'git annex init' works. - - precondition: repoPath contains a pre-cloned repo set up by doInitAnnexRepository(). -*/ -func doAnnexInitTest(remoteRepoPath, repoPath string) (err error) { - _, _, err = git.NewCommandContextNoGlobals(git.DefaultContext, "annex", "init", "cloned-repo").RunStdString(&git.RunOpts{Dir: repoPath}) - if err != nil { - return fmt.Errorf("Couldn't `git annex init`: %w", err) - } - - // - method 0: 'git config remote.origin.annex-uuid'. - // Demonstrates that 'git annex init' successfully contacted - // the remote git-annex and was able to learn its ID number. - readAnnexUUID, _, err := git.NewCommandContextNoGlobals(git.DefaultContext, "config", "remote.origin.annex-uuid").RunStdString(&git.RunOpts{Dir: repoPath}) - if err != nil { - return fmt.Errorf("Couldn't read remote `git config remote.origin.annex-uuid`: %w", err) - } - readAnnexUUID = strings.TrimSpace(readAnnexUUID) - - match := regexp.MustCompile("^[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}$").MatchString(readAnnexUUID) - if !match { - return fmt.Errorf("'git config remote.origin.annex-uuid' should have been able to download the remote's uuid; but instead read '%s'", readAnnexUUID) - } - - remoteAnnexUUID, _, err := git.NewCommandContextNoGlobals(git.DefaultContext, "config", "annex.uuid").RunStdString(&git.RunOpts{Dir: remoteRepoPath}) - if err != nil { - return fmt.Errorf("Couldn't read local `git config annex.uuid`: %w", err) - } - - remoteAnnexUUID = strings.TrimSpace(remoteAnnexUUID) - match = regexp.MustCompile("^[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}$").MatchString(remoteAnnexUUID) - if !match { - return fmt.Errorf("'git annex init' should have been able to download the remote's uuid; but instead read '%s'", remoteAnnexUUID) - } - - if readAnnexUUID != remoteAnnexUUID { - return fmt.Errorf("'git annex init' should have read the expected annex UUID '%s', but instead got '%s'", remoteAnnexUUID, readAnnexUUID) - } - - // - method 1: 'git annex whereis'. - // Demonstrates that git-annex understands annexed files can be found in the remote annex. - annexWhereis, _, err := git.NewCommandContextNoGlobals(git.DefaultContext, "annex", "whereis", "annexed.bin").RunStdString(&git.RunOpts{Dir: repoPath}) - if err != nil { - return fmt.Errorf("Couldn't `git annex whereis`: %w", err) - } - // Note: this regex is unanchored because 'whereis' outputs multiple lines containing - // headers and 1+ remotes and we just want to find one of them. - match = regexp.MustCompile(regexp.QuoteMeta(remoteAnnexUUID) + " -- .* \\[origin\\]\n").MatchString(annexWhereis) - if !match { - return errors.New("'git annex whereis' should report files are known to be in [origin]") - } - - return nil -} - -func doAnnexTestremoteReadWriteTest(repoPath string) (err error) { - _, _, err = git.NewCommandContextNoGlobals(git.DefaultContext, "annex", "testremote", "origin").RunStdString(&git.RunOpts{Dir: repoPath}) - if err != nil { - return err - } - return nil -} - -func doAnnexTestremoteReadOnlyTest(repoPath string) (err error) { - _, _, err = git.NewCommandContextNoGlobals(git.DefaultContext, "annex", "testremote", "origin", "--test-readonly", "annexed.tiff").RunStdString(&git.RunOpts{Dir: repoPath}) - if err != nil { - return err - } - return nil -} - -func doAnnexDownloadTest(remoteRepoPath, repoPath string) (err error) { - // NB: this test does something slightly different if run separately from "doAnnexInitTest()": - // "git annex copy" will notice and run "git annex init", silently. - // This shouldn't change any results, but be aware in case it does. - - _, _, err = git.NewCommandContextNoGlobals(git.DefaultContext, "annex", "copy", "--from", "origin").RunStdString(&git.RunOpts{Dir: repoPath}) - if err != nil { - return err - } - - // verify the files downloaded - - cmp := func(filename string) error { - localObjectPath, err := contentLocation(repoPath, filename) - if err != nil { - return err - } - // localObjectPath := path.Join(repoPath, filename) // or, just compare against the checked-out file - - remoteObjectPath, err := contentLocation(remoteRepoPath, filename) - if err != nil { - return err - } - - match, err := tests.FileCmp(localObjectPath, remoteObjectPath, 0) - if err != nil { - return err - } - if !match { - return errors.New("Annexed files should be the same") - } - - return nil - } - - // this is the annex-symlink file - stat, err := os.Lstat(path.Join(repoPath, "annexed.tiff")) - if err != nil { - return fmt.Errorf("Lstat: %w", err) - } - if !((stat.Mode() & os.ModeSymlink) != 0) { - // this line is really just double-checking that the text fixture is set up correctly - return errors.New("*.tiff should be a symlink") - } - if err = cmp("annexed.tiff"); err != nil { - return err - } - - // this is the annex-pointer file - stat, err = os.Lstat(path.Join(repoPath, "annexed.bin")) - if err != nil { - return fmt.Errorf("Lstat: %w", err) - } - if !((stat.Mode() & os.ModeSymlink) == 0) { - // this line is really just double-checking that the text fixture is set up correctly - return errors.New("*.bin should not be a symlink") - } - err = cmp("annexed.bin") - - return err -} - -func doAnnexLocalDropTest(repoPath string) (err error) { - // This test assumes that files are present in repoPath, i.e. it is run after doAnnexDownloadTest. - // This test drops all files from the repository clone. - binPath, err := contentLocation(repoPath, "annexed.bin") - if err != nil { - return err - } - _, err = os.Stat(binPath) - if err != nil { - return err - } - tiffPath, err := contentLocation(repoPath, "annexed.tiff") - if err != nil { - return err - } - _, err = os.Stat(tiffPath) - if err != nil { - return err - } - _, _, err = git.NewCommandContextNoGlobals(git.DefaultContext, "annex", "drop").RunStdString(&git.RunOpts{Dir: repoPath}) - if err != nil { - return err - } - _, err = os.Stat(binPath) - if !errors.Is(err, os.ErrNotExist) { - return fmt.Errorf("annexed.bin wasn't dropped properly: %w", err) - } - _, err = os.Stat(tiffPath) - if !errors.Is(err, os.ErrNotExist) { - return fmt.Errorf("annexed.tiff wasn't dropped properly: %w", err) - } - return nil -} - -func doAnnexUploadTest(remoteRepoPath, repoPath string) (err error) { - // NB: this test does something slightly different if run separately from "Init": - // it first runs "git annex init" silently in the background. - // This shouldn't change any results, but be aware in case it does. - - err = generateRandomFile(1024*1024/4, path.Join(repoPath, "contribution.bin")) - if err != nil { - return err - } - - err = git.AddChanges(repoPath, false, ".") - if err != nil { - return err - } - - err = git.CommitChanges(repoPath, git.CommitChangesOptions{Message: "Annex another file"}) - if err != nil { - return err - } - - _, _, err = git.NewCommandContextNoGlobals(git.DefaultContext, "annex", "copy", "--to", "origin").RunStdString(&git.RunOpts{Dir: repoPath}) - if err != nil { - return err - } - - // verify the file was uploaded - blob, err := blobForFile(repoPath, "contribution.bin") - if err != nil { - return err - } - key, err := annex.LookupKey(blob) - if err != nil { - return err - } - localObjectPath, err := annex.ContentLocationFromKey(repoPath, key) - if err != nil { - return err - } - - remoteObjectPath, err := annex.ContentLocationFromKey(remoteRepoPath, key) - if err != nil { - return err - } - - match, err := tests.FileCmp(localObjectPath, remoteObjectPath, 0) - if err != nil { - return err - } - if !match { - return errors.New("Annexed files should be the same") - } - - return nil -} - -func doAnnexRemoteDropTest(remoteRepoPath, repoPath string) (err error) { - // This test assumes that files are present in repoPath, i.e. it is run after doAnnexDownloadTest. - // This test drops all files from the remote repository. - binPath, err := contentLocation(remoteRepoPath, "annexed.bin") - if err != nil { - return err - } - _, err = os.Stat(binPath) - if err != nil { - return err - } - tiffPath, err := contentLocation(remoteRepoPath, "annexed.tiff") - if err != nil { - return err - } - _, err = os.Stat(tiffPath) - if err != nil { - return err - } - _, _, err = git.NewCommandContextNoGlobals(git.DefaultContext, "annex", "drop", "--from", "origin").RunStdString(&git.RunOpts{Dir: repoPath}) - if err != nil { - return err - } - _, err = os.Stat(binPath) - if !errors.Is(err, os.ErrNotExist) { - return fmt.Errorf("annexed.bin wasn't dropped properly: %w", err) - } - _, err = os.Stat(tiffPath) - if !errors.Is(err, os.ErrNotExist) { - return fmt.Errorf("annexed.tiff wasn't dropped properly: %w", err) - } - return nil -} - -// ---- Helpers ---- - -func generateRandomFile(size int, path string) (err error) { - // Generate random file - - // XXX TODO: maybe this should not be random, but instead a predictable pattern, so that the test is deterministic - bufSize := 4 * 1024 - if bufSize > size { - bufSize = size - } - - buffer := make([]byte, bufSize) - - f, err := os.Create(path) - if err != nil { - return err - } - defer f.Close() - - written := 0 - for written < size { - n := size - written - if n > bufSize { - n = bufSize - } - _, err := rand.Read(buffer[:n]) - if err != nil { - return err - } - n, err = f.Write(buffer[:n]) - if err != nil { - return err - } - written += n - } - if err != nil { - return err - } - - return nil -} - -// ---- Annex-specific helpers ---- - -/* -Initialize a repo with some baseline annexed and non-annexed files. - - TODO: perhaps this generator could be replaced with a fixture (see - integrations/gitea-repositories-meta/ and models/fixtures/repository.yml). - However we reuse this template for -different- repos, so maybe not. -*/ -func doInitAnnexRepository(repoPath string) error { - // set up what files should be annexed - // in this case, all *.bin files will be annexed - // without this, git-annex's default config annexes every file larger than some number of megabytes - f, err := os.Create(path.Join(repoPath, ".gitattributes")) - if err != nil { - return err - } - defer f.Close() - - // set up git-annex to store certain filetypes via *annex* pointers - // (https://git-annex.branchable.com/internals/pointer_file/). - // but only when run via 'git add' (see git-annex-smudge(1)) - _, err = f.WriteString("* annex.largefiles=anything\n") - if err != nil { - return err - } - _, err = f.WriteString("*.bin filter=annex\n") - if err != nil { - return err - } - _, err = f.WriteString("*.rst filter=annex\n") - if err != nil { - return err - } - _, err = f.WriteString("*.markdown filter=annex\n") - if err != nil { - return err - } - f.Close() - - err = git.AddChanges(repoPath, false, ".") - if err != nil { - return err - } - err = git.CommitChanges(repoPath, git.CommitChangesOptions{Message: "Configure git-annex settings"}) - if err != nil { - return err - } - - // 'git annex init' - err = git.NewCommandContextNoGlobals(git.DefaultContext, "annex", "init", "test-repo").Run(&git.RunOpts{Dir: repoPath}) - if err != nil { - return err - } - - // add files to the annex, stored via annex symlinks - // // a binary file - err = generateRandomFile(1024*1024/4, path.Join(repoPath, "annexed.tiff")) - if err != nil { - return err - } - - // // a text file - err = os.WriteFile(path.Join(repoPath, "annexed.md"), []byte("Overview\n=====\n\n1. Profit\n2. ???\n3. Review Life Activations\n"), 0o777) - if err != nil { - return err - } - - // // a markdown file - err = os.WriteFile(path.Join(repoPath, "annexed.txt"), []byte("We're going to see the wizard\nThe wonderful\nMonkey of\nBoz\n"), 0o777) - if err != nil { - return err - } - - err = git.NewCommandContextNoGlobals(git.DefaultContext, "annex", "add", ".").Run(&git.RunOpts{Dir: repoPath}) - if err != nil { - return err - } - - // add files to the annex, stored via git-annex-smudge - // // a binary file - err = generateRandomFile(1024*1024/4, path.Join(repoPath, "annexed.bin")) - if err != nil { - return err - } - - // // a text file - err = os.WriteFile(path.Join(repoPath, "annexed.rst"), []byte("Title\n=====\n\n- this is to test annexing a text file\n- lists are fun\n"), 0o777) - if err != nil { - return err - } - - // // a markdown file - err = os.WriteFile(path.Join(repoPath, "annexed.markdown"), []byte("Overview\n=====\n\n1. Profit\n2. ???\n3. Review Life Activations\n"), 0o777) - if err != nil { - return err - } - - err = git.AddChanges(repoPath, false, ".") - if err != nil { - return err - } - - // save everything - err = git.CommitChanges(repoPath, git.CommitChangesOptions{Message: "Annex files"}) - if err != nil { - return err - } - - return nil -} - -/* -Initialize a remote repo with some baseline annexed and non-annexed files. -*/ -func doInitRemoteAnnexRepository(t *testing.T, repoURL *url.URL) error { - repoPath := path.Join(t.TempDir(), path.Base(repoURL.Path)) - // This clone is immediately thrown away, which - // helps force the tests to be end-to-end. - defer util.RemoveAll(repoPath) - - doGitClone(repoPath, repoURL)(t) // TODO: this call is the only reason for the testing.T; can it be removed? - - err := doInitAnnexRepository(repoPath) - if err != nil { - return err - } - - _, _, err = git.NewCommandContextNoGlobals(git.DefaultContext, "annex", "sync", "--content").RunStdString(&git.RunOpts{Dir: repoPath}) - if err != nil { - return err - } - - return nil -} - -func blobForFile(repoPath, file string) (*git.Blob, error) { - repo, err := git.OpenRepository(git.DefaultContext, repoPath) - if err != nil { - return nil, err - } - defer repo.Close() - - commitID, err := repo.GetRefCommitID("HEAD") // NB: to examine a *branch*, prefix with "refs/branch/", or call repo.GetBranchCommitID(); ditto for tags - if err != nil { - return nil, err - } - - commit, err := repo.GetCommit(commitID) - if err != nil { - return nil, err - } - - treeEntry, err := commit.GetTreeEntryByPath(file) - if err != nil { - return nil, err - } - - return treeEntry.Blob(), nil -} - -/* -Find the path in .git/annex/objects/ of the contents for a given annexed file. - - repoPath: the git repository to examine - file: the path (in the repo's current HEAD) of the annex pointer - - TODO: pass a parameter to allow examining non-HEAD branches -*/ -func contentLocation(repoPath, file string) (path string, err error) { - blob, err := blobForFile(repoPath, file) - if err != nil { - return "", err - } - return annex.ContentLocation(blob) -} - -/* like withKeyFile(), but automatically sets it the account given in ctx for use by git-annex */ -func withAnnexCtxKeyFile(t *testing.T, ctx APITestContext, callback func()) { - _gitAnnexUseGitSSH, gitAnnexUseGitSSHExists := os.LookupEnv("GIT_ANNEX_USE_GIT_SSH") - defer func() { - // reset - if gitAnnexUseGitSSHExists { - t.Setenv("GIT_ANNEX_USE_GIT_SSH", _gitAnnexUseGitSSH) - } - }() - - t.Setenv("GIT_ANNEX_USE_GIT_SSH", "1") // withKeyFile works by setting GIT_SSH_COMMAND, but git-annex only respects that if this is set - - withCtxKeyFile(t, ctx, callback) -} - -/* -Like withKeyFile(), but sets HTTP credentials instead of SSH credentials. - - It does this by temporarily arranging through `git config --global` - to use git-credential-store(1) with the password written to a tempfile. - - This is the only reliable way to pass HTTP credentials non-interactively - to git-annex. See https://git-annex.branchable.com/bugs/http_remotes_ignore_annex.web-options_--netrc/#comment-b5a299e9826b322f2d85c96d4929a430 - for joeyh's proclamation on the subject. - - This **is only effective** when used around git.NewCommandContextNoGlobals() calls. - git.NewCommand() disables credential.helper as a precaution (see modules/git/git.go). - - In contrast, the tests in git_test.go put the password in the remote's URL like - `git config remote.origin.url http://user2:password@localhost:3003/user2/repo-name.git`, - writing the password in repoPath+"/.git/config". That would be equally good, except - that git-annex ignores it! -*/ -func withAnnexCtxHTTPPassword(t *testing.T, u *url.URL, ctx APITestContext, callback func()) { - credentialedURL := *u - credentialedURL.User = url.UserPassword(ctx.Username, userPassword) // NB: all test users use the same password - - credentialedAnnexURL := *u - credentialedAnnexURL.Host = strings.ReplaceAll(credentialedAnnexURL.Host, "127.0.0.1", "localhost") - credentialedAnnexURL.Scheme = "annex+" + credentialedAnnexURL.Scheme - credentialedAnnexURL.Path += "git-annex-p2phttp" - credentialedAnnexURL.User = url.UserPassword(ctx.Username, userPassword) // NB: all test users use the same password - - creds := path.Join(t.TempDir(), "creds") - require.NoError(t, os.WriteFile(creds, []byte(credentialedURL.String()+"\n"+credentialedAnnexURL.String()+"\n"), 0o600)) - - originalCredentialHelper, _, err := git.NewCommandContextNoGlobals(git.DefaultContext, "config").AddOptionValues("--global", "credential.helper").RunStdString(&git.RunOpts{}) - if err != nil && !git.IsErrorExitCode(err, 1) { - // ignore the 'error' thrown when credential.helper is unset (when git config returns 1) - // but catch all others - require.NoError(t, err) - } - hasOriginalCredentialHelper := (err == nil) - - _, _, err = git.NewCommandContextNoGlobals(git.DefaultContext, "config").AddOptionValues("--global", "credential.helper", fmt.Sprintf("store --file=%s", creds)).RunStdString(&git.RunOpts{}) - require.NoError(t, err) - - defer (func() { - // reset - if hasOriginalCredentialHelper { - _, _, err = git.NewCommandContextNoGlobals(git.DefaultContext, "config").AddOptionValues("--global").AddArguments("credential.helper").AddDynamicArguments(originalCredentialHelper).RunStdString(&git.RunOpts{}) - } else { - _, _, err = git.NewCommandContextNoGlobals(git.DefaultContext, "config").AddOptionValues("--global").AddOptionValues("--unset").AddArguments("credential.helper").RunStdString(&git.RunOpts{}) - } - require.NoError(t, err) - })() - - callback() -} diff --git a/tests/integration/git_helper_for_declarative_test.go b/tests/integration/git_helper_for_declarative_test.go index 575f01d..83d8177 100644 --- a/tests/integration/git_helper_for_declarative_test.go +++ b/tests/integration/git_helper_for_declarative_test.go @@ -42,28 +42,6 @@ func withKeyFile(t *testing.T, keyname string, callback func(string)) { "ssh -o \"UserKnownHostsFile=/dev/null\" -o \"StrictHostKeyChecking=no\" -o \"IdentitiesOnly=yes\" -i \""+keyFile+"\" \"$@\""), 0o700) require.NoError(t, err) - // reset ssh wrapper afterwards - _gitSSH, gitSSHExists := os.LookupEnv("GIT_SSH") - defer func() { - if gitSSHExists { - os.Setenv("GIT_SSH", _gitSSH) - } - }() - - _gitSSHCommand, gitSSHCommandExists := os.LookupEnv("GIT_SSH_COMMAND") - defer func() { - if gitSSHCommandExists { - os.Setenv("GIT_SSH_COMMAND", _gitSSHCommand) - } - }() - - _gitSSHVariant, gitSSHVariantExists := os.LookupEnv("GIT_SSH_VARIANT") - defer func() { - if gitSSHVariantExists { - os.Setenv("GIT_SSH_VARIANT", _gitSSHVariant) - } - }() - // Setup ssh wrapper t.Setenv("GIT_SSH", path.Join(tmpDir, "ssh")) t.Setenv("GIT_SSH_COMMAND", @@ -73,13 +51,6 @@ func withKeyFile(t *testing.T, keyname string, callback func(string)) { callback(keyFile) } -func createHTTPUrl(gitPath string, u *url.URL) *url.URL { - // this assumes u contains the HTTP base URL that Gitea is running on - u2 := *u - u2.Path = gitPath - return &u2 -} - func createSSHUrl(gitPath string, u *url.URL) *url.URL { u2 := *u u2.Scheme = "ssh" diff --git a/tests/mysql.ini.tmpl b/tests/mysql.ini.tmpl index b832026..e15e799 100644 --- a/tests/mysql.ini.tmpl +++ b/tests/mysql.ini.tmpl @@ -97,9 +97,6 @@ DISABLE_QUERY_AUTH_TOKEN = true [lfs] PATH = tests/{{TEST_TYPE}}/gitea-{{TEST_TYPE}}-mysql/data/lfs -[annex] -ENABLED = true - [packages] ENABLED = true diff --git a/tests/pgsql.ini.tmpl b/tests/pgsql.ini.tmpl index 781508a..340531f 100644 --- a/tests/pgsql.ini.tmpl +++ b/tests/pgsql.ini.tmpl @@ -122,9 +122,6 @@ MINIO_LOCATION = us-east-1 MINIO_USE_SSL = false MINIO_CHECKSUM_ALGORITHM = md5 -[annex] -ENABLED = true - [packages] ENABLED = true diff --git a/tests/sqlite.ini.tmpl b/tests/sqlite.ini.tmpl index 231c0d1..277916a 100644 --- a/tests/sqlite.ini.tmpl +++ b/tests/sqlite.ini.tmpl @@ -102,9 +102,6 @@ JWT_SECRET = KZb_QLUd4fYVyxetjxC4eZkrBgWM2SndOOWDNtgUUko [lfs] PATH = tests/{{TEST_TYPE}}/gitea-{{TEST_TYPE}}-sqlite/data/lfs -[annex] -ENABLED = true - [packages] ENABLED = true diff --git a/tests/test_utils.go b/tests/test_utils.go index 6e9374d..b3c03a3 100644 --- a/tests/test_utils.go +++ b/tests/test_utils.go @@ -5,11 +5,9 @@ package tests import ( - "bytes" "context" "database/sql" "fmt" - "io" "os" "path" "path/filepath" @@ -490,80 +488,3 @@ func CreateDeclarativeRepo(t *testing.T, owner *user_model.User, name string, en return CreateDeclarativeRepoWithOptions(t, owner, opts) } - -// Decide if two files have the same contents or not. -// chunkSize is the size of the blocks to scan by; pass 0 to get a sensible default. -// *Follows* symlinks. -// -// May return an error if something else goes wrong; in this case, you should ignore the value of 'same'. -// -// derived from https://stackoverflow.com/a/30038571 -// under CC-BY-SA-4.0 by several contributors -func FileCmp(file1, file2 string, chunkSize int) (same bool, err error) { - if chunkSize == 0 { - chunkSize = 4 * 1024 - } - - // shortcuts: check file metadata - stat1, err := os.Stat(file1) - if err != nil { - return false, err - } - - stat2, err := os.Stat(file2) - if err != nil { - return false, err - } - - // are inputs are literally the same file? - if os.SameFile(stat1, stat2) { - return true, nil - } - - // do inputs at least have the same size? - if stat1.Size() != stat2.Size() { - return false, nil - } - - // long way: compare contents - f1, err := os.Open(file1) - if err != nil { - return false, err - } - defer f1.Close() - - f2, err := os.Open(file2) - if err != nil { - return false, err - } - defer f2.Close() - - b1 := make([]byte, chunkSize) - b2 := make([]byte, chunkSize) - for { - n1, err1 := io.ReadFull(f1, b1) - n2, err2 := io.ReadFull(f2, b2) - - // https://pkg.go.dev/io#Reader - // > Callers should always process the n > 0 bytes returned - // > before considering the error err. Doing so correctly - // > handles I/O errors that happen after reading some bytes - // > and also both of the allowed EOF behaviors. - - if !bytes.Equal(b1[:n1], b2[:n2]) { - return false, nil - } - - if (err1 == io.EOF && err2 == io.EOF) || (err1 == io.ErrUnexpectedEOF && err2 == io.ErrUnexpectedEOF) { - return true, nil - } - - // some other error, like a dropped network connection or a bad transfer - if err1 != nil { - return false, err1 - } - if err2 != nil { - return false, err2 - } - } -} diff --git a/web_src/css/repo.css b/web_src/css/repo.css index c3f3292..d6faa4b 100644 --- a/web_src/css/repo.css +++ b/web_src/css/repo.css @@ -244,7 +244,6 @@ td .commit-summary { } .repository.file.list #repo-files-table tbody .svg.octicon-file, -.repository.file.list #repo-files-table tbody .svg.octicon-file-binary, .repository.file.list #repo-files-table tbody .svg.octicon-file-symlink-file, .repository.file.list #repo-files-table tbody .svg.octicon-file-directory-symlink { color: var(--color-secondary-dark-7); diff --git a/web_src/js/features/common-global.js b/web_src/js/features/common-global.js index ded82ad..5a304d9 100644 --- a/web_src/js/features/common-global.js +++ b/web_src/js/features/common-global.js @@ -1,19 +1,19 @@ import $ from 'jquery'; import '../vendor/jquery.are-you-sure.js'; -import { clippie } from 'clippie'; -import { createDropzone } from './dropzone.js'; -import { showGlobalErrorMessage } from '../bootstrap.js'; -import { handleGlobalEnterQuickSubmit } from './comp/QuickSubmit.js'; -import { svg } from '../svg.js'; -import { hideElem, showElem, toggleElem, initSubmitEventPolyfill, submitEventSubmitter } from '../utils/dom.js'; -import { htmlEscape } from 'escape-goat'; -import { showTemporaryTooltip } from '../modules/tippy.js'; -import { confirmModal } from './comp/ConfirmModal.js'; -import { showErrorToast } from '../modules/toast.js'; -import { request, POST, GET } from '../modules/fetch.js'; +import {clippie} from 'clippie'; +import {createDropzone} from './dropzone.js'; +import {showGlobalErrorMessage} from '../bootstrap.js'; +import {handleGlobalEnterQuickSubmit} from './comp/QuickSubmit.js'; +import {svg} from '../svg.js'; +import {hideElem, showElem, toggleElem, initSubmitEventPolyfill, submitEventSubmitter} from '../utils/dom.js'; +import {htmlEscape} from 'escape-goat'; +import {showTemporaryTooltip} from '../modules/tippy.js'; +import {confirmModal} from './comp/ConfirmModal.js'; +import {showErrorToast} from '../modules/toast.js'; +import {request, POST, GET} from '../modules/fetch.js'; import '../htmx.js'; -const { appUrl, appSubUrl, csrfToken, i18n } = window.config; +const {appUrl, appSubUrl, csrfToken, i18n} = window.config; export function initGlobalFormDirtyLeaveConfirm() { // Warn users that try to leave a page after entering data into a form. @@ -82,7 +82,7 @@ async function fetchActionDoRequest(actionElem, url, opt) { try { const resp = await request(url, opt); if (resp.status === 200) { - let { redirect } = await resp.json(); + let {redirect} = await resp.json(); redirect = redirect || actionElem.getAttribute('data-redirect'); actionElem.classList.remove('dirty'); // remove the areYouSure check before reloading if (redirect) { @@ -96,7 +96,7 @@ async function fetchActionDoRequest(actionElem, url, opt) { // the code was quite messy, sometimes the backend uses "err", sometimes it uses "error", and even "user_error" // but at the moment, as a new approach, we only use "errorMessage" here, backend can use JSONError() to respond. if (data.errorMessage) { - showErrorToast(data.errorMessage, { useHtmlBody: data.renderFormat === 'html' }); + showErrorToast(data.errorMessage, {useHtmlBody: data.renderFormat === 'html'}); } else { showErrorToast(`server error: ${resp.status}`); } @@ -134,7 +134,7 @@ async function formFetchAction(e) { } let reqUrl = formActionUrl; - const reqOpt = { method: formMethod.toUpperCase() }; + const reqOpt = {method: formMethod.toUpperCase()}; if (formMethod.toLowerCase() === 'get') { const params = new URLSearchParams(); for (const [key, value] of formData) { @@ -212,7 +212,7 @@ export function initDropzone(el) { const $dropzone = $(el); const _promise = createDropzone(el, { url: $dropzone.data('upload-url'), - headers: { 'X-Csrf-Token': csrfToken }, + headers: {'X-Csrf-Token': csrfToken}, maxFiles: $dropzone.data('max-file'), maxFilesize: $dropzone.data('max-size'), acceptedFiles: (['*/*', ''].includes($dropzone.data('accepts'))) ? null : $dropzone.data('accepts'), @@ -229,8 +229,7 @@ export function initDropzone(el) { this.on('success', (file, data) => { file.uuid = data.uuid; const $input = $(``).val(data.uuid); - const $inputPath = $(``); - $dropzone.find('.files').append($input).append($inputPath); + $dropzone.find('.files').append($input); // Create a "Copy Link" element, to conveniently copy the image // or file link as Markdown to the clipboard const copyLinkElement = document.createElement('div'); @@ -251,15 +250,10 @@ export function initDropzone(el) { file.previewTemplate.append(copyLinkElement); }); this.on('removedfile', (file) => { - // Remove the hidden input for the file $(`#${file.uuid}`).remove(); - - // Remove the hidden input for files_fullpath - $(`input[name="files_fullpath[${file.uuid}]"]`).remove(); - if ($dropzone.data('remove-url')) { POST($dropzone.data('remove-url'), { - data: new URLSearchParams({ file: file.uuid }), + data: new URLSearchParams({file: file.uuid}), }); } }); @@ -282,7 +276,7 @@ async function linkAction(e) { const url = el.getAttribute('data-url'); const doRequest = async () => { el.disabled = true; - await fetchActionDoRequest(el, url, { method: 'POST' }); + await fetchActionDoRequest(el, url, {method: 'POST'}); el.disabled = false; }; @@ -293,7 +287,7 @@ async function linkAction(e) { } const isRisky = el.classList.contains('red') || el.classList.contains('yellow') || el.classList.contains('orange') || el.classList.contains('negative'); - if (await confirmModal({ content: modalConfirmContent, buttonColor: isRisky ? 'orange' : 'primary' })) { + if (await confirmModal({content: modalConfirmContent, buttonColor: isRisky ? 'orange' : 'primary'})) { await doRequest(); } } @@ -337,7 +331,7 @@ export function initGlobalLinkActions() { } } - const response = await POST($this.data('url'), { data: postData }); + const response = await POST($this.data('url'), {data: postData}); if (response.ok) { const data = await response.json(); window.location.href = data.redirect; diff --git a/web_src/js/features/imagediff.js b/web_src/js/features/imagediff.js index 52c9017..d1b139f 100644 --- a/web_src/js/features/imagediff.js +++ b/web_src/js/features/imagediff.js @@ -92,17 +92,7 @@ export function initImageDiff() { return loadElem(img, info.path); })); // only the first images is associated with $boundsInfo - if (!success) { - const blobContent = await GET(info.path.replace('/media/', '/raw/')).then((response) => response.text()); - if (blobContent.startsWith('.git/annex/objects')) { - for (const item of document.querySelectorAll('.image-diff .overflow-menu-items .item')) { - item.style.display = 'none'; - } - info.$boundsInfo[0].parentElement.textContent = 'annexed file is not present on the server'; - } else { - info.$boundsInfo.text('(image error)'); - } - } + if (!success) info.$boundsInfo.text('(image error)'); if (info.mime === 'image/svg+xml') { const resp = await GET(info.path); const text = await resp.text();