Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
elldritch committed Jun 7, 2018
1 parent 1423561 commit 52f7fd3
Show file tree
Hide file tree
Showing 30 changed files with 1,131 additions and 101 deletions.
62 changes: 62 additions & 0 deletions TODO
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
analyzers:
- ant
- bower
- dotnet
- golang
- gradle
- cocoapods
- maven
- nodejs
- php
- python
- ruby
- scala

tools:
- ant
- bazel
- boot
- bower
- brew
- buck
- buckaroo
- cabal
- cargo
- carthage
- composer
- conan
- cran
- docker
- dub
- elm
- go
- gradle
- haxelib
- helm
- hunter
- ivy
- julia
- leiningen
- maven
- meson
- meteor
- mill
- mix
- nimble
- npm
- nuget
- pants
- perl
- pip
- pipenv
- pub
- puppet
- pursuit
- raco
- rebar3
- ruby
- sbt
- shards
- stack
- swiftpm
- vcpkg
65 changes: 65 additions & 0 deletions analyzers/analyzer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Package analyzers defines analyzers for various package types.
package analyzers

import (
"errors"

"github.com/fossas/fossa-cli/analyzers/golang"
"github.com/fossas/fossa-cli/pkg"
"github.com/fossas/fossa-cli/project"
)

// Errors that occur when loading analyzers.
var (
ErrUnknownPackageType = errors.New("could not find analyzer for package type")
ErrAnalyzerNotImplemented = errors.New("analyzer is not implemented for package type")
)

// An Analyzer is an implementation of functionality for different build systems.
type Analyzer interface {
Discover(dir string) ([]project.Project, error) // Finds projects in a given directory.

// These methods all make best-effort attempts.
Clean(config project.Project) error // Cleans build artifacts.
Build(config project.Project) error // Builds the program.
IsBuilt(config project.Project) (bool, error) // Checks whether a program has been built.

Analyze(config project.Project) (project.Project, error) // Runs an analysis of a program.
}

// New returns the analyzer for any given package type.
func New(key pkg.Type, options interface{}) (Analyzer, error) {
switch key {
case pkg.Ant:
return nil, ErrAnalyzerNotImplemented
case pkg.Bower:
return nil, ErrAnalyzerNotImplemented
case pkg.Cocoapods:
return nil, ErrAnalyzerNotImplemented
case pkg.Composer:
return nil, ErrAnalyzerNotImplemented
case pkg.Go:
opts, ok := options.(project.GoOptions)
if !ok {
return nil, errors.New("golang analyzer requires GoOptions")
}
return golang.New(opts)
case pkg.Gradle:
return nil, ErrAnalyzerNotImplemented
case pkg.Maven:
return nil, ErrAnalyzerNotImplemented
case pkg.NodeJS:
return nil, ErrAnalyzerNotImplemented
case pkg.NuGet:
return nil, ErrAnalyzerNotImplemented
case pkg.Python:
return nil, ErrAnalyzerNotImplemented
case pkg.Ruby:
return nil, ErrAnalyzerNotImplemented
case pkg.Scala:
return nil, ErrAnalyzerNotImplemented
case pkg.VendoredArchives:
return nil, ErrAnalyzerNotImplemented
}
return nil, ErrUnknownPackageType
}
77 changes: 77 additions & 0 deletions analyzers/golang/golang.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
//
//
// Strategies:
// - default
// - use specific tool, read specific manifest at weird location (anki)
//
// options:
// - allow unresolved
// - allow unresolved prefix (hashi govendor)
package golang

import (
"os"

"github.com/fossas/fossa-cli/buildtools/golang"
"github.com/fossas/fossa-cli/exec"
"github.com/fossas/fossa-cli/project"
)

// An Analyzer contains structs used in the analysis of Go packages.
type Analyzer struct {
Go golang.Go
GoVersion string
}

// New constructs an Analyzer given GoOptions.
func New(options project.GoOptions) (*Analyzer, error) {
cmd, version, err := exec.Which("version", os.Getenv("FOSSA_GO_CMD"), "go")
if err != nil {
return nil, err
}
return &Analyzer{
Go: golang.Go{
Cmd: cmd,
OS: options.BuildOS,
Arch: options.BuildArch,
},
GoVersion: version,
}, nil
}

// Discover runs `go list ./...`.
func (a *Analyzer) Discover(dir string) ([]project.Project, error) {
a.Go.List([]string{"./..."})
return nil, nil
}

// Clean runs `go clean $PKG`.
func (a *Analyzer) Clean(p project.Project) error {
return a.Go.Clean([]string{p.BuildTarget})
}

// Build runs `go build $PKG`.
func (a *Analyzer) Build(p project.Project) error {
return a.Go.Build([]string{p.BuildTarget})
}

// IsBuilt runs `go list $PKG` and checks for errors.
func (a *Analyzer) IsBuilt(p project.Project) (bool, error) {
pkg, err := a.Go.ListOne(p.BuildTarget)
if err != nil {
return false, err
}
return pkg.Error == nil, nil
}

// Analyze builds a dependency graph using go list and then looks up revisions
// using tool-specific lockfiles.
func (a *Analyzer) Analyze(p project.Project) (project.Project, error) {
options := p.Options.(project.GoOptions)
switch options.Strategy {
case "manifest:godep":
return a.ResolveManifest(p)
default:
return p, nil
}
}
89 changes: 89 additions & 0 deletions analyzers/golang/manifest.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package golang

import (
"path/filepath"

"github.com/fossas/fossa-cli/buildtools/godep"
"github.com/fossas/fossa-cli/pkg"
"github.com/fossas/fossa-cli/project"
)

func (a *Analyzer) ResolveManifest(p project.Project) (project.Project, error) {
options := p.Options.(project.GoOptions)
// Read the lockfile.
lockfilePath := options.LockfilePath
if lockfilePath == "" {
lockfilePath = filepath.Join(p.Dir, "Godeps", "Godeps.json")
}
godepPkgs, err := godep.ReadFile(options.LockfilePath)
if err != nil {
return p, err
}
revisions := make(map[string]godep.Package)
for _, godepPkg := range godepPkgs {
revisions[godepPkg.ImportPath] = godepPkg
}

// Trace dependencies.
// Get direct dependencies.
goPkg, err := a.Go.ListOne(p.BuildTarget)
if err != nil {
return p, err
}
for _, goImport := range goPkg.Imports {
revision, ok := revisions[goImport]
if !ok {
// we're missing a lock
}
p.Imports = append(p.Imports, pkg.ID{
Type: pkg.Go,
Name: goImport,
Revision: revision.Rev,
Location: goImport,
})
}
// Get transitive dependencies.
var pkgnames []string
for _, godepPkg := range godepPkgs {
pkgnames = append(pkgnames, godepPkg.ImportPath)
}
goPkgs, err := a.Go.List(pkgnames)
if err != nil {
return p, err
}
p.Deps = make(map[pkg.ID]pkg.Package)
for _, goPkg := range goPkgs {
revision, ok := revisions[goPkg.Name]
if !ok {
// we're missing a lock
}
id := pkg.ID{
Type: pkg.Go,
Name: goPkg.Name,
Revision: revision.Rev,
Location: goPkg.Name,
}
var imports []pkg.Import
for _, imported := range goPkg.Imports {
importedRevision, ok := revisions[imported]
if !ok {
// we're missing a lock
}
imports = append(imports, pkg.Import{
Target: "",
Resolved: &pkg.ID{
Type: pkg.Go,
Name: imported,
Revision: importedRevision.Rev,
Location: imported,
},
})
}
p.Deps[id] = pkg.Package{
ID: id,
Imports: imports,
Strategy: "manifest:godep",
}
}
return p, nil
}
23 changes: 23 additions & 0 deletions analyzers/golang/project.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package golang

// The project of a package is the nearest ancestor with a supported lockfile.
// Two packages in the same project can import each other without needing a
// revision in the lockfile, because these are ostensibly internal.
// Conceptually, anything in the same VCS project (and not in a vendor folder) should be an "internal" import
// because they'll all be versioned together.
// Looking upwards for a lockfile is a subset of this: it's never possible to
// have a lockfile "in between" directories that are not repositories (you'll
// never have github.com/hashicorp/Gopkg.lock), but your internal import might
// be higher up than the closest lockfile.
// alternatively: search upwards for .git or other VCS marker?
// A vendored package always in a different project than the one vendoring it.
func GetProjectFolder(pkg string) (string, error) {
return "", nil
}

// i know the location of every package on disk.
// every package that goes up to the same VCS metadata folder _and_ is not in a
// vendor folder is in the same project.
//
// fall back to using lockfile location for project discovery _iff_ no VCS is
// found (e.g. new project without git init)
15 changes: 15 additions & 0 deletions analyzers/golang/resolve.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package golang

import (
"github.com/fossas/fossa-cli/project"
)

func Resolve(a Analyzer, p project.Project) {
// exec.Run(exec.Cmd{
// Name: a.GoCmd,
// Argv: []string{"list", "-json", p.BuildTarget},
// })
// golang.List()
// dep.UsedIn()
// dep.Read()
}

0 comments on commit 52f7fd3

Please sign in to comment.