Skip to content

Commit 85c4c42

Browse files
authored
Merge pull request #332 from gitpod-io/wv/fix-cached-package-dependency-validation
fix: validate all transitive dependencies for cached packages
2 parents 7e12607 + 2842154 commit 85c4c42

File tree

2 files changed

+41
-15
lines changed

2 files changed

+41
-15
lines changed

pkg/leeway/build.go

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1470,17 +1470,19 @@ func (p *Package) packagesToDownload(inLocalCache map[*Package]struct{}, inRemot
14701470
// if all its dependencies are also available. This prevents build failures when
14711471
// a package is cached but one of its dependencies failed to download.
14721472
func validateDependenciesAvailable(p *Package, localCache cache.LocalCache, pkgstatus map[*Package]PackageBuildStatus) bool {
1473-
var deps []*Package
1474-
switch p.Type {
1475-
case YarnPackage, GoPackage:
1476-
// Go and Yarn packages need all transitive dependencies
1477-
deps = p.GetTransitiveDependencies()
1478-
case GenericPackage, DockerPackage:
1479-
// Generic and Docker packages only need direct dependencies
1480-
deps = p.GetDependencies()
1481-
default:
1482-
deps = p.GetDependencies()
1483-
}
1473+
// Always check ALL transitive dependencies for cached packages.
1474+
// This is necessary because a cached package might be used by a Go/Yarn package
1475+
// that needs all transitive dependencies available. If we only check direct
1476+
// dependencies for Generic/Docker packages, a Go package consuming them would
1477+
// fail during prep when it tries to access a missing transitive dependency.
1478+
//
1479+
// Example: GoPackage X -> GenericPackage A -> DockerPackage B
1480+
// If A is cached but B is not, and we only check A's direct deps (B),
1481+
// we'd correctly invalidate A. But if the chain is:
1482+
// GoPackage X -> GenericPackage A -> GenericPackage B -> DockerPackage C
1483+
// And A is cached, B is cached, but C is not, we need to check transitively
1484+
// to ensure C is available, otherwise X's build will fail.
1485+
deps := p.GetTransitiveDependencies()
14841486

14851487
for _, dep := range deps {
14861488
if dep.Ephemeral {

pkg/leeway/build_validate_deps_test.go

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -216,24 +216,48 @@ func TestValidateDependenciesAvailable(t *testing.T) {
216216
expectedResult: true,
217217
},
218218
{
219-
name: "Docker package only checks direct dependencies",
219+
name: "Docker package checks all transitive dependencies",
220220
setupPackages: func() (*Package, map[*Package]PackageBuildStatus, *mockLocalCache) {
221221
depB := newTestPackage("test:dep-b", GenericPackage)
222222
depA := newTestPackage("test:dep-a", GenericPackage)
223223
depA.dependencies = []*Package{depB}
224224

225-
pkg := newTestPackage("test:pkg", DockerPackage) // Docker only needs direct deps
225+
pkg := newTestPackage("test:pkg", DockerPackage)
226226
pkg.dependencies = []*Package{depA}
227227

228228
pkgstatus := map[*Package]PackageBuildStatus{
229229
pkg: PackageDownloaded,
230230
depA: PackageBuilt,
231-
// depB has no status - but Docker doesn't need it
231+
// depB has no status - validation should fail because
232+
// cached packages need all transitive deps available
232233
}
233234
cache := newMockLocalCache()
234235
cache.addPackage("test:pkg", "/cache/test-pkg.tar.gz")
235236
cache.addPackage("test:dep-a", "/cache/test-dep-a.tar.gz")
236-
// depB is NOT in cache - but Docker doesn't check transitive deps
237+
// depB is NOT in cache - validation should fail
238+
return pkg, pkgstatus, cache
239+
},
240+
expectedResult: false, // Changed: now checks transitive deps
241+
},
242+
{
243+
name: "Docker package with all transitive dependencies available",
244+
setupPackages: func() (*Package, map[*Package]PackageBuildStatus, *mockLocalCache) {
245+
depB := newTestPackage("test:dep-b", GenericPackage)
246+
depA := newTestPackage("test:dep-a", GenericPackage)
247+
depA.dependencies = []*Package{depB}
248+
249+
pkg := newTestPackage("test:pkg", DockerPackage)
250+
pkg.dependencies = []*Package{depA}
251+
252+
pkgstatus := map[*Package]PackageBuildStatus{
253+
pkg: PackageDownloaded,
254+
depA: PackageBuilt,
255+
depB: PackageBuilt,
256+
}
257+
cache := newMockLocalCache()
258+
cache.addPackage("test:pkg", "/cache/test-pkg.tar.gz")
259+
cache.addPackage("test:dep-a", "/cache/test-dep-a.tar.gz")
260+
cache.addPackage("test:dep-b", "/cache/test-dep-b.tar.gz")
237261
return pkg, pkgstatus, cache
238262
},
239263
expectedResult: true,

0 commit comments

Comments
 (0)