Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Forced Regeneration Flag #173

Merged
merged 2 commits into from
Dec 22, 2023
Merged
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
23 changes: 20 additions & 3 deletions internal/cmd/resolve/resolve.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@ var (
exclusions = file.Exclusions()
verbose bool
npmPreferred bool
regenerate int
)

const (
ExclusionFlag = "exclusion"
VerboseFlag = "verbose"
NpmPreferredFlag = "prefer-npm"
RegenerateFlag = "regenerate"
)

func NewResolveCmd(resolver resolution.IResolver) *cobra.Command {
Expand Down Expand Up @@ -53,6 +55,17 @@ Exclude flags could alternatively be set using DEBRICKED_EXCLUSIONS="path1,path2
Example:
$ debricked resolve . `+exampleFlags)
cmd.Flags().BoolVar(&verbose, VerboseFlag, true, "set to false to disable extensive resolution error messages")
regenerateDoc := strings.Join(
[]string{
"Toggles regeneration of already existing lock files between 3 modes:\n",
"Force Regeneration Level | Meaning",
"------------------------ | -------",
"0 (default) | No regeneration",
"1 | Regenerates existing non package manager native Debricked lock files",
"2 | Regenerates all existing lock files",
"\nExample:\n$ debricked resolve . --regenerate=1",
}, "\n")
cmd.Flags().IntVar(&regenerate, RegenerateFlag, 0, regenerateDoc)

npmPreferredDoc := strings.Join(
[]string{
Expand All @@ -73,9 +86,13 @@ func RunE(resolver resolution.IResolver) func(_ *cobra.Command, args []string) e
if len(args) == 0 {
args = append(args, ".")
}

resolver.SetNpmPreferred(viper.GetBool(NpmPreferredFlag))
_, err := resolver.Resolve(args, viper.GetStringSlice(ExclusionFlag), viper.GetBool(VerboseFlag))
options := resolution.DebrickedOptions{
Exclusions: viper.GetStringSlice(ExclusionFlag),
Verbose: viper.GetBool(VerboseFlag),
Regenerate: viper.GetInt(RegenerateFlag),
NpmPreferred: viper.GetBool(NpmPreferredFlag),
}
_, err := resolver.Resolve(args, options)

return err
}
Expand Down
14 changes: 14 additions & 0 deletions internal/cmd/scan/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ var repositoryUrl string
var integrationName string
var exclusions = file.Exclusions()
var verbose bool
var regenerate int
var noResolve bool
var noFingerprint bool
var passOnDowntime bool
Expand All @@ -38,6 +39,7 @@ const (
IntegrationFlag = "integration"
ExclusionFlag = "exclusion"
VerboseFlag = "verbose"
RegenerateFlag = "regenerate"
NoResolveFlag = "no-resolve"
FingerprintFlag = "fingerprint"
PassOnTimeOut = "pass-on-timeout"
Expand Down Expand Up @@ -95,6 +97,17 @@ Exclude flags could alternatively be set using DEBRICKED_EXCLUSIONS="path1,path2
Examples:
$ debricked scan . `+exampleFlags)
cmd.Flags().BoolVar(&verbose, VerboseFlag, true, "set to false to disable extensive resolution error messages")
regenerateDoc := strings.Join(
[]string{
"Toggles regeneration of already existing lock files between 3 modes:\n",
"Force Regeneration Level | Meaning",
"------------------------ | -------",
"0 (default) | No regeneration",
"1 | Regenerates existing non package manager native Debricked lock files",
"2 | Regenerates all existing lock files",
"\nExample:\n$ debricked resolve . --regenerate=1",
}, "\n")
cmd.Flags().IntVar(&regenerate, RegenerateFlag, 0, regenerateDoc)
cmd.Flags().BoolVarP(&passOnDowntime, PassOnTimeOut, "p", false, "pass scan if there is a service access timeout")
cmd.Flags().BoolVar(&noResolve, NoResolveFlag, false, `disables resolution of manifest files that lack lock files. Resolving manifest files enables more accurate dependency scanning since the whole dependency tree will be analysed.
For example, if there is a "go.mod" in the target path, its dependencies are going to get resolved onto a lock file, and latter scanned.`)
Expand Down Expand Up @@ -136,6 +149,7 @@ func RunE(s *scan.IScanner) func(_ *cobra.Command, args []string) error {
Fingerprint: viper.GetBool(FingerprintFlag),
Exclusions: viper.GetStringSlice(ExclusionFlag),
Verbose: viper.GetBool(VerboseFlag),
Regenerate: viper.GetInt(RegenerateFlag),
RepositoryName: viper.GetString(RepositoryFlag),
CommitName: viper.GetString(CommitFlag),
BranchName: viper.GetString(BranchFlag),
Expand Down
70 changes: 59 additions & 11 deletions internal/resolution/resolver.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package resolution

import (
"errors"
"os"
"path"
"regexp"

"github.com/debricked/cli/internal/file"
resolutionFile "github.com/debricked/cli/internal/resolution/file"
Expand All @@ -11,9 +13,12 @@ import (
"github.com/debricked/cli/internal/tui"
)

var (
BadOptsErr = errors.New("failed to type case IOptions")
)

type IResolver interface {
Resolve(paths []string, exclusions []string, verbose bool) (IResolution, error)
SetNpmPreferred(npmPreferred bool)
Resolve(paths []string, options IOptions) (IResolution, error)
}

type Resolver struct {
Expand All @@ -24,6 +29,16 @@ type Resolver struct {
npmPreferred bool
}

type IOptions interface{}

type DebrickedOptions struct {
Path string
Exclusions []string
Verbose bool
Regenerate int
NpmPreferred bool
}

func NewResolver(
finder file.IFinder,
batchFactory resolutionFile.IBatchFactory,
Expand All @@ -39,16 +54,20 @@ func NewResolver(
}
}

func (r Resolver) SetNpmPreferred(npmPreferred bool) {
func (r Resolver) setNpmPreferred(npmPreferred bool) {
r.batchFactory.SetNpmPreferred(npmPreferred)
}

func (r Resolver) Resolve(paths []string, exclusions []string, verbose bool) (IResolution, error) {
files, err := r.refinePaths(paths, exclusions)
func (r Resolver) Resolve(paths []string, options IOptions) (IResolution, error) {
dOptions, ok := options.(DebrickedOptions)
if !ok {
return nil, BadOptsErr
}
files, err := r.refinePaths(paths, dOptions.Exclusions, dOptions.Regenerate)
if err != nil {
return nil, err
}

r.setNpmPreferred(dOptions.NpmPreferred)
pmBatches := r.batchFactory.Make(files)

var jobs []job.IJob
Expand All @@ -67,13 +86,13 @@ func (r Resolver) Resolve(paths []string, exclusions []string, verbose bool) (IR

if resolution.HasErr() {
jobErrList := tui.NewJobsErrorList(os.Stdout, resolution.Jobs())
err = jobErrList.Render(verbose)
err = jobErrList.Render(dOptions.Verbose)
}

return resolution, err
}

func (r Resolver) refinePaths(paths []string, exclusions []string) ([]string, error) {
func (r Resolver) refinePaths(paths []string, exclusions []string, regenerate int) ([]string, error) {
var fileSet = map[string]bool{}
var dirs []string
for _, arg := range paths {
Expand All @@ -96,7 +115,7 @@ func (r Resolver) refinePaths(paths []string, exclusions []string) ([]string, er
}
}

err := r.searchDirs(fileSet, dirs, exclusions)
err := r.searchDirs(fileSet, dirs, exclusions, regenerate)
if err != nil {
return nil, err
}
Expand All @@ -109,7 +128,7 @@ func (r Resolver) refinePaths(paths []string, exclusions []string) ([]string, er
return files, nil
}

func (r Resolver) searchDirs(fileSet map[string]bool, dirs []string, exclusions []string) error {
func (r Resolver) searchDirs(fileSet map[string]bool, dirs []string, exclusions []string, regenerate int) error {
for _, dir := range dirs {
fileGroups, err := r.finder.GetGroups(
dir,
Expand All @@ -121,11 +140,40 @@ func (r Resolver) searchDirs(fileSet map[string]bool, dirs []string, exclusions
return err
}
for _, fileGroup := range fileGroups.ToSlice() {
if fileGroup.HasFile() && !fileGroup.HasLockFiles() {
shouldGenerate := shouldGenerateLock(fileGroup, regenerate)
if shouldGenerate {
fileSet[fileGroup.ManifestFile] = true
}
}
}

return nil
}

func shouldGenerateLock(fileGroup file.Group, regenerate int) bool {
if !fileGroup.HasFile() {
return false
}
switch regenerate {
case 0:
return !fileGroup.HasLockFiles()
case 1:
return onlyNonNativeLockFiles(fileGroup.LockFiles)
case 2:
return true
}

return false
}

func onlyNonNativeLockFiles(lockFiles []string) bool {
debrickedLockFilePattern := regexp.MustCompile(`.*\.debricked\.lock`)
for _, lockFile := range lockFiles {
if !debrickedLockFilePattern.MatchString(lockFile) {
return false
}
}

return true

}
74 changes: 61 additions & 13 deletions internal/resolution/resolver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,12 @@ func TestResolve(t *testing.T) {
strategyTestdata.NewStrategyFactoryMock(),
NewScheduler(workers),
)

res, err := r.Resolve([]string{"../../go.mod"}, nil, true)
options := DebrickedOptions{
Exclusions: nil,
Verbose: true,
Regenerate: 0,
}
res, err := r.Resolve([]string{"../../go.mod"}, options)
assert.NotEmpty(t, res.Jobs())
assert.NoError(t, err)
}
Expand All @@ -52,8 +56,12 @@ func TestResolveInvokeError(t *testing.T) {
strategyTestdata.NewStrategyFactoryErrorMock(),
NewScheduler(workers),
)

_, err := r.Resolve([]string{"../../go.mod"}, nil, true)
options := DebrickedOptions{
Exclusions: nil,
Verbose: true,
Regenerate: 0,
}
_, err := r.Resolve([]string{"../../go.mod"}, options)
assert.NotNil(t, err)
}

Expand All @@ -65,7 +73,12 @@ func TestResolveStrategyError(t *testing.T) {
NewScheduler(workers),
)

res, err := r.Resolve([]string{"../../go.mod"}, nil, true)
options := DebrickedOptions{
Exclusions: nil,
Verbose: true,
Regenerate: 0,
}
res, err := r.Resolve([]string{"../../go.mod"}, options)
assert.Empty(t, res.Jobs())
assert.NoError(t, err)
}
Expand All @@ -79,7 +92,12 @@ func TestResolveScheduleError(t *testing.T) {
SchedulerMock{Err: errAssertion},
)

res, err := r.Resolve([]string{"../../go.mod"}, nil, true)
options := DebrickedOptions{
Exclusions: nil,
Verbose: true,
Regenerate: 0,
}
res, err := r.Resolve([]string{"../../go.mod"}, options)
assert.NotEmpty(t, res.Jobs())
assert.ErrorIs(t, err, errAssertion)
}
Expand All @@ -92,7 +110,12 @@ func TestResolveDirWithoutManifestFiles(t *testing.T) {
SchedulerMock{},
)

res, err := r.Resolve([]string{"."}, nil, true)
options := DebrickedOptions{
Exclusions: nil,
Verbose: true,
Regenerate: 0,
}
res, err := r.Resolve([]string{"."}, options)
assert.Empty(t, res.Jobs())
assert.NoError(t, err)
}
Expand All @@ -105,7 +128,12 @@ func TestResolveInvalidDir(t *testing.T) {
SchedulerMock{},
)

_, err := r.Resolve([]string{"invalid-dir"}, nil, true)
options := DebrickedOptions{
Exclusions: nil,
Verbose: true,
Regenerate: 0,
}
_, err := r.Resolve([]string{"invalid-dir"}, options)
assert.Error(t, err)
}

Expand All @@ -121,7 +149,12 @@ func TestResolveGetGroupsErr(t *testing.T) {
SchedulerMock{},
)

_, err := r.Resolve([]string{"."}, nil, true)
options := DebrickedOptions{
Exclusions: nil,
Verbose: true,
Regenerate: 0,
}
_, err := r.Resolve([]string{"."}, options)
assert.ErrorIs(t, testErr, err)
}

Expand All @@ -147,9 +180,14 @@ func TestResolveDirWithManifestFiles(t *testing.T) {
SchedulerMock{},
)

for _, dir := range cases {
for i, dir := range cases {
options := DebrickedOptions{
Exclusions: nil,
Verbose: true,
Regenerate: i % 3, // To test the different regenerate values
}
t.Run(fmt.Sprintf("Case: %s", dir), func(t *testing.T) {
res, err := r.Resolve([]string{dir}, nil, true)
res, err := r.Resolve([]string{dir}, options)
assert.Len(t, res.Jobs(), 1)
j := res.Jobs()[0]
assert.False(t, j.Errors().HasError())
Expand All @@ -172,7 +210,12 @@ func TestResolveDirWithExclusions(t *testing.T) {
SchedulerMock{},
)

res, err := r.Resolve([]string{"."}, []string{"dir"}, true)
options := DebrickedOptions{
Exclusions: []string{"dir"},
Verbose: true,
Regenerate: 0,
}
res, err := r.Resolve([]string{"."}, options)

assert.Len(t, res.Jobs(), 1)
j := res.Jobs()[0]
Expand All @@ -199,7 +242,12 @@ func TestResolveHasResolutionErrs(t *testing.T) {
schedulerMock,
)

res, err := r.Resolve([]string{""}, []string{""}, true)
options := DebrickedOptions{
Exclusions: []string{""},
Verbose: true,
Regenerate: 0,
}
res, err := r.Resolve([]string{""}, options)

assert.NoError(t, err)
assert.Len(t, res.Jobs(), 1)
Expand Down
Loading
Loading