mirror of
https://github.com/sourcegraph/sourcegraph.git
synced 2026-02-06 17:31:43 +00:00
codeintel: Infer go module version via git tags during auto indexing (#11084)
This commit is contained in:
parent
6596c95fd2
commit
384e74be0e
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
@ -231,6 +231,7 @@ Dockerfile @sourcegraph/distribution
|
||||
# Precise code intel
|
||||
/cmd/precise-code-intel-bundle-manager/ @sourcegraph/code-intel
|
||||
/cmd/precise-code-intel-worker/ @sourcegraph/code-intel
|
||||
/cmd/precise-code-intel-indexer/ @sourcegraph/code-intel
|
||||
/internal/cmd/precise-code-intel-test @sourcegraph/code-intel
|
||||
/internal/codeintel @sourcegraph/code-intel
|
||||
/enterprise/internal/codeintel @sourcegraph/code-intel
|
||||
|
||||
@ -41,9 +41,17 @@ func (p *processor) Process(ctx context.Context, index db.Index) error {
|
||||
}
|
||||
|
||||
func (p *processor) index(ctx context.Context, repoDir string, index db.Index) error {
|
||||
tag, exact, err := p.gitserverClient.Tags(ctx, p.db, index.RepositoryID, index.Commit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exact {
|
||||
tag = fmt.Sprintf("%s-%s", tag, index.Commit[:12])
|
||||
}
|
||||
|
||||
args := []string{
|
||||
"--repositoryRoot=.",
|
||||
"--moduleVersion=NONE", // TODO - find git tags on this commit to support module version
|
||||
fmt.Sprintf("--moduleVersion=%s", tag),
|
||||
}
|
||||
|
||||
return command(repoDir, "lsif-go", args...)
|
||||
|
||||
@ -27,6 +27,11 @@ type Client interface {
|
||||
|
||||
// FileExists determines whether a file exists in a particular commit of a repository.
|
||||
FileExists(ctx context.Context, db db.DB, repositoryID int, commit, file string) (bool, error)
|
||||
|
||||
// Tags returns the git tags associated with the given commit along with a boolean indicating whether
|
||||
// or not the tag was attached directly to the commit. If no tags exist at or before this commit, the
|
||||
// tag is an empty string.
|
||||
Tags(ctx context.Context, db db.DB, repositoryID int, commit string) (string, bool, error)
|
||||
}
|
||||
|
||||
type defaultClient struct{}
|
||||
@ -52,3 +57,7 @@ func (c *defaultClient) Archive(ctx context.Context, db db.DB, repositoryID int,
|
||||
func (c *defaultClient) FileExists(ctx context.Context, db db.DB, repositoryID int, commit, file string) (bool, error) {
|
||||
return FileExists(ctx, db, repositoryID, commit, file)
|
||||
}
|
||||
|
||||
func (c *defaultClient) Tags(ctx context.Context, db db.DB, repositoryID int, commit string) (string, bool, error) {
|
||||
return Tags(ctx, db, repositoryID, commit)
|
||||
}
|
||||
|
||||
@ -29,6 +29,9 @@ type MockClient struct {
|
||||
// HeadFunc is an instance of a mock function object controlling the
|
||||
// behavior of the method Head.
|
||||
HeadFunc *ClientHeadFunc
|
||||
// TagsFunc is an instance of a mock function object controlling the
|
||||
// behavior of the method Tags.
|
||||
TagsFunc *ClientTagsFunc
|
||||
}
|
||||
|
||||
// NewMockClient creates a new mock of the Client interface. All methods
|
||||
@ -60,6 +63,11 @@ func NewMockClient() *MockClient {
|
||||
return "", nil
|
||||
},
|
||||
},
|
||||
TagsFunc: &ClientTagsFunc{
|
||||
defaultHook: func(context.Context, db.DB, int, string) (string, bool, error) {
|
||||
return "", false, nil
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -82,6 +90,9 @@ func NewMockClientFrom(i gitserver.Client) *MockClient {
|
||||
HeadFunc: &ClientHeadFunc{
|
||||
defaultHook: i.Head,
|
||||
},
|
||||
TagsFunc: &ClientTagsFunc{
|
||||
defaultHook: i.Tags,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -658,3 +669,120 @@ func (c ClientHeadFuncCall) Args() []interface{} {
|
||||
func (c ClientHeadFuncCall) Results() []interface{} {
|
||||
return []interface{}{c.Result0, c.Result1}
|
||||
}
|
||||
|
||||
// ClientTagsFunc describes the behavior when the Tags method of the parent
|
||||
// MockClient instance is invoked.
|
||||
type ClientTagsFunc struct {
|
||||
defaultHook func(context.Context, db.DB, int, string) (string, bool, error)
|
||||
hooks []func(context.Context, db.DB, int, string) (string, bool, error)
|
||||
history []ClientTagsFuncCall
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
// Tags delegates to the next hook function in the queue and stores the
|
||||
// parameter and result values of this invocation.
|
||||
func (m *MockClient) Tags(v0 context.Context, v1 db.DB, v2 int, v3 string) (string, bool, error) {
|
||||
r0, r1, r2 := m.TagsFunc.nextHook()(v0, v1, v2, v3)
|
||||
m.TagsFunc.appendCall(ClientTagsFuncCall{v0, v1, v2, v3, r0, r1, r2})
|
||||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// SetDefaultHook sets function that is called when the Tags method of the
|
||||
// parent MockClient instance is invoked and the hook queue is empty.
|
||||
func (f *ClientTagsFunc) SetDefaultHook(hook func(context.Context, db.DB, int, string) (string, bool, error)) {
|
||||
f.defaultHook = hook
|
||||
}
|
||||
|
||||
// PushHook adds a function to the end of hook queue. Each invocation of the
|
||||
// Tags method of the parent MockClient instance inovkes the hook at the
|
||||
// front of the queue and discards it. After the queue is empty, the default
|
||||
// hook function is invoked for any future action.
|
||||
func (f *ClientTagsFunc) PushHook(hook func(context.Context, db.DB, int, string) (string, bool, error)) {
|
||||
f.mutex.Lock()
|
||||
f.hooks = append(f.hooks, hook)
|
||||
f.mutex.Unlock()
|
||||
}
|
||||
|
||||
// SetDefaultReturn calls SetDefaultDefaultHook with a function that returns
|
||||
// the given values.
|
||||
func (f *ClientTagsFunc) SetDefaultReturn(r0 string, r1 bool, r2 error) {
|
||||
f.SetDefaultHook(func(context.Context, db.DB, int, string) (string, bool, error) {
|
||||
return r0, r1, r2
|
||||
})
|
||||
}
|
||||
|
||||
// PushReturn calls PushDefaultHook with a function that returns the given
|
||||
// values.
|
||||
func (f *ClientTagsFunc) PushReturn(r0 string, r1 bool, r2 error) {
|
||||
f.PushHook(func(context.Context, db.DB, int, string) (string, bool, error) {
|
||||
return r0, r1, r2
|
||||
})
|
||||
}
|
||||
|
||||
func (f *ClientTagsFunc) nextHook() func(context.Context, db.DB, int, string) (string, bool, error) {
|
||||
f.mutex.Lock()
|
||||
defer f.mutex.Unlock()
|
||||
|
||||
if len(f.hooks) == 0 {
|
||||
return f.defaultHook
|
||||
}
|
||||
|
||||
hook := f.hooks[0]
|
||||
f.hooks = f.hooks[1:]
|
||||
return hook
|
||||
}
|
||||
|
||||
func (f *ClientTagsFunc) appendCall(r0 ClientTagsFuncCall) {
|
||||
f.mutex.Lock()
|
||||
f.history = append(f.history, r0)
|
||||
f.mutex.Unlock()
|
||||
}
|
||||
|
||||
// History returns a sequence of ClientTagsFuncCall objects describing the
|
||||
// invocations of this function.
|
||||
func (f *ClientTagsFunc) History() []ClientTagsFuncCall {
|
||||
f.mutex.Lock()
|
||||
history := make([]ClientTagsFuncCall, len(f.history))
|
||||
copy(history, f.history)
|
||||
f.mutex.Unlock()
|
||||
|
||||
return history
|
||||
}
|
||||
|
||||
// ClientTagsFuncCall is an object that describes an invocation of method
|
||||
// Tags on an instance of MockClient.
|
||||
type ClientTagsFuncCall struct {
|
||||
// Arg0 is the value of the 1st argument passed to this method
|
||||
// invocation.
|
||||
Arg0 context.Context
|
||||
// Arg1 is the value of the 2nd argument passed to this method
|
||||
// invocation.
|
||||
Arg1 db.DB
|
||||
// Arg2 is the value of the 3rd argument passed to this method
|
||||
// invocation.
|
||||
Arg2 int
|
||||
// Arg3 is the value of the 4th argument passed to this method
|
||||
// invocation.
|
||||
Arg3 string
|
||||
// Result0 is the value of the 1st result returned from this method
|
||||
// invocation.
|
||||
Result0 string
|
||||
// Result1 is the value of the 2nd result returned from this method
|
||||
// invocation.
|
||||
Result1 bool
|
||||
// Result2 is the value of the 3rd result returned from this method
|
||||
// invocation.
|
||||
Result2 error
|
||||
}
|
||||
|
||||
// Args returns an interface slice containing the arguments of this
|
||||
// invocation.
|
||||
func (c ClientTagsFuncCall) Args() []interface{} {
|
||||
return []interface{}{c.Arg0, c.Arg1, c.Arg2, c.Arg3}
|
||||
}
|
||||
|
||||
// Results returns an interface slice containing the results of this
|
||||
// invocation.
|
||||
func (c ClientTagsFuncCall) Results() []interface{} {
|
||||
return []interface{}{c.Result0, c.Result1, c.Result2}
|
||||
}
|
||||
|
||||
38
internal/codeintel/gitserver/tags.go
Normal file
38
internal/codeintel/gitserver/tags.go
Normal file
@ -0,0 +1,38 @@
|
||||
package gitserver
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/sourcegraph/sourcegraph/internal/codeintel/db"
|
||||
)
|
||||
|
||||
// Tags returns the git tags associated with the given commit along with a boolean indicating whether
|
||||
// or not the tag was attached directly to the commit. If no tags exist at or before this commit, the
|
||||
// tag is an empty string.
|
||||
func Tags(ctx context.Context, db db.DB, repositoryID int, commit string) (string, bool, error) {
|
||||
tag, err := execGitCommand(ctx, db, repositoryID, "tag", "-l", "--points-at", commit)
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
if tag != "" {
|
||||
return tag, true, nil
|
||||
}
|
||||
|
||||
// git describe --tags will exit with status 128 (fatal: No names found, cannot describe anything)
|
||||
// when there are no tags known to the given repo. In order to prevent a gitserver error from
|
||||
// occurring, we first check to see if there are any tags and early-exit.
|
||||
tags, err := execGitCommand(ctx, db, repositoryID, "tag", commit)
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
if tags == "" {
|
||||
return "", false, nil
|
||||
}
|
||||
|
||||
tag, err = execGitCommand(ctx, db, repositoryID, "describe", "--tags", "--abbrev=0", commit)
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
|
||||
return tag, false, nil
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user