diff --git a/cli/go.mod b/cli/go.mod index 151fcb9a5946e..04ba14feaca8c 100644 --- a/cli/go.mod +++ b/cli/go.mod @@ -30,6 +30,7 @@ require ( github.com/pyr-sh/dag v1.0.0 github.com/sabhiram/go-gitignore v0.0.0-20201211210132-54b8a0bf510f github.com/sourcegraph/go-diff v0.6.1 + github.com/spf13/afero v1.8.2 github.com/spf13/cobra v1.3.0 github.com/stretchr/testify v1.7.0 github.com/yosuke-furukawa/json5 v0.1.1 diff --git a/cli/go.sum b/cli/go.sum index af92ceb7a0a97..ffaa1c04a2529 100644 --- a/cli/go.sum +++ b/cli/go.sum @@ -3,6 +3,7 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= @@ -15,6 +16,7 @@ cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOY cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= @@ -45,6 +47,7 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/AlecAivazis/survey/v2 v2.2.12 h1:5a07y93zA6SZ09gOa9wLVLznF5zTJMQ+pJ3cZK4IuO8= github.com/AlecAivazis/survey/v2 v2.2.12/go.mod h1:6d4saEvBsfSHXeN1a5OA5m2+HJ2LuVokllnC77pAIKI= @@ -205,6 +208,7 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= @@ -218,6 +222,7 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= +github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= @@ -351,6 +356,7 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= @@ -387,6 +393,8 @@ github.com/sourcegraph/go-diff v0.6.1/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag07 github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo= +github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v1.3.0 h1:R7cSvGu+Vv+qX0gW5R/85dx2kmmJT5z5NM8ifdYjdn0= github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4= @@ -438,8 +446,11 @@ golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa h1:idItI2DDfCokpg0N51B2VtiLdJ4vAuXC9fnCb2gACo4= +golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -510,6 +521,7 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= @@ -591,6 +603,7 @@ golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -598,6 +611,7 @@ golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -677,6 +691,7 @@ golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= @@ -761,7 +776,9 @@ google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= diff --git a/cli/internal/globby/globby.go b/cli/internal/globby/globby.go index a3b17913c64af..576dc1e21ec21 100644 --- a/cli/internal/globby/globby.go +++ b/cli/internal/globby/globby.go @@ -1,15 +1,21 @@ package globby import ( - "github.com/vercel/turborepo/cli/internal/fs" - + "os" "path/filepath" "strings" "github.com/bmatcuk/doublestar/v4" + "github.com/spf13/afero" ) +var _osFS = afero.NewOsFs() + func GlobFiles(basePath string, includePatterns []string, excludePatterns []string) []string { + return globFilesFs(_osFS, basePath, includePatterns, excludePatterns) +} + +func globFilesFs(fs afero.Fs, basePath string, includePatterns []string, excludePatterns []string) []string { var include []string var exclude []string var result []string @@ -24,8 +30,10 @@ func GlobFiles(basePath string, includePatterns []string, excludePatterns []stri includePattern := "{" + strings.Join(include, ",") + "}" excludePattern := "{" + strings.Join(exclude, ",") + "}" - _ = fs.Walk(basePath, func(p string, isDir bool) error { - if val, _ := doublestar.PathMatch(excludePattern, p); val { + + _ = afero.Walk(fs, basePath, func(path string, info os.FileInfo, err error) error { + isDir := info.IsDir() + if val, _ := doublestar.PathMatch(excludePattern, path); val { if isDir { return filepath.SkipDir } @@ -36,8 +44,8 @@ func GlobFiles(basePath string, includePatterns []string, excludePatterns []stri return nil } - if val, _ := doublestar.PathMatch(includePattern, p); val || len(includePatterns) == 0 { - result = append(result, p) + if val, _ := doublestar.PathMatch(includePattern, path); val || len(includePatterns) == 0 { + result = append(result, path) } return nil diff --git a/cli/internal/globby/globby_test.go b/cli/internal/globby/globby_test.go new file mode 100644 index 0000000000000..d645d4d2eb1ff --- /dev/null +++ b/cli/internal/globby/globby_test.go @@ -0,0 +1,520 @@ +package globby + +import ( + "path/filepath" + "reflect" + "testing" + + "github.com/spf13/afero" +) + +// setup prepares the test file system contents and returns the file system. +func setup(files []string) afero.Fs { + fs := afero.NewMemMapFs() + + for _, file := range files { + // We don't need the handle, we don't need the error. + // We'll know if it errors because the tests will not pass. + // nolint:errcheck + fs.Create(file) + } + + return fs +} + +func TestGlobFilesFs(t *testing.T) { + type args struct { + basePath string + includePatterns []string + excludePatterns []string + } + tests := []struct { + name string + files []string + args args + want []string + }{ + { + name: "hello world", + files: []string{"/test.txt"}, + args: args{ + basePath: "/", + includePatterns: []string{"*.txt"}, + excludePatterns: []string{}, + }, + want: []string{"/test.txt"}, + }, + { + name: "finding workspace package.json files", + files: []string{ + "/external/file.txt", + "/repos/some-app/apps/docs/package.json", + "/repos/some-app/apps/web/package.json", + "/repos/some-app/bower_components/readline/package.json", + "/repos/some-app/examples/package.json", + "/repos/some-app/node_modules/gulp/bower_components/readline/package.json", + "/repos/some-app/node_modules/react/package.json", + "/repos/some-app/package.json", + "/repos/some-app/packages/colors/package.json", + "/repos/some-app/packages/faker/package.json", + "/repos/some-app/packages/left-pad/package.json", + "/repos/some-app/test/mocks/kitchen-sink/package.json", + "/repos/some-app/tests/mocks/kitchen-sink/package.json", + }, + args: args{ + basePath: "/repos/some-app/", + includePatterns: []string{"packages/*/package.json", "apps/*/package.json"}, + excludePatterns: []string{"**/node_modules/", "**/bower_components/", "**/test/", "**/tests/"}, + }, + want: []string{ + "/repos/some-app/apps/docs/package.json", + "/repos/some-app/apps/web/package.json", + "/repos/some-app/packages/colors/package.json", + "/repos/some-app/packages/faker/package.json", + "/repos/some-app/packages/left-pad/package.json", + }, + }, + { + name: "excludes unexpected workspace package.json files", + files: []string{ + "/external/file.txt", + "/repos/some-app/apps/docs/package.json", + "/repos/some-app/apps/web/package.json", + "/repos/some-app/bower_components/readline/package.json", + "/repos/some-app/examples/package.json", + "/repos/some-app/node_modules/gulp/bower_components/readline/package.json", + "/repos/some-app/node_modules/react/package.json", + "/repos/some-app/package.json", + "/repos/some-app/packages/colors/package.json", + "/repos/some-app/packages/faker/package.json", + "/repos/some-app/packages/left-pad/package.json", + "/repos/some-app/test/mocks/spanish-inquisition/package.json", + "/repos/some-app/tests/mocks/spanish-inquisition/package.json", + }, + args: args{ + basePath: "/repos/some-app/", + includePatterns: []string{"**/package.json"}, + excludePatterns: []string{"**/node_modules/", "**/bower_components/", "**/test/", "**/tests/"}, + }, + want: []string{ + "/repos/some-app/apps/docs/package.json", + "/repos/some-app/apps/web/package.json", + "/repos/some-app/examples/package.json", + "/repos/some-app/package.json", + "/repos/some-app/packages/colors/package.json", + "/repos/some-app/packages/faker/package.json", + "/repos/some-app/packages/left-pad/package.json", + }, + }, + { + name: "nested packages work", + files: []string{ + "/external/file.txt", + "/repos/some-app/apps/docs/package.json", + "/repos/some-app/apps/web/package.json", + "/repos/some-app/bower_components/readline/package.json", + "/repos/some-app/examples/package.json", + "/repos/some-app/node_modules/gulp/bower_components/readline/package.json", + "/repos/some-app/node_modules/react/package.json", + "/repos/some-app/package.json", + "/repos/some-app/packages/xzibit/package.json", + "/repos/some-app/packages/xzibit/node_modules/street-legal/package.json", + "/repos/some-app/packages/xzibit/node_modules/paint-colors/package.json", + "/repos/some-app/packages/xzibit/packages/yo-dawg/package.json", + "/repos/some-app/packages/xzibit/packages/yo-dawg/node_modules/meme/package.json", + "/repos/some-app/packages/xzibit/packages/yo-dawg/node_modules/yo-dawg/package.json", + "/repos/some-app/packages/colors/package.json", + "/repos/some-app/packages/faker/package.json", + "/repos/some-app/packages/left-pad/package.json", + "/repos/some-app/test/mocks/spanish-inquisition/package.json", + "/repos/some-app/tests/mocks/spanish-inquisition/package.json", + }, + args: args{ + basePath: "/repos/some-app/", + includePatterns: []string{"packages/**/package.json"}, + excludePatterns: []string{"**/node_modules/", "**/bower_components/", "**/test/", "**/tests/"}, + }, + want: []string{ + "/repos/some-app/packages/colors/package.json", + "/repos/some-app/packages/faker/package.json", + "/repos/some-app/packages/left-pad/package.json", + "/repos/some-app/packages/xzibit/package.json", + "/repos/some-app/packages/xzibit/packages/yo-dawg/package.json", + }, + }, + { + name: "includes do not override excludes", + files: []string{ + "/external/file.txt", + "/repos/some-app/apps/docs/package.json", + "/repos/some-app/apps/web/package.json", + "/repos/some-app/bower_components/readline/package.json", + "/repos/some-app/examples/package.json", + "/repos/some-app/node_modules/gulp/bower_components/readline/package.json", + "/repos/some-app/node_modules/react/package.json", + "/repos/some-app/package.json", + "/repos/some-app/packages/xzibit/package.json", + "/repos/some-app/packages/xzibit/node_modules/street-legal/package.json", + "/repos/some-app/packages/xzibit/node_modules/paint-colors/package.json", + "/repos/some-app/packages/xzibit/packages/yo-dawg/package.json", + "/repos/some-app/packages/xzibit/packages/yo-dawg/node_modules/meme/package.json", + "/repos/some-app/packages/xzibit/packages/yo-dawg/node_modules/yo-dawg/package.json", + "/repos/some-app/packages/colors/package.json", + "/repos/some-app/packages/faker/package.json", + "/repos/some-app/packages/left-pad/package.json", + "/repos/some-app/test/mocks/spanish-inquisition/package.json", + "/repos/some-app/tests/mocks/spanish-inquisition/package.json", + }, + args: args{ + basePath: "/repos/some-app/", + includePatterns: []string{"packages/**/package.json", "tests/mocks/*/package.json"}, + excludePatterns: []string{"**/node_modules/", "**/bower_components/", "**/test/", "**/tests/"}, + }, + want: []string{ + "/repos/some-app/packages/colors/package.json", + "/repos/some-app/packages/faker/package.json", + "/repos/some-app/packages/left-pad/package.json", + "/repos/some-app/packages/xzibit/package.json", + "/repos/some-app/packages/xzibit/packages/yo-dawg/package.json", + }, + }, + { + name: "output globbing grabs the desired content", + files: []string{ + "/external/file.txt", + "/repos/some-app/src/index.js", + "/repos/some-app/public/src/css/index.css", + "/repos/some-app/.turbo/turbo-build.log", + "/repos/some-app/.turbo/somebody-touched-this-file-into-existence.txt", + "/repos/some-app/.next/log.txt", + "/repos/some-app/.next/cache/db6a76a62043520e7aaadd0bb2104e78.txt", + "/repos/some-app/dist/index.html", + "/repos/some-app/dist/js/index.js", + "/repos/some-app/dist/js/lib.js", + "/repos/some-app/dist/js/node_modules/browserify.js", + "/repos/some-app/public/dist/css/index.css", + "/repos/some-app/public/dist/images/rick_astley.jpg", + }, + args: args{ + basePath: "/repos/some-app/", + includePatterns: []string{".turbo/turbo-build.log", "dist/**", ".next/**", "public/dist/**"}, + excludePatterns: []string{}, + }, + want: []string{ + "/repos/some-app/.next/cache/db6a76a62043520e7aaadd0bb2104e78.txt", + "/repos/some-app/.next/log.txt", + "/repos/some-app/.turbo/turbo-build.log", + "/repos/some-app/dist/index.html", + "/repos/some-app/dist/js/index.js", + "/repos/some-app/dist/js/lib.js", + "/repos/some-app/dist/js/node_modules/browserify.js", + "/repos/some-app/public/dist/css/index.css", + "/repos/some-app/public/dist/images/rick_astley.jpg", + }, + }, + { + name: "passing ** captures all children", + files: []string{ + "/repos/some-app/dist/index.html", + "/repos/some-app/dist/js/index.js", + "/repos/some-app/dist/js/lib.js", + "/repos/some-app/dist/js/node_modules/browserify.js", + }, + args: args{ + basePath: "/repos/some-app/", + includePatterns: []string{"dist/**"}, + excludePatterns: []string{}, + }, + want: []string{ + "/repos/some-app/dist/index.html", + "/repos/some-app/dist/js/index.js", + "/repos/some-app/dist/js/lib.js", + "/repos/some-app/dist/js/node_modules/browserify.js", + }, + }, + { + name: "passing just a directory captures no children", + files: []string{ + "/repos/some-app/dist/index.html", + "/repos/some-app/dist/js/index.js", + "/repos/some-app/dist/js/lib.js", + "/repos/some-app/dist/js/node_modules/browserify.js", + }, + args: args{ + basePath: "/repos/some-app/", + includePatterns: []string{"dist"}, + excludePatterns: []string{}, + }, + want: []string{}, + }, + { + name: "redundant includes do not duplicate", + files: []string{ + "/repos/some-app/dist/index.html", + "/repos/some-app/dist/js/index.js", + "/repos/some-app/dist/js/lib.js", + "/repos/some-app/dist/js/node_modules/browserify.js", + }, + args: args{ + basePath: "/repos/some-app/", + includePatterns: []string{"**/*", "dist/**"}, + excludePatterns: []string{}, + }, + want: []string{ + "/repos/some-app/dist/index.html", + "/repos/some-app/dist/js/index.js", + "/repos/some-app/dist/js/lib.js", + "/repos/some-app/dist/js/node_modules/browserify.js", + }, + }, + { + name: "exclude everything, include everything", + files: []string{ + "/repos/some-app/dist/index.html", + "/repos/some-app/dist/js/index.js", + "/repos/some-app/dist/js/lib.js", + "/repos/some-app/dist/js/node_modules/browserify.js", + }, + args: args{ + basePath: "/repos/some-app/", + includePatterns: []string{"**"}, + excludePatterns: []string{"**"}, + }, + want: []string{}, + }, + { + name: "passing just a directory to exclude prevents capture of children", + files: []string{ + "/repos/some-app/dist/index.html", + "/repos/some-app/dist/js/index.js", + "/repos/some-app/dist/js/lib.js", + "/repos/some-app/dist/js/node_modules/browserify.js", + }, + args: args{ + basePath: "/repos/some-app/", + includePatterns: []string{"dist/**"}, + excludePatterns: []string{"dist/js"}, + }, + want: []string{ + "/repos/some-app/dist/index.html", + }, + }, + { + name: "passing ** to exclude prevents capture of children", + files: []string{ + "/repos/some-app/dist/index.html", + "/repos/some-app/dist/js/index.js", + "/repos/some-app/dist/js/lib.js", + "/repos/some-app/dist/js/node_modules/browserify.js", + }, + args: args{ + basePath: "/repos/some-app/", + includePatterns: []string{"dist/**"}, + excludePatterns: []string{"dist/js/**"}, + }, + want: []string{ + "/repos/some-app/dist/index.html", + }, + }, + { + name: "exclude everything with folder . does not apply at base path", + files: []string{ + "/repos/some-app/dist/index.html", + "/repos/some-app/dist/js/index.js", + "/repos/some-app/dist/js/lib.js", + "/repos/some-app/dist/js/node_modules/browserify.js", + }, + args: args{ + basePath: "/repos/some-app/", + includePatterns: []string{"**"}, + excludePatterns: []string{"./"}, + }, + want: []string{ + "/repos/some-app/dist/index.html", + "/repos/some-app/dist/js/index.js", + "/repos/some-app/dist/js/lib.js", + "/repos/some-app/dist/js/node_modules/browserify.js", + }, + }, + { + name: "exclude everything with traversal applies at a non-base path", + files: []string{ + "/repos/some-app/dist/index.html", + "/repos/some-app/dist/js/index.js", + "/repos/some-app/dist/js/lib.js", + "/repos/some-app/dist/js/node_modules/browserify.js", + }, + args: args{ + basePath: "/repos/some-app/", + includePatterns: []string{"**"}, + excludePatterns: []string{"./dist"}, + }, + want: []string{}, + }, + { + name: "exclude everything with folder traversal (..) does not apply at base path", + files: []string{ + "/repos/some-app/dist/index.html", + "/repos/some-app/dist/js/index.js", + "/repos/some-app/dist/js/lib.js", + "/repos/some-app/dist/js/node_modules/browserify.js", + }, + args: args{ + basePath: "/repos/some-app/", + includePatterns: []string{"**"}, + excludePatterns: []string{"dist/../"}, + }, + want: []string{ + "/repos/some-app/dist/index.html", + "/repos/some-app/dist/js/index.js", + "/repos/some-app/dist/js/lib.js", + "/repos/some-app/dist/js/node_modules/browserify.js", + }, + }, + { + name: "how do globs even work bad glob microformat", + files: []string{ + "/repos/some-app/dist/index.html", + "/repos/some-app/dist/js/index.js", + "/repos/some-app/dist/js/lib.js", + "/repos/some-app/dist/js/node_modules/browserify.js", + }, + args: args{ + basePath: "/repos/some-app/", + includePatterns: []string{"**/**/**"}, + excludePatterns: []string{}, + }, + want: []string{ + "/repos/some-app/dist/index.html", + "/repos/some-app/dist/js/index.js", + "/repos/some-app/dist/js/lib.js", + "/repos/some-app/dist/js/node_modules/browserify.js", + }, + }, + { + name: "directory traversal stops at base path", + files: []string{ + "/repos/spanish-inquisition/index.html", + "/repos/some-app/dist/index.html", + "/repos/some-app/dist/js/index.js", + "/repos/some-app/dist/js/lib.js", + "/repos/some-app/dist/js/node_modules/browserify.js", + }, + args: args{ + basePath: "/repos/some-app/", + includePatterns: []string{"../spanish-inquisition/**", "dist/**"}, + excludePatterns: []string{}, + }, + want: []string{ + "/repos/some-app/dist/index.html", + "/repos/some-app/dist/js/index.js", + "/repos/some-app/dist/js/lib.js", + "/repos/some-app/dist/js/node_modules/browserify.js", + }, + }, + { + name: "globs and traversal and globs do not cross base path", + files: []string{ + "/repos/spanish-inquisition/index.html", + "/repos/some-app/dist/index.html", + "/repos/some-app/dist/js/index.js", + "/repos/some-app/dist/js/lib.js", + "/repos/some-app/dist/js/node_modules/browserify.js", + }, + args: args{ + basePath: "/repos/some-app/", + includePatterns: []string{"**/../../spanish-inquisition/**"}, + excludePatterns: []string{}, + }, + want: []string{}, + }, + { + name: "traversal works within base path", + files: []string{ + "/repos/some-app/dist/index.html", + "/repos/some-app/dist/js/index.js", + "/repos/some-app/dist/js/lib.js", + "/repos/some-app/dist/js/node_modules/browserify.js", + }, + args: args{ + basePath: "/repos/some-app/", + includePatterns: []string{"dist/js/../**"}, + excludePatterns: []string{}, + }, + want: []string{ + "/repos/some-app/dist/index.html", + "/repos/some-app/dist/js/index.js", + "/repos/some-app/dist/js/lib.js", + "/repos/some-app/dist/js/node_modules/browserify.js", + }, + }, + { + name: "self-references (.) work", + files: []string{ + "/repos/some-app/dist/index.html", + "/repos/some-app/dist/js/index.js", + "/repos/some-app/dist/js/lib.js", + "/repos/some-app/dist/js/node_modules/browserify.js", + }, + args: args{ + basePath: "/repos/some-app/", + includePatterns: []string{"dist/./././**"}, + excludePatterns: []string{}, + }, + want: []string{ + "/repos/some-app/dist/index.html", + "/repos/some-app/dist/js/index.js", + "/repos/some-app/dist/js/lib.js", + "/repos/some-app/dist/js/node_modules/browserify.js", + }, + }, + { + name: "depth of 1 includes does not capture folders", + files: []string{ + "/repos/some-app/package.json", + "/repos/some-app/dist/index.html", + "/repos/some-app/dist/js/index.js", + "/repos/some-app/dist/js/lib.js", + "/repos/some-app/dist/js/node_modules/browserify.js", + }, + args: args{ + basePath: "/repos/some-app/", + includePatterns: []string{"*"}, + excludePatterns: []string{}, + }, + want: []string{"/repos/some-app/package.json"}, + }, + { + name: "depth of 1 excludes prevents capturing folders", + files: []string{ + "/repos/some-app/package.json", + "/repos/some-app/dist/index.html", + "/repos/some-app/dist/js/index.js", + "/repos/some-app/dist/js/lib.js", + "/repos/some-app/dist/js/node_modules/browserify.js", + }, + args: args{ + basePath: "/repos/some-app/", + includePatterns: []string{"**"}, + excludePatterns: []string{"dist/*"}, + }, + want: []string{"/repos/some-app/package.json"}, + }, + } + for _, tt := range tests { + fs := setup(tt.files) + + t.Run(tt.name, func(t *testing.T) { + got := globFilesFs(fs, tt.args.basePath, tt.args.includePatterns, tt.args.excludePatterns) + + gotToSlash := make([]string, len(got)) + for index, path := range got { + gotToSlash[index] = filepath.ToSlash(path) + } + + if !reflect.DeepEqual(gotToSlash, tt.want) { + t.Errorf("globFilesFs() = %v, want %v", gotToSlash, tt.want) + } + }) + } +} diff --git a/cli/internal/run/run.go b/cli/internal/run/run.go index 2a45ac9cdf8bb..f8299662d9b93 100644 --- a/cli/internal/run/run.go +++ b/cli/internal/run/run.go @@ -1006,8 +1006,19 @@ func (e *execContext) exec(pt *packageTask, deps dag.Set) error { outputs := pt.HashableOutputs() targetLogger.Debug("caching output", "outputs", outputs) ignore := []string{} - filesToBeCached := globby.GlobFiles(pt.pkg.Dir, outputs, ignore) - if err := e.turboCache.Put(pt.pkg.Dir, hash, int(time.Since(cmdTime).Milliseconds()), filesToBeCached); err != nil { + filesToBeCached := globby.GlobFiles(filepath.Join(e.rs.Opts.cwd, pt.pkg.Dir), outputs, ignore) + relativePaths := make([]string, len(filesToBeCached)) + + for index, value := range filesToBeCached { + relativePath, err := filepath.Rel(e.rs.Opts.cwd, value) + if err != nil { + e.logError(targetLogger, "", fmt.Errorf("File path cannot be made relative: %w", err)) + continue + } + relativePaths[index] = relativePath + } + + if err := e.turboCache.Put(pt.pkg.Dir, hash, int(time.Since(cmdTime).Milliseconds()), relativePaths); err != nil { e.logError(targetLogger, "", fmt.Errorf("error caching output: %w", err)) } }