Skip to content

Commit

Permalink
First pass at getting glice audit working.
Browse files Browse the repository at this point in the history
Incomplete as of this commit.
  • Loading branch information
Mike Schinkel committed Oct 18, 2022
1 parent c425f38 commit f28b4a2
Show file tree
Hide file tree
Showing 16 changed files with 284 additions and 70 deletions.
32 changes: 32 additions & 0 deletions cmd/audit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package cmd

import (
"github.com/ribice/glice/v3/pkg"
"github.com/spf13/cobra"
)

// auditCmd represents the audit command
var auditCmd = &cobra.Command{
Use: "audit",
Run: glice.RunAudit,
Short: "Audit your project's path for disallowed open-source licenses",
Long: `Audit your project's path for Go-specific dependencies using disallowed open-source licenses ` +
`while comparing with allowed licenses and dependency overrides in glice.yaml and only auditing ` +
`those dependencies that have not been audited within a specifiable TTL (time-to-live) where` +
`the default TLL is 24*60*60 seconds (1 day)`,
}

func init() {
rootCmd.AddCommand(auditCmd)
initCmd.Flags().Int("ttl", 24*60*60, "Time-to-Live for data in the cache file allowing recently audited dependencies to be skipped")

// Here you will define your flags and configuration settings.

// Cobra supports Persistent Flags which will work for this command
// and all subcommands, e.g.:
// auditCmd.PersistentFlags().String("foo", "", "A help for foo")

// Cobra supports local flags which will only run when this command
// is called directly, e.g.:
// auditCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}
5 changes: 2 additions & 3 deletions cmd/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,11 @@ var initCmd = &cobra.Command{
`'init' will scan the go.mod file for dependencies and write ` +
`them to the YAML file which can then be hand-edited to add ` +
`overrides. Optionally it can generate a cache-file to allow ` +
`future invocations of the 'scan' command to assume data to ` +
`be current if last scanned within a specifiable TTL (time-to-live.)`,
`future invocations of the 'audit' command to assume data to ` +
`be current if last audited within a specifiable TTL (time-to-live.)`,
}

func init() {
rootCmd.AddCommand(initCmd)
initCmd.Flags().String("cache-file", glice.CacheFilepath(), "Full filepath for cache file to create")
initCmd.Flags().String("path", glice.SourceDir(""), "Directory path to your project's top-level go.mod file")
}
8 changes: 2 additions & 6 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,7 @@ Commands & Switches:
--log
--logfile
init - Initialize glice.yaml for a directory
--cache-file={cache_file}
--path={repo_dir}
scan - CI check
--cache-file={cache_file}
--path={repo_dir}
audit - CI check
--ttl={cache_ttl}
report - Generate a license report
- print - Print license report to stdout
Expand Down Expand Up @@ -64,7 +60,7 @@ func init() {
pf.BoolVar(&verbose, "verbose", false, "Generate verbose output")
pf.BoolVar(&logOutput, "log", false, "Log output to default logging filepath.")
pf.StringVar(&logfile, "logfile", "", "File to log output to.")
pf.StringVar(&source, "source", glice.SourceDir(""), "Directory where go.mod for the repo to scan is located.")
pf.StringVar(&source, "source", glice.SourceDir(""), "Source directory where go.mod for the repo to audit is located.")
pf.StringVar(&cachefile, "cache-file", glice.CacheFilepath(), "Full filepath to the cachefile to create.")
pf.BoolVar(&nocache, "nocache", false, "Disable use of caching")
}
Expand Down
41 changes: 0 additions & 41 deletions cmd/scan.go

This file was deleted.

49 changes: 49 additions & 0 deletions pkg/audit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package glice

import (
"fmt"
"github.com/spf13/cobra"
"os"
)

func RunAudit(cmd *cobra.Command, args []string) {
var err error
var deps Dependencies

options := GetOptions()

fmt.Println("\nAuditing...")
yf, err := LoadYAMLFile(options.SourceDir)
if err != nil {
LogAndExit(exitYAMLFileDoesNotExist,
"Cannot run scan; %s",
err.Error())

}
fmt.Printf("YAML file %s loaded\n", yf.Filepath)

fmt.Print("Scanning dependencies...")
deps, err = ScanDependencies(options)
if err != nil {
LogAndExit(exitCannotParseDependencies,
"Failed while parsing dependencies: %s",
err.Error())
}

changes, el := yf.AuditDependencies(deps)
if !changes.HasChanges() {
fmt.Println("\nNo chances detected")
} else {
fmt.Println()
changes.Print()
}

if !el.HasErrors() {
fmt.Println("\nNo disallowed licenses detected")
} else {
el.LogPrintWithHeader("ERROR! Disallowed Licenses Detected:")
os.Exit(exitAuditFoundDisallowedLicenses)
}

fmt.Println("\nAudit completed successfully")
}
40 changes: 40 additions & 0 deletions pkg/changes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package glice

import (
"log"
)

type Changes struct {
Old []string
New []string
}

func NewChanges() *Changes {
return &Changes{
Old: make([]string, 0),
New: make([]string, 0),
}
}

// HasChanges returns true if there are either old or new changes
func (c *Changes) HasChanges() bool {
return len(c.Old) > 0 || len(c.New) > 0
}

// Print outputs all changes, old and new
func (c *Changes) Print() {
LogPrintFunc(func() {
showChanges(c.Old, "Old", "These imports were not found in glice.yaml but were found when scanning.")
showChanges(c.New, "New", "These imports were not found when scanning but were found in glice.yaml.")
})
}

func showChanges(list []string, _type, descr string) {
log.Printf("\nChanges: %s", _type)
log.Println("------------")
log.Println(descr)
for _, imp := range list {
log.Printf(" - %s\n", imp)
}
log.Println("")
}
2 changes: 1 addition & 1 deletion pkg/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ func (c *Client) Validate() (err error) {
}

if !ModFileExists(c.path) {
err = ErrNoGoMod
err = ErrNoGoModFile
goto end
}

Expand Down
12 changes: 11 additions & 1 deletion pkg/dependency.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,17 @@ import (
"log"
)

type Dependencies = []*Dependency
type Dependencies []*Dependency
type DependencyMap map[string]*Dependency

// ToMap creates a map indexed by Dependency Import of Dependencies
func (deps Dependencies) ToMap() DependencyMap {
newDeps := make(DependencyMap, len(deps))
for _, dep := range deps {
newDeps[dep.Import] = dep
}
return newDeps
}

// Dependency holds information about a dependency
type Dependency struct {
Expand Down
23 changes: 20 additions & 3 deletions pkg/error_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,32 @@ var _ error = (*ErrorList)(nil)

type ErrorList []error

// HasErrors returns true if ErrorList has one or more errors
func (el ErrorList) HasErrors() bool {
return len(el) > 0
}

// Error returns all errors as a single string
func (el ErrorList) Error() (s string) {
for _, err := range el {
s = fmt.Sprintf("%s | %s", s, err.Error())
}
return fmt.Sprintf("[%s ]", s[2:])
}

// LogPrint outputs all errors in list individually
func (el ErrorList) LogPrint() {
for _, err := range el {
log.Print(err.Error())
}
el.LogPrintWithHeader("")
}

// LogPrintWithHeader outputs all errors in list individually but with header
func (el ErrorList) LogPrintWithHeader(header string) {
LogPrintFunc(func() {
if header != "" {
log.Printf("\n%s\n", header)
}
for _, err := range el {
log.Printf("- %s\n", err.Error())
}
})
}
6 changes: 4 additions & 2 deletions pkg/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ package glice
import "errors"

var (
// ErrNoGoMod is returned when path doesn't contain go.mod file
ErrNoGoMod = errors.New("no go.mod file present")
// ErrNoGoModFile is returned when path doesn't contain go.mod file
ErrNoGoModFile = errors.New("no go.mod file present")

ErrNoYAMLFile = errors.New("glice.yaml file does not exist")

// ErrNoAPIKey is returned when thanks flag is enabled without providing GITHUB_API_KEY env variable
ErrNoAPIKey = errors.New("cannot use thanks feature without github api key")
Expand Down
18 changes: 10 additions & 8 deletions pkg/exit.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,20 @@ import (
)

const (
exitCannotGetWorkingDir = 1
exitCannotGetCacheDir = 2
exitCannotCreateCacheDir = 3
exitCannotParseDependencies = 4
exitCannotInitializeYAMLFile = 5
exitCannotStatFile = 6
exitYAMLExistsCannotOverwrite = 7
exitAuditFoundDisallowedLicenses = 1
exitCannotGetWorkingDir = 2
exitCannotGetCacheDir = 3
exitCannotCreateCacheDir = 4
exitCannotParseDependencies = 5
exitCannotInitializeYAMLFile = 6
exitCannotStatFile = 7
exitYAMLFileExistsCannotOverwrite = 8
exitYAMLFileDoesNotExist = 9
)

func LogAndExit(status int, msg string, args ...interface{}) {
log.Printf(msg, args...)
log.SetOutput(os.Stderr)
log.Printf(msg, args...)
log.Printf("\n"+msg, args...)
os.Exit(status)
}
4 changes: 2 additions & 2 deletions pkg/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ func RunInit(cmd *cobra.Command, args []string) {

yf := NewYAMLFile(options.SourceDir)
if yf.Exists() {
LogAndExit(exitYAMLExistsCannotOverwrite,
"\nCannot overwrite existing YAML file %s.\nRename or delete file then re-run 'glice init'.",
LogAndExit(exitYAMLFileExistsCannotOverwrite,
"Cannot overwrite existing YAML file %s.\nRename or delete file then re-run 'glice init'.",
yf.Filepath)
}
fmt.Printf("\nCreating %s\n", yf.Filepath)
Expand Down
11 changes: 11 additions & 0 deletions pkg/license.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,17 @@ const (
Unspecified LicenseStatus = "unspecified"
)

type LicenseIDs []string
type LicenseIDMap map[string]exists

func (ids LicenseIDs) ToMap() LicenseIDMap {
idMap := make(LicenseIDMap, len(ids))
for _, id := range ids {
idMap[id] = exists{}
}
return idMap
}

type License struct {
ID string `json:"licenseId"`
Reference string `json:"reference"`
Expand Down
18 changes: 18 additions & 0 deletions pkg/log.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package glice

import (
"log"
"os"
)

const LogFilename = "glice.log"

var logFilepath = SourceDir(LogFilename)
Expand All @@ -10,3 +15,16 @@ func LogFilepath() string {
func SetLogFilepath(fp string) {
logFilepath = fp
}

func LogPrintFunc(show func()) {
var out = log.Writer()
opt := GetOptions()
if !opt.LogVerbosely && opt.LogOuput {
// If not Logging Verbosely we would be outputting
// the same information twice
show()
}
log.SetOutput(os.Stderr)
show()
log.SetOutput(out)
}
6 changes: 6 additions & 0 deletions pkg/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,11 +154,17 @@ func (r *Repository) checkURL() {

func (r *Repository) GetLicenseID() string {
r.ResolveLicenseWithLogging(r.Context, GetOptions())
if r.license == nil {
return "Inaccessible"
}
return r.license.ID
}

func (r *Repository) GetLicenseURL() string {
r.ResolveLicenseWithLogging(r.Context, GetOptions())
if r.license == nil {
return "http://inaccessible"
}
return r.license.URL
}

Expand Down
Loading

0 comments on commit f28b4a2

Please sign in to comment.