diff --git a/README.rst b/README.rst index 2d251e8..ae9a101 100644 --- a/README.rst +++ b/README.rst @@ -33,6 +33,7 @@ Supported Ecosystems - **swift**: https://www.swift.org/documentation/package-manager/ - **cocoapods**: https://cocoapods.org/ - **pypi**: https://pypi.org/ +- **nuget**: https://www.nuget.org/ Installation diff --git a/cmd/nuget.go b/cmd/nuget.go new file mode 100644 index 0000000..e4eb3e3 --- /dev/null +++ b/cmd/nuget.go @@ -0,0 +1,39 @@ +/* + +Copyright (c) nexB Inc. and others. All rights reserved. +ScanCode is a trademark of nexB Inc. +SPDX-License-Identifier: Apache-2.0 +See http://www.apache.org/licenses/LICENSE-2.0 for the license text. +See https://github.com/nexB/dependency-inspector for support or download. +See https://aboutcode.org for more information about nexB OSS projects. + +*/ + +package cmd + +import ( + "github.com/nexB/dependency-inspector/internal" + "github.com/spf13/cobra" +) + +func nugetCmd() *cobra.Command { + forced := false + + nugetCmd := &cobra.Command{ + Use: "nuget [path]", + Short: "Generate lockfile for NuGet project", + Long: `Create lockfile for NuGet project if it doesn't exist in the specified [path]. +If no path is provided, the command defaults to the current directory.`, + Args: cobra.MaximumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + internal.CreateLockFileNuGet( + args, + forced, + ) + }, + } + + nugetCmd.Flags().BoolVarP(&forced, "force", "f", false, "Generate lockfile forcibly, ignoring existing lockfiles") + + return nugetCmd +} diff --git a/cmd/root.go b/cmd/root.go index 4537e53..f899083 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -25,9 +25,10 @@ var ecosystems = []func() *cobra.Command{ swiftCmd, cocoapodsCmd, pypiCmd, + nugetCmd, } -func NewRootCmd() *cobra.Command { +func rootCmd() *cobra.Command { rootCmd := &cobra.Command{ Use: "deplock", Short: "DepLock: Dependency Locker CLI", @@ -48,7 +49,7 @@ func initConfig(rootCmd *cobra.Command) { } func Execute() { - rootCmd := NewRootCmd() + rootCmd := rootCmd() if err := rootCmd.Execute(); err != nil { fmt.Fprintln(os.Stderr, "Error:", err) diff --git a/go.mod b/go.mod index 5b316d8..386643a 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,10 @@ module github.com/nexB/dependency-inspector go 1.22.3 -require github.com/spf13/cobra v1.8.0 +require ( + github.com/bmatcuk/doublestar/v4 v4.6.1 + github.com/spf13/cobra v1.8.0 +) require ( github.com/inconshreveable/mousetrap v1.1.0 // indirect diff --git a/go.sum b/go.sum index d0e8c2c..0ed62cc 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/bmatcuk/doublestar/v4 v4.6.1 h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwNy7PA4I= +github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= diff --git a/internal/utils.go b/internal/utils.go index 161c5e3..0c60467 100644 --- a/internal/utils.go +++ b/internal/utils.go @@ -16,22 +16,18 @@ import ( "os" "os/exec" "path/filepath" + + "github.com/bmatcuk/doublestar/v4" ) func CreateLockFile(lockFiles []string, cmdArgs []string, lockGenCmd []string, outputFileName string, forced bool) { - - path := "." - if len(cmdArgs) > 0 { - path = cmdArgs[0] - } - - absPath, err := filepath.Abs(path) - if err != nil { - fmt.Fprintln(os.Stderr, "Error: Failed to retrieve absolute path: ", err) + absPath := getAbsPath(cmdArgs) + if absPath == "" { return } if !forced { + // If any lockfile is present already then skip lockfile generation. for _, lockFile := range lockFiles { lockFileAbsPath := filepath.Join(absPath, lockFile) @@ -66,8 +62,23 @@ func DoesFileExists(absPath string) bool { return false } +func getAbsPath(cmdArgs []string) string { + path := "." + if len(cmdArgs) > 0 { + path = cmdArgs[0] + } + + absPath, err := filepath.Abs(path) + if err != nil { + fmt.Fprintln(os.Stderr, "Error: Failed to retrieve absolute path: ", err) + return "" + } + + return absPath +} + func genLock(lockGenCmd []string, absPath string, outputFileName string) { - fmt.Printf("Generating lockfile using '%s'\n", lockGenCmd) + fmt.Printf("Generating lockfile at '%s' using '%s'\n", absPath, lockGenCmd) // #nosec G204 command := exec.Command(lockGenCmd[0], lockGenCmd[1:]...) @@ -96,3 +107,35 @@ func genLock(lockGenCmd []string, absPath string, outputFileName string) { fmt.Println("Lock file generated successfully.") } + +func CreateLockFileNuGet(cmdArgs []string, force bool) { + nuGetLockFileName := "packages.lock.json" + nuGetLockFileGenCmd := []string{"dotnet", "restore", "--use-lock-file"} + + project_path := getAbsPath(cmdArgs) + if project_path == "" { + return + } + + fs := os.DirFS(project_path) + csproj_pattern := "**/*.csproj" + + csproj_files, _ := doublestar.Glob(fs, csproj_pattern) + if len(csproj_files) == 0 { + fmt.Fprintln(os.Stderr, "Error: Path does not contain a NuGet project") + return + } + + // Generate lockfile for all NuGet projects + for _, file := range csproj_files { + fullPath := filepath.Join(project_path, file) + dir := filepath.Dir(fullPath) + + lockFile := filepath.Join(dir, nuGetLockFileName) + if force || !DoesFileExists(lockFile) { + genLock(nuGetLockFileGenCmd, dir, "") + } + + } + +}