aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRunxi Yu <me@runxiyu.org>2025-04-02 03:06:54 +0800
committerRunxi Yu <me@runxiyu.org>2025-04-02 03:06:54 +0800
commit35efa2a9c96f6f6660e8f1ed5b964141ced9995b (patch)
tree6bdf06bc91ea378d75c0eecb32eec704ee6ccc46
parentGit: Return fsPath from OpenRepo (diff)
downloadforge-35efa2a9c96f6f6660e8f1ed5b964141ced9995b.tar.gz
forge-35efa2a9c96f6f6660e8f1ed5b964141ced9995b.tar.zst
forge-35efa2a9c96f6f6660e8f1ed5b964141ced9995b.zip
LMTP: Stub patch application
-rw-r--r--.golangci.yaml2
-rw-r--r--go.mod1
-rw-r--r--go.sum2
-rw-r--r--lmtp_handle_patch.go79
-rw-r--r--lmtp_server.go3
5 files changed, 83 insertions, 4 deletions
diff --git a/.golangci.yaml b/.golangci.yaml
index 00ba1ea..7edd4c3 100644
--- a/.golangci.yaml
+++ b/.golangci.yaml
@@ -16,6 +16,8 @@ linters:
- 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 6555e3b..2ee258f 100644
--- a/go.mod
+++ b/go.mod
@@ -5,6 +5,7 @@ go 1.24.1
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 70aa113..593a0f6 100644
--- a/go.sum
+++ b/go.sum
@@ -19,6 +19,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd
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 a3064d1..4e49d55 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 998b53c..44dc8d4 100644
--- a/lmtp_server.go
+++ b/lmtp_server.go
@@ -163,8 +163,9 @@ func (session *lmtpSession) Data(r io.Reader) error {
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: