.
This commit is contained in:
parent
f40888ea45
commit
a2f2631bdf
@ -1,12 +1,15 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
//func (h *RequestHandler) ProcessBranchList() []string {
|
||||
@ -203,23 +206,223 @@ func (e *RequestHandler) GitExec(cwd string, params ...string) ExecStream {
|
||||
return e
|
||||
}
|
||||
|
||||
type writerFunc func([]byte) (int, error)
|
||||
|
||||
// Implement the Write method for the custom type
|
||||
func (f writerFunc) Write(p []byte) (int, error) {
|
||||
return f(p)
|
||||
type ChanIO struct {
|
||||
ch chan byte
|
||||
}
|
||||
|
||||
func (e *RequestHandler) GitSubmoduleCommitId(cwd, packageName string) (string, bool) {
|
||||
func (c *ChanIO) Write(p []byte) (int, error) {
|
||||
for i := range len(p) {
|
||||
c.ch <- p[i]
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// read at least 1 byte, but don't block if nothing in channel
|
||||
func (c *ChanIO) Read(p []byte) (int, error) {
|
||||
data, ok := <-c.ch
|
||||
for i := 0; i < cap(p); i++ {
|
||||
data, ok = <-c.ch
|
||||
|
||||
if !ok {
|
||||
return len(p), io.EOF
|
||||
}
|
||||
|
||||
p[i] = data
|
||||
if len(c.ch) < 1 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
type gitMsg struct {
|
||||
hash string
|
||||
itemType string
|
||||
size int
|
||||
}
|
||||
|
||||
type commit struct {
|
||||
Tree string
|
||||
Msg string
|
||||
}
|
||||
|
||||
type tree_entry struct {
|
||||
name string
|
||||
mode int
|
||||
hash string
|
||||
}
|
||||
|
||||
type tree struct {
|
||||
items []tree_entry
|
||||
}
|
||||
|
||||
func parseGitMsg(data <-chan byte) (gitMsg, error) {
|
||||
var id []byte = make([]byte, 64)
|
||||
var msgType []byte = make([]byte, 16)
|
||||
var size int
|
||||
|
||||
pos := 0
|
||||
for c := <-data; c != ' '; c = <-data {
|
||||
if (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') {
|
||||
id[pos] = c
|
||||
pos++
|
||||
} else {
|
||||
return gitMsg{}, errors.New("Invalid character during object hash parse")
|
||||
}
|
||||
}
|
||||
id = id[:pos]
|
||||
|
||||
pos = 0
|
||||
for c := <-data; c != ' '; c = <-data {
|
||||
if c >= 'a' && c <= 'z' {
|
||||
msgType[pos] = c
|
||||
pos++
|
||||
} else {
|
||||
return gitMsg{}, errors.New("Invalid character during object type parse")
|
||||
}
|
||||
}
|
||||
msgType = msgType[:pos]
|
||||
|
||||
switch string(msgType) {
|
||||
case "commit", "tree":
|
||||
break
|
||||
default:
|
||||
return gitMsg{}, fmt.Errorf("Invalid object type: '%s'", string(msgType))
|
||||
}
|
||||
|
||||
for c := <-data; c != '\000'; c = <-data {
|
||||
if c >= '0' && c <= '9' {
|
||||
size = size*10 + (int(c) - '0')
|
||||
} else {
|
||||
return gitMsg{}, errors.New("Invalid character during commit size parse")
|
||||
}
|
||||
}
|
||||
|
||||
return gitMsg{
|
||||
hash: string(id[:]),
|
||||
itemType: string(msgType),
|
||||
size: size,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseGitCommitHdr(data <-chan byte) ([2]string, error) {
|
||||
hdr := make([]byte, 0, 60)
|
||||
val := make([]byte, 0, 1000)
|
||||
|
||||
c := <-data
|
||||
if c != '\n' { // end of header marker
|
||||
for ; c != ' '; c = <-data {
|
||||
hdr = append(hdr, c)
|
||||
}
|
||||
for c := <-data; c != '\n'; c = <-data {
|
||||
val = append(val, c)
|
||||
}
|
||||
}
|
||||
|
||||
return [2]string{string(hdr), string(val)}, nil
|
||||
}
|
||||
|
||||
func parseGitCommitMsg(data <-chan byte, l int) (string, error) {
|
||||
msg := make([]byte, 0, l)
|
||||
|
||||
for c := <-data; c != '\x00'; c = <-data {
|
||||
msg = append(msg, c)
|
||||
}
|
||||
|
||||
return string(msg), nil
|
||||
}
|
||||
|
||||
func parseGitCommit(data <-chan byte) (commit, error) {
|
||||
hdr, err := parseGitMsg(data)
|
||||
if err != nil {
|
||||
return commit{}, err
|
||||
} else if hdr.itemType != "commit" {
|
||||
return commit{}, fmt.Errorf("expected commit but parsed %s", hdr.itemType)
|
||||
}
|
||||
|
||||
var c commit
|
||||
l := hdr.size
|
||||
for {
|
||||
hdr, err := parseGitCommitHdr(data)
|
||||
if err != nil {
|
||||
return commit{}, nil
|
||||
}
|
||||
|
||||
if len(hdr[0])+len(hdr[1]) == 0 { // hdr end marker
|
||||
break
|
||||
}
|
||||
|
||||
switch hdr[0] {
|
||||
case "tree":
|
||||
c.Tree = hdr[1]
|
||||
}
|
||||
|
||||
l -= len(hdr[0]) + len(hdr[1]) + 2
|
||||
}
|
||||
l--
|
||||
|
||||
c.Msg, err = parseGitCommitMsg(data, l)
|
||||
return c, err
|
||||
}
|
||||
|
||||
func parseTreeEntry(data <-chan byte) (tree_entry, error) {
|
||||
var e tree_entry
|
||||
|
||||
return e, nil
|
||||
}
|
||||
|
||||
func parseGitTree(data <-chan byte) (tree, error) {
|
||||
|
||||
return tree{}, nil
|
||||
}
|
||||
|
||||
func (e *RequestHandler) GitSubmoduleCommitId(cwd, packageName, headId string) (string, bool) {
|
||||
if e.Error != nil {
|
||||
return "", false
|
||||
}
|
||||
|
||||
cmd := exec.Command("/usr/bin/git", "cat-file", "--batch")
|
||||
cmd.Dir = filepath.Join(e.GitPath, cwd)
|
||||
cmd.Stdout = writerFunc(func(p []byte) (int, error) {
|
||||
return len(p), nil
|
||||
})
|
||||
data_in, data_out := ChanIO{make(chan byte, 256)}, ChanIO{make(chan byte, 70)}
|
||||
var commitId string
|
||||
var foundLock sync.Mutex
|
||||
|
||||
return "", false
|
||||
foundLock.Lock()
|
||||
|
||||
go func() {
|
||||
defer foundLock.Unlock()
|
||||
defer close(data_out.ch)
|
||||
|
||||
data_out.Write([]byte("HEAD\n"))
|
||||
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 <- '\n'
|
||||
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.name == packageName {
|
||||
commitId = te.hash
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
cmd := exec.Command("/usr/bin/git", "cat-file", "--batch", "-Z")
|
||||
cmd.Dir = filepath.Join(e.GitPath, cwd)
|
||||
cmd.Stdout = &data_in
|
||||
cmd.Stdin = &data_out
|
||||
e.Log("command run: %v", cmd.Run())
|
||||
|
||||
foundLock.Lock()
|
||||
return commitId, len(commitId) == len(headId)
|
||||
}
|
||||
|
219
bots-common/git_utils_test.go
Normal file
219
bots-common/git_utils_test.go
Normal file
@ -0,0 +1,219 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGitMsgParsing(t *testing.T) {
|
||||
t.Run("tree message with size 56", func(t *testing.T) {
|
||||
const hdr = "f40888ea4515fe2e8eea617a16f5f50a45f652d894de3ad181d58de3aafb8f98 tree 56\x00"
|
||||
|
||||
data := make(chan byte, 500)
|
||||
for _, b := range []byte(hdr) {
|
||||
data <- b
|
||||
}
|
||||
gitHdr, err := parseGitMsg(data)
|
||||
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if gitHdr.hash != "f40888ea4515fe2e8eea617a16f5f50a45f652d894de3ad181d58de3aafb8f98" {
|
||||
t.Errorf("Invalid hash %s", gitHdr.hash)
|
||||
}
|
||||
|
||||
if gitHdr.size != 56 {
|
||||
t.Errorf("Invalid msg size: %d", gitHdr.size)
|
||||
}
|
||||
|
||||
if gitHdr.itemType != "tree" {
|
||||
t.Errorf("Invalid msg type: %s", gitHdr.itemType)
|
||||
}
|
||||
})
|
||||
t.Run("commit message with size 256", func(t *testing.T) {
|
||||
const hdr = "f40888ea4515fe2e8eea617a16f5f50a45f652d894de3ad181d58de3aafb8f99 commit 256\x00"
|
||||
|
||||
data := make(chan byte, 500)
|
||||
for _, b := range []byte(hdr) {
|
||||
data <- b
|
||||
}
|
||||
gitHdr, err := parseGitMsg(data)
|
||||
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if gitHdr.hash != "f40888ea4515fe2e8eea617a16f5f50a45f652d894de3ad181d58de3aafb8f99" {
|
||||
t.Errorf("Invalid hash %s", gitHdr.hash)
|
||||
}
|
||||
|
||||
if gitHdr.size != 256 {
|
||||
t.Errorf("Invalid msg size: %d", gitHdr.size)
|
||||
}
|
||||
|
||||
if gitHdr.itemType != "commit" {
|
||||
t.Errorf("Invalid msg type: %s", gitHdr.itemType)
|
||||
}
|
||||
})
|
||||
t.Run("invalid tree message with size 56", func(t *testing.T) {
|
||||
const hdr = "f408r8ea4515fe2e8eea617a16f5f50a45f652d894de3ad181d58de3aafb8f98 tree 56\x00"
|
||||
|
||||
data := make(chan byte, 500)
|
||||
for _, b := range []byte(hdr) {
|
||||
data <- b
|
||||
}
|
||||
gitHdr, err := parseGitMsg(data)
|
||||
|
||||
if err.Error() != "Invalid character during object hash parse" {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if gitHdr.hash != "" {
|
||||
t.Errorf("Invalid hash %s", gitHdr.hash)
|
||||
}
|
||||
|
||||
if gitHdr.size != 0 {
|
||||
t.Errorf("Invalid msg size: %d", gitHdr.size)
|
||||
}
|
||||
|
||||
if gitHdr.itemType != "" {
|
||||
t.Errorf("Invalid msg type: %s", gitHdr.itemType)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestGitCommitParsing(t *testing.T) {
|
||||
t.Run("parse valid commit message", func(t *testing.T) {
|
||||
const commitData = "f40888ea4515fe2e8eea617a16f5f50a45f652d894de3ad181d58de3aafb8f99 commit 254\000"+
|
||||
`tree e20033df9f18780756ba4a96dbc7eb1a626253961039cb674156f266ba7a4e53
|
||||
parent 429cc2fe02170ca5668f0461928c7e7430c7a17cd64ac298286d7162572a7703
|
||||
author Adam Majer <amajer@suse.com> 1720709149 +0200
|
||||
committer Adam Majer <amajer@suse.com> 1720709149 +0200
|
||||
|
||||
.`+"\000"
|
||||
ch := make(chan byte, 5000)
|
||||
for _, b := range []byte(commitData) {
|
||||
ch <- b
|
||||
}
|
||||
commit, err := parseGitCommit(ch)
|
||||
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if commit.Tree != "e20033df9f18780756ba4a96dbc7eb1a626253961039cb674156f266ba7a4e53" {
|
||||
t.Errorf("Invalid commit object: %#v", commit)
|
||||
}
|
||||
|
||||
if commit.Msg != "." {
|
||||
t.Errorf("Invalid commit msg: '%s'", commit.Msg)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("parse multiline headers", func(t *testing.T) {
|
||||
const commitData = "cae5831ab48470ff060a5aaa12eb6e5a7acaf91e commit 1492\x00" +
|
||||
`tree 1f9c8fe8099615d6d3921528402ac53f09213b02
|
||||
parent e08a654fae0ecc91678819e0b62a2e014bad3339
|
||||
author Yagiz Nizipli <yagiz@nizipli.com> 1720967314 -0400
|
||||
committer GitHub <noreply@github.com> 1720967314 +0200
|
||||
gpgsig -----BEGIN PGP SIGNATURE-----
|
||||
|
||||
wsFcBAABCAAQBQJmk+CSCRC1aQ7uu5UhlAAAQIYQAEQXCl3bUUuegiz5/oitIIF7
|
||||
6xhndcjQIuqY4dONIeOARrGwbKh8OtMHpfJhMRUmvWvXrsTA6P1PWl0YcyyIMzHZ
|
||||
a4sBsWyxA0uSztVywpvksvk6EdMoEXeXrHS3cBxePsH8bI+Pwnsv27PsevEwpyIT
|
||||
reB4zZsoGySFVqf2lnXxG5hSRMYw++BDXSDMZk2BP9BvueRXasJ0lT1c7HlbHepF
|
||||
TWzwyHZ91OhXjrdPY7qLQEEV/frwuM+UrxOPb2e83ZTg81vXFuugURfhHNx4Iu+F
|
||||
LCMvOeaF2vO5yJtMe8+tY1l0Wb8S1aWcGCECN2XCXmmnWxt+yYh2gjqxq3y0DMcz
|
||||
zvg6arQIepDFLkQPZMDlUCIjIJQn4FbAaQAvoyMF8Pi5YmxhRqgo3iOB5SBE8eES
|
||||
63ifZ311izuSdD+o3ObFpzLoTgq62kglwfegZN/X8CzTSIqrT1norYJEbSwkWID1
|
||||
WeRHUHfC7f6N3XK8zeb83zmhBU58ghW9sp5/LcefGMRJmVhBWhjBCpeMUaFHdKhl
|
||||
/dfgPl5gJrrJ+wM3O6iaay0R1Iv4Upe/yXrbQnIgGj/qqgMLEPBY8lzNYimVTLxd
|
||||
2ObrcXnERo3wwxeUgWaAARbEGQjC51DK/2SXxVGUh+IokicsBNRKU7lVWwwFFely
|
||||
ntjtge6Gs9pA5rSIilPH
|
||||
=V1bK
|
||||
-----END PGP SIGNATURE-----
|
||||
|
||||
|
||||
meta: change email address of anonrig
|
||||
|
||||
PR-URL: https://github.com/nodejs/node/pull/53829
|
||||
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
|
||||
Reviewed-By: James M Snell <jasnell@gmail.com>
|
||||
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
|
||||
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
|
||||
Reviewed-By: Ulises Gascón <ulisesgascongonzalez@gmail.com>
|
||||
Reviewed-By: Richard Lau <rlau@redhat.com>
|
||||
Reviewed-By: Marco Ippolito <marcoippolito54@gmail.com>` + "\x00"
|
||||
|
||||
ch := make(chan byte, 5000)
|
||||
for _, b := range []byte(commitData) {
|
||||
ch <- b
|
||||
}
|
||||
commit, err := parseGitCommit(ch)
|
||||
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if commit.Tree != "1f9c8fe8099615d6d3921528402ac53f09213b02" {
|
||||
t.Errorf("Invalid commit object: %#v", commit)
|
||||
}
|
||||
|
||||
if commit.Msg[len(commit.Msg)-55:] != "Reviewed-By: Marco Ippolito <marcoippolito54@gmail.com>" {
|
||||
t.Errorf("Invalid commit msg: '%s'", commit.Msg[len(commit.Msg)-55:])
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("parse tree object", func(t *testing.T) {
|
||||
const treeData = "\x31\x31\x34\x31\x32\x39\x32\x30\x34\x38\x36\x38\x62\x30\x38\x65\x35\x36\x65\x39\x61\x66\x30\x64\x32\x39\x66\x37\x38\x36\x33\x34\x34\x37\x64\x32\x31\x36\x61\x62\x31\x35\x37\x32\x31\x30\x66\x65\x64\x37\x63\x36\x32\x37\x31\x64\x64\x35\x31\x30\x66\x35\x36\x62\x20\x74\x72\x65\x65\x20\x32\x30\x35\x0a\x34\x30\x30\x30\x30\x20\x62\x6f\x74\x73\x2d\x63\x6f\x6d\x6d\x6f\x6e\x00\x81\xac\x2e\xbd\x40\x77\xcb\x63\xbf\x40\x8c\xf8\xbc\xcd\x6f\xbe\x06\x0d\xfa\xd7\x45\x16\x85\x4f\xd0\xa0\x67\xb4\x21\x39\x11\x84\x34\x30\x30\x30\x30\x20\x6f\x62\x73\x2d\x73\x74\x61\x67\x69\x6e\x67\x2d\x62\x6f\x74\x00\x79\x77\x8b\x28\x7d\x37\x10\x59\xb9\x71\x28\x36\xed\x20\x31\x5f\xfb\xe1\xed\xb5\xba\x4f\x5e\xbb\x65\x65\x68\x23\x77\x32\x58\xfe\x34\x30\x30\x30\x30\x20\x70\x72\x2d\x72\x65\x76\x69\x65\x77\x00\x31\x6a\x4a\x1e\x67\xbb\xdd\x21\x89\xa4\x46\xf7\x62\x75\x60\x84\x73\x28\x6f\xb7\x3c\x51\x7f\x4a\x14\xa2\x28\xf9\x6e\x0b\xa7\x95\x34\x30\x30\x30\x30\x20\x70\x72\x6a\x67\x69\x74\x2d\x75\x70\x64\x61\x74\x65\x72\x00\xb4\x0b\x1c\xf5\xfb\xec\x9a\xb2\x9f\x48\x3e\x21\x18\x0d\x51\xb7\x98\x6e\x21\x99\x74\x84\x67\x71\x41\x24\x42\xfc\xc9\x04\x12\x99\x0a"
|
||||
|
||||
ch := make(chan byte, 1000)
|
||||
for _, b := range []byte(treeData) {
|
||||
ch <- b
|
||||
}
|
||||
|
||||
tree, err := parseGitTree(ch)
|
||||
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
found := false
|
||||
for _, item := range tree.items {
|
||||
if item.name == "bots-common" && item.hash == "81ac2ebd4077cb63bf408cf8bccd6fbe060dfad74516854fd0a067b421391184" && item.mode == 0o40000 {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Error("expected sub-tree not found")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("parse tree object with submodules", func(t *testing.T) {
|
||||
const treeData = "\x34\x38\x34\x66\x31\x62\x65\x65\x63\x39\x35\x63\x61\x36\x62\x38\x36\x30\x64\x30\x64\x37\x63\x35\x65\x34\x38\x37\x31\x36\x36\x62\x30\x38\x65\x31\x61\x34\x35\x36\x37\x36\x62\x31\x37\x64\x39\x65\x65\x66\x32\x39\x39\x39\x65\x39\x64\x37\x34\x63\x62\x63\x64\x36\x20\x74\x72\x65\x65\x20\x33\x34\x39\x00\x31\x30\x30\x36\x34\x34\x20\x2e\x67\x69\x74\x6d\x6f\x64\x75\x6c\x65\x73\x00\xc8\x1e\x14\x29\xc5\xd4\x07\xfc\x32\xf1\xd7\xe2\x64\xee\x88\xfc\xce\xfc\x44\xf7\xae\x2e\xc4\x6e\x2c\x15\x27\x26\x65\xd4\xb8\x78\x31\x30\x30\x36\x34\x34\x20\x52\x45\x41\x44\x4d\x45\x2e\x6d\x64\x00\x90\x22\x84\x13\xbf\xd3\x5c\xfb\x1e\x27\x6b\xd6\x17\x3e\x89\xed\x0f\xc6\x31\x85\x24\x58\x6d\x9d\xf0\x6a\x1e\x17\x57\x19\x8d\xc2\x31\x36\x30\x30\x30\x30\x20\x6d\x69\x6e\x67\x77\x33\x32\x2d\x67\x63\x63\x00\xdc\x55\xb8\x28\x32\x8c\x8e\x49\x4f\x67\x87\x4a\x7d\x8c\x03\xdd\x1c\x6b\x4e\x02\xd1\x6b\x86\xe0\x8e\x47\xd7\x0e\xcd\x79\x96\x80\x31\x36\x30\x30\x30\x30\x20\x6e\x6f\x64\x65\x6a\x73\x2d\x63\x6f\x6d\x6d\x6f\x6e\x00\xd6\xa7\x4c\x08\x40\x6c\xe4\x0c\xc8\xf7\xbf\xf2\xd5\xcf\x30\x90\x87\xa8\x72\x83\x61\xcc\x75\x35\x4b\x08\x62\xba\x50\x81\x93\xb8\x31\x36\x30\x30\x30\x30\x20\x6e\x6f\x64\x65\x6a\x73\x32\x31\x00\x24\xee\x6b\xee\x74\x59\xa3\x86\xda\xda\xbf\x8a\x9f\x6a\xe4\xfa\x15\xc3\xf8\x10\xbf\xa0\x1c\xee\x52\x38\x13\x8a\xa2\x14\xd1\x80\x31\x36\x30\x30\x30\x30\x20\x6e\x6f\x64\x65\x6a\x73\x32\x32\x00\x87\x3a\x32\x3b\x26\x2e\xbb\x3b\xd7\x7b\x25\x92\xb2\xe1\x1b\xdd\x08\xdb\xc7\x21\xcb\xf4\xac\x9f\x97\x63\x7e\x58\xe1\xff\xfc\xe7\x31\x36\x30\x30\x30\x30\x20\x70\x79\x74\x68\x6f\x6e\x33\x31\x31\x00\x35\xc7\x02\xe8\x50\x1e\xed\xeb\x5c\xe4\x3d\x6f\x34\x60\xd1\x1c\x79\x1c\xfe\xfa\xa7\x72\x48\xf0\x8c\xad\x55\xd0\x0c\x37\xe7\x3a\x00"
|
||||
|
||||
ch := make(chan byte, 1000)
|
||||
for _, b := range []byte(treeData) {
|
||||
ch <- b
|
||||
}
|
||||
|
||||
tree, err := parseGitTree(ch)
|
||||
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
found := false
|
||||
for _, item := range tree.items {
|
||||
t.Log(item)
|
||||
if item.name == "nodejs22" && item.hash == "873a323b262ebb3bd77b2592b2e11bdd08dbc721cbf4ac9f97637e58e1fffce7" {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Error("expected submodule not found")
|
||||
}
|
||||
})
|
||||
}
|
Loading…
Reference in New Issue
Block a user