This commit is contained in:
Adam Majer 2024-07-29 15:28:03 +02:00
parent 65d821d388
commit 249fbab882
4 changed files with 178 additions and 37 deletions

View File

@ -303,7 +303,8 @@ func parseGitMsg(data <-chan byte) (gitMsg, error) {
id = id[:pos]
pos = 0
for c := <-data; c != ' '; c = <-data {
var c byte
for c = <-data; c != ' ' && c != '\x00'; c = <-data {
if c >= 'a' && c <= 'z' {
msgType[pos] = c
pos++
@ -314,13 +315,22 @@ func parseGitMsg(data <-chan byte) (gitMsg, error) {
msgType = msgType[:pos]
switch string(msgType) {
case "commit", "tree":
case "commit", "tree", "blob":
break
case "missing":
if c != '\x00' {
return gitMsg{}, fmt.Errorf("Missing format weird")
}
return gitMsg{
hash: string(id[:]),
itemType: "missing",
size: 0,
}, fmt.Errorf("Object not found: '%s'", string(id))
default:
return gitMsg{}, fmt.Errorf("Invalid object type: '%s'", string(msgType))
}
for c := <-data; c != '\000'; c = <-data {
for c = <-data; c != '\000'; c = <-data {
if c >= '0' && c <= '9' {
size = size*10 + (int(c) - '0')
} else {
@ -357,6 +367,12 @@ func parseGitCommitMsg(data <-chan byte, l int) (string, error) {
for c := <-data; c != '\x00'; c = <-data {
msg = append(msg, c)
l--
}
// l--
if l != 0 {
return "", fmt.Errorf("Unexpected data in the git commit msg: l=%d", l)
}
return string(msg), nil
@ -434,7 +450,8 @@ func parseGitTree(data <-chan byte) (tree, error) {
// max capacity to length of hash
t := tree{items: make([]tree_entry, 0, hdr.size/len(hdr.hash))}
for parsedLen := 0; parsedLen+1 < hdr.size; {
parsedLen := 0
for parsedLen < hdr.size {
entry, err := parseTreeEntry(data, len(hdr.hash)/2)
if err != nil {
return tree{}, nil
@ -443,10 +460,103 @@ func parseGitTree(data <-chan byte) (tree, error) {
t.items = append(t.items, entry)
parsedLen += entry.size
}
c := <-data // \0 read
if c != '\x00' {
return t, fmt.Errorf("Unexpected character during git tree data read")
}
if parsedLen != hdr.size {
return t, fmt.Errorf("Invalid size of git tree data")
}
return t, nil
}
func parseGitBlob(data <-chan byte) ([]byte, error) {
hdr, err := parseGitMsg(data)
if err != nil {
return []byte{}, err
}
d := make([]byte, hdr.size)
for l:=0; l<hdr.size; l++ {
d[l] = <-data
}
eob := <-data
if eob != '\x00' {
return d, fmt.Errorf("invalid byte read in parseGitBlob")
}
return d, nil
}
// TODO: support sub-trees
func (e *RequestHandler) GitCatFile(cwd, commitId, filename string) []byte {
var done sync.Mutex
var data []byte
done.Lock()
data_in, data_out := ChanIO{make(chan byte, 256)}, ChanIO{make(chan byte, 70)}
go func() {
defer done.Unlock()
defer close(data_out.ch)
data_out.Write([]byte(commitId))
data_out.ch <- '\x00'
c, err := parseGitCommit(data_in.ch)
if err != nil {
e.Error = err
e.LogError("Error parsing git commit: %v", err)
return
}
data_out.Write([]byte(c.Tree))
data_out.ch <- '\x00'
tree, err := parseGitTree(data_in.ch)
if err != nil {
e.Error = err
e.LogError("Error parsing git tree: %v", err)
return
}
for _, te := range tree.items {
if te.isBlob() && te.name == filename {
data_out.Write([]byte(te.hash))
data_out.ch <- '\x00'
data, err = parseGitBlob(data_in.ch)
if err != nil {
e.Error = err
e.LogError("Error reading blob data: %v", err)
}
return
}
}
e.Error = fmt.Errorf("file not found: '%s'", filename)
e.LogPlainError(e.Error)
}()
cmd := exec.Command("/usr/bin/git", "cat-file", "--batch", "-Z")
cmd.Env = []string{
"GIT_CEILING_DIRECTORIES=" + e.GitPath,
"GIT_CONFIG_GLOBAL=/dev/null",
}
cmd.Dir = filepath.Join(e.GitPath, cwd)
cmd.Stdout = &data_in
cmd.Stdin = &data_out
cmd.Stderr = writeFunc(func(data []byte) (int, error) {
e.Logger.LogError("%s", data)
return len(data), nil
})
e.Log("command run: %v", cmd.Args)
e.Error = cmd.Run()
done.Lock()
return data
}
func (e *RequestHandler) GitSubmoduleList(cwd, commitId string) map[string]string {
var done sync.Mutex
submoduleList := make(map[string]string)

View File

@ -88,7 +88,7 @@ func TestGitMsgParsing(t *testing.T) {
func TestGitCommitParsing(t *testing.T) {
t.Run("parse valid commit message", func(t *testing.T) {
const commitData = "f40888ea4515fe2e8eea617a16f5f50a45f652d894de3ad181d58de3aafb8f99 commit 254\000" +
const commitData = "f40888ea4515fe2e8eea617a16f5f50a45f652d894de3ad181d58de3aafb8f99 commit 253\000" +
`tree e20033df9f18780756ba4a96dbc7eb1a626253961039cb674156f266ba7a4e53
parent 429cc2fe02170ca5668f0461928c7e7430c7a17cd64ac298286d7162572a7703
author Adam Majer <amajer@suse.com> 1720709149 +0200
@ -115,7 +115,7 @@ committer Adam Majer <amajer@suse.com> 1720709149 +0200
})
t.Run("parse multiline headers", func(t *testing.T) {
const commitData = "cae5831ab48470ff060a5aaa12eb6e5a7acaf91e commit 1492\x00" +
const commitData = "cae5831ab48470ff060a5aaa12eb6e5a7acaf91e commit 1491\x00" +
`tree 1f9c8fe8099615d6d3921528402ac53f09213b02
parent e08a654fae0ecc91678819e0b62a2e014bad3339
author Yagiz Nizipli <yagiz@nizipli.com> 1720967314 -0400
@ -253,4 +253,22 @@ func TestCommitTreeParsingOfHead(t *testing.T) {
t.Errorf("hash doesn't match: %s vs. expected %s", id, nodejs21)
}
})
t.Run("reads README.md", func (t *testing.T) {
h := RequestHandler{
GitPath: gitDir,
Logger: CreateTestLogger(t),
}
data := h.GitCatFile("", commitId, "README.md")
if h.HasError() {
t.Errorf("failed parse: %v", h.Error)
}
if string(data) != "foo\n" || len(data) != 4 {
t.Errorf("Wrong data of len: %d", len(data))
}
})
t.Run("try to parse unknown item", func(t *testing.T) {
})
}

View File

@ -1,8 +1,12 @@
#!/usr/bin/bash
git init -q --bare --object-format=sha256
# 81aba862107f1e2f5312e165453955485f424612f313d6c2fb1b31fef9f82a14
blobA=$(echo "help" | git hash-object --stdin -w)
# 47d6aca82756ff2e61e53520bfdf1faa6c86d933be4854eb34840c57d12e0c85
blobB=$(echo "foo" | git hash-object --stdin -w)
tree=$(printf "100644 blob $blobA help\n100644 blob $blobB README.md\n160000 commit dc55b828328c8e494f67874a7d8c03dd1c6b4e02d16b86e08e47d70ecd799680 mingw32-gcc\n160000 commit d6a74c08406ce40cc8f7bff2d5cf309087a8728361cc75354b0862ba508193b8 nodejs-common\n160000 commit c678c57007d496a98bec668ae38f2c26a695f94af78012f15d044ccf066ccb41 nodejs21\n160000 commit 873a323b262ebb3bd77b2592b2e11bdd08dbc721cbf4ac9f97637e58e1fffce7 nodejs22\n160000 commit 35c702e8501eedeb5ce43d6f3460d11c791cfefaa77248f08cad55d00c37e73a python311\n"|git mktree)
commit=$(git commit-tree -m 'OK' $tree)
git reset --soft $commit

View File

@ -45,6 +45,16 @@ func fetchPrGit(h *common.RequestHandler, pr *models.PullRequest) error {
return h.Error
}
func getObsProjectAssociatedWithPr(baseProject string, pr *models.PullRequest) string {
return fmt.Sprintf(
"%s:%s:%s:PR:%d",
baseProject,
common.ObsSafeProjectName(pr.Base.Repo.Owner.UserName),
common.ObsSafeProjectName(pr.Base.Repo.Name),
pr.Index,
)
}
func processPullNotification(h *common.RequestHandler, notification *models.NotificationSubject) {
rx := regexp.MustCompile(`^https://src\.(?:open)?suse\.(?:org|de)/api/v\d+/repos/(?<org>[a-zA-Z0-9]+)/(?<project>[_a-zA-Z0-9]+)/issues/(?<num>[0-9]+)$`)
match := rx.FindStringSubmatch(notification.URL)
@ -104,38 +114,40 @@ func processPullNotification(h *common.RequestHandler, notification *models.Noti
continue
}
err := fetchPrGit(h, pr)
if err != nil {
h.LogError("Cannot fetch PR git: %s", pr.URL)
return
}
dir := pr.Head.Sha
headSubmodules := h.GitSubmoduleList(dir, pr.Head.Sha)
baseSubmodules := h.GitSubmoduleList(dir, pr.Base.Sha)
modifiedOrNew := make([]string, 0, 16)
for pkg, headOid := range headSubmodules {
if baseOid, exists := baseSubmodules[pkg]; !exists || baseOid != headOid {
modifiedOrNew = append(modifiedOrNew, pkg)
}
}
// find modified submodules and new submodules -- build them
h.Log("processing state...")
switch review.State {
// create build project, if doesn't exist, and add it to pending requests
case common.ReviewStateUnknown, common.ReviewStateRequestReview:
h.Log("repo content fetching ...")
buildPrjBytes, err := h.GetPullRequestFileContent(pr, "project.build")
err := fetchPrGit(h, pr)
if err != nil {
h.LogPlainError(err)
h.LogError("Cannot fetch PR git: %s", pr.URL)
return
}
// find modified submodules and new submodules -- build them
dir := pr.Head.Sha
headSubmodules := h.GitSubmoduleList(dir, pr.Head.Sha)
baseSubmodules := h.GitSubmoduleList(dir, pr.Base.Sha)
modifiedOrNew := make([]string, 0, 16)
for pkg, headOid := range headSubmodules {
if baseOid, exists := baseSubmodules[pkg]; !exists || baseOid != headOid {
modifiedOrNew = append(modifiedOrNew, pkg)
}
}
h.Log("repo content fetching ...")
buildPrjBytes := h.GitCatFile(dir, pr.Head.Sha, "project.build")
// buildPrjBytes, err := h.GetPullRequestFileContent(pr, "project.build")
if h.HasError() {
h.LogPlainError(h.Error)
/*
_, err := h.AddReviewComment(pr, common.ReviewStateRequestChanges, "Cannot find reference project")
if err != nil {
h.LogPlainError(err)
}
*/
return
}
@ -150,12 +162,7 @@ func processPullNotification(h *common.RequestHandler, notification *models.Noti
// disable publishing
// TODO: escape things here
meta.Name = fmt.Sprintf("%s:%s:%s:PR:%d",
obsClient.HomeProject,
common.ObsSafeProjectName(pr.Base.Repo.Owner.UserName),
common.ObsSafeProjectName(pr.Base.Repo.Name),
pr.Index,
)
meta.Name = getObsProjectAssociatedWithPr(obsClient.HomeProject, pr)
meta.Description = fmt.Sprintf(`Pull request build job: %s%s PR#%d`,
"https://src.opensuse.org", pr.Base.Repo.Name, pr.Index)
urlPkg := make([]string, 0, len(modifiedOrNew))
@ -185,12 +192,14 @@ func processPullNotification(h *common.RequestHandler, notification *models.Noti
// set the review state to pending
case common.ReviewStatePending:
// waiting for build results
// waiting for build results
// project := getObsProjectAssociatedWithPr(obsClient.HomeProject, pr)
case common.ReviewStateApproved:
// done, mark notification as read
// done, mark notification as read
h.Log("processing request for success build ...")
case common.ReviewStateRequestChanges:
h.Log("processing request for failed request changes...")
// build failures, nothing to do here, mark notification as read
h.Log("processing request for failed request changes...")
}
break