diff --git a/deb/list.go b/deb/list.go index 40478027..aff52d98 100644 --- a/deb/list.go +++ b/deb/list.go @@ -326,6 +326,19 @@ func (l *PackageList) Architectures(includeSource bool) (result []string) { return } +// IndexArchitectures returns repository index architectures present in the list. +// If includeSource is true, the source meta-architecture is included. +func (l *PackageList) IndexArchitectures(includeSource bool) (result []string) { + result = make([]string, 0, 10) + for _, pkg := range l.packages { + arch := pkg.IndexArchitecture() + if arch != ArchitectureAll && (arch != ArchitectureSource || includeSource) && !utils.StrSliceHasItem(result, arch) { + result = append(result, arch) + } + } + return +} + // Strings builds list of strings with package keys func (l *PackageList) Strings() []string { result := make([]string, l.Len()) diff --git a/deb/list_test.go b/deb/list_test.go index 6849ef6d..0b4505d6 100644 --- a/deb/list_test.go +++ b/deb/list_test.go @@ -684,3 +684,25 @@ func (s *PackageListSuite) TestFilterLatestNil(c *C) { c.Assert(err, ErrorMatches, "package list is nil") c.Assert(filtered, IsNil) } + +func (s *PackageListSuite) TestArchitectureVariantIndexArchitectures(c *C) { + for _, p := range []*Package{ + {Name: "example", Version: "1.0", Architecture: "amd64"}, + {Name: "example", Version: "1.0", Architecture: "amd64", ArchitectureVariant: "amd64v3"}, + {Name: "example", Version: "2.0", Architecture: "amd64", ArchitectureVariant: "amd64v3"}, + {Name: "example", Version: "1.0", Architecture: "i386"}, + {Name: "data", Version: "1.0", Architecture: "all"}, + {Name: "example", Version: "1.0", Architecture: "source", IsSource: true}, + } { + c.Assert(s.list.Add(p), IsNil) + } + indexes := s.list.IndexArchitectures(false) + sort.Strings(indexes) + c.Check(indexes, DeepEquals, []string{"amd64", "amd64v3", "i386"}) + indexes = s.list.IndexArchitectures(true) + sort.Strings(indexes) + c.Check(indexes, DeepEquals, []string{"amd64", "amd64v3", "i386", "source"}) + base := s.list.Architectures(false) + sort.Strings(base) + c.Check(base, DeepEquals, []string{"amd64", "i386"}) +} diff --git a/deb/package.go b/deb/package.go index f8885da9..17caf4c4 100644 --- a/deb/package.go +++ b/deb/package.go @@ -352,6 +352,14 @@ func (p *Package) MatchesArchitecture(arch string) bool { return p.Architecture == arch } +// MatchesIndexArchitecture checks membership in a repository index architecture. +func (p *Package) MatchesIndexArchitecture(arch string) bool { + if p.Architecture == ArchitectureAll && arch != ArchitectureSource { + return true + } + return p.IndexArchitecture() == arch +} + func JoinErrors(errs ...error) error { var combinedErr error for _, err := range errs { diff --git a/deb/package_test.go b/deb/package_test.go index 03c41b70..a2ba093d 100644 --- a/deb/package_test.go +++ b/deb/package_test.go @@ -580,3 +580,26 @@ SHA256: bbb3a2cb07f741c3995b6d4bb08d772d83582b93a0236d4ea7736bc0370fc320` const installerPackageMeta = `9d8bb14044dee520f4706ab197dfff10e9e39ecb3c1a402331712154e8284b2e ./MANIFEST.udebs dab96042d8e25e0f6bbb8d7c5bd78543afb5eb31a4a8b122ece68ab197228028 ./udeb.list` + +func (s *PackageSuite) TestArchitectureVariantMatchesIndexArchitecture(c *C) { + for _, tc := range []struct { + base, variant, target string + matches bool + }{ + {"amd64", "", "amd64", true}, + {"amd64", "", "amd64v3", false}, + {"amd64", "amd64v3", "amd64", false}, + {"amd64", "amd64v3", "amd64v3", true}, + {"all", "", "amd64", true}, + {"all", "", "amd64v3", true}, + {"all", "", "source", false}, + {"source", "", "source", true}, + {"source", "", "amd64v3", false}, + } { + p := &Package{Architecture: tc.base, ArchitectureVariant: tc.variant, IsSource: tc.base == "source"} + c.Check(p.MatchesIndexArchitecture(tc.target), Equals, tc.matches, Commentf("base=%s variant=%s target=%s", tc.base, tc.variant, tc.target)) + } + p := &Package{Architecture: "amd64", ArchitectureVariant: "amd64v3"} + c.Check(p.MatchesArchitecture("amd64"), Equals, true) + c.Check(p.MatchesArchitecture("amd64v3"), Equals, false) +} diff --git a/deb/publish.go b/deb/publish.go index 4dd3a457..08726795 100644 --- a/deb/publish.go +++ b/deb/publish.go @@ -883,7 +883,7 @@ func (p *PublishedRepo) Publish(packagePool aptly.PackagePool, publishedStorageP if !p.rePublishing { if len(p.Architectures) == 0 { for _, list := range lists { - p.Architectures = append(p.Architectures, list.Architectures(true)...) + p.Architectures = append(p.Architectures, list.IndexArchitectures(true)...) } } @@ -941,7 +941,7 @@ func (p *PublishedRepo) Publish(packagePool aptly.PackagePool, publishedStorageP } for _, arch := range p.Architectures { - if pkg.MatchesArchitecture(arch) { + if pkg.MatchesIndexArchitecture(arch) { hadUdebs = hadUdebs || pkg.IsUdeb var relPath string @@ -979,7 +979,7 @@ func (p *PublishedRepo) Publish(packagePool aptly.PackagePool, publishedStorageP batch := tempDB.CreateBatch() for _, arch := range p.Architectures { - if pkg.MatchesArchitecture(arch) { + if pkg.MatchesIndexArchitecture(arch) { var bufWriter *bufio.Writer if !p.SkipContents && !pkg.IsInstaller { diff --git a/deb/publish_test.go b/deb/publish_test.go index 12d858d2..fd21a3d4 100644 --- a/deb/publish_test.go +++ b/deb/publish_test.go @@ -382,6 +382,83 @@ func (s *PublishedRepoSuite) TestUpdate(c *C) { c.Assert(result.RemovedComponents(), DeepEquals, []string{}) } +func (s *PublishedRepoSuite) checkArchitectureVariantPublication(c *C, architectures []string) { + list := NewPackageList() + for _, arch := range []string{"amd64", "amd64v3"} { + filename := "example_1.0_" + arch + ".deb" + stanza := Stanza{"Package": "example", "Version": "1.0", "Architecture": "amd64", "Filename": filename} + if arch == "amd64v3" { + stanza["Architecture-Variant"] = "amd64v3" + } + p := NewPackageFromControlFile(stanza) + inputPath := filepath.Join(c.MkDir(), filename) + c.Assert(os.WriteFile(inputPath, nil, 0644), IsNil) + var err error + p.Files()[0].PoolPath, err = s.packagePool.Import(inputPath, filename, &p.Files()[0].Checksums, false, s.cs) + c.Assert(err, IsNil) + p.UpdateFiles(p.Files()) + c.Assert(s.packageCollection.Update(p), IsNil) + c.Assert(list.Add(p), IsNil) + } + local := NewLocalRepo("architecture-variant", "") + local.packageRefs = NewPackageRefListFromPackageList(list) + c.Assert(s.factory.LocalRepoCollection().Add(local), IsNil) + repo, err := NewPublishedRepo("", "variant-test", "test", architectures, []string{"main"}, []interface{}{local}, s.factory, false) + c.Assert(err, IsNil) + repo.SkipContents = true + c.Assert(repo.Publish(s.packagePool, s.provider, s.factory, &NullSigner{}, nil, false, ""), IsNil) + + root := filepath.Join(s.publishedStorage.PublicPath(), "variant-test") + releaseData, err := os.ReadFile(filepath.Join(root, "dists/test/Release")) + c.Assert(err, IsNil) + release, err := NewControlFileReader(bytes.NewReader(releaseData), true, false).ReadStanza() + c.Assert(err, IsNil) + c.Check(release["Architectures"], Equals, "amd64 amd64v3") + + for _, arch := range []string{"amd64", "amd64v3"} { + filename := "example_1.0_" + arch + ".deb" + c.Check(filepath.Join(root, "pool/main/e/example", filename), PathExists) + indexPath := filepath.Join(root, "dists/test/main", "binary-"+arch, "Packages") + data, err := os.ReadFile(indexPath) + if !c.Check(err, IsNil, Commentf("index %s must exist", arch)) { + continue + } + reader := NewControlFileReader(bytes.NewReader(data), false, false) + var entries []Stanza + for { + entry, err := reader.ReadStanza() + c.Assert(err, IsNil) + if entry == nil { + break + } + entries = append(entries, entry) + } + c.Check(entries, HasLen, 1, Commentf("index %s must contain exactly one package", arch)) + var filenames []string + for _, entry := range entries { + filenames = append(filenames, entry["Filename"]) + c.Check(entry["Package"], Equals, "example") + c.Check(entry["Version"], Equals, "1.0") + c.Check(entry["Architecture"], Equals, "amd64") + if entry["Filename"] == "pool/main/e/example/example_1.0_amd64v3.deb" { + c.Check(entry["Architecture-Variant"], Equals, "amd64v3") + } else { + _, hasVariant := entry["Architecture-Variant"] + c.Check(hasVariant, Equals, false) + } + } + c.Check(filenames, DeepEquals, []string{"pool/main/e/example/" + filename}, Commentf("index %s package membership", arch)) + } +} + +func (s *PublishedRepoSuite) TestArchitectureVariantPublishAutoArchitectures(c *C) { + s.checkArchitectureVariantPublication(c, nil) +} + +func (s *PublishedRepoSuite) TestArchitectureVariantPublishExplicitArchitectures(c *C) { + s.checkArchitectureVariantPublication(c, []string{"amd64", "amd64v3"}) +} + func (s *PublishedRepoSuite) TestPublish(c *C) { err := s.repo.Publish(s.packagePool, s.provider, s.factory, &NullSigner{}, nil, false, "") c.Assert(err, IsNil)