mirror of
https://github.com/github/codeql.git
synced 2025-12-17 01:03:14 +01:00
Merge pull request #15361 from github/mbg/go/legacy-gopath-mode-deprecated
Go: Update autobuilder to deal with the upcoming deprecation of the legacy GOPATH mode
This commit is contained in:
@@ -70,12 +70,33 @@ func tryBuild(cmd string, args ...string) bool {
|
||||
return res && (!CheckExtracted || checkExtractorRun())
|
||||
}
|
||||
|
||||
// Autobuild attempts to detect build system and run the corresponding command.
|
||||
func Autobuild() bool {
|
||||
return tryBuildIfExists("Makefile", "make") ||
|
||||
tryBuildIfExists("makefile", "make") ||
|
||||
tryBuildIfExists("GNUmakefile", "make") ||
|
||||
tryBuildIfExists("build.ninja", "ninja") ||
|
||||
tryBuildIfExists("build", "./build") ||
|
||||
tryBuildIfExists("build.sh", "./build.sh")
|
||||
// If a project is accompanied by a build script (such as a makefile), then we try executing such
|
||||
// build scripts to build the project. This type represents pairs of script names to check for
|
||||
// and the names of corresponding build tools to invoke if those scripts exist.
|
||||
type BuildScript struct {
|
||||
Tool string // The name of the command to execute if the build script exists
|
||||
Filename string // The name of the build script to check for
|
||||
}
|
||||
|
||||
// An array of build scripts to check for and corresponding commands that we can execute
|
||||
// if they exist.
|
||||
var BuildScripts = []BuildScript{
|
||||
{Tool: "make", Filename: "Makefile"},
|
||||
{Tool: "make", Filename: "makefile"},
|
||||
{Tool: "make", Filename: "GNUmakefile"},
|
||||
{Tool: "ninja", Filename: "build.ninja"},
|
||||
{Tool: "./build", Filename: "build"},
|
||||
{Tool: "./build.sh", Filename: "build.sh"},
|
||||
}
|
||||
|
||||
// Autobuild attempts to detect build systems based on the presence of build scripts from the
|
||||
// list in `BuildScripts` and run the corresponding command. This may invoke zero or more
|
||||
// build scripts in the order given by `BuildScripts`.
|
||||
func Autobuild() bool {
|
||||
for _, script := range BuildScripts {
|
||||
if tryBuildIfExists(script.Filename, script.Tool) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -267,15 +267,22 @@ func outputEnvironmentJson(version string) {
|
||||
// Get the version of Go to install and output it to stdout as json.
|
||||
func IdentifyEnvironment() {
|
||||
var v versionInfo
|
||||
buildInfo := project.GetBuildInfo(false)
|
||||
goVersionInfo := project.TryReadGoDirective(buildInfo)
|
||||
v.goModVersion, v.goModVersionFound = goVersionInfo.Version, goVersionInfo.Found
|
||||
workspaces := project.GetWorkspaceInfo(false)
|
||||
|
||||
// Remove temporary extractor files (e.g. auto-generated go.mod files) when we are done
|
||||
defer project.RemoveTemporaryExtractorFiles()
|
||||
|
||||
// Find the greatest Go version required by any of the workspaces.
|
||||
greatestGoVersion := project.RequiredGoVersion(&workspaces)
|
||||
v.goModVersion, v.goModVersionFound = greatestGoVersion.Version, greatestGoVersion.Found
|
||||
|
||||
// Find which, if any, version of Go is installed on the system already.
|
||||
v.goEnvVersionFound = toolchain.IsInstalled()
|
||||
if v.goEnvVersionFound {
|
||||
v.goEnvVersion = toolchain.GetEnvGoVersion()[2:]
|
||||
}
|
||||
|
||||
// Determine which version of Go we should recommend to install.
|
||||
msg, versionToInstall := getVersionToInstall(v)
|
||||
log.Println(msg)
|
||||
|
||||
|
||||
@@ -56,23 +56,6 @@ Build behavior:
|
||||
fmt.Fprintf(os.Stderr, "Usage:\n\n %s\n", os.Args[0])
|
||||
}
|
||||
|
||||
// Returns the current Go version in semver format, e.g. v1.14.4
|
||||
func getEnvGoSemVer() string {
|
||||
goVersion := toolchain.GetEnvGoVersion()
|
||||
if !strings.HasPrefix(goVersion, "go") {
|
||||
log.Fatalf("Expected 'go version' output of the form 'go1.2.3'; got '%s'", goVersion)
|
||||
}
|
||||
// Go versions don't follow the SemVer format, but the only exception we normally care about
|
||||
// is release candidates; so this is a horrible hack to convert e.g. `go1.22rc1` into `go1.22-rc1`
|
||||
// which is compatible with the SemVer specification
|
||||
rcIndex := strings.Index(goVersion, "rc")
|
||||
if rcIndex != -1 {
|
||||
return semver.Canonical("v"+goVersion[2:rcIndex]) + "-" + goVersion[rcIndex:]
|
||||
} else {
|
||||
return semver.Canonical("v" + goVersion[2:])
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the import path of the package being built, or "" if it cannot be determined.
|
||||
func getImportPath() (importpath string) {
|
||||
importpath = os.Getenv("LGTM_INDEX_IMPORT_PATH")
|
||||
@@ -177,8 +160,8 @@ func getSourceDir() string {
|
||||
}
|
||||
|
||||
// fixGoVendorIssues fixes issues with go vendor for go version >= 1.14
|
||||
func fixGoVendorIssues(buildInfo *project.BuildInfo, goModVersionFound bool) {
|
||||
if buildInfo.ModMode == project.ModVendor {
|
||||
func fixGoVendorIssues(workspace *project.GoWorkspace, goModVersionFound bool) {
|
||||
if workspace.ModMode == project.ModVendor {
|
||||
// fix go vendor issues with go versions >= 1.14 when no go version is specified in the go.mod
|
||||
// if this is the case, and dependencies were vendored with an old go version (and therefore
|
||||
// do not contain a '## explicit' annotation, the go command will fail and refuse to do any
|
||||
@@ -186,7 +169,7 @@ func fixGoVendorIssues(buildInfo *project.BuildInfo, goModVersionFound bool) {
|
||||
//
|
||||
// we work around this by adding an explicit go version of 1.13, which is the last version
|
||||
// where this is not an issue
|
||||
if buildInfo.DepMode == project.GoGetWithModules {
|
||||
if workspace.DepMode == project.GoGetWithModules {
|
||||
if !goModVersionFound {
|
||||
// if the go.mod does not contain a version line
|
||||
modulesTxt, err := os.ReadFile("vendor/modules.txt")
|
||||
@@ -197,7 +180,7 @@ func fixGoVendorIssues(buildInfo *project.BuildInfo, goModVersionFound bool) {
|
||||
log.Println("Adding a version directive to the go.mod file as the modules.txt does not have explicit annotations")
|
||||
if !addVersionToMod("1.13") {
|
||||
log.Println("Failed to add a version to the go.mod file to fix explicitly required package bug; not using vendored dependencies")
|
||||
buildInfo.ModMode = project.ModMod
|
||||
workspace.ModMode = project.ModMod
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -206,9 +189,9 @@ func fixGoVendorIssues(buildInfo *project.BuildInfo, goModVersionFound bool) {
|
||||
}
|
||||
|
||||
// Determines whether the project needs a GOPATH set up
|
||||
func getNeedGopath(buildInfo project.BuildInfo, importpath string) bool {
|
||||
func getNeedGopath(workspace project.GoWorkspace, importpath string) bool {
|
||||
needGopath := true
|
||||
if buildInfo.DepMode == project.GoGetWithModules {
|
||||
if workspace.DepMode == project.GoGetWithModules {
|
||||
needGopath = false
|
||||
}
|
||||
// if `LGTM_INDEX_NEED_GOPATH` is set, it overrides the value for `needGopath` inferred above
|
||||
@@ -229,44 +212,46 @@ func getNeedGopath(buildInfo project.BuildInfo, importpath string) bool {
|
||||
}
|
||||
|
||||
// Try to update `go.mod` and `go.sum` if the go version is >= 1.16.
|
||||
func tryUpdateGoModAndGoSum(buildInfo project.BuildInfo) {
|
||||
func tryUpdateGoModAndGoSum(workspace project.GoWorkspace) {
|
||||
// Go 1.16 and later won't automatically attempt to update go.mod / go.sum during package loading, so try to update them here:
|
||||
if buildInfo.ModMode != project.ModVendor && buildInfo.DepMode == project.GoGetWithModules && semver.Compare(getEnvGoSemVer(), "v1.16") >= 0 {
|
||||
// stat go.mod and go.sum
|
||||
goModPath := filepath.Join(buildInfo.BaseDir, "go.mod")
|
||||
beforeGoModFileInfo, beforeGoModErr := os.Stat(goModPath)
|
||||
if beforeGoModErr != nil {
|
||||
log.Println("Failed to stat go.mod before running `go mod tidy -e`")
|
||||
}
|
||||
|
||||
goSumPath := filepath.Join(buildInfo.BaseDir, "go.sum")
|
||||
beforeGoSumFileInfo, beforeGoSumErr := os.Stat(goSumPath)
|
||||
|
||||
// run `go mod tidy -e`
|
||||
cmd := exec.Command("go", "mod", "tidy", "-e")
|
||||
cmd.Dir = buildInfo.BaseDir
|
||||
res := util.RunCmd(cmd)
|
||||
|
||||
if !res {
|
||||
log.Println("Failed to run `go mod tidy -e`")
|
||||
} else {
|
||||
if beforeGoModFileInfo != nil {
|
||||
afterGoModFileInfo, afterGoModErr := os.Stat(goModPath)
|
||||
if afterGoModErr != nil {
|
||||
log.Println("Failed to stat go.mod after running `go mod tidy -e`")
|
||||
} else if afterGoModFileInfo.ModTime().After(beforeGoModFileInfo.ModTime()) {
|
||||
// if go.mod has been changed then notify the user
|
||||
log.Println("We have run `go mod tidy -e` and it altered go.mod. You may wish to check these changes into version control. ")
|
||||
}
|
||||
if workspace.ModMode != project.ModVendor && workspace.DepMode == project.GoGetWithModules && semver.Compare(toolchain.GetEnvGoSemVer(), "v1.16") >= 0 {
|
||||
for _, goMod := range workspace.Modules {
|
||||
// stat go.mod and go.sum
|
||||
goModPath := goMod.Path
|
||||
goModDir := filepath.Dir(goModPath)
|
||||
beforeGoModFileInfo, beforeGoModErr := os.Stat(goModPath)
|
||||
if beforeGoModErr != nil {
|
||||
log.Printf("Failed to stat %s before running `go mod tidy -e`\n", goModPath)
|
||||
}
|
||||
|
||||
afterGoSumFileInfo, afterGoSumErr := os.Stat(goSumPath)
|
||||
if afterGoSumErr != nil {
|
||||
log.Println("Failed to stat go.sum after running `go mod tidy -e`")
|
||||
goSumPath := filepath.Join(goModDir, "go.sum")
|
||||
beforeGoSumFileInfo, beforeGoSumErr := os.Stat(goSumPath)
|
||||
|
||||
// run `go mod tidy -e`
|
||||
cmd := toolchain.TidyModule(goModDir)
|
||||
res := util.RunCmd(cmd)
|
||||
|
||||
if !res {
|
||||
log.Printf("Failed to run `go mod tidy -e` in %s\n", goModDir)
|
||||
} else {
|
||||
if beforeGoSumErr != nil || afterGoSumFileInfo.ModTime().After(beforeGoSumFileInfo.ModTime()) {
|
||||
// if go.sum has been changed then notify the user
|
||||
log.Println("We have run `go mod tidy -e` and it altered go.sum. You may wish to check these changes into version control. ")
|
||||
if beforeGoModFileInfo != nil {
|
||||
afterGoModFileInfo, afterGoModErr := os.Stat(goModPath)
|
||||
if afterGoModErr != nil {
|
||||
log.Printf("Failed to stat %s after running `go mod tidy -e`: %s\n", goModPath, afterGoModErr.Error())
|
||||
} else if afterGoModFileInfo.ModTime().After(beforeGoModFileInfo.ModTime()) {
|
||||
// if go.mod has been changed then notify the user
|
||||
log.Println("We have run `go mod tidy -e` and it altered go.mod. You may wish to check these changes into version control. ")
|
||||
}
|
||||
}
|
||||
|
||||
afterGoSumFileInfo, afterGoSumErr := os.Stat(goSumPath)
|
||||
if afterGoSumErr != nil {
|
||||
log.Printf("Failed to stat %s after running `go mod tidy -e`: %s\n", goSumPath, afterGoSumErr.Error())
|
||||
} else {
|
||||
if beforeGoSumErr != nil || afterGoSumFileInfo.ModTime().After(beforeGoSumFileInfo.ModTime()) {
|
||||
// if go.sum has been changed then notify the user
|
||||
log.Println("We have run `go mod tidy -e` and it altered go.sum. You may wish to check these changes into version control. ")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -406,7 +391,7 @@ func buildWithoutCustomCommands(modMode project.ModMode) bool {
|
||||
log.Println("Build failed, continuing to install dependencies.")
|
||||
|
||||
shouldInstallDependencies = true
|
||||
} else if util.DepErrors("./...", modMode.ArgsForGoVersion(getEnvGoSemVer())...) {
|
||||
} else if util.DepErrors("./...", modMode.ArgsForGoVersion(toolchain.GetEnvGoSemVer())...) {
|
||||
log.Println("Dependencies are still not resolving after the build, continuing to install dependencies.")
|
||||
|
||||
shouldInstallDependencies = true
|
||||
@@ -449,10 +434,10 @@ func buildWithCustomCommands(inst string) {
|
||||
}
|
||||
|
||||
// Install dependencies using the given dependency installer mode.
|
||||
func installDependencies(buildInfo project.BuildInfo) {
|
||||
func installDependencies(workspace project.GoWorkspace) {
|
||||
// automatically determine command to install dependencies
|
||||
var install *exec.Cmd
|
||||
if buildInfo.DepMode == project.Dep {
|
||||
if workspace.DepMode == project.Dep {
|
||||
// set up the dep cache if SEMMLE_CACHE is set
|
||||
cacheDir := os.Getenv("SEMMLE_CACHE")
|
||||
if cacheDir != "" {
|
||||
@@ -482,47 +467,80 @@ func installDependencies(buildInfo project.BuildInfo) {
|
||||
install = exec.Command("dep", "ensure", "-v")
|
||||
}
|
||||
log.Println("Installing dependencies using `dep ensure`.")
|
||||
} else if buildInfo.DepMode == project.Glide {
|
||||
util.RunCmd(install)
|
||||
} else if workspace.DepMode == project.Glide {
|
||||
install = exec.Command("glide", "install")
|
||||
log.Println("Installing dependencies using `glide install`")
|
||||
util.RunCmd(install)
|
||||
} else {
|
||||
// explicitly set go module support
|
||||
if buildInfo.DepMode == project.GoGetWithModules {
|
||||
os.Setenv("GO111MODULE", "on")
|
||||
} else if buildInfo.DepMode == project.GoGetNoModules {
|
||||
os.Setenv("GO111MODULE", "off")
|
||||
if workspace.Modules == nil {
|
||||
project.InitGoModForLegacyProject(workspace.BaseDir)
|
||||
workspace.Modules = project.LoadGoModules([]string{filepath.Join(workspace.BaseDir, "go.mod")})
|
||||
}
|
||||
|
||||
// get dependencies
|
||||
install = exec.Command("go", "get", "-v", "./...")
|
||||
install.Dir = buildInfo.BaseDir
|
||||
log.Printf("Installing dependencies using `go get -v ./...` in `%s`.\n", buildInfo.BaseDir)
|
||||
// get dependencies for all modules
|
||||
for _, module := range workspace.Modules {
|
||||
path := filepath.Dir(module.Path)
|
||||
|
||||
if util.DirExists(filepath.Join(path, "vendor")) {
|
||||
vendor := toolchain.VendorModule(path)
|
||||
log.Printf("Synchronizing vendor file using `go mod vendor` in %s.\n", path)
|
||||
util.RunCmd(vendor)
|
||||
}
|
||||
|
||||
install = exec.Command("go", "get", "-v", "./...")
|
||||
install.Dir = path
|
||||
log.Printf("Installing dependencies using `go get -v ./...` in `%s`.\n", path)
|
||||
util.RunCmd(install)
|
||||
}
|
||||
}
|
||||
util.RunCmd(install)
|
||||
}
|
||||
|
||||
// Run the extractor.
|
||||
func extract(buildInfo project.BuildInfo) {
|
||||
func extract(workspace project.GoWorkspace) bool {
|
||||
extractor, err := util.GetExtractorPath()
|
||||
if err != nil {
|
||||
log.Fatalf("Could not determine path of extractor: %v.\n", err)
|
||||
}
|
||||
|
||||
extractorArgs := []string{}
|
||||
if buildInfo.DepMode == project.GoGetWithModules {
|
||||
extractorArgs = append(extractorArgs, buildInfo.ModMode.ArgsForGoVersion(getEnvGoSemVer())...)
|
||||
if workspace.DepMode == project.GoGetWithModules {
|
||||
extractorArgs = append(extractorArgs, workspace.ModMode.ArgsForGoVersion(toolchain.GetEnvGoSemVer())...)
|
||||
}
|
||||
extractorArgs = append(extractorArgs, "./...")
|
||||
|
||||
log.Printf("Running extractor command '%s %v' from directory '%s'.\n", extractor, extractorArgs, buildInfo.BaseDir)
|
||||
if len(workspace.Modules) == 0 {
|
||||
// There may be no modules if we are using e.g. Dep or Glide
|
||||
extractorArgs = append(extractorArgs, "./...")
|
||||
} else {
|
||||
for _, module := range workspace.Modules {
|
||||
relModPath, relErr := filepath.Rel(workspace.BaseDir, filepath.Dir(module.Path))
|
||||
|
||||
if relErr != nil {
|
||||
log.Printf(
|
||||
"Unable to make module path %s relative to workspace base dir %s: %s\n",
|
||||
filepath.Dir(module.Path), workspace.BaseDir, relErr.Error())
|
||||
} else {
|
||||
if relModPath != "." {
|
||||
extractorArgs = append(extractorArgs, "."+string(os.PathSeparator)+relModPath+"/...")
|
||||
} else {
|
||||
extractorArgs = append(extractorArgs, relModPath+"/...")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("Running extractor command '%s %v' from directory '%s'.\n", extractor, extractorArgs, workspace.BaseDir)
|
||||
cmd := exec.Command(extractor, extractorArgs...)
|
||||
cmd.Dir = buildInfo.BaseDir
|
||||
cmd.Dir = workspace.BaseDir
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
log.Fatalf("Extraction failed: %s\n", err.Error())
|
||||
log.Printf("Extraction failed for %s: %s\n", workspace.BaseDir, err.Error())
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Build the project and run the extractor.
|
||||
@@ -536,75 +554,120 @@ func installDependenciesAndBuild() {
|
||||
|
||||
// determine how to install dependencies and whether a GOPATH needs to be set up before
|
||||
// extraction
|
||||
buildInfo := project.GetBuildInfo(true)
|
||||
workspaces := project.GetWorkspaceInfo(true)
|
||||
if _, present := os.LookupEnv("GO111MODULE"); !present {
|
||||
os.Setenv("GO111MODULE", "auto")
|
||||
}
|
||||
|
||||
goVersionInfo := project.TryReadGoDirective(buildInfo)
|
||||
// Remove temporary extractor files (e.g. auto-generated go.mod files) when we are done
|
||||
defer project.RemoveTemporaryExtractorFiles()
|
||||
|
||||
// If there is only one workspace and it needs a GOPATH set up, which may be the case if
|
||||
// we don't use Go modules, then we move the repository to a temporary directory and set
|
||||
// the GOPATH to it.
|
||||
if len(workspaces) == 1 {
|
||||
workspace := workspaces[0]
|
||||
|
||||
importpath := getImportPath()
|
||||
needGopath := getNeedGopath(workspace, importpath)
|
||||
|
||||
inLGTM := os.Getenv("LGTM_SRC") != "" || os.Getenv("LGTM_INDEX_NEED_GOPATH") != ""
|
||||
|
||||
if inLGTM && needGopath {
|
||||
paths := moveToTemporaryGopath(srcdir, importpath)
|
||||
|
||||
// schedule restoring the contents of newdir to their original location after this function completes:
|
||||
defer restoreRepoLayout(paths.newdir, paths.files, filepath.Base(paths.scratch), srcdir)
|
||||
|
||||
pt := createPathTransformerFile(paths.newdir)
|
||||
defer os.Remove(pt.Name())
|
||||
|
||||
writePathTransformerFile(pt, paths.realSrc, paths.root, paths.newdir)
|
||||
setGopath(paths.root)
|
||||
}
|
||||
}
|
||||
|
||||
// Find the greatest version of Go that is required by the workspaces to check it against the version
|
||||
// of Go that is installed on the system.
|
||||
greatestGoVersion := project.RequiredGoVersion(&workspaces)
|
||||
|
||||
// This diagnostic is not required if the system Go version is 1.21 or greater, since the
|
||||
// Go tooling should install required Go versions as needed.
|
||||
if semver.Compare(getEnvGoSemVer(), "v1.21.0") < 0 && goVersionInfo.Found && semver.Compare("v"+goVersionInfo.Version, getEnvGoSemVer()) > 0 {
|
||||
diagnostics.EmitNewerGoVersionNeeded(getEnvGoSemVer(), "v"+goVersionInfo.Version)
|
||||
if semver.Compare(toolchain.GetEnvGoSemVer(), "v1.21.0") < 0 && greatestGoVersion.Found && semver.Compare("v"+greatestGoVersion.Version, toolchain.GetEnvGoSemVer()) > 0 {
|
||||
diagnostics.EmitNewerGoVersionNeeded(toolchain.GetEnvGoSemVer(), "v"+greatestGoVersion.Version)
|
||||
if val, _ := os.LookupEnv("GITHUB_ACTIONS"); val == "true" {
|
||||
log.Printf(
|
||||
"The go.mod file requires version %s of Go, but version %s is installed. Consider adding an actions/setup-go step to your workflow.\n",
|
||||
"v"+goVersionInfo.Version,
|
||||
getEnvGoSemVer())
|
||||
"A go.mod file requires version %s of Go, but version %s is installed. Consider adding an actions/setup-go step to your workflow.\n",
|
||||
"v"+greatestGoVersion.Version,
|
||||
toolchain.GetEnvGoSemVer())
|
||||
}
|
||||
}
|
||||
|
||||
fixGoVendorIssues(&buildInfo, goVersionInfo.Found)
|
||||
// Attempt to extract all workspaces; we will tolerate individual extraction failures here
|
||||
for i, workspace := range workspaces {
|
||||
goVersionInfo := workspace.RequiredGoVersion()
|
||||
|
||||
tryUpdateGoModAndGoSum(buildInfo)
|
||||
fixGoVendorIssues(&workspace, goVersionInfo.Found)
|
||||
|
||||
importpath := getImportPath()
|
||||
needGopath := getNeedGopath(buildInfo, importpath)
|
||||
tryUpdateGoModAndGoSum(workspace)
|
||||
|
||||
inLGTM := os.Getenv("LGTM_SRC") != "" || os.Getenv("LGTM_INDEX_NEED_GOPATH") != ""
|
||||
|
||||
if inLGTM && needGopath {
|
||||
paths := moveToTemporaryGopath(srcdir, importpath)
|
||||
|
||||
// schedule restoring the contents of newdir to their original location after this function completes:
|
||||
defer restoreRepoLayout(paths.newdir, paths.files, filepath.Base(paths.scratch), srcdir)
|
||||
|
||||
pt := createPathTransformerFile(paths.newdir)
|
||||
defer os.Remove(pt.Name())
|
||||
|
||||
writePathTransformerFile(pt, paths.realSrc, paths.root, paths.newdir)
|
||||
setGopath(paths.root)
|
||||
}
|
||||
|
||||
// check whether an explicit dependency installation command was provided
|
||||
inst := util.Getenv("CODEQL_EXTRACTOR_GO_BUILD_COMMAND", "LGTM_INDEX_BUILD_COMMAND")
|
||||
shouldInstallDependencies := false
|
||||
if inst == "" {
|
||||
shouldInstallDependencies = buildWithoutCustomCommands(buildInfo.ModMode)
|
||||
} else {
|
||||
buildWithCustomCommands(inst)
|
||||
}
|
||||
|
||||
if buildInfo.ModMode == project.ModVendor {
|
||||
// test if running `go` with -mod=vendor works, and if it doesn't, try to fallback to -mod=mod
|
||||
// or not set if the go version < 1.14. Note we check this post-build in case the build brings
|
||||
// the vendor directory up to date.
|
||||
if !checkVendor() {
|
||||
buildInfo.ModMode = project.ModMod
|
||||
log.Println("The vendor directory is not consistent with the go.mod; not using vendored dependencies.")
|
||||
}
|
||||
}
|
||||
|
||||
if shouldInstallDependencies {
|
||||
if buildInfo.ModMode == project.ModVendor {
|
||||
log.Printf("Skipping dependency installation because a Go vendor directory was found.")
|
||||
// check whether an explicit dependency installation command was provided
|
||||
inst := util.Getenv("CODEQL_EXTRACTOR_GO_BUILD_COMMAND", "LGTM_INDEX_BUILD_COMMAND")
|
||||
shouldInstallDependencies := false
|
||||
if inst == "" {
|
||||
shouldInstallDependencies = buildWithoutCustomCommands(workspace.ModMode)
|
||||
} else {
|
||||
installDependencies(buildInfo)
|
||||
buildWithCustomCommands(inst)
|
||||
}
|
||||
|
||||
if workspace.ModMode == project.ModVendor {
|
||||
// test if running `go` with -mod=vendor works, and if it doesn't, try to fallback to -mod=mod
|
||||
// or not set if the go version < 1.14. Note we check this post-build in case the build brings
|
||||
// the vendor directory up to date.
|
||||
if !checkVendor() {
|
||||
workspace.ModMode = project.ModMod
|
||||
log.Println("The vendor directory is not consistent with the go.mod; not using vendored dependencies.")
|
||||
}
|
||||
}
|
||||
|
||||
if shouldInstallDependencies {
|
||||
if workspace.ModMode == project.ModVendor {
|
||||
log.Printf("Skipping dependency installation because a Go vendor directory was found.")
|
||||
} else {
|
||||
installDependencies(workspace)
|
||||
}
|
||||
}
|
||||
|
||||
workspaces[i].Extracted = extract(workspace)
|
||||
}
|
||||
|
||||
// Find all projects which could not be extracted successfully
|
||||
var unsuccessfulProjects = []string{}
|
||||
|
||||
for _, workspace := range workspaces {
|
||||
if !workspace.Extracted {
|
||||
unsuccessfulProjects = append(unsuccessfulProjects, workspace.BaseDir)
|
||||
}
|
||||
}
|
||||
|
||||
extract(buildInfo)
|
||||
// If all projects could not be extracted successfully, we fail the overall extraction.
|
||||
if len(unsuccessfulProjects) == len(workspaces) {
|
||||
log.Fatalln("Extraction failed for all discovered Go projects.")
|
||||
}
|
||||
|
||||
// If there is at least one project that could not be extracted successfully,
|
||||
// emit a diagnostic that reports which projects we could not extract successfully.
|
||||
// We only consider this a warning, since there may be test projects etc. which
|
||||
// do not matter if they cannot be extracted successfully.
|
||||
if len(unsuccessfulProjects) > 0 {
|
||||
log.Printf(
|
||||
"Warning: extraction failed for %d project(s): %s\n",
|
||||
len(unsuccessfulProjects),
|
||||
strings.Join(unsuccessfulProjects, ", "))
|
||||
diagnostics.EmitExtractionFailedForProjects(unsuccessfulProjects)
|
||||
} else {
|
||||
log.Println("Success: extraction succeeded for all discovered projects.")
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
@@ -493,3 +493,18 @@ func EmitNewerSystemGoRequired(requiredVersion string) {
|
||||
noLocation,
|
||||
)
|
||||
}
|
||||
|
||||
func EmitExtractionFailedForProjects(path []string) {
|
||||
emitDiagnostic(
|
||||
"go/autobuilder/extraction-failed-for-project",
|
||||
fmt.Sprintf("Unable to extract %d Go projects", len(path)),
|
||||
fmt.Sprintf(
|
||||
"The following %d Go project%s could not be extracted successfully:\n\n`%s`\n",
|
||||
len(path),
|
||||
plural(len(path), "", "s"),
|
||||
strings.Join(path, "`, `")),
|
||||
severityWarning,
|
||||
fullVisibility,
|
||||
noLocation,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,22 +5,104 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/github/codeql-go/extractor/diagnostics"
|
||||
"github.com/github/codeql-go/extractor/toolchain"
|
||||
"github.com/github/codeql-go/extractor/util"
|
||||
"golang.org/x/mod/modfile"
|
||||
"golang.org/x/mod/semver"
|
||||
)
|
||||
|
||||
func getDirs(paths []string) []string {
|
||||
dirs := make([]string, len(paths))
|
||||
for i, path := range paths {
|
||||
dirs[i] = filepath.Dir(path)
|
||||
}
|
||||
return dirs
|
||||
// DependencyInstallerMode is an enum describing how dependencies should be installed
|
||||
type DependencyInstallerMode int
|
||||
|
||||
const (
|
||||
// GoGetNoModules represents dependency installation using `go get` without modules
|
||||
GoGetNoModules DependencyInstallerMode = iota
|
||||
// GoGetWithModules represents dependency installation using `go get` with modules
|
||||
GoGetWithModules
|
||||
// Dep represent dependency installation using `dep ensure`
|
||||
Dep
|
||||
// Glide represents dependency installation using `glide install`
|
||||
Glide
|
||||
)
|
||||
|
||||
// Represents information about a `go.mod` file: this is at least the path to the `go.mod` file,
|
||||
// plus the parsed contents of the file, if available.
|
||||
type GoModule struct {
|
||||
Path string // The path to the `go.mod` file
|
||||
Module *modfile.File // The parsed contents of the `go.mod` file
|
||||
}
|
||||
|
||||
// Represents information about a Go project workspace: this may either be a folder containing
|
||||
// a `go.work` file or a collection of `go.mod` files.
|
||||
type GoWorkspace struct {
|
||||
BaseDir string // The base directory for this workspace
|
||||
WorkspaceFile *modfile.WorkFile // The `go.work` file for this workspace
|
||||
Modules []*GoModule // A list of `go.mod` files
|
||||
DepMode DependencyInstallerMode // A value indicating how to install dependencies for this workspace
|
||||
ModMode ModMode // A value indicating which module mode to use for this workspace
|
||||
Extracted bool // A value indicating whether this workspace was extracted successfully
|
||||
}
|
||||
|
||||
// Represents a nullable version string.
|
||||
type GoVersionInfo struct {
|
||||
// The version string, if any
|
||||
Version string
|
||||
// A value indicating whether a version string was found
|
||||
Found bool
|
||||
}
|
||||
|
||||
// Determines the version of Go that is required by this workspace. This is, in order of preference:
|
||||
// 1. The Go version specified in the `go.work` file, if any.
|
||||
// 2. The greatest Go version specified in any `go.mod` file, if any.
|
||||
func (workspace *GoWorkspace) RequiredGoVersion() GoVersionInfo {
|
||||
if workspace.WorkspaceFile != nil && workspace.WorkspaceFile.Go != nil {
|
||||
// If we have parsed a `go.work` file, return the version number from it.
|
||||
return GoVersionInfo{Version: workspace.WorkspaceFile.Go.Version, Found: true}
|
||||
} else if workspace.Modules != nil && len(workspace.Modules) > 0 {
|
||||
// Otherwise, if we have `go.work` files, find the greatest Go version in those.
|
||||
var greatestVersion string = ""
|
||||
for _, module := range workspace.Modules {
|
||||
if module.Module != nil && module.Module.Go != nil {
|
||||
// If we have parsed the file, retrieve the version number we have already obtained.
|
||||
if greatestVersion == "" || semver.Compare("v"+module.Module.Go.Version, "v"+greatestVersion) > 0 {
|
||||
greatestVersion = module.Module.Go.Version
|
||||
}
|
||||
} else {
|
||||
modVersion := tryReadGoDirective(module.Path)
|
||||
if modVersion.Found && (greatestVersion == "" || semver.Compare("v"+modVersion.Version, "v"+greatestVersion) > 0) {
|
||||
greatestVersion = modVersion.Version
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we have found some version, return it.
|
||||
if greatestVersion != "" {
|
||||
return GoVersionInfo{Version: greatestVersion, Found: true}
|
||||
}
|
||||
}
|
||||
|
||||
return GoVersionInfo{Version: "", Found: false}
|
||||
}
|
||||
|
||||
// Finds the greatest Go version required by any of the given `workspaces`.
|
||||
// Returns a `GoVersionInfo` value with `Found: false` if no version information is available.
|
||||
func RequiredGoVersion(workspaces *[]GoWorkspace) GoVersionInfo {
|
||||
greatestGoVersion := GoVersionInfo{Version: "", Found: false}
|
||||
for _, workspace := range *workspaces {
|
||||
goVersionInfo := workspace.RequiredGoVersion()
|
||||
if goVersionInfo.Found && (!greatestGoVersion.Found || semver.Compare("v"+goVersionInfo.Version, "v"+greatestGoVersion.Version) > 0) {
|
||||
greatestGoVersion = goVersionInfo
|
||||
}
|
||||
}
|
||||
return greatestGoVersion
|
||||
}
|
||||
|
||||
// Determines whether any of the directory paths in the input are nested.
|
||||
func checkDirsNested(inputDirs []string) (string, bool) {
|
||||
// replace "." with "" so that we can check if all the paths are nested
|
||||
dirs := make([]string, len(inputDirs))
|
||||
@@ -42,70 +124,353 @@ func checkDirsNested(inputDirs []string) (string, bool) {
|
||||
return dirs[0], true
|
||||
}
|
||||
|
||||
// Returns the directory to run the go build in and whether to use a go.mod
|
||||
// file.
|
||||
func findGoModFiles(emitDiagnostics bool) (baseDir string, useGoMod bool) {
|
||||
goModPaths := util.FindAllFilesWithName(".", "go.mod", "vendor")
|
||||
if len(goModPaths) == 0 {
|
||||
baseDir = "."
|
||||
useGoMod = false
|
||||
// A list of files we created that should be removed after we are done.
|
||||
var filesToRemove []string = []string{}
|
||||
|
||||
// Try to initialize a go.mod file for projects that do not already have one.
|
||||
func InitGoModForLegacyProject(path string) {
|
||||
log.Printf("Project appears to be a legacy Go project, attempting to initialize go.mod in %s\n", path)
|
||||
|
||||
modInit := toolchain.InitModule(path)
|
||||
|
||||
if !util.RunCmd(modInit) {
|
||||
log.Printf("Failed to initialize go.mod file for this project.")
|
||||
return
|
||||
}
|
||||
goModDirs := getDirs(goModPaths)
|
||||
if util.AnyGoFilesOutsideDirs(".", goModDirs...) {
|
||||
|
||||
// Add the go.mod file to a list of files we should remove later.
|
||||
filesToRemove = append(filesToRemove, filepath.Join(path, "go.mod"))
|
||||
|
||||
modTidy := toolchain.TidyModule(path)
|
||||
out, err := modTidy.CombinedOutput()
|
||||
log.Println(string(out))
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Failed to determine module requirements for this project.")
|
||||
}
|
||||
|
||||
if strings.Contains(string(out), "is relative, but relative import paths are not supported in module mode") {
|
||||
diagnostics.EmitRelativeImportPaths()
|
||||
}
|
||||
}
|
||||
|
||||
// Attempts to remove all files that we created.
|
||||
func RemoveTemporaryExtractorFiles() {
|
||||
for _, path := range filesToRemove {
|
||||
err := os.Remove(path)
|
||||
if err != nil {
|
||||
log.Printf("Unable to remove file we created at %s: %s\n", path, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
filesToRemove = []string{}
|
||||
}
|
||||
|
||||
// Find all go.work files in the working directory and its subdirectories
|
||||
func findGoWorkFiles() []string {
|
||||
return util.FindAllFilesWithName(".", "go.work", "vendor")
|
||||
}
|
||||
|
||||
// Find all go.mod files in the specified directory and its subdirectories
|
||||
func findGoModFiles(root string) []string {
|
||||
return util.FindAllFilesWithName(root, "go.mod", "vendor")
|
||||
}
|
||||
|
||||
// Given a list of `go.mod` file paths, try to parse them all. The resulting array of `GoModule` objects
|
||||
// will be the same length as the input array and the objects will contain at least the `go.mod` path.
|
||||
// If parsing the corresponding file is successful, then the parsed contents will also be available.
|
||||
func LoadGoModules(goModFilePaths []string) []*GoModule {
|
||||
results := make([]*GoModule, len(goModFilePaths))
|
||||
|
||||
for i, goModFilePath := range goModFilePaths {
|
||||
results[i] = new(GoModule)
|
||||
results[i].Path = goModFilePath
|
||||
|
||||
modFileSrc, err := os.ReadFile(goModFilePath)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Unable to read %s: %s.\n", goModFilePath, err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
modFile, err := modfile.ParseLax(goModFilePath, modFileSrc, nil)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Unable to parse %s: %s.\n", goModFilePath, err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
results[i].Module = modFile
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
// Given a path to a `go.work` file, this function attempts to parse the `go.work` file. If unsuccessful,
|
||||
// we attempt to discover `go.mod` files within subdirectories of the directory containing the `go.work`
|
||||
// file ourselves.
|
||||
func discoverWorkspace(workFilePath string) GoWorkspace {
|
||||
log.Printf("Loading %s...\n", workFilePath)
|
||||
baseDir := filepath.Dir(workFilePath)
|
||||
workFileSrc, err := os.ReadFile(workFilePath)
|
||||
|
||||
if err != nil {
|
||||
// We couldn't read the `go.work` file for some reason; let's try to find `go.mod` files ourselves
|
||||
log.Printf("Unable to read %s, falling back to finding `go.mod` files manually:\n%s\n", workFilePath, err.Error())
|
||||
return GoWorkspace{
|
||||
BaseDir: baseDir,
|
||||
Modules: LoadGoModules(findGoModFiles(baseDir)),
|
||||
DepMode: GoGetWithModules,
|
||||
ModMode: getModMode(GoGetWithModules, baseDir),
|
||||
}
|
||||
}
|
||||
|
||||
workFile, err := modfile.ParseWork(workFilePath, workFileSrc, nil)
|
||||
|
||||
if err != nil {
|
||||
// The `go.work` file couldn't be parsed for some reason; let's try to find `go.mod` files ourselves
|
||||
log.Printf("Unable to parse %s, falling back to finding `go.mod` files manually:\n%s\n", workFilePath, err.Error())
|
||||
return GoWorkspace{
|
||||
BaseDir: baseDir,
|
||||
Modules: LoadGoModules(findGoModFiles(baseDir)),
|
||||
DepMode: GoGetWithModules,
|
||||
ModMode: getModMode(GoGetWithModules, baseDir),
|
||||
}
|
||||
}
|
||||
|
||||
// Get the paths of all of the `go.mod` files that we read from the `go.work` file.
|
||||
goModFilePaths := make([]string, len(workFile.Use))
|
||||
|
||||
for i, use := range workFile.Use {
|
||||
if filepath.IsAbs(use.Path) {
|
||||
// TODO: This case might be problematic for some other logic (e.g. stray file detection)
|
||||
goModFilePaths[i] = filepath.Join(use.Path, "go.mod")
|
||||
} else {
|
||||
goModFilePaths[i] = filepath.Join(filepath.Dir(workFilePath), use.Path, "go.mod")
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("%s uses the following Go modules:\n%s\n", workFilePath, strings.Join(goModFilePaths, "\n"))
|
||||
|
||||
return GoWorkspace{
|
||||
BaseDir: baseDir,
|
||||
WorkspaceFile: workFile,
|
||||
Modules: LoadGoModules(goModFilePaths),
|
||||
DepMode: GoGetWithModules,
|
||||
ModMode: ModReadonly, // Workspaces only support "readonly"
|
||||
}
|
||||
}
|
||||
|
||||
// Analyse the working directory to discover workspaces.
|
||||
func discoverWorkspaces(emitDiagnostics bool) []GoWorkspace {
|
||||
// Try to find any `go.work` files which may exist in the working directory.
|
||||
goWorkFiles := findGoWorkFiles()
|
||||
|
||||
if len(goWorkFiles) == 0 {
|
||||
// There is no `go.work` file. Find all `go.mod` files in the working directory.
|
||||
log.Println("Found no go.work files in the workspace; looking for go.mod files...")
|
||||
|
||||
goModFiles := findGoModFiles(".")
|
||||
|
||||
// Return a separate workspace for each `go.mod` file that we found.
|
||||
results := make([]GoWorkspace, len(goModFiles))
|
||||
|
||||
for i, goModFile := range goModFiles {
|
||||
results[i] = GoWorkspace{
|
||||
BaseDir: filepath.Dir(goModFile),
|
||||
Modules: LoadGoModules([]string{goModFile}),
|
||||
DepMode: GoGetWithModules,
|
||||
ModMode: getModMode(GoGetWithModules, filepath.Dir(goModFile)),
|
||||
}
|
||||
}
|
||||
|
||||
return results
|
||||
} else {
|
||||
// We have found `go.work` files, try to load them all.
|
||||
log.Printf("Found go.work file(s) in: %s.\n", strings.Join(goWorkFiles, ", "))
|
||||
|
||||
if emitDiagnostics {
|
||||
diagnostics.EmitGoWorkFound(goWorkFiles)
|
||||
}
|
||||
|
||||
results := make([]GoWorkspace, len(goWorkFiles))
|
||||
for i, workFilePath := range goWorkFiles {
|
||||
results[i] = discoverWorkspace(workFilePath)
|
||||
}
|
||||
|
||||
// Add all stray `go.mod` files (i.e. those not referenced by `go.work` files)
|
||||
// as separate workspaces.
|
||||
goModFiles := findGoModFiles(".")
|
||||
|
||||
for _, goModFile := range goModFiles {
|
||||
// Check to see whether we already have this module file under an existing workspace.
|
||||
found := false
|
||||
for _, workspace := range results {
|
||||
if workspace.Modules == nil {
|
||||
break
|
||||
}
|
||||
|
||||
for _, module := range workspace.Modules {
|
||||
if module.Path == goModFile {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if found {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// If not, add it to the array.
|
||||
if !found {
|
||||
log.Printf("Module %s is not referenced by any go.work file; adding it separately.\n", goModFile)
|
||||
results = append(results, GoWorkspace{
|
||||
BaseDir: filepath.Dir(goModFile),
|
||||
Modules: LoadGoModules([]string{goModFile}),
|
||||
DepMode: GoGetWithModules,
|
||||
ModMode: getModMode(GoGetWithModules, filepath.Dir(goModFile)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
}
|
||||
|
||||
// Discovers Go workspaces in the current working directory.
|
||||
// Returns an array of Go workspaces and the total number of module files which we discovered.
|
||||
func getBuildRoots(emitDiagnostics bool) (goWorkspaces []GoWorkspace, totalModuleFiles int) {
|
||||
goWorkspaces = discoverWorkspaces(emitDiagnostics)
|
||||
|
||||
// Determine the total number of `go.mod` files that we discovered.
|
||||
totalModuleFiles = 0
|
||||
|
||||
for _, goWorkspace := range goWorkspaces {
|
||||
totalModuleFiles += len(goWorkspace.Modules)
|
||||
}
|
||||
|
||||
// If there are no `go.mod` files at all, create one in line with https://go.dev/blog/migrating-to-go-modules
|
||||
if totalModuleFiles == 0 {
|
||||
// Check for other, legacy package managers
|
||||
if util.FileExists("Gopkg.toml") {
|
||||
if emitDiagnostics {
|
||||
diagnostics.EmitGopkgTomlFound()
|
||||
}
|
||||
log.Println("Found Gopkg.toml, using dep instead of go get")
|
||||
goWorkspaces = []GoWorkspace{{
|
||||
BaseDir: ".",
|
||||
DepMode: Dep,
|
||||
ModMode: ModUnset,
|
||||
}}
|
||||
totalModuleFiles = 0
|
||||
return
|
||||
}
|
||||
|
||||
if util.FileExists("glide.yaml") {
|
||||
if emitDiagnostics {
|
||||
diagnostics.EmitGlideYamlFound()
|
||||
}
|
||||
log.Println("Found glide.yaml, using Glide instead of go get")
|
||||
goWorkspaces = []GoWorkspace{{
|
||||
BaseDir: ".",
|
||||
DepMode: Glide,
|
||||
ModMode: ModUnset,
|
||||
}}
|
||||
totalModuleFiles = 0
|
||||
return
|
||||
}
|
||||
|
||||
// If we have no `go.mod` files, then the project appears to be a legacy project without
|
||||
// a `go.mod` file. Check that there are actually Go source files before initializing a module
|
||||
// so that we correctly fail the extraction later.
|
||||
if !util.FindGoFiles(".") {
|
||||
goWorkspaces = []GoWorkspace{{
|
||||
BaseDir: ".",
|
||||
DepMode: GoGetNoModules,
|
||||
ModMode: ModUnset,
|
||||
}}
|
||||
totalModuleFiles = 0
|
||||
return
|
||||
}
|
||||
|
||||
goWorkspaces = []GoWorkspace{{
|
||||
BaseDir: ".",
|
||||
DepMode: GoGetNoModules,
|
||||
ModMode: getModMode(GoGetWithModules, "."),
|
||||
}}
|
||||
totalModuleFiles = 0
|
||||
return
|
||||
}
|
||||
|
||||
// Get the paths to all `go.mod` files
|
||||
i := 0
|
||||
goModPaths := make([]string, totalModuleFiles)
|
||||
|
||||
for _, goWorkspace := range goWorkspaces {
|
||||
for _, goModule := range goWorkspace.Modules {
|
||||
goModPaths[i] = goModule.Path
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
goModDirs := util.GetParentDirs(goModPaths)
|
||||
straySourceFiles := util.GoFilesOutsideDirs(".", goModDirs...)
|
||||
if len(straySourceFiles) > 0 {
|
||||
if emitDiagnostics {
|
||||
diagnostics.EmitGoFilesOutsideGoModules(goModPaths)
|
||||
}
|
||||
baseDir = "."
|
||||
useGoMod = false
|
||||
|
||||
// We need to initialise Go modules for the stray source files. Our goal is to initialise
|
||||
// as few Go modules as possible, in locations which do not overlap with existing Go
|
||||
// modules.
|
||||
for _, straySourceFile := range straySourceFiles {
|
||||
path := "."
|
||||
components := strings.Split(filepath.Dir(straySourceFile), string(os.PathSeparator))
|
||||
|
||||
for _, component := range components {
|
||||
path = filepath.Join(path, component)
|
||||
|
||||
// Try to initialize a `go.mod` file automatically for the stray source files.
|
||||
if !slices.Contains(goModDirs, path) {
|
||||
goWorkspaces = append(goWorkspaces, GoWorkspace{
|
||||
BaseDir: path,
|
||||
DepMode: GoGetNoModules,
|
||||
ModMode: ModUnset,
|
||||
})
|
||||
goModDirs = append(goModDirs, path)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
if len(goModPaths) > 1 {
|
||||
// currently not supported
|
||||
baseDir = "."
|
||||
commonRoot, nested := checkDirsNested(goModDirs)
|
||||
if nested && commonRoot == "" {
|
||||
useGoMod = true
|
||||
} else {
|
||||
useGoMod = false
|
||||
}
|
||||
if emitDiagnostics {
|
||||
|
||||
// If we are emitted diagnostics, report some details about the workspace structure.
|
||||
if emitDiagnostics {
|
||||
if totalModuleFiles > 1 {
|
||||
_, nested := checkDirsNested(goModDirs)
|
||||
|
||||
if nested {
|
||||
diagnostics.EmitMultipleGoModFoundNested(goModPaths)
|
||||
} else {
|
||||
diagnostics.EmitMultipleGoModFoundNotNested(goModPaths)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
if emitDiagnostics {
|
||||
if goModDirs[0] == "." {
|
||||
diagnostics.EmitSingleRootGoModFound(goModPaths[0])
|
||||
} else {
|
||||
diagnostics.EmitSingleNonRootGoModFound(goModPaths[0])
|
||||
} else if totalModuleFiles == 1 {
|
||||
if goModDirs[0] == "." {
|
||||
diagnostics.EmitSingleRootGoModFound(goModPaths[0])
|
||||
} else {
|
||||
diagnostics.EmitSingleNonRootGoModFound(goModPaths[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
baseDir = goModDirs[0]
|
||||
useGoMod = true
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// DependencyInstallerMode is an enum describing how dependencies should be installed
|
||||
type DependencyInstallerMode int
|
||||
|
||||
const (
|
||||
// GoGetNoModules represents dependency installation using `go get` without modules
|
||||
GoGetNoModules DependencyInstallerMode = iota
|
||||
// GoGetWithModules represents dependency installation using `go get` with modules
|
||||
GoGetWithModules
|
||||
// Dep represent dependency installation using `dep ensure`
|
||||
Dep
|
||||
// Glide represents dependency installation using `glide install`
|
||||
Glide
|
||||
)
|
||||
|
||||
// Returns the appropriate DependencyInstallerMode for the current project
|
||||
func getDepMode(emitDiagnostics bool) (DependencyInstallerMode, string) {
|
||||
// Finds Go workspaces in the current working directory.
|
||||
func GetWorkspaceInfo(emitDiagnostics bool) []GoWorkspace {
|
||||
bazelPaths := util.FindAllFilesWithName(".", "BUILD", "vendor")
|
||||
bazelPaths = append(bazelPaths, util.FindAllFilesWithName(".", "BUILD.bazel", "vendor")...)
|
||||
if len(bazelPaths) > 0 {
|
||||
@@ -115,36 +480,10 @@ func getDepMode(emitDiagnostics bool) (DependencyInstallerMode, string) {
|
||||
}
|
||||
}
|
||||
|
||||
goWorkPaths := util.FindAllFilesWithName(".", "go.work", "vendor")
|
||||
if len(goWorkPaths) > 0 {
|
||||
// currently not supported
|
||||
if emitDiagnostics {
|
||||
diagnostics.EmitGoWorkFound(goWorkPaths)
|
||||
}
|
||||
}
|
||||
goWorkspaces, totalModuleFiles := getBuildRoots(emitDiagnostics)
|
||||
log.Printf("Found %d go.mod file(s).\n", totalModuleFiles)
|
||||
|
||||
baseDir, useGoMod := findGoModFiles(emitDiagnostics)
|
||||
if useGoMod {
|
||||
log.Println("Found go.mod, enabling go modules")
|
||||
return GoGetWithModules, baseDir
|
||||
}
|
||||
|
||||
if util.FileExists("Gopkg.toml") {
|
||||
if emitDiagnostics {
|
||||
diagnostics.EmitGopkgTomlFound()
|
||||
}
|
||||
log.Println("Found Gopkg.toml, using dep instead of go get")
|
||||
return Dep, "."
|
||||
}
|
||||
|
||||
if util.FileExists("glide.yaml") {
|
||||
if emitDiagnostics {
|
||||
diagnostics.EmitGlideYamlFound()
|
||||
}
|
||||
log.Println("Found glide.yaml, using Glide instead of go get")
|
||||
return Glide, "."
|
||||
}
|
||||
return GoGetNoModules, "."
|
||||
return goWorkspaces
|
||||
}
|
||||
|
||||
// ModMode corresponds to the possible values of the -mod flag for the Go compiler
|
||||
@@ -194,38 +533,18 @@ func getModMode(depMode DependencyInstallerMode, baseDir string) ModMode {
|
||||
return ModUnset
|
||||
}
|
||||
|
||||
type BuildInfo struct {
|
||||
DepMode DependencyInstallerMode
|
||||
ModMode ModMode
|
||||
BaseDir string
|
||||
}
|
||||
|
||||
func GetBuildInfo(emitDiagnostics bool) BuildInfo {
|
||||
depMode, baseDir := getDepMode(true)
|
||||
modMode := getModMode(depMode, baseDir)
|
||||
return BuildInfo{depMode, modMode, baseDir}
|
||||
}
|
||||
|
||||
type GoVersionInfo struct {
|
||||
// The version string, if any
|
||||
Version string
|
||||
// A value indicating whether a version string was found
|
||||
Found bool
|
||||
}
|
||||
|
||||
// Tries to open `go.mod` and read a go directive, returning the version and whether it was found.
|
||||
func TryReadGoDirective(buildInfo BuildInfo) GoVersionInfo {
|
||||
if buildInfo.DepMode == GoGetWithModules {
|
||||
versionRe := regexp.MustCompile(`(?m)^go[ \t\r]+([0-9]+\.[0-9]+(\.[0-9]+)?)$`)
|
||||
goMod, err := os.ReadFile(filepath.Join(buildInfo.BaseDir, "go.mod"))
|
||||
if err != nil {
|
||||
log.Println("Failed to read go.mod to check for missing Go version")
|
||||
} else {
|
||||
matches := versionRe.FindSubmatch(goMod)
|
||||
if matches != nil {
|
||||
if len(matches) > 1 {
|
||||
return GoVersionInfo{string(matches[1]), true}
|
||||
}
|
||||
// The version string is returned in the "1.2.3" format.
|
||||
func tryReadGoDirective(path string) GoVersionInfo {
|
||||
versionRe := regexp.MustCompile(`(?m)^go[ \t\r]+([0-9]+\.[0-9]+(\.[0-9]+)?)$`)
|
||||
goMod, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
log.Println("Failed to read go.mod to check for missing Go version")
|
||||
} else {
|
||||
matches := versionRe.FindSubmatch(goMod)
|
||||
if matches != nil {
|
||||
if len(matches) > 1 {
|
||||
return GoVersionInfo{string(matches[1]), true}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/mod/semver"
|
||||
)
|
||||
|
||||
// Check if Go is installed in the environment.
|
||||
@@ -36,6 +38,23 @@ func GetEnvGoVersion() string {
|
||||
return goVersion
|
||||
}
|
||||
|
||||
// Returns the current Go version in semver format, e.g. v1.14.4
|
||||
func GetEnvGoSemVer() string {
|
||||
goVersion := GetEnvGoVersion()
|
||||
if !strings.HasPrefix(goVersion, "go") {
|
||||
log.Fatalf("Expected 'go version' output of the form 'go1.2.3'; got '%s'", goVersion)
|
||||
}
|
||||
// Go versions don't follow the SemVer format, but the only exception we normally care about
|
||||
// is release candidates; so this is a horrible hack to convert e.g. `go1.22rc1` into `go1.22-rc1`
|
||||
// which is compatible with the SemVer specification
|
||||
rcIndex := strings.Index(goVersion, "rc")
|
||||
if rcIndex != -1 {
|
||||
return semver.Canonical("v"+goVersion[2:rcIndex]) + "-" + goVersion[rcIndex:]
|
||||
} else {
|
||||
return semver.Canonical("v" + goVersion[2:])
|
||||
}
|
||||
}
|
||||
|
||||
// The 'go version' command may output warnings on separate lines before
|
||||
// the actual version string is printed. This function parses the output
|
||||
// to retrieve just the version string.
|
||||
@@ -47,3 +66,29 @@ func parseGoVersion(data string) string {
|
||||
}
|
||||
return strings.Fields(lastLine)[2]
|
||||
}
|
||||
|
||||
// Returns a value indicating whether the system Go toolchain supports workspaces.
|
||||
func SupportsWorkspaces() bool {
|
||||
return semver.Compare(GetEnvGoSemVer(), "v1.18.0") >= 0
|
||||
}
|
||||
|
||||
// Run `go mod tidy -e` in the directory given by `path`.
|
||||
func TidyModule(path string) *exec.Cmd {
|
||||
cmd := exec.Command("go", "mod", "tidy", "-e")
|
||||
cmd.Dir = path
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Run `go mod init` in the directory given by `path`.
|
||||
func InitModule(path string) *exec.Cmd {
|
||||
modInit := exec.Command("go", "mod", "init", "codeql/auto-project")
|
||||
modInit.Dir = path
|
||||
return modInit
|
||||
}
|
||||
|
||||
// Constructs a command to run `go mod vendor -e` in the directory given by `path`.
|
||||
func VendorModule(path string) *exec.Cmd {
|
||||
modVendor := exec.Command("go", "mod", "vendor", "-e")
|
||||
modVendor.Dir = path
|
||||
return modVendor
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"slices"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -204,13 +205,13 @@ func RunCmd(cmd *exec.Cmd) bool {
|
||||
in, _ := cmd.StdinPipe()
|
||||
err := cmd.Start()
|
||||
if err != nil {
|
||||
log.Printf("Running %s failed, continuing anyway: %s\n", cmd.Path, err.Error())
|
||||
log.Printf("Running %s %v failed, continuing anyway: %s\n", cmd.Path, cmd.Args, err.Error())
|
||||
return false
|
||||
}
|
||||
in.Close()
|
||||
err = cmd.Wait()
|
||||
if err != nil {
|
||||
log.Printf("Running %s failed, continuing anyway: %s\n", cmd.Path, err.Error())
|
||||
log.Printf("Running %s %v failed, continuing anyway: %s\n", cmd.Path, cmd.Args, err.Error())
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -319,18 +320,16 @@ func FindAllFilesWithName(root string, name string, dirsToSkip ...string) []stri
|
||||
return paths
|
||||
}
|
||||
|
||||
// Determines whether there are any Go source files in locations which do not have a Go.mod
|
||||
// file in the same directory or higher up in the file hierarchy, relative to the `root`.
|
||||
func AnyGoFilesOutsideDirs(root string, dirsToSkip ...string) bool {
|
||||
found := false
|
||||
filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if d.IsDir() {
|
||||
for _, dirToSkip := range dirsToSkip {
|
||||
if path == dirToSkip {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
}
|
||||
if d.IsDir() && slices.Contains(dirsToSkip, path) {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
if filepath.Ext(d.Name()) == ".go" {
|
||||
found = true
|
||||
@@ -340,3 +339,34 @@ func AnyGoFilesOutsideDirs(root string, dirsToSkip ...string) bool {
|
||||
})
|
||||
return found
|
||||
}
|
||||
|
||||
// Returns an array of any Go source files in locations which do not have a Go.mod
|
||||
// file in the same directory or higher up in the file hierarchy, relative to the `root`.
|
||||
func GoFilesOutsideDirs(root string, dirsToSkip ...string) []string {
|
||||
result := []string{}
|
||||
|
||||
filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if d.IsDir() && slices.Contains(dirsToSkip, path) {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
if filepath.Ext(d.Name()) == ".go" {
|
||||
log.Printf("Found stray Go source file in %s.\n", path)
|
||||
result = append(result, path)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// For every file path in the input array, return the parent directory.
|
||||
func GetParentDirs(paths []string) []string {
|
||||
dirs := make([]string, len(paths))
|
||||
for i, path := range paths {
|
||||
dirs[i] = filepath.Dir(path)
|
||||
}
|
||||
return dirs
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
extractedFiles
|
||||
| work/subdir/go.mod:0:0:0:0 | work/subdir/go.mod |
|
||||
| work/subdir/test.go:0:0:0:0 | work/subdir/test.go |
|
||||
#select
|
||||
@@ -10,7 +10,7 @@ from diagnostics_test_utils import *
|
||||
goPath = os.path.join(os.path.abspath(os.getcwd()), ".go")
|
||||
os.environ['GOPATH'] = goPath
|
||||
os.environ['LGTM_INDEX_IMPORT_PATH'] = "test"
|
||||
run_codeql_database_create([], lang="go", source="work", db=None, runFunction=runUnsuccessfully)
|
||||
run_codeql_database_create([], lang="go", source="work")
|
||||
|
||||
check_diagnostics()
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import go
|
||||
import semmle.go.DiagnosticsReporting
|
||||
|
||||
query predicate extractedFiles(File f) { any() }
|
||||
|
||||
from string msg, int sev
|
||||
where reportableDiagnostics(_, msg, sev)
|
||||
select msg, sev
|
||||
@@ -10,7 +10,7 @@ from diagnostics_test_utils import *
|
||||
goPath = os.path.join(os.path.abspath(os.getcwd()), ".go")
|
||||
os.environ['GOPATH'] = goPath
|
||||
os.environ['GITHUB_REPOSITORY'] = "a/b"
|
||||
run_codeql_database_create([], lang="go", source="work", db=None, runFunction=runUnsuccessfully)
|
||||
run_codeql_database_create([], lang="go", source="work", db=None)
|
||||
|
||||
check_diagnostics()
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"markdownMessage": "A single `go.mod` file was found.\n\n`go.mod`",
|
||||
"severity": "note",
|
||||
"source": {
|
||||
"extractorName": "go",
|
||||
"id": "go/autobuilder/single-root-go-mod-found",
|
||||
"name": "A single `go.mod` file was found in the root"
|
||||
},
|
||||
"visibility": {
|
||||
"cliSummaryTable": false,
|
||||
"statusPage": false,
|
||||
"telemetry": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
# go get has been observed to sometimes fail when multiple tests try to simultaneously fetch the same package.
|
||||
goget
|
||||
@@ -0,0 +1,3 @@
|
||||
require golang.org/x/net v0.0.0-20200505041828-1ed23360d12c
|
||||
|
||||
module test
|
||||
@@ -0,0 +1,7 @@
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/net v0.0.0-20200505041828-1ed23360d12c h1:zJ0mtu4jCalhKg6Oaukv6iIkb+cOvDrajDH9DH46Q4M=
|
||||
golang.org/x/net v0.0.0-20200505041828-1ed23360d12c/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -0,0 +1,5 @@
|
||||
package subdir
|
||||
|
||||
func Add(a, b int) int {
|
||||
return a + b
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"test/subdir"
|
||||
|
||||
"golang.org/x/net/ipv4"
|
||||
)
|
||||
|
||||
func test() {
|
||||
|
||||
header := ipv4.Header{}
|
||||
header.Version = subdir.Add(2, 2)
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
extractedFiles
|
||||
| src/go.mod:0:0:0:0 | src/go.mod |
|
||||
| src/subdir/add.go:0:0:0:0 | src/subdir/add.go |
|
||||
| src/test.go:0:0:0:0 | src/test.go |
|
||||
#select
|
||||
@@ -0,0 +1,18 @@
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
from create_database_utils import *
|
||||
from diagnostics_test_utils import *
|
||||
|
||||
# Set up a GOPATH relative to this test's root directory;
|
||||
# we set os.environ instead of using extra_env because we
|
||||
# need it to be set for the call to "go clean -modcache" later
|
||||
goPath = os.path.join(os.path.abspath(os.getcwd()), ".go")
|
||||
os.environ['GOPATH'] = goPath
|
||||
run_codeql_database_create([], lang="go", source="src")
|
||||
|
||||
check_diagnostics()
|
||||
|
||||
# Clean up the temporary GOPATH to prevent Bazel failures next
|
||||
# time the tests are run; see https://github.com/golang/go/issues/27161
|
||||
subprocess.call(["go", "clean", "-modcache"])
|
||||
@@ -0,0 +1,8 @@
|
||||
import go
|
||||
import semmle.go.DiagnosticsReporting
|
||||
|
||||
query predicate extractedFiles(File f) { any() }
|
||||
|
||||
from string msg, int sev
|
||||
where reportableDiagnostics(_, msg, sev)
|
||||
select msg, sev
|
||||
@@ -1,17 +1,3 @@
|
||||
{
|
||||
"markdownMessage": "1 package could not be found:\n\n`subdir/subsubdir`.\n\nDefinitions in those packages may not be recognized by CodeQL, and files that use them may only be partially analyzed.\n\nCheck that the paths are correct and make sure any private packages can be accessed. If any of the packages are present in the repository then you may need a [custom build command](https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-the-codeql-workflow-for-compiled-languages).",
|
||||
"severity": "warning",
|
||||
"source": {
|
||||
"extractorName": "go",
|
||||
"id": "go/autobuilder/package-not-found",
|
||||
"name": "Some packages could not be found"
|
||||
},
|
||||
"visibility": {
|
||||
"cliSummaryTable": true,
|
||||
"statusPage": true,
|
||||
"telemetry": true
|
||||
}
|
||||
}
|
||||
{
|
||||
"markdownMessage": "Go files were found outside of the Go modules corresponding to these `go.mod` files.\n\n`subdir/go.mod`",
|
||||
"severity": "note",
|
||||
|
||||
@@ -1,2 +1,7 @@
|
||||
| Extraction failed in subdir/test.go with error cannot find package "subdir/subsubdir" in any of:\n\t(absolute path) (from $GOROOT)\n\t(absolute path) (from $GOPATH) | 2 |
|
||||
| Extraction failed in subdir/test.go with error could not import subdir/subsubdir (invalid package name: "") | 2 |
|
||||
extractedFiles
|
||||
| src/go.mod:0:0:0:0 | src/go.mod |
|
||||
| src/main.go:0:0:0:0 | src/main.go |
|
||||
| src/subdir/go.mod:0:0:0:0 | src/subdir/go.mod |
|
||||
| src/subdir/subsubdir/add.go:0:0:0:0 | src/subdir/subsubdir/add.go |
|
||||
| src/subdir/test.go:0:0:0:0 | src/subdir/test.go |
|
||||
#select
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import go
|
||||
import semmle.go.DiagnosticsReporting
|
||||
|
||||
query predicate extractedFiles(File f) { any() }
|
||||
|
||||
from string msg, int sev
|
||||
where reportableDiagnostics(_, msg, sev)
|
||||
select msg, sev
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
extractedFiles
|
||||
| src/go.mod:0:0:0:0 | src/go.mod |
|
||||
| src/subdir/add.go:0:0:0:0 | src/subdir/add.go |
|
||||
| src/test.go:0:0:0:0 | src/test.go |
|
||||
#select
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import go
|
||||
import semmle.go.DiagnosticsReporting
|
||||
|
||||
query predicate extractedFiles(File f) { any() }
|
||||
|
||||
from string msg, int sev
|
||||
where reportableDiagnostics(_, msg, sev)
|
||||
select msg, sev
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
extractedFiles
|
||||
| src/subdir/go.mod:0:0:0:0 | src/subdir/go.mod |
|
||||
| src/subdir/subsubdir/add.go:0:0:0:0 | src/subdir/subsubdir/add.go |
|
||||
| src/subdir/test.go:0:0:0:0 | src/subdir/test.go |
|
||||
#select
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import go
|
||||
import semmle.go.DiagnosticsReporting
|
||||
|
||||
query predicate extractedFiles(File f) { any() }
|
||||
|
||||
from string msg, int sev
|
||||
where reportableDiagnostics(_, msg, sev)
|
||||
select msg, sev
|
||||
|
||||
@@ -26,17 +26,3 @@
|
||||
"telemetry": true
|
||||
}
|
||||
}
|
||||
{
|
||||
"markdownMessage": "2 packages could not be found:\n\n`subdir1/subsubdir1`, `subdir2/subsubdir2`.\n\nDefinitions in those packages may not be recognized by CodeQL, and files that use them may only be partially analyzed.\n\nCheck that the paths are correct and make sure any private packages can be accessed. If any of the packages are present in the repository then you may need a [custom build command](https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-the-codeql-workflow-for-compiled-languages).",
|
||||
"severity": "warning",
|
||||
"source": {
|
||||
"extractorName": "go",
|
||||
"id": "go/autobuilder/package-not-found",
|
||||
"name": "Some packages could not be found"
|
||||
},
|
||||
"visibility": {
|
||||
"cliSummaryTable": true,
|
||||
"statusPage": true,
|
||||
"telemetry": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
| Extraction failed in modules/subdir1/test.go with error cannot find package "subdir1/subsubdir1" in any of:\n\t(absolute path) (from $GOROOT)\n\t(absolute path) (from $GOPATH) | 2 |
|
||||
| Extraction failed in modules/subdir1/test.go with error could not import subdir1/subsubdir1 (invalid package name: "") | 2 |
|
||||
| Extraction failed in modules/subdir2/test.go with error cannot find package "subdir2/subsubdir2" in any of:\n\t(absolute path) (from $GOROOT)\n\t(absolute path) (from $GOPATH) | 2 |
|
||||
| Extraction failed in modules/subdir2/test.go with error could not import subdir2/subsubdir2 (invalid package name: "") | 2 |
|
||||
extractedFiles
|
||||
| src/modules/subdir1/go.mod:0:0:0:0 | src/modules/subdir1/go.mod |
|
||||
| src/modules/subdir1/subsubdir1/add.go:0:0:0:0 | src/modules/subdir1/subsubdir1/add.go |
|
||||
| src/modules/subdir1/test.go:0:0:0:0 | src/modules/subdir1/test.go |
|
||||
| src/modules/subdir2/go.mod:0:0:0:0 | src/modules/subdir2/go.mod |
|
||||
| src/modules/subdir2/subsubdir2/add.go:0:0:0:0 | src/modules/subdir2/subsubdir2/add.go |
|
||||
| src/modules/subdir2/test.go:0:0:0:0 | src/modules/subdir2/test.go |
|
||||
#select
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import go
|
||||
import semmle.go.DiagnosticsReporting
|
||||
|
||||
query predicate extractedFiles(File f) { any() }
|
||||
|
||||
from string msg, int sev
|
||||
where reportableDiagnostics(_, msg, sev)
|
||||
select msg, sev
|
||||
|
||||
@@ -12,17 +12,3 @@
|
||||
"telemetry": true
|
||||
}
|
||||
}
|
||||
{
|
||||
"markdownMessage": "2 packages could not be found:\n\n`test/subdir2`, `subdir1/subsubdir1`.\n\nDefinitions in those packages may not be recognized by CodeQL, and files that use them may only be partially analyzed.\n\nCheck that the paths are correct and make sure any private packages can be accessed. If any of the packages are present in the repository then you may need a [custom build command](https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-the-codeql-workflow-for-compiled-languages).",
|
||||
"severity": "warning",
|
||||
"source": {
|
||||
"extractorName": "go",
|
||||
"id": "go/autobuilder/package-not-found",
|
||||
"name": "Some packages could not be found"
|
||||
},
|
||||
"visibility": {
|
||||
"cliSummaryTable": true,
|
||||
"statusPage": true,
|
||||
"telemetry": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
| Extraction failed in subdir0/subdir1/test.go with error cannot find package "subdir1/subsubdir1" in any of:\n\t(absolute path) (from $GOROOT)\n\t(absolute path) (from $GOPATH) | 2 |
|
||||
| Extraction failed in subdir0/subdir1/test.go with error could not import subdir1/subsubdir1 (invalid package name: "") | 2 |
|
||||
| Extraction failed in subdir0/test.go with error cannot find package "test/subdir2" in any of:\n\t(absolute path) (from $GOROOT)\n\t(absolute path) (from $GOPATH) | 2 |
|
||||
| Extraction failed in subdir0/test.go with error could not import test/subdir2 (invalid package name: "") | 2 |
|
||||
extractedFiles
|
||||
| src/subdir0/go.mod:0:0:0:0 | src/subdir0/go.mod |
|
||||
| src/subdir0/subdir1/go.mod:0:0:0:0 | src/subdir0/subdir1/go.mod |
|
||||
| src/subdir0/subdir1/subsubdir1/add.go:0:0:0:0 | src/subdir0/subdir1/subsubdir1/add.go |
|
||||
| src/subdir0/subdir1/test.go:0:0:0:0 | src/subdir0/subdir1/test.go |
|
||||
| src/subdir0/subdir2/add.go:0:0:0:0 | src/subdir0/subdir2/add.go |
|
||||
| src/subdir0/test.go:0:0:0:0 | src/subdir0/test.go |
|
||||
#select
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import go
|
||||
import semmle.go.DiagnosticsReporting
|
||||
|
||||
query predicate extractedFiles(File f) { any() }
|
||||
|
||||
from string msg, int sev
|
||||
where reportableDiagnostics(_, msg, sev)
|
||||
select msg, sev
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
extractedFiles
|
||||
| src/go.mod:0:0:0:0 | src/go.mod |
|
||||
| src/subdir1/go.mod:0:0:0:0 | src/subdir1/go.mod |
|
||||
| src/subdir1/subsubdir1/add.go:0:0:0:0 | src/subdir1/subsubdir1/add.go |
|
||||
| src/subdir1/test.go:0:0:0:0 | src/subdir1/test.go |
|
||||
| src/subdir2/add.go:0:0:0:0 | src/subdir2/add.go |
|
||||
| src/test.go:0:0:0:0 | src/test.go |
|
||||
#select
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import go
|
||||
import semmle.go.DiagnosticsReporting
|
||||
|
||||
query predicate extractedFiles(File f) { any() }
|
||||
|
||||
from string msg, int sev
|
||||
where reportableDiagnostics(_, msg, sev)
|
||||
select msg, sev
|
||||
|
||||
@@ -12,17 +12,3 @@
|
||||
"telemetry": true
|
||||
}
|
||||
}
|
||||
{
|
||||
"markdownMessage": "2 packages could not be found:\n\n`subdir1/subsubdir1`, `subdir2/subsubdir2`.\n\nDefinitions in those packages may not be recognized by CodeQL, and files that use them may only be partially analyzed.\n\nCheck that the paths are correct and make sure any private packages can be accessed. If any of the packages are present in the repository then you may need a [custom build command](https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-the-codeql-workflow-for-compiled-languages).",
|
||||
"severity": "warning",
|
||||
"source": {
|
||||
"extractorName": "go",
|
||||
"id": "go/autobuilder/package-not-found",
|
||||
"name": "Some packages could not be found"
|
||||
},
|
||||
"visibility": {
|
||||
"cliSummaryTable": true,
|
||||
"statusPage": true,
|
||||
"telemetry": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
| Extraction failed in subdir1/test.go with error cannot find package "subdir1/subsubdir1" in any of:\n\t(absolute path) (from $GOROOT)\n\t(absolute path) (from $GOPATH) | 2 |
|
||||
| Extraction failed in subdir1/test.go with error could not import subdir1/subsubdir1 (invalid package name: "") | 2 |
|
||||
| Extraction failed in subdir2/test.go with error cannot find package "subdir2/subsubdir2" in any of:\n\t(absolute path) (from $GOROOT)\n\t(absolute path) (from $GOPATH) | 2 |
|
||||
| Extraction failed in subdir2/test.go with error could not import subdir2/subsubdir2 (invalid package name: "") | 2 |
|
||||
extractedFiles
|
||||
| src/subdir1/go.mod:0:0:0:0 | src/subdir1/go.mod |
|
||||
| src/subdir1/subsubdir1/add.go:0:0:0:0 | src/subdir1/subsubdir1/add.go |
|
||||
| src/subdir1/test.go:0:0:0:0 | src/subdir1/test.go |
|
||||
| src/subdir2/go.mod:0:0:0:0 | src/subdir2/go.mod |
|
||||
| src/subdir2/subsubdir2/add.go:0:0:0:0 | src/subdir2/subsubdir2/add.go |
|
||||
| src/subdir2/test.go:0:0:0:0 | src/subdir2/test.go |
|
||||
#select
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import go
|
||||
import semmle.go.DiagnosticsReporting
|
||||
|
||||
query predicate extractedFiles(File f) { any() }
|
||||
|
||||
from string msg, int sev
|
||||
where reportableDiagnostics(_, msg, sev)
|
||||
select msg, sev
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"markdownMessage": "2 `go.mod` files were found:\n\n`subdir1/go.mod`, `subdir2/go.mod`",
|
||||
"severity": "note",
|
||||
"source": {
|
||||
"extractorName": "go",
|
||||
"id": "go/autobuilder/multiple-go-mod-found-not-nested",
|
||||
"name": "Multiple `go.mod` files found, not all nested under one root `go.mod` file"
|
||||
},
|
||||
"visibility": {
|
||||
"cliSummaryTable": false,
|
||||
"statusPage": false,
|
||||
"telemetry": true
|
||||
}
|
||||
}
|
||||
{
|
||||
"markdownMessage": "The following 1 Go project could not be extracted successfully:\n\n`subdir2`\n",
|
||||
"severity": "warning",
|
||||
"source": {
|
||||
"extractorName": "go",
|
||||
"id": "go/autobuilder/extraction-failed-for-project",
|
||||
"name": "Unable to extract 1 Go projects"
|
||||
},
|
||||
"visibility": {
|
||||
"cliSummaryTable": true,
|
||||
"statusPage": true,
|
||||
"telemetry": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
# go get has been observed to sometimes fail when multiple tests try to simultaneously fetch the same package.
|
||||
goget
|
||||
@@ -0,0 +1,5 @@
|
||||
go 1.14
|
||||
|
||||
require golang.org/x/net v0.0.0-20200505041828-1ed23360d12c
|
||||
|
||||
module subdir1
|
||||
@@ -0,0 +1,7 @@
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/net v0.0.0-20200505041828-1ed23360d12c h1:zJ0mtu4jCalhKg6Oaukv6iIkb+cOvDrajDH9DH46Q4M=
|
||||
golang.org/x/net v0.0.0-20200505041828-1ed23360d12c/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -0,0 +1,5 @@
|
||||
package subsubdir1
|
||||
|
||||
func Add(a, b int) int {
|
||||
return a + b
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package subdir
|
||||
|
||||
import (
|
||||
"subdir1/subsubdir1"
|
||||
|
||||
"golang.org/x/net/ipv4"
|
||||
)
|
||||
|
||||
func test() {
|
||||
|
||||
header := ipv4.Header{}
|
||||
header.Version = subsubdir1.Add(2, 2)
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
go 1.14
|
||||
|
||||
require (
|
||||
github.com/microsoft/go-mssqldb v0.12.0
|
||||
)
|
||||
|
||||
module subdir2
|
||||
@@ -0,0 +1,30 @@
|
||||
github.com/Azure/go-autorest v13.3.2+incompatible h1:VxzPyuhtnlBOzc4IWCZHqpyH2d+QMLQEuy3wREyY4oc=
|
||||
github.com/Azure/go-autorest/autorest v0.9.0 h1:MRvx8gncNaXJqOoLmhNjUAKh33JJF8LyxPhomEtOsjs=
|
||||
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
|
||||
github.com/Azure/go-autorest/autorest v0.9.4 h1:1cM+NmKw91+8h5vfjgzK4ZGLuN72k87XVZBWyGwNjUM=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.8.1 h1:pZdL8o72rK+avFWl+p9nE8RWi1JInZrWJYlnpfXJwHk=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.8.1/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
|
||||
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
|
||||
github.com/Azure/go-autorest/autorest/date v0.2.0 h1:yW+Zlqf26583pE43KhfnhFcdmSWlm5Ew6bxipnr/tbM=
|
||||
github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
|
||||
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
|
||||
github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k=
|
||||
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
|
||||
github.com/microsoft/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73 h1:OGNva6WhsKst5OZf7eZOklDztV3hwtTHovdrLHV+MsA=
|
||||
github.com/microsoft/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 h1:ULYEB3JvPRE/IfO+9uO7vKV/xzVTO7XPAwm8xbf4w2g=
|
||||
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -0,0 +1,5 @@
|
||||
package subsubdir2
|
||||
|
||||
func Add(a, b int) int {
|
||||
return a + b
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package subdir
|
||||
|
||||
import (
|
||||
mssql "github.com/microsoft/go-mssqldb"
|
||||
)
|
||||
|
||||
func test() {
|
||||
connString := "hello"
|
||||
connector, err := mssql.NewAccessTokenConnector(
|
||||
connString, nil)
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
extractedFiles
|
||||
| src/subdir1/go.mod:0:0:0:0 | src/subdir1/go.mod |
|
||||
| src/subdir1/subsubdir1/add.go:0:0:0:0 | src/subdir1/subsubdir1/add.go |
|
||||
| src/subdir1/test.go:0:0:0:0 | src/subdir1/test.go |
|
||||
#select
|
||||
@@ -0,0 +1,18 @@
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
from create_database_utils import *
|
||||
from diagnostics_test_utils import *
|
||||
|
||||
# Set up a GOPATH relative to this test's root directory;
|
||||
# we set os.environ instead of using extra_env because we
|
||||
# need it to be set for the call to "go clean -modcache" later
|
||||
goPath = os.path.join(os.path.abspath(os.getcwd()), ".go")
|
||||
os.environ['GOPATH'] = goPath
|
||||
run_codeql_database_create([], lang="go", source="src")
|
||||
|
||||
check_diagnostics()
|
||||
|
||||
# Clean up the temporary GOPATH to prevent Bazel failures next
|
||||
# time the tests are run; see https://github.com/golang/go/issues/27161
|
||||
subprocess.call(["go", "clean", "-modcache"])
|
||||
@@ -0,0 +1,8 @@
|
||||
import go
|
||||
import semmle.go.DiagnosticsReporting
|
||||
|
||||
query predicate extractedFiles(File f) { any() }
|
||||
|
||||
from string msg, int sev
|
||||
where reportableDiagnostics(_, msg, sev)
|
||||
select msg, sev
|
||||
Reference in New Issue
Block a user