@@ -246,6 +246,9 @@ var allTests = []func(t *testing.T, sb integration.Sandbox){
246246 testHTTPResolveSourceMetadata ,
247247 testHTTPPruneAfterCacheKey ,
248248 testHTTPPruneAfterResolveMeta ,
249+ testHTTPResolveMetaReuse ,
250+ testHTTPResolveMultiBuild ,
251+ testGitResolveMutatedSource ,
249252}
250253
251254func TestIntegration (t * testing.T ) {
@@ -12270,6 +12273,265 @@ func testHTTPPruneAfterResolveMeta(t *testing.T, sb integration.Sandbox) {
1227012273 checkAllReleasable (t , c , sb , false )
1227112274}
1227212275
12276+ func testHTTPResolveMetaReuse (t * testing.T , sb integration.Sandbox ) {
12277+ // the difference with testHTTPPruneAfterResolveMeta is that here we change content with the etag on the server
12278+ // but because the URL was already resolved once, the new content should not be seen
12279+ ctx := sb .Context ()
12280+ c , err := New (ctx , sb .Address ())
12281+ require .NoError (t , err )
12282+ defer c .Close ()
12283+
12284+ resp := & httpserver.Response {
12285+ Etag : identity .NewID (),
12286+ Content : []byte ("content1" ),
12287+ }
12288+ server := httpserver .NewTestServer (map [string ]* httpserver.Response {
12289+ "/foo" : resp ,
12290+ })
12291+ defer server .Close ()
12292+
12293+ dest := t .TempDir ()
12294+ _ , err = c .Build (ctx , SolveOpt {
12295+ Exports : []ExportEntry {
12296+ {
12297+ Type : ExporterLocal ,
12298+ OutputDir : dest ,
12299+ },
12300+ },
12301+ }, "test" , func (ctx context.Context , gc gateway.Client ) (* gateway.Result , error ) {
12302+ id := server .URL + "/foo"
12303+ md , err := gc .ResolveSourceMetadata (ctx , & pb.SourceOp {
12304+ Identifier : id ,
12305+ }, sourceresolver.Opt {})
12306+ if err != nil {
12307+ return nil , err
12308+ }
12309+ require .NotNil (t , md .HTTP )
12310+
12311+ resp .Etag = identity .NewID ()
12312+ resp .Content = []byte ("content2" ) // etag changed so new content would be returned if re-resolving
12313+
12314+ st := llb .Scratch ().File (llb .Copy (llb .HTTP (id ), "foo" , "bar" ))
12315+ def , err := st .Marshal (sb .Context ())
12316+ if err != nil {
12317+ return nil , err
12318+ }
12319+ return gc .Solve (ctx , gateway.SolveRequest {
12320+ Definition : def .ToPB (),
12321+ })
12322+ }, nil )
12323+ require .NoError (t , err )
12324+
12325+ dt , err := os .ReadFile (filepath .Join (dest , "bar" ))
12326+ require .NoError (t , err )
12327+ require .Equal (t , "content1" , string (dt ))
12328+ }
12329+
12330+ // testHTTPResolveMultiBuild is a negative test for testHTTPResolveMetaReuse to ensure that
12331+ // URLs are resolved in between separate builds
12332+ func testHTTPResolveMultiBuild (t * testing.T , sb integration.Sandbox ) {
12333+ ctx := sb .Context ()
12334+ c , err := New (ctx , sb .Address ())
12335+ require .NoError (t , err )
12336+ defer c .Close ()
12337+
12338+ resp := & httpserver.Response {
12339+ Etag : identity .NewID (),
12340+ Content : []byte ("content1" ),
12341+ }
12342+ server := httpserver .NewTestServer (map [string ]* httpserver.Response {
12343+ "/foo" : resp ,
12344+ })
12345+ defer server .Close ()
12346+
12347+ dest := t .TempDir ()
12348+ _ , err = c .Build (ctx , SolveOpt {
12349+ Exports : []ExportEntry {
12350+ {
12351+ Type : ExporterLocal ,
12352+ OutputDir : dest ,
12353+ },
12354+ },
12355+ }, "test" , func (ctx context.Context , gc gateway.Client ) (* gateway.Result , error ) {
12356+ id := server .URL + "/foo"
12357+ md , err := gc .ResolveSourceMetadata (ctx , & pb.SourceOp {
12358+ Identifier : id ,
12359+ }, sourceresolver.Opt {})
12360+ if err != nil {
12361+ return nil , err
12362+ }
12363+ require .NotNil (t , md .HTTP )
12364+ require .Equal (t , digest .FromBytes (resp .Content ), md .HTTP .Digest )
12365+
12366+ st := llb .Scratch ().File (llb .Copy (llb .HTTP (id ), "foo" , "bar" ))
12367+ def , err := st .Marshal (sb .Context ())
12368+ if err != nil {
12369+ return nil , err
12370+ }
12371+ return gc .Solve (ctx , gateway.SolveRequest {
12372+ Definition : def .ToPB (),
12373+ })
12374+ }, nil )
12375+ require .NoError (t , err )
12376+
12377+ dt , err := os .ReadFile (filepath .Join (dest , "bar" ))
12378+ require .NoError (t , err )
12379+ require .Equal (t , "content1" , string (dt ))
12380+
12381+ resp .Etag = identity .NewID ()
12382+ resp .Content = []byte ("content2" )
12383+
12384+ _ , err = c .Build (ctx , SolveOpt {
12385+ Exports : []ExportEntry {
12386+ {
12387+ Type : ExporterLocal ,
12388+ OutputDir : dest ,
12389+ },
12390+ },
12391+ }, "test" , func (ctx context.Context , gc gateway.Client ) (* gateway.Result , error ) {
12392+ id := server .URL + "/foo"
12393+ md , err := gc .ResolveSourceMetadata (ctx , & pb.SourceOp {
12394+ Identifier : id ,
12395+ }, sourceresolver.Opt {})
12396+ if err != nil {
12397+ return nil , err
12398+ }
12399+ require .NotNil (t , md .HTTP )
12400+ require .Equal (t , digest .FromBytes (resp .Content ), md .HTTP .Digest )
12401+ st := llb .Scratch ().File (llb .Copy (llb .HTTP (id ), "foo" , "bar" ))
12402+ def , err := st .Marshal (sb .Context ())
12403+ if err != nil {
12404+ return nil , err
12405+ }
12406+ return gc .Solve (ctx , gateway.SolveRequest {
12407+ Definition : def .ToPB (),
12408+ })
12409+ }, nil )
12410+ require .NoError (t , err )
12411+
12412+ dt , err = os .ReadFile (filepath .Join (dest , "bar" ))
12413+ require .NoError (t , err )
12414+ require .Equal (t , "content2" , string (dt ))
12415+ }
12416+
12417+ func testGitResolveMutatedSource (t * testing.T , sb integration.Sandbox ) {
12418+ integration .SkipOnPlatform (t , "windows" )
12419+ ctx := sb .Context ()
12420+ c , err := New (ctx , sb .Address ())
12421+ require .NoError (t , err )
12422+ defer c .Close ()
12423+
12424+ gitDir := t .TempDir ()
12425+ gitCommands := []string {
12426+ "git init" ,
12427+ "git config --local user.email test" ,
12428+ "git config --local user.name test" ,
12429+ "echo a > a" ,
12430+ "git add a" ,
12431+ "git commit -m a" ,
12432+ "git tag -a v0.1 -m v0.1" ,
12433+ "echo b > b" ,
12434+ "git add b" ,
12435+ "git commit -m b" ,
12436+ "git checkout -B v2" ,
12437+ "git update-server-info" ,
12438+ }
12439+ err = runInDir (gitDir , gitCommands ... )
12440+ require .NoError (t , err )
12441+
12442+ // cmd := exec.Command("git", "rev-parse", "HEAD")
12443+ // cmd.Dir = gitDir
12444+ // out, err := cmd.Output()
12445+ // require.NoError(t, err)
12446+ // commitHEAD := strings.TrimSpace(string(out))
12447+
12448+ cmd := exec .Command ("git" , "rev-parse" , "v0.1" )
12449+ cmd .Dir = gitDir
12450+ out , err := cmd .Output ()
12451+ require .NoError (t , err )
12452+ commitTag := strings .TrimSpace (string (out ))
12453+
12454+ cmd = exec .Command ("git" , "rev-parse" , "v0.1^{commit}" )
12455+ cmd .Dir = gitDir
12456+ out , err = cmd .Output ()
12457+ require .NoError (t , err )
12458+ commitTagCommit := strings .TrimSpace (string (out ))
12459+
12460+ server := httptest .NewServer (http .FileServer (http .Dir (filepath .Clean (gitDir ))))
12461+ defer server .Close ()
12462+
12463+ dest := t .TempDir ()
12464+
12465+ _ , err = c .Build (ctx , SolveOpt {
12466+ Exports : []ExportEntry {
12467+ {
12468+ Type : ExporterLocal ,
12469+ OutputDir : dest ,
12470+ },
12471+ },
12472+ }, "test" , func (ctx context.Context , c gateway.Client ) (* gateway.Result , error ) {
12473+ id := "git://" + strings .TrimPrefix (server .URL , "http://" ) + "/.git#v0.1"
12474+ md , err := c .ResolveSourceMetadata (ctx , & pb.SourceOp {
12475+ Identifier : id ,
12476+ Attrs : map [string ]string {
12477+ "git.fullurl" : server .URL + "/.git" ,
12478+ },
12479+ }, sourceresolver.Opt {})
12480+ if err != nil {
12481+ return nil , err
12482+ }
12483+ require .NotNil (t , md .Git )
12484+ require .Equal (t , "refs/tags/v0.1" , md .Git .Ref )
12485+ require .Equal (t , commitTag , md .Git .Checksum )
12486+ require .Equal (t , commitTagCommit , md .Git .CommitChecksum )
12487+ require .Equal (t , id , md .Op .Identifier )
12488+ require .Equal (t , server .URL + "/.git" , md .Op .Attrs ["git.fullurl" ])
12489+
12490+ // update the tag to point to a different commit
12491+ err = runInDir (gitDir , []string {
12492+ "git tag -f v0.1" ,
12493+ "git update-server-info" ,
12494+ }... )
12495+ require .NoError (t , err )
12496+
12497+ md , err = c .ResolveSourceMetadata (ctx , & pb.SourceOp {
12498+ Identifier : id ,
12499+ Attrs : map [string ]string {
12500+ "git.fullurl" : server .URL + "/.git" ,
12501+ },
12502+ }, sourceresolver.Opt {})
12503+ if err != nil {
12504+ return nil , err
12505+ }
12506+ require .NotNil (t , md .Git )
12507+ require .Equal (t , "refs/tags/v0.1" , md .Git .Ref )
12508+ require .Equal (t , commitTag , md .Git .Checksum )
12509+ require .Equal (t , commitTagCommit , md .Git .CommitChecksum )
12510+ require .Equal (t , id , md .Op .Identifier )
12511+ require .Equal (t , server .URL + "/.git" , md .Op .Attrs ["git.fullurl" ])
12512+
12513+ st := llb .Git (server .URL + "/.git" , "" , llb .GitRef ("v0.1" ))
12514+ def , err := st .Marshal (sb .Context ())
12515+ if err != nil {
12516+ return nil , err
12517+ }
12518+ return c .Solve (ctx , gateway.SolveRequest {
12519+ Definition : def .ToPB (),
12520+ })
12521+ }, nil )
12522+ require .NoError (t , err )
12523+
12524+ _ , err = os .ReadFile (filepath .Join (dest , "b" ))
12525+ require .Error (t , err )
12526+ require .True (t , os .IsNotExist (err ), "expected file b to not exist" )
12527+
12528+ dt , err := os .ReadFile (filepath .Join (dest , "a" ))
12529+ require .NoError (t , err )
12530+ require .Equal (t , "a\n " , string (dt ))
12531+
12532+ checkAllReleasable (t , c , sb , false )
12533+ }
12534+
1227312535func runInDir (dir string , cmds ... string ) error {
1227412536 for _ , args := range cmds {
1227512537 var cmd * exec.Cmd
0 commit comments