diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index aa8304f8..2e3b150a 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -13,4 +13,6 @@ We contributors to BuildSafe: * Praful(@Horiodino) * Sanyam(@sanyamjain04) * Hanshal(@hanshal101) -* Balaaditya(@BalaadityaPatanjali) \ No newline at end of file +* Manik(@manik2708) +* Balaaditya(@BalaadityaPatanjali) + diff --git a/bsf.lock b/bsf.lock index 5f235dfe..80ae6bd5 100644 --- a/bsf.lock +++ b/bsf.lock @@ -5,19 +5,28 @@ "packages": [ { "package": { - "name": "golangci-lint", - "revision": "7445ccd775d8b892fc56448d17345443a05f7fb4", - "version": "1.59.0", - "description": "Fast linters Runner for Go", - "homepage": "https://golangci-lint.run/", + "name": "cacert", + "revision": "ac5c1886fd9fe49748d7ab80accc4c847481df14", + "version": "3.95", + "description": "A bundle of X.509 certificates of public Certificate Authorities (CA)", + "homepage": "https://curl.haxx.se/docs/caextract.html", "free": true, - "spdx_id": "GPL-3.0-or-later", - "epoch_seconds": 1716993062, + "spdx_id": "MPL-2.0", + "epoch_seconds": 1699289668, "platforms": [ + "i686-cygwin", + "x86_64-cygwin", "x86_64-darwin", "i686-darwin", "aarch64-darwin", "armv7a-darwin", + "i686-freebsd13", + "x86_64-freebsd13", + "aarch64-genode", + "i686-genode", + "x86_64-genode", + "x86_64-solaris", + "javascript-ghcjs", "aarch64-linux", "armv5tel-linux", "armv6l-linux", @@ -39,55 +48,52 @@ "s390-linux", "s390x-linux", "x86_64-linux", + "mmix-mmixware", + "aarch64-netbsd", + "armv6l-netbsd", + "armv7a-netbsd", + "armv7l-netbsd", + "i686-netbsd", + "m68k-netbsd", + "mipsel-netbsd", + "powerpc-netbsd", + "riscv32-netbsd", + "riscv64-netbsd", + "x86_64-netbsd", + "aarch64_be-none", + "aarch64-none", + "arm-none", + "armv6l-none", + "avr-none", + "i686-none", + "microblaze-none", + "microblazeel-none", + "mips-none", + "mips64-none", + "msp430-none", + "or1k-none", + "m68k-none", + "powerpc-none", + "powerpcle-none", + "riscv32-none", + "riscv64-none", + "rx-none", + "s390-none", + "s390x-none", + "vc4-none", + "x86_64-none", + "i686-openbsd", + "x86_64-openbsd", + "x86_64-redox", "wasm64-wasi", - "wasm32-wasi" + "wasm32-wasi", + "x86_64-windows", + "i686-windows" ], "attr_name": "golangci-lint" + }, - "runtime": false - }, - { - "package": { - "name": "go-task", - "revision": "7445ccd775d8b892fc56448d17345443a05f7fb4", - "version": "3.37.2", - "description": "A task runner / simpler Make alternative written in Go", - "homepage": "https://taskfile.dev/", - "free": true, - "spdx_id": "MIT", - "epoch_seconds": 1716993062, - "platforms": [ - "x86_64-darwin", - "i686-darwin", - "aarch64-darwin", - "armv7a-darwin", - "aarch64-linux", - "armv5tel-linux", - "armv6l-linux", - "armv7a-linux", - "armv7l-linux", - "i686-linux", - "loongarch64-linux", - "m68k-linux", - "microblaze-linux", - "microblazeel-linux", - "mips-linux", - "mips64-linux", - "mips64el-linux", - "mipsel-linux", - "powerpc64-linux", - "powerpc64le-linux", - "riscv32-linux", - "riscv64-linux", - "s390-linux", - "s390x-linux", - "x86_64-linux", - "wasm64-wasi", - "wasm32-wasi" - ], - "attr_name": "go-task" - }, - "runtime": false + "runtime": true }, { "package": { @@ -124,8 +130,11 @@ "riscv64-linux", "s390-linux", "s390x-linux", - "x86_64-linux" - ] + "x86_64-linux", + "wasm64-wasi", + "wasm32-wasi" + ], + "attr_name": "go-task" }, "runtime": false }, @@ -171,28 +180,19 @@ }, { "package": { - "name": "cacert", - "revision": "ac5c1886fd9fe49748d7ab80accc4c847481df14", - "version": "3.95", - "description": "A bundle of X.509 certificates of public Certificate Authorities (CA)", - "homepage": "https://curl.haxx.se/docs/caextract.html", + "name": "go-task", + "revision": "7445ccd775d8b892fc56448d17345443a05f7fb4", + "version": "3.37.2", + "description": "A task runner / simpler Make alternative written in Go", + "homepage": "https://taskfile.dev/", "free": true, - "spdx_id": "MPL-2.0", - "epoch_seconds": 1699289668, + "spdx_id": "MIT", + "epoch_seconds": 1716993062, "platforms": [ - "i686-cygwin", - "x86_64-cygwin", "x86_64-darwin", "i686-darwin", "aarch64-darwin", "armv7a-darwin", - "i686-freebsd13", - "x86_64-freebsd13", - "aarch64-genode", - "i686-genode", - "x86_64-genode", - "x86_64-solaris", - "javascript-ghcjs", "aarch64-linux", "armv5tel-linux", "armv6l-linux", @@ -214,50 +214,12 @@ "s390-linux", "s390x-linux", "x86_64-linux", - "mmix-mmixware", - "aarch64-netbsd", - "armv6l-netbsd", - "armv7a-netbsd", - "armv7l-netbsd", - "i686-netbsd", - "m68k-netbsd", - "mipsel-netbsd", - "powerpc-netbsd", - "riscv32-netbsd", - "riscv64-netbsd", - "x86_64-netbsd", - "aarch64_be-none", - "aarch64-none", - "arm-none", - "armv6l-none", - "avr-none", - "i686-none", - "microblaze-none", - "microblazeel-none", - "mips-none", - "mips64-none", - "msp430-none", - "or1k-none", - "m68k-none", - "powerpc-none", - "powerpcle-none", - "riscv32-none", - "riscv64-none", - "rx-none", - "s390-none", - "s390x-none", - "vc4-none", - "x86_64-none", - "i686-openbsd", - "x86_64-openbsd", - "x86_64-redox", "wasm64-wasi", - "wasm32-wasi", - "x86_64-windows", - "i686-windows" - ] + "wasm32-wasi" + ], + "attr_name": "go-task" }, - "runtime": true + "runtime": false }, { "package": { @@ -297,7 +259,7 @@ "x86_64-linux" ] }, - "runtime": false - } + "runtime": true + }, ] } \ No newline at end of file diff --git a/bsf/flake.nix b/bsf/flake.nix index 77d57100..dab29a4b 100644 --- a/bsf/flake.nix +++ b/bsf/flake.nix @@ -3,8 +3,8 @@ description = ""; inputs = { - nixpkgs-a731d0cb71c58f56895f71a5b02eda2962a46746.url = "github:nixos/nixpkgs/a731d0cb71c58f56895f71a5b02eda2962a46746"; nixpkgs-ac5c1886fd9fe49748d7ab80accc4c847481df14.url = "github:nixos/nixpkgs/ac5c1886fd9fe49748d7ab80accc4c847481df14"; + nixpkgs-a731d0cb71c58f56895f71a5b02eda2962a46746.url = "github:nixos/nixpkgs/a731d0cb71c58f56895f71a5b02eda2962a46746"; nixpkgs-7445ccd775d8b892fc56448d17345443a05f7fb4.url = "github:nixos/nixpkgs/7445ccd775d8b892fc56448d17345443a05f7fb4"; nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; @@ -26,8 +26,8 @@ - nixpkgs-a731d0cb71c58f56895f71a5b02eda2962a46746, nixpkgs-ac5c1886fd9fe49748d7ab80accc4c847481df14, + nixpkgs-a731d0cb71c58f56895f71a5b02eda2962a46746, nixpkgs-7445ccd775d8b892fc56448d17345443a05f7fb4, }: let supportedSystems = [ "x86_64-linux" "aarch64-darwin" "x86_64-darwin" "aarch64-linux" ]; @@ -36,8 +36,8 @@ forEachSupportedSystem = f: nixpkgs.lib.genAttrs supportedSystems (system: f { inherit system; - nixpkgs-a731d0cb71c58f56895f71a5b02eda2962a46746-pkgs = import nixpkgs-a731d0cb71c58f56895f71a5b02eda2962a46746 { inherit system; }; nixpkgs-ac5c1886fd9fe49748d7ab80accc4c847481df14-pkgs = import nixpkgs-ac5c1886fd9fe49748d7ab80accc4c847481df14 { inherit system; }; + nixpkgs-a731d0cb71c58f56895f71a5b02eda2962a46746-pkgs = import nixpkgs-a731d0cb71c58f56895f71a5b02eda2962a46746 { inherit system; }; nixpkgs-7445ccd775d8b892fc56448d17345443a05f7fb4-pkgs = import nixpkgs-7445ccd775d8b892fc56448d17345443a05f7fb4 { inherit system; }; buildGoApplication = gomod2nix.legacyPackages.${system}.buildGoApplication; @@ -51,8 +51,8 @@ buildGoApplication, - nixpkgs-a731d0cb71c58f56895f71a5b02eda2962a46746-pkgs, nixpkgs-ac5c1886fd9fe49748d7ab80accc4c847481df14-pkgs, + nixpkgs-a731d0cb71c58f56895f71a5b02eda2962a46746-pkgs, nixpkgs-7445ccd775d8b892fc56448d17345443a05f7fb4-pkgs, ... }: { default = pkgs.callPackage ./default.nix { @@ -68,8 +68,8 @@ buildGoApplication, - nixpkgs-a731d0cb71c58f56895f71a5b02eda2962a46746-pkgs, nixpkgs-ac5c1886fd9fe49748d7ab80accc4c847481df14-pkgs, + nixpkgs-a731d0cb71c58f56895f71a5b02eda2962a46746-pkgs, nixpkgs-7445ccd775d8b892fc56448d17345443a05f7fb4-pkgs, ... }: { devShell = pkgs.mkShell { @@ -89,7 +89,7 @@ buildGoApplication, - nixpkgs-a731d0cb71c58f56895f71a5b02eda2962a46746-pkgs, nixpkgs-ac5c1886fd9fe49748d7ab80accc4c847481df14-pkgs, nixpkgs-7445ccd775d8b892fc56448d17345443a05f7fb4-pkgs, ... }: { + nixpkgs-ac5c1886fd9fe49748d7ab80accc4c847481df14-pkgs, nixpkgs-a731d0cb71c58f56895f71a5b02eda2962a46746-pkgs, nixpkgs-7445ccd775d8b892fc56448d17345443a05f7fb4-pkgs, ... }: { runtime = pkgs.buildEnv { name = "runtimeenv"; paths = [ @@ -103,7 +103,7 @@ buildGoApplication, - nixpkgs-a731d0cb71c58f56895f71a5b02eda2962a46746-pkgs, nixpkgs-ac5c1886fd9fe49748d7ab80accc4c847481df14-pkgs, nixpkgs-7445ccd775d8b892fc56448d17345443a05f7fb4-pkgs, ... }: { + nixpkgs-ac5c1886fd9fe49748d7ab80accc4c847481df14-pkgs, nixpkgs-a731d0cb71c58f56895f71a5b02eda2962a46746-pkgs, nixpkgs-7445ccd775d8b892fc56448d17345443a05f7fb4-pkgs, ... }: { development = pkgs.buildEnv { name = "devenv"; paths = [ diff --git a/bsf/gomod2nix.toml b/bsf/gomod2nix.toml index 475d2070..12afa611 100644 --- a/bsf/gomod2nix.toml +++ b/bsf/gomod2nix.toml @@ -308,8 +308,8 @@ schema = 3 version = "v0.0.0-20240617180043-68d350f18fd4" hash = "sha256-ass/74EkCljwk7DaASDtK2zipn2cZv6tCLKvwONUWgY=" [mod."google.golang.org/grpc"] - version = "v1.64.0" - hash = "sha256-04Noi8lrzr+4ac2BA7KNXUXN/xZL/A2SsEpC2Hern84=" + version = "v1.64.1" + hash = "sha256-A1+kiePmeqRIdigryUGNJWZiILLacDPtMTEyO6CqDpY=" [mod."google.golang.org/protobuf"] version = "v1.34.2" hash = "sha256-nMTlrDEE2dbpWz50eQMPBQXCyQh4IdjrTIccaU0F3m0=" diff --git a/cmd/build/build.go b/cmd/build/build.go index 06d12939..7187ec45 100644 --- a/cmd/build/build.go +++ b/cmd/build/build.go @@ -60,6 +60,11 @@ var BuildCmd = &cobra.Command{ err = bgit.Add("bsf/") if err != nil { + if _, ok:=err.(*bgit.ErrFileNotAddedToVersionControl); ok{ + fmt.Println(styles.ErrorStyle.Render(err.Error() + "\n Please ensure all necessary files are added/committed in your version control system")) + fmt.Println(styles.HintStyle.Render("hint: run git add . ")) + os.Exit(1) + } fmt.Println(styles.ErrorStyle.Render("error: ", err.Error())) os.Exit(1) } diff --git a/go.mod b/go.mod index e0b71090..2f3c4ce1 100644 --- a/go.mod +++ b/go.mod @@ -23,6 +23,7 @@ require ( github.com/tidwall/gjson v1.17.1 golang.org/x/mod v0.17.0 golang.org/x/oauth2 v0.21.0 + golang.org/x/sync v0.7.0 golang.org/x/term v0.21.0 google.golang.org/grpc v1.64.1 google.golang.org/protobuf v1.34.2 @@ -39,7 +40,6 @@ require ( github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.0 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect - golang.org/x/sync v0.7.0 // indirect ) require ( diff --git a/pkg/git/git.go b/pkg/git/git.go index 1ca70beb..caead91a 100644 --- a/pkg/git/git.go +++ b/pkg/git/git.go @@ -1,12 +1,20 @@ package git import ( + "fmt" "os" "path/filepath" "strings" + "github.com/buildsafedev/bsf/pkg/langdetect" "github.com/go-git/go-git/v5" ) +type ErrFileNotAddedToVersionControl struct{ + fileName string +} +func (e *ErrFileNotAddedToVersionControl) Error() string { + return fmt.Sprint(e.fileName, " is not added to version control") +} // Add adds the path to the git work tree func Add(path string) error { @@ -40,7 +48,6 @@ func Add(path string) error { if leafDir != "" { path = leafDir + "/" + path } - // Add all changes to the working directory err = w.AddWithOptions(&git.AddOptions{ Path: path, @@ -48,6 +55,25 @@ func Add(path string) error { if err != nil { return err } + status, err :=w.Status() + if err !=nil{ + return err + } + pt, _, err:=langdetect.FindProjectType() + if err!=nil{ + return err + } + entryFile:= langdetect.GetEntryFileOfProject(pt) + _, exis:= status[entryFile] + // We need to assume that if file is not in status map, then it is comitted + if exis{ + fl:= status.File(entryFile) + if fl.Staging==git.Untracked{ + return &ErrFileNotAddedToVersionControl{ + fileName: entryFile, + } + } + } return nil } diff --git a/pkg/git/git_test.go b/pkg/git/git_test.go new file mode 100644 index 00000000..43790a5f --- /dev/null +++ b/pkg/git/git_test.go @@ -0,0 +1,296 @@ +package git + +import ( + "os" + "strconv" + "strings" + "testing" + + "github.com/go-git/go-git/v5" +) + +func getRootDirectory(dir string) string { + arr:= strings.Split(dir, "/") + var indexOfBsf int + for index, value:= range arr { + if value == "bsf"{ + indexOfBsf = index + break; + } + } + arr = arr[:indexOfBsf] + str:= strings.Join(arr, "/") + return str +} + +func initializeTestEnv(fileName string) (string, string, *os.File, error) { + oldDir, err:= os.Getwd(); + newDir:=getRootDirectory(oldDir) + if(err!=nil){ + return "","",nil, err + } + err=os.Mkdir(newDir+"/bsf-temp", 0777) + if err!=nil { + return "","",nil,err + } + err=os.Chdir(newDir+"/bsf-temp") + if err!=nil { + return "","",nil,err + } + file, err:=os.Create(fileName) + if err!=nil{ + return "","",nil,err + } + _,err = os.Create("sample.txt") + if err!=nil{ + return "","",nil,err + } + // Previous directory is returned so as to return to the project directory + return oldDir, newDir, file, nil +} + +func initiateGitEnv() (*git.Worktree, error) { + _, err:=git.PlainInit(".", false) + if err!=nil{ + return nil, err + } + r, err := git.PlainOpenWithOptions(".", &git.PlainOpenOptions{DetectDotGit: true}) + if err!=nil{ + return nil, err + } + w, err := r.Worktree() + if err!=nil{ + return nil, err + } + err = w.AddWithOptions(&git.AddOptions{ + Path: ".", + }) + if err != nil { + return nil, err + } + return w, nil +} + +func cleanTestEnv(oldDir string, newDir string) error { + err:=os.RemoveAll(newDir+"/bsf-temp") + if err!=nil{ + return err + } + err = os.Chdir(oldDir) + if err!=nil{ + return err + } + return nil +} + + +func TestGitAdd(t *testing.T){ + t.Run("git.Add() should run without any error for go module", func(t *testing.T) { + oldDir, newDir, file, err:=initializeTestEnv("go.mod") + if err!=nil{ + t.Fatal() + } + defer func (){ + err = cleanTestEnv(oldDir, newDir) + }() + goContent:=`module test` + _, err = file.WriteString(goContent) + if err!=nil{ + t.Fatalf(err.Error()) + } + _, err = initiateGitEnv() + if err!=nil{ + t.Fatalf(err.Error()) + } + err = Add("./") + }) + + tests:=[]struct{ + langName string + fileName string + }{ + { + langName: "Javascript", + fileName: "package-lock.json", + }, + { + langName: "Poetry", + fileName: "poetry.lock", + }, + { + langName: "Rust", + fileName: "Cargo.lock", + }, + } + for _,tt:= range tests{ + testName:= "git.Add() should run without any error for"+ tt.langName + t.Run(testName, func (t *testing.T) { + oldDir, newDir, _, err:=initializeTestEnv(tt.fileName) + if err!=nil{ + t.Fatal() + } + defer func (){ + err = cleanTestEnv(oldDir, newDir) + }() + _, err = initiateGitEnv() + if err!=nil{ + t.Fatalf(err.Error()) + } + errors:=Add("./") + if errors!=nil{ + t.Errorf("want nil but found error: %s", errors.Error()) + } + }) + } + + t.Run("git.Add() should throw error of go.mod not added to version control", func(t *testing.T){ + oldDir, newDir, file, err:=initializeTestEnv("go.mod") + if err!=nil{ + t.Fatal() + } + defer func (){ + err = cleanTestEnv(oldDir, newDir) + }() + goContent:=`module test` + _, err = file.WriteString(goContent) + if err!=nil{ + t.Fatalf(err.Error()) + } + _, err = git.PlainInit(".", false) + if err!=nil{ + t.Fatalf(err.Error()) + } + _, err = git.PlainOpenWithOptions(".", &git.PlainOpenOptions{DetectDotGit: true}) + if err!=nil{ + t.Fatalf(err.Error()) + } + errors:= Add("sample.txt") + if errors==nil{ + t.Errorf("want error but found nil") + } + if _, ok:=errors.(*ErrFileNotAddedToVersionControl); !ok{ + t.Errorf("want ErrFilesNotAddedToVersionControl but found %s", errors.Error()) + } + }) + + for _,tt:= range tests{ + testName:= "git.Add() should throw error of file not added to version control for "+tt.langName + t.Run(testName, func (t *testing.T) { + oldDir, newDir, _, err:=initializeTestEnv(tt.fileName) + if err!=nil{ + t.Fatal() + } + defer func (){ + err = cleanTestEnv(oldDir, newDir) + }() + _, err = git.PlainInit(".", false) + if err!=nil{ + t.Fatalf(err.Error()) + } + _, err = git.PlainOpenWithOptions(".", &git.PlainOpenOptions{DetectDotGit: true}) + if err!=nil{ + t.Fatalf(err.Error()) + } + errors:= Add("sample.txt") + if errors==nil{ + t.Errorf("want error but found nil") + } + if _, ok:=errors.(*ErrFileNotAddedToVersionControl); !ok{ + t.Errorf("want ErrFilesNotAddedToVersionControl but found %s", errors.Error()) + } + }) + } +} + +func TestGitIgnore(t *testing.T){ + t.Run("git.Ignore() creates .gitignore and adds the path to it", func(t *testing.T) { + oldDir, newDir, _, err:=initializeTestEnv("go.mod") + if err!=nil{ + t.Errorf(err.Error()) + } + defer func (){ + err = cleanTestEnv(oldDir, newDir) + }() + err = Ignore("sample.txt") + file, err:=os.ReadFile(".gitignore") + if err!=nil{ + t.Fatal() + } + want:= strings.Contains(string(file), "sample.txt") + if want==false{ + t.Errorf("want true recieved true") + } + }) + t.Run("git.Ignore() appends the path in already created .gitignore", func(t *testing.T) { + oldDir, newDir, _, err:=initializeTestEnv("go.mod") + if err!=nil{ + t.Errorf(err.Error()) + } + defer func (){ + err = cleanTestEnv(oldDir, newDir) + }() + fl, err:= os.Create(".gitignore") + if err!=nil{ + t.Fatal() + } + _,err = fl.WriteString(` + /path/to/be/added/1 + /path/to/be/added/2 + /path/to/be/added/3 + `) + err = Ignore("sample.txt") + file, err:=os.ReadFile(".gitignore") + if err!=nil{ + t.Fatal() + } + want:= strings.Contains(string(file), "sample.txt") + if want==false{ + t.Errorf("want true recieved false") + } + for i:=1; i<=3; i++{ + want:=strings.Contains(string(file), "/path/to/be/added/"+strconv.Itoa(i)) + if want==false{ + t.Errorf("want true recieved false") + } + } + }) + t.Run("git.Ignore() adds nothing for already added path", func(t *testing.T) { + oldDir, newDir, _, err:=initializeTestEnv("go.mod") + if err!=nil{ + t.Errorf(err.Error()) + } + defer func (){ + err = cleanTestEnv(oldDir, newDir) + }() + fl, err:= os.Create(".gitignore") + if err!=nil{ + t.Fatal() + } + _, err = fl.WriteString(` + /path/to/be/added/1 + /path/to/be/added/2 + /path/to/be/added/3 + sample.txt + `) + err = Ignore("sample.txt") + file, err:=os.ReadFile(".gitignore") + if err!=nil{ + t.Fatal() + } + want:= strings.Contains(string(file), "sample.txt") + if want==false{ + t.Errorf("want true recieved false") + } + for i:=1; i<=3; i++{ + want:=strings.Contains(string(file), "/path/to/be/added/"+strconv.Itoa(i)) + if want==false{ + t.Errorf("want true recieved false") + } + } + count:=strings.Count(string(file), "sample.txt") + if count!=1{ + t.Errorf("want count 1 recieved %d", count) + } + }) +} + diff --git a/pkg/langdetect/langdetect.go b/pkg/langdetect/langdetect.go index f13945c9..a5d47d98 100644 --- a/pkg/langdetect/langdetect.go +++ b/pkg/langdetect/langdetect.go @@ -101,3 +101,18 @@ func binaryFromModule(mod *modfile.File) string { // Otherwise, return the last part return lastPart } + +func GetEntryFileOfProject(pt ProjectType) string { + var fileName string + switch pt{ + case GoModule: + fileName = "go.mod" + case RustCargo: + fileName = "Cargo.lock" + case JsNpm: + fileName = "package-lock.json" + case PythonPoetry: + fileName = "poetry.lock" + } + return fileName +} diff --git a/pkg/langdetect/langdetect_test.go b/pkg/langdetect/langdetect_test.go index 8382080b..80b3047e 100644 --- a/pkg/langdetect/langdetect_test.go +++ b/pkg/langdetect/langdetect_test.go @@ -79,3 +79,40 @@ func TestBinaryFromModule(t *testing.T) { }) } } + +func TestGetEntryFileOfProject(t *testing.T){ + tests:= []struct{ + name string + pType ProjectType + wantFileName string + }{ + { + name: "Test for Rust", + pType: RustCargo, + wantFileName: "Cargo.lock", + }, + { + name: "Test for Go", + pType: GoModule, + wantFileName: "go.mod", + }, + { + name: "Test for Js", + pType: JsNpm, + wantFileName: "package-lock.json", + }, + { + name: "Test for Poetry", + pType: PythonPoetry, + wantFileName: "poetry.lock", + }, + } + + for _,tt:= range tests{ + t.Run(tt.name, func(t *testing.T) { + if wantFileName:= GetEntryFileOfProject(tt.pType); wantFileName!= tt.wantFileName{ + t.Errorf("GetEntryFileOfProject() = %v, want %v", wantFileName, tt.wantFileName) + } + }) + } +} diff --git a/pkg/nix/cmd/build.go b/pkg/nix/cmd/build.go index 1279ab5c..1ab219a8 100644 --- a/pkg/nix/cmd/build.go +++ b/pkg/nix/cmd/build.go @@ -1,9 +1,13 @@ package cmd import ( + "bufio" "fmt" + "io" "os" "os/exec" + + "golang.org/x/sync/errgroup" ) // Build invokes nix build to build the project @@ -13,9 +17,24 @@ func Build(dir string, attribute string) error { } cmd := exec.Command("nix", "build", attribute, "-o", dir) - cmd.Stdout = os.Stdout - // TODO: in future- we can pipe to stderr pipe and modify error messages to be understandable by the user - cmd.Stderr = os.Stderr + stderr, err := cmd.StderrPipe() + if err != nil { + return err + } + stdout, err := cmd.StdoutPipe() + if err != nil { + return err + } + + g:= new(errgroup.Group) + + g.Go(func()error{ + return ManageStdErr(stderr) + }) + + g.Go(func()error{ + return ManageStdOutput(stdout) + }) if err := cmd.Start(); err != nil { return fmt.Errorf("error starting command: %v", err) @@ -24,5 +43,34 @@ func Build(dir string, attribute string) error { if err := cmd.Wait(); err != nil { return fmt.Errorf("error waiting for command: %v", err) } + + return g.Wait() +} + +func ManageStdErr(stderr io.ReadCloser) error { + scanner:= bufio.NewScanner(stderr) + for scanner.Scan() { + stdErr:= scanner.Text() + dir, err:= os.Getwd() + if err!=nil{ + return err + } + warning:= fmt.Sprintf("warning: Git tree '%s' is dirty", dir) + if stdErr==warning{ + stdErr = fmt.Sprintf("warning: Git tree '%s' is dirty.\nThis implies you have not checked-in files in the git work tree (hint: git add)", dir) + } + stdErr = fmt.Sprint(stdErr, "\n") + os.Stderr.Write([]byte(stdErr)) + } return nil } + +func ManageStdOutput(stdout io.ReadCloser) error { + scanner:= bufio.NewScanner(stdout) + for scanner.Scan() { + stdOut:= scanner.Text() + stdOut = fmt.Sprint(stdOut, "\n") + os.Stdout.Write([]byte(stdOut)) + } + return nil +} \ No newline at end of file diff --git a/pkg/nix/cmd/build_test.go b/pkg/nix/cmd/build_test.go new file mode 100644 index 00000000..546134e1 --- /dev/null +++ b/pkg/nix/cmd/build_test.go @@ -0,0 +1,106 @@ +package cmd + +import ( + "bytes" + "fmt" + "io" + "math/rand" + "os" + "strings" + "testing" +) + + + +func getRandomStringForTest() []string { + str:= "Example string" + rand1:= fmt.Sprint(str, rand.Int()) + rand2:= fmt.Sprint(str, rand.Int()) + rand3:= fmt.Sprint(str, rand.Int()) + return []string{rand1, rand2, rand3} +} + +func TestManageStdErr(t *testing.T){ + t.Run("Test for piping stderr without warning", func(t *testing.T) { + strArr:= getRandomStringForTest() + origStderr := os.Stderr + defer func() { os.Stderr = origStderr }() + r, w, err := os.Pipe() + if err!=nil{ + t.Fatalf(err.Error()) + } + os.Stderr = w + inputStr:= strings.Join(strArr, "\n") + input := strings.NewReader(inputStr) + err = ManageStdErr(io.NopCloser(input)) + if err != nil { + t.Fatalf("ManageStdErr returned error: %v", err) + } + w.Close() + var buf bytes.Buffer + _, err = io.Copy(&buf, r) + if err!=nil{ + t.Fatalf(err.Error()) + } + str:= buf.String() + expectedStr := strings.Join(strArr, "\n") + "\n" + if str!=expectedStr{ + t.Errorf("ManageStdErr want %v got %v", inputStr, str) + } + }) + + t.Run("Test for piping stderr with warning", func(t *testing.T) { + origStderr := os.Stderr + defer func() { os.Stderr = origStderr }() + r, w, _ := os.Pipe() + os.Stderr = w + dir, err:= os.Getwd() + if err!=nil{ + t.Fatalf(err.Error()) + } + inputStr:= fmt.Sprintf("warning: Git tree '%s' is dirty", dir) + input := strings.NewReader(inputStr) + err = ManageStdErr(io.NopCloser(input)) + if err!=nil{ + t.Fatalf(err.Error()) + } + w.Close() + var buf bytes.Buffer + _,err = io.Copy(&buf, r) + if err!=nil{ + t.Fatalf(err.Error()) + } + str:= buf.String() + expectedStr := fmt.Sprintf("warning: Git tree '%s' is dirty.\nThis implies you have not checked-in files in the git work tree (hint: git add)\n", dir) + if str!=expectedStr{ + t.Errorf("ManageStdErr want %v got %v", inputStr, str) + } + }) +} + +func TestManageStdOutput(t *testing.T){ + t.Run("Test for piping stderr without warning", func(t *testing.T) { + strArr:= getRandomStringForTest() + origStdout := os.Stdout + defer func() { os.Stdout = origStdout }() + r, w, _ := os.Pipe() + os.Stdout = w + inputStr:= strings.Join(strArr, "\n") + input := strings.NewReader(inputStr) + err := ManageStdOutput(io.NopCloser(input)) + if err != nil { + t.Fatalf("ManageStdOut returned error: %v", err) + } + w.Close() + var buf bytes.Buffer + _, err = io.Copy(&buf, r) + if err!=nil{ + t.Fatalf(err.Error()) + } + str:= buf.String() + expectedStr := strings.Join(strArr, "\n") + "\n" + if str!=expectedStr{ + t.Errorf("ManageStdOutput want %v got %v", inputStr, str) + } + }) +} \ No newline at end of file