From 35efa2a9c96f6f6660e8f1ed5b964141ced9995b Mon Sep 17 00:00:00 2001 From: Runxi Yu Date: Wed, 02 Apr 2025 03:06:54 +0800 Subject: [PATCH] LMTP: Stub patch application --- .golangci.yaml | 2 ++ go.mod | 1 + go.sum | 2 ++ lmtp_handle_patch.go | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++-- lmtp_server.go | 3 ++- diff --git a/.golangci.yaml b/.golangci.yaml index 00ba1ea7cafe82c9c91607f24365b136daf1c5b7..7edd4c30bd1dba973d6558c92cee7b8803dca30e 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -16,6 +16,8 @@ - nonamedreturns # i like named returns - wrapcheck # wrapping all errors is just not necessary - varnamelen # "from" and "to" are very valid - stylecheck + - containedctx + - godot - maintidx # e - nestif # e - gocognit # e diff --git a/go.mod b/go.mod index 6555e3b99749e3abd1cecea00c3a6275b59d4f3a..2ee258f25001a23f4d67598742b490de4f63663c 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ require ( github.com/alecthomas/chroma/v2 v2.15.0 github.com/alexedwards/argon2id v1.0.0 + github.com/bluekeyes/go-gitdiff v0.8.1 github.com/dgraph-io/ristretto/v2 v2.1.0 github.com/dustin/go-humanize v1.0.1 github.com/gliderlabs/ssh v0.3.8 diff --git a/go.sum b/go.sum index 70aa113c54c0f6a6727f003345ea7e5f1240359c..593a0f6acb84feb0d0c192169fc379c45bc2ac6a 100644 --- a/go.sum +++ b/go.sum @@ -19,6 +19,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= +github.com/bluekeyes/go-gitdiff v0.8.1 h1:lL1GofKMywO17c0lgQmJYcKek5+s8X6tXVNOLxy4smI= +github.com/bluekeyes/go-gitdiff v0.8.1/go.mod h1:WWAk1Mc6EgWarCrPFO+xeYlujPu98VuLW3Tu+B/85AE= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cloudflare/circl v1.6.0 h1:cr5JKic4HI+LkINy2lg3W2jF8sHCVTBncJr5gIIq7qk= diff --git a/lmtp_handle_patch.go b/lmtp_handle_patch.go index a3064d1a45bf6a482bdd268123f12ca6b6d7d2ac..4e49d555d62e467e6b4ea404ffbe0a330fbd756f 100644 --- a/lmtp_handle_patch.go +++ b/lmtp_handle_patch.go @@ -4,12 +4,85 @@ package main import ( - "log/slog" + "bytes" + // "crypto/rand" + // "fmt" + "os" + "os/exec" + "github.com/bluekeyes/go-gitdiff/gitdiff" "github.com/emersion/go-message" + "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/plumbing" + "github.com/go-git/go-git/v5/plumbing/object" ) -func lmtpHandlePatch(groupPath []string, repoName string, email *message.Entity) (err error) { - slog.Info("Pretend like I'm handling a patch!") +func lmtpHandlePatch(session *lmtpSession, groupPath []string, repoName string, email *message.Entity) (err error) { + var diffFiles []*gitdiff.File + var preamble string + if diffFiles, preamble, err = gitdiff.Parse(email.Body); err != nil { + return + } + + var repo *git.Repository + var fsPath string + repo, _, _, fsPath, err = openRepo(session.ctx, groupPath, repoName) + if err != nil { + return + } + + var headRef *plumbing.Reference + if headRef, err = repo.Head(); err != nil { + return + } + + var headCommit *object.Commit + if headCommit, err = repo.CommitObject(headRef.Hash()); err != nil { + return + } + + var headTree *object.Tree + if headTree, err = headCommit.Tree(); err != nil { + return + } + + // TODO: Try to not shell out + + for _, diffFile := range diffFiles { + var sourceFile *object.File + if sourceFile, err = headTree.File(diffFile.OldName); err != nil { + return err + } + var sourceString string + if sourceString, err = sourceFile.Contents(); err != nil { + return err + } + hashBuf := bytes.Buffer{} + patchedBuf := bytes.Buffer{} + sourceBuf := bytes.NewReader(stringToBytes(sourceString)) + if err = gitdiff.Apply(&patchedBuf, sourceBuf, diffFile); err != nil { + return err + } + proc := exec.CommandContext(session.ctx, "git", "hash-object", "w", "-t", "blob", "--stdin") + proc.Env = append(os.Environ(), "GIT_DIR="+fsPath) + proc.Stdout = &hashBuf + proc.Stdin = &patchedBuf + if err = proc.Start(); err != nil { + return err + } + newHash := hashBuf.Bytes() + if len(newHash) != 20*2+1 { // TODO: Hardcoded from the size of plumbing.Hash + panic("unexpected hash size") + } + // TODO: Add to tree + } + + // contribBranchName := rand.Text() + + // TODO: Store the branch + + // fmt.Println(repo, diffFiles, preamble) + _ = preamble + return nil } diff --git a/lmtp_server.go b/lmtp_server.go index 998b53c810fa0c5f30f9ccf46c0d189980576be3..44dc8d41db3ee1fa38a8520db2b25ae3c7a6cb7e 100644 --- a/lmtp_server.go +++ b/lmtp_server.go @@ -163,8 +163,9 @@ moduleType := segments[sepIndex+1] moduleName := segments[sepIndex+2] switch moduleType { case "repos": - err = lmtpHandlePatch(groupPath, moduleName, email) + err = lmtpHandlePatch(session, groupPath, moduleName, email) if err != nil { + slog.Error("error handling patch", "error", err) goto end } default: -- 2.48.1