Skip to content

Commit

Permalink
Highlight Specific Dependencies (#23)
Browse files Browse the repository at this point in the history
* feat($atlas): add highlight flag

Signed-off-by: Rain Wu <[email protected]>
  • Loading branch information
RainrainWu authored Dec 1, 2022
1 parent 492b4db commit e270c01
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 33 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ Telescope is a dependencies scanner that helps developers sort out outdated depe
```
$ docker run --rm docker.io/r41nwu/telescope:latest
Usage: telescope [-f file_path] [-s outdated_scope] [-i ignored_dependency] [--skip-unknown] [--strict-semver]
Usage: telescope [-f file_path] [-s outdated_scope] [-i ignored_dependency] [-c critical_dependency] [--skip-unknown] [--strict-semver]
-c value
highlight critical dependency
-f string
dependencies file path (default "go.mod")
-i value
Expand Down
59 changes: 51 additions & 8 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"flag"
"fmt"
"os"
"strings"
"telescope/telescope"
)

Expand All @@ -24,12 +25,50 @@ func (i *IgnoredDependencies) Set(value string) error {
return nil
}

type CriticalDependencies map[string]telescope.OutdatedScope

func (c *CriticalDependencies) String() string {

var buffer string
for dep, scp := range map[string]telescope.OutdatedScope(*c) {

buffer += fmt.Sprintf("[ %s ] %s\n", scp, dep)
}
return buffer
}

func (c *CriticalDependencies) Set(value string) error {

var depName string
var desiredScope telescope.OutdatedScope

result := strings.Split(value, ":")
c_deps := map[string]telescope.OutdatedScope(*c)
if len(result) == 2 {
depName, desiredScope = result[1], telescope.OutdatedScopeStrToEnum(result[0])
} else if len(result) == 1 {
depName, desiredScope = result[0], telescope.MAJOR
} else {
panic(fmt.Errorf("invalid expression: %s", value))
}

if registeredScope, ok := c_deps[depName]; ok {
c_deps[depName] = telescope.GetTopScope(
[]telescope.OutdatedScope{registeredScope, desiredScope},
)
} else {
c_deps[depName] = desiredScope
}
return nil
}

var (
filePath string
outdatedScope string
skipUnknown bool
strictSemVer bool
ignoredDependencies IgnoredDependencies = make(map[string]bool)
filePath string
outdatedScope string
skipUnknown bool
strictSemVer bool
ignoredDependencies IgnoredDependencies = make(map[string]bool)
criticalDependencies CriticalDependencies = make(map[string]telescope.OutdatedScope)
)

func init() {
Expand All @@ -39,22 +78,26 @@ func init() {
flag.BoolVar(&skipUnknown, "skip-unknown", false, "skip dependencies with unknown versions")
flag.BoolVar(&strictSemVer, "strict-semver", false, "parse dependencies file with strict SemVer format")
flag.Var(&ignoredDependencies, "i", "ignore specific dependency")
flag.Var(&criticalDependencies, "c", "highlight critical dependency")
flag.Usage = usage
}

func usage() {

fmt.Fprintf(os.Stderr, "Usage: telescope [-f file_path] [-s outdated_scope] [-i ignored_dependency] [--skip-unknown] [--strict-semver]\n")
fmt.Fprintf(os.Stderr, "Usage: telescope [-f file_path] [-s outdated_scope] [-i ignored_dependency] [-c critical_dependency] [--skip-unknown] [--strict-semver]\n")
flag.PrintDefaults()
}

func main() {

flag.Parse()

atlas := telescope.NewAtlas(filePath, strictSemVer, ignoredDependencies)
atlas.ReportOutdated(
atlas := telescope.NewAtlas(filePath, strictSemVer, ignoredDependencies, criticalDependencies)
criticalFound := atlas.ReportOutdated(
telescope.OutdatedScopeStrToEnum(outdatedScope),
skipUnknown,
)
if criticalFound {
os.Exit(1)
}
}
62 changes: 43 additions & 19 deletions telescope/atlas.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,13 @@ func (l Language) String() string {
}

type IReportable interface {
sortLexicographically()
queryVersionsInformation()
buildOutdatedMap()
ReportOutdated(scope OutdatedScope, skipUnknown bool)
ReportOutdated(scope OutdatedScope, skipUnknown bool) bool
}

type Atlas struct {
name string
language Language
criticalMap map[string]OutdatedScope
dependencies []IDependable
outdatedMap map[OutdatedScope][]IDependable
}
Expand All @@ -47,7 +45,12 @@ type PoetryLock struct {
Packages []PoetryLockPackage `toml:"package"`
}

func NewAtlas(filePath string, strictSemVer bool, ignoredDeps map[string]bool) IReportable {
func NewAtlas(
filePath string,
strictSemVer bool,
ignoredDeps map[string]bool,
criticalDeps map[string]OutdatedScope,
) IReportable {

var atlas IReportable

Expand All @@ -57,15 +60,15 @@ func NewAtlas(filePath string, strictSemVer bool, ignoredDeps map[string]bool) I

switch fileName {
case "go.mod":
atlas = buildAtlasGoMod(fileBytes, strictSemVer, ignoredDeps)
atlas = buildAtlasGoMod(fileBytes, strictSemVer, ignoredDeps, criticalDeps)
case "poetry.lock":
atlas = buildAtlasPoetryLock(fileBytes, strictSemVer, ignoredDeps)
atlas = buildAtlasPoetryLock(fileBytes, strictSemVer, ignoredDeps, criticalDeps)
default:
panic(fmt.Errorf("unknown dep file: %s", filePath))
}
atlas.sortLexicographically()
atlas.queryVersionsInformation()
atlas.buildOutdatedMap()
atlas.(*Atlas).sortLexicographically()
atlas.(*Atlas).queryVersionsInformation()
atlas.(*Atlas).buildOutdatedMap()
return atlas
}

Expand All @@ -80,7 +83,7 @@ func parseDependenciesFile(filePath string) []byte {
return fileBytes
}

func buildAtlasGoMod(fileBytes []byte, strictSemVer bool, ignoredDeps map[string]bool) IReportable {
func buildAtlasGoMod(fileBytes []byte, strictSemVer bool, ignoredDeps map[string]bool, criticalDeps map[string]OutdatedScope) IReportable {

modObject, err := modfile.Parse("go.mod", fileBytes, nil)
if err != nil {
Expand All @@ -90,6 +93,7 @@ func buildAtlasGoMod(fileBytes []byte, strictSemVer bool, ignoredDeps map[string
atlas := Atlas{
name: modObject.Module.Mod.Path,
language: GO,
criticalMap: criticalDeps,
dependencies: []IDependable{},
}
for _, require := range modObject.Require {
Expand All @@ -103,7 +107,7 @@ func buildAtlasGoMod(fileBytes []byte, strictSemVer bool, ignoredDeps map[string
return &atlas
}

func buildAtlasPoetryLock(fileBytes []byte, strictSemVer bool, ignoredDeps map[string]bool) IReportable {
func buildAtlasPoetryLock(fileBytes []byte, strictSemVer bool, ignoredDeps map[string]bool, criticalDeps map[string]OutdatedScope) IReportable {

var poetryLock PoetryLock
err := toml.Unmarshal(fileBytes, &poetryLock)
Expand All @@ -115,6 +119,7 @@ func buildAtlasPoetryLock(fileBytes []byte, strictSemVer bool, ignoredDeps map[s
name: "",
language: PYTHON,
dependencies: []IDependable{},
criticalMap: criticalDeps,
outdatedMap: map[OutdatedScope][]IDependable{},
}
for _, pkg := range poetryLock.Packages {
Expand Down Expand Up @@ -177,18 +182,22 @@ func (a *Atlas) buildOutdatedMap() {
a.outdatedMap = outdatedMap
}

func (a *Atlas) ReportOutdated(desiredScope OutdatedScope, skipUnknown bool) {
func (a *Atlas) ReportOutdated(desiredScope OutdatedScope, skipUnknown bool) bool {

var criticalFound bool

for _, scp := range [3]OutdatedScope{MAJOR, MINOR, PATCH} {
if scp > desiredScope {
break
}
color := MapScopeColor[scp]
a.reportByScope(scp, color)
criticalFound = a.reportByScope(scp, color) || criticalFound
}
if !skipUnknown {
a.reportUnknownDependencies()
}

return criticalFound
}

func buildReportItem(dep IDependable) string {
Expand All @@ -208,10 +217,15 @@ func buildReportItem(dep IDependable) string {
)
}

func (a *Atlas) reportByScope(scope OutdatedScope, color int) {
func (a *Atlas) reportByScope(scope OutdatedScope, color int) bool {

var criticalFound, criticalAnyway bool
if scp, ok := a.criticalMap["*"]; ok {
criticalAnyway = scp >= scope
}

fmt.Printf(
"\033[%dm\n[ %d %s Version Outdated ]%s\n",
"\033[%dm\n[ %d %s Version Outdated ]%s\n\n",
color,
len(a.outdatedMap[scope]),
scope.String(),
Expand All @@ -220,10 +234,20 @@ func (a *Atlas) reportByScope(scope OutdatedScope, color int) {
if len(a.outdatedMap[scope]) == 0 {
fmt.Printf("no outdated dependencies")
}

for _, dep := range a.outdatedMap[scope] {
fmt.Println(buildReportItem(dep))

scp, ok := a.criticalMap[dep.(*Dependency).Name]
if criticalAnyway || (ok && scp >= scope) {
criticalFound = criticalFound || true
fmt.Printf("* %s\n", buildReportItem(dep))
} else {
fmt.Printf(" %s\n", buildReportItem(dep))
}
}
fmt.Print("\n\033[0m")

return criticalFound
}

func (a *Atlas) reportUnknownDependencies() {
Expand All @@ -232,11 +256,11 @@ func (a *Atlas) reportUnknownDependencies() {
return
}
fmt.Printf(
"\n[ %d UNKNOWN dependencies ]%s\n",
"\n[ %d UNKNOWN dependencies ]%s\n\n",
len(a.outdatedMap[UNKNOWN]),
strings.Repeat("=", 40),
)
for _, dep := range a.outdatedMap[UNKNOWN] {
fmt.Println(buildReportItem(dep))
fmt.Printf(" %s\n", buildReportItem(dep))
}
}
28 changes: 23 additions & 5 deletions telescope/scope.go
Original file line number Diff line number Diff line change
@@ -1,39 +1,57 @@
package telescope

import (
"errors"
"fmt"
"strings"
)

type OutdatedScope int

const (
MAJOR OutdatedScope = iota
UP_TO_DATE OutdatedScope = iota
MAJOR
MINOR
PATCH
UP_TO_DATE
UNKNOWN
)

var OutdatedScopeLiteral [5]string = [...]string{"UP_TO_DATE", "MAJOR", "MINOR", "PATCH", "UNKNOWN"}

var MapScopeColor map[OutdatedScope]int = map[OutdatedScope]int{
UP_TO_DATE: 92,
MAJOR: 91,
MINOR: 93,
PATCH: 94,
UP_TO_DATE: 92,
UNKNOWN: 97,
}

func (o OutdatedScope) String() string {
return [...]string{"MAJOR", "MINOR", "PATCH", "UP_TO_DATE", "UNKNOWN"}[o]
return OutdatedScopeLiteral[o]
}

func OutdatedScopeStrToEnum(scopeStr string) OutdatedScope {

scopeStr = strings.ToUpper(scopeStr)
for idx, scp := range [...]string{"MAJOR", "MINOR", "PATCH"} {
for idx, scp := range OutdatedScopeLiteral {
if scopeStr == scp {
return OutdatedScope(idx)
}
}
panic(fmt.Errorf("unknown scope %s", scopeStr))
}

func GetTopScope(scopes []OutdatedScope) OutdatedScope {

if len(scopes) == 0 {
panic(errors.New("should provide at least one item"))
}

var hold OutdatedScope = UNKNOWN
for _, scp := range scopes {
if scp < hold {
hold = scp
}
}
return hold
}

0 comments on commit e270c01

Please sign in to comment.