Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions src/cli/spinner.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package cli

import (
"fmt"

"time"

"github.com/briandowns/spinner"
)

func RunWithSpinner(message string, task func() error) error {
s := spinner.New(spinner.CharSets[11], 100*time.Millisecond)
s.Suffix = fmt.Sprintf(" %s", message)
s.Start()

err := task()

s.Stop()

return err
}
135 changes: 0 additions & 135 deletions src/cmd/fix.go

This file was deleted.

4 changes: 2 additions & 2 deletions src/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"github.com/spf13/cobra"
)

func NewRootCmd() *cobra.Command {
func RootCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "trood",
Short: "A CLI tool for detecting and fixing issues in your projects.",
Expand All @@ -13,7 +13,7 @@ identify potential issues, and provide actionable fixes.`,
CompletionOptions: cobra.CompletionOptions{DisableDefaultCmd: true},
}

cmd.AddCommand(newFixCmd())
cmd.AddCommand(ScanCmd())

return cmd
}
32 changes: 32 additions & 0 deletions src/cmd/scan.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package cmd

import (
"fmt"

"github.com/spf13/cobra"
"github.com/troodinc/trood/internal/scanner/node"
)

func ScanCmd() *cobra.Command {
var path string

cmd := &cobra.Command{
Use: "scan",
Short: "Scan a project for dependency issues.",
Long: `The scan command will check a Node.js project for:
- Missing dependencies
- Unused dependencies
- Security vulnerabilities`,
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("please provide the path to scan")
}
path := args[0]
return node.RunNodeScan(path)
},
}

cmd.Flags().StringVarP(&path, "path", "p", "", "Path to the project to scan (defaults to current directory)")

return cmd
}
4 changes: 4 additions & 0 deletions src/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@ module github.com/troodinc/trood
go 1.23.2

require (
github.com/briandowns/spinner v1.23.2 // indirect
github.com/fatih/color v1.7.0 // indirect
github.com/gdamore/encoding v1.0.1 // indirect
github.com/gdamore/tcell/v2 v2.8.1 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-colorable v0.1.2 // indirect
github.com/mattn/go-isatty v0.0.8 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/rivo/tview v0.0.0-20241227133733-17b7edb88c57 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
Expand Down
9 changes: 9 additions & 0 deletions src/go.sum
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
github.com/briandowns/spinner v1.23.2 h1:Zc6ecUnI+YzLmJniCfDNaMbW0Wid1d5+qcTq4L2FW8w=
github.com/briandowns/spinner v1.23.2/go.mod h1:LaZeM4wm2Ywy6vO571mvhQNRcWfRUnXOs0RcKV0wYKM=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
github.com/gdamore/encoding v1.0.1 h1:YzKZckdBL6jVt2Gc+5p82qhrGiqMdG/eNs6Wy0u3Uhw=
Expand All @@ -12,6 +16,10 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
Expand Down Expand Up @@ -54,6 +62,7 @@ golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
Expand Down
11 changes: 11 additions & 0 deletions src/internal/scanner/detect.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package scanner

import "github.com/troodinc/trood/internal/scanner/node"

func DetectEnvironment() string {
if node.IsNodeProject() {
return "nodejs"
}

return "unknown"
}
50 changes: 50 additions & 0 deletions src/internal/scanner/node/dependencies.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package node

import (
"errors"
"fmt"
"os/exec"
"strings"
)

func CheckMissingDependencies(path string) error {
cmd := exec.Command("npm", "install", "--dry-run")
cmd.Dir = path
output, _ := cmd.CombinedOutput()

if strings.Contains(string(output), "added") {
fmt.Println("\n\n⚠️ Missing dependencies detected")
fmt.Println(string(output))
} else {
fmt.Printf("✅ No missing dependencies found.")
}

return nil
}

func CheckUnusedDependencies(path string) error {
cmd := exec.Command("npx", "depcheck")
cmd.Dir = path
output, _ := cmd.CombinedOutput()

if len(output) > 0 {
return errors.New(string(output))
}

return nil
}

func CheckSecurityIssues(path string) error {
cmd := exec.Command("npm", "audit", "--json")
cmd.Dir = path
output, _ := cmd.CombinedOutput()

if len(output) > 0 {
fmt.Println("\n\n📢 Security vulnerabilities detected")
fmt.Println(string(output))
} else {
fmt.Println("✅ No security vulnerabilities found.")
}

return nil
}
8 changes: 8 additions & 0 deletions src/internal/scanner/node/detect.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package node

import "os"

func IsNodeProject() bool {
_, err := os.Stat("package.json")
return err == nil
}
21 changes: 21 additions & 0 deletions src/internal/scanner/node/scan.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package node

import (
"github.com/troodinc/trood/cli"
)

func RunNodeScan(path string) error {
cli.RunWithSpinner("Checking for missing dependencies", func() error {
return CheckMissingDependencies(path)
})

cli.RunWithSpinner("Checking for unused dependencies", func() error {
return CheckUnusedDependencies(path)
})

cli.RunWithSpinner("Checking for security vulnerabilities", func() error {
return CheckSecurityIssues(path)
})

return nil
}
5 changes: 3 additions & 2 deletions src/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,19 @@ package main

import (
"context"
"github.com/troodinc/trood/cmd"
"log"
"os"
"os/signal"
"syscall"

"github.com/troodinc/trood/cmd"
)

func main() {
ctx, shutdown := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer shutdown()

root := cmd.NewRootCmd()
root := cmd.RootCmd()
if err := root.ExecuteContext(ctx); err != nil {
log.Fatal(err)
}
Expand Down