Skip to content

Commit 8e7efc7

Browse files
modules/lfs_ssh/transfer: vendor from charmbracelet
Big changes are inevitable due to the difference in Gitea's approach to storing LFS files. And upstream hasn't tagged for a while anyway.
1 parent 72586e7 commit 8e7efc7

File tree

15 files changed

+1070
-0
lines changed

15 files changed

+1070
-0
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ require (
3737
github.com/ethantkoenig/rupture v1.0.1
3838
github.com/felixge/fgprof v0.9.4
3939
github.com/fsnotify/fsnotify v1.7.0
40+
github.com/git-lfs/pktline v0.0.0-20210330133718-06e9096e2825
4041
github.com/gliderlabs/ssh v0.3.7
4142
github.com/go-ap/activitypub v0.0.0-20240408091739-ba76b44c2594
4243
github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
276276
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
277277
github.com/fxamacker/cbor/v2 v2.6.0 h1:sU6J2usfADwWlYDAFhZBQ6TnLFBHxgesMrQfQgk1tWA=
278278
github.com/fxamacker/cbor/v2 v2.6.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
279+
github.com/git-lfs/pktline v0.0.0-20210330133718-06e9096e2825 h1:riQhgheTL7tMF4d5raz9t3+IzoR1i1wqxE1kZC6dY+U=
280+
github.com/git-lfs/pktline v0.0.0-20210330133718-06e9096e2825/go.mod h1:fenKRzpXDjNpsIBhuhUzvjCKlDjKam0boRAenTE0Q6A=
279281
github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE=
280282
github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8=
281283
github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=

modules/lfs_ssh/transfer/LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2022-2023 Charmbracelet, Inc
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

modules/lfs_ssh/transfer/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# LFS SSH transfer protocol server
2+
3+
Module vendored from charmbracelet's implementation of the protocol:
4+
https://github.com/charmbracelet/git-lfs-transfer/
5+
Modified to suit Gitea's internal LFS API.
6+
7+
MIT Licensed, written by original authors with reference from:
8+
https://github.com/bk2204/scutiger

modules/lfs_ssh/transfer/args.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package transfer
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
)
7+
8+
// batch request argument keys.
9+
const (
10+
HashAlgoKey = "hash-algo"
11+
TransferKey = "transfer"
12+
RefnameKey = "refname"
13+
ExpiresInKey = "expires-in"
14+
ExpiresAtKey = "expires-at"
15+
SizeKey = "size"
16+
PathKey = "path"
17+
LimitKey = "limit"
18+
CursorKey = "cursor"
19+
)
20+
21+
// ParseArgs parses the given args.
22+
func ParseArgs(parts []string) (Args, error) {
23+
args := make(Args, 0)
24+
for _, line := range parts {
25+
parts := strings.SplitN(line, "=", 2)
26+
if len(parts) != 2 {
27+
return nil, fmt.Errorf("invalid argument: %q", line)
28+
}
29+
key, value := parts[0], parts[1]
30+
args[key] = value
31+
}
32+
return args, nil
33+
}
34+
35+
// ArgsToList converts the given args to a list.
36+
func ArgsToList(args Args) []string {
37+
list := make([]string, 0)
38+
for key, value := range args {
39+
list = append(list, fmt.Sprintf("%s=%s", key, value))
40+
}
41+
return list
42+
}
43+
44+
// Args is a key-value pair of arguments.
45+
type Args map[string]string
46+
47+
// String returns the string representation of the arguments.
48+
func (a Args) String() string {
49+
return strings.Join(ArgsToList(a), " ")
50+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package transfer
2+
3+
import (
4+
"io"
5+
"io/fs"
6+
)
7+
8+
const (
9+
// UploadOperation is an upload operation.
10+
UploadOperation = "upload"
11+
// DownloadOperation is a download operation.
12+
DownloadOperation = "download"
13+
)
14+
15+
// Backend is a Git LFS backend.
16+
type Backend interface {
17+
Batch(op string, pointers []BatchItem, args Args) ([]BatchItem, error)
18+
StartUpload(oid string, r io.Reader, args Args) (io.Closer, error)
19+
FinishUpload(state io.Closer, args Args) error
20+
Verify(oid string, args Args) (Status, error)
21+
Download(oid string, args Args) (fs.File, error)
22+
LockBackend(args Args) LockBackend
23+
}
24+
25+
// Lock is a Git LFS lock.
26+
type Lock interface {
27+
Unlock() error
28+
ID() string
29+
Path() string
30+
FormattedTimestamp() string
31+
OwnerName() string
32+
AsLockSpec(ownerID bool) ([]string, error)
33+
AsArguments() []string
34+
}
35+
36+
// LockBackend is a Git LFS lock backend.
37+
type LockBackend interface {
38+
// Create creates a lock for the given path and refname.
39+
// Refname can be empty.
40+
Create(path, refname string) (Lock, error)
41+
Unlock(lock Lock) error
42+
FromPath(path string) (Lock, error)
43+
FromID(id string) (Lock, error)
44+
Range(cursor string, limit int, iter func(Lock) error) (string, error)
45+
}

modules/lfs_ssh/transfer/batch.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package transfer
2+
3+
// BatchItem is a Git LFS batch item.
4+
type BatchItem struct {
5+
Pointer
6+
7+
// Present is used to determine the action to take for the batch item.
8+
Present bool
9+
10+
// Args is an optional oid-line key-value pairs.
11+
Args Args
12+
}

modules/lfs_ssh/transfer/caps.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package transfer
2+
3+
// Version is the git-lfs-transfer protocol version number.
4+
const Version = "1"
5+
6+
// Capabilities is a list of Git LFS capabilities supported by this package.
7+
var Capabilities = []string{
8+
"version=" + Version,
9+
"locking",
10+
}

modules/lfs_ssh/transfer/errors.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package transfer
2+
3+
import (
4+
"errors"
5+
)
6+
7+
var (
8+
// ErrConflict is the conflict error.
9+
ErrConflict = errors.New("conflict")
10+
// ErrParseError is the parse error.
11+
ErrParseError = errors.New("parse error")
12+
// ErrMissingData is the missing data error.
13+
ErrMissingData = errors.New("missing data")
14+
// ErrExtraData is the extra data error.
15+
ErrExtraData = errors.New("extra data")
16+
// ErrCorruptData is the corrupt data error.
17+
ErrCorruptData = errors.New("corrupt data")
18+
// ErrNotAllowed is the not allowed error.
19+
ErrNotAllowed = errors.New("not allowed")
20+
// ErrInvalidPacket is the invalid packet error.
21+
ErrInvalidPacket = errors.New("invalid packet")
22+
// ErrNotFound is the not found error.
23+
ErrNotFound = errors.New("not found")
24+
// ErrUnauthorized is the unauthorized error.
25+
ErrUnauthorized = errors.New("unauthorized")
26+
// ErrUnauthorized is the forbidden error.
27+
ErrForbidden = errors.New("forbidden")
28+
)

modules/lfs_ssh/transfer/hash.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package transfer
2+
3+
import (
4+
"encoding/hex"
5+
"hash"
6+
"io"
7+
)
8+
9+
var _ io.Reader = (*HashingReader)(nil)
10+
11+
// HashingReader is a reader that hashes the data it reads.
12+
type HashingReader struct {
13+
r io.Reader
14+
hash hash.Hash
15+
size int64
16+
}
17+
18+
// NewHashingReader creates a new hashing reader.
19+
func NewHashingReader(r io.Reader, hash hash.Hash) *HashingReader {
20+
return &HashingReader{
21+
r: r,
22+
hash: hash,
23+
}
24+
}
25+
26+
// Size returns the number of bytes read.
27+
func (h *HashingReader) Size() int64 {
28+
return h.size
29+
}
30+
31+
// Oid returns the hash of the data read.
32+
func (h *HashingReader) Oid() string {
33+
return hex.EncodeToString(h.hash.Sum(nil))
34+
}
35+
36+
// Read reads data from the underlying reader and hashes it.
37+
func (h *HashingReader) Read(p []byte) (int, error) {
38+
n, err := h.r.Read(p)
39+
h.size += int64(n)
40+
h.hash.Write(p[:n])
41+
return n, err
42+
}

0 commit comments

Comments
 (0)