@@ -8,10 +8,13 @@ import (
8
8
"encoding/hex"
9
9
"fmt"
10
10
"io"
11
+ "net/http"
11
12
"net/url"
13
+ "os"
12
14
"path/filepath"
13
15
"regexp"
14
16
"strings"
17
+ "time"
15
18
16
19
"github.com/pkg/errors"
17
20
"github.com/samber/lo"
@@ -53,6 +56,12 @@ type Package struct {
53
56
normalizedPackageAttributePathCache string // memoized value from normalizedPackageAttributePath()
54
57
}
55
58
59
+ // isNarInfoInCache checks if the .narinfo for this package is in the `BinaryCache`.
60
+ // The key is the `Package.Raw` string.
61
+ // This cannot be a field on the Package struct, because that struct
62
+ // is constructed multiple times in a request (TODO: we could fix that).
63
+ var isNarInfoInCache = map [string ]bool {}
64
+
56
65
// PackageFromStrings constructs Package from the list of package names provided.
57
66
// These names correspond to devbox packages from the devbox.json config.
58
67
func PackageFromStrings (rawNames []string , l lock.Locker ) []* Package {
@@ -481,7 +490,7 @@ func (p *Package) IsInBinaryCache() (bool, error) {
481
490
}
482
491
483
492
// Check if the user's system's info is present in the lockfile
484
- _ , ok := entry .Systems [nix .System ()]
493
+ sysInfo , ok := entry .Systems [nix .System ()]
485
494
if ! ok {
486
495
return false , nil
487
496
}
@@ -492,7 +501,33 @@ func (p *Package) IsInBinaryCache() (bool, error) {
492
501
}
493
502
494
503
// enable for nix >= 2.17
495
- return vercheck .SemverCompare (version , "2.17.0" ) >= 0 , nil
504
+ if vercheck .SemverCompare (version , "2.17.0" ) < 0 {
505
+ return false , nil
506
+ }
507
+
508
+ // Check if the narinfo is present in the binary cache
509
+ if exists , ok := isNarInfoInCache [p .Raw ]; ok {
510
+ fmt .Printf ("narInfo cache hit: %v\n " , exists )
511
+ return exists , nil
512
+ }
513
+
514
+ httpClient := & http.Client {
515
+ Timeout : time .Second * 5 ,
516
+ }
517
+ pathParts := newStorePathParts (sysInfo .StorePath )
518
+ reqURL := BinaryCache + "/" + pathParts .hash + ".narinfo"
519
+ res , err := httpClient .Head (reqURL )
520
+ if err != nil {
521
+ if os .IsTimeout (err ) {
522
+ isNarInfoInCache [p .Raw ] = false // set this to avoid re-tries
523
+ return false , nil
524
+ }
525
+ return false , err
526
+ }
527
+ fmt .Printf ("res status code %d\n " , res .StatusCode )
528
+
529
+ isNarInfoInCache [p .Raw ] = res .StatusCode == 200
530
+ return isNarInfoInCache [p .Raw ], nil
496
531
}
497
532
498
533
// InputAddressedPath is the input-addressed path in /nix/store
@@ -542,3 +577,30 @@ func (p *Package) EnsureUninstallableIsInLockfile() error {
542
577
_ , err := p .lockfile .Resolve (p .Raw )
543
578
return err
544
579
}
580
+
581
+ // storePath are the constituent parts of
582
+ // /nix/store/<hash>-<name>-<version>
583
+ //
584
+ // This is a helper struct for analyzing the string representation
585
+ type storePathParts struct {
586
+ hash string
587
+ name string
588
+ version string
589
+ }
590
+
591
+ func newStorePathParts (path string ) * storePathParts {
592
+ path = strings .TrimPrefix (path , "/nix/store/" )
593
+ // path is now <hash>-<name>-<version
594
+ parts := strings .Split (path , "-" )
595
+
596
+ // writing this a bit defensively to avoid edge-cases that may break
597
+ var hash , name , version string
598
+ hash = parts [0 ]
599
+ if len (parts ) > 1 {
600
+ name = parts [1 ]
601
+ }
602
+ if len (parts ) > 2 {
603
+ version = parts [2 ]
604
+ }
605
+ return & storePathParts {hash , name , version }
606
+ }
0 commit comments