snapshots: fix snapshot of snapshot

### api/snapshot.go — fix apiSnapshotsCreate

 The function was building the new snapshot's ref list only from b.PackageRefs, completely ignoring SourceSnapshots package contents (they were stored as provenance metadata only). The fix
 mirrors the approach from apiSnapshotsMerge:

 1. Start with source snapshots: merge all freshSources[i].RefList() together using Merge(overrideMatching=true)
 2. Layer explicit PackageRefs on top: only enter the package-loading loop if b.PackageRefs is non-empty, then merge the result into refList
 3. Pass the combined refList to NewSnapshotFromRefList

 This means an empty snapshot (SourceSnapshots: [], PackageRefs: []) still correctly produces an empty ref list, single-source and multi-source snapshot-of-snapshot cases are now handled,
 and PackageRefs can still augment or override on top of the merged sources.

 ### system/t12_api/publish.py — fix PublishSwitchAPITestSnapshot

 - Removed the # FIXME comment
 - Changed check_not_exists → check_exists for pyspi-0.6.1-1.3.stripped.dsc after the publish-switch to snapshot2, which is now the correct expectation since snapshot2 inherits all packages
   from snapshot1
This commit is contained in:
André Roth
2026-06-08 13:44:03 +02:00
parent 75eaa23f3a
commit 0a98d9fdc3
2 changed files with 30 additions and 18 deletions
+28 -15
View File
@@ -209,24 +209,37 @@ func apiSnapshotsCreate(c *gin.Context) {
}
}
list := deb.NewPackageList()
// verify package refs and build package list using fresh factory
for _, ref := range b.PackageRefs {
p, err := taskPackageCollection.ByKey([]byte(ref))
if err != nil {
if err == database.ErrNotFound {
return &task.ProcessReturnValue{Code: http.StatusNotFound, Value: nil}, fmt.Errorf("package %s: %s", ref, err)
}
return &task.ProcessReturnValue{Code: http.StatusInternalServerError, Value: nil}, err
}
err = list.Add(p)
if err != nil {
return &task.ProcessReturnValue{Code: http.StatusBadRequest, Value: nil}, err
// Merge packages from all source snapshots
var refList *deb.PackageRefList
if len(freshSources) > 0 {
refList = freshSources[0].RefList()
for i := 1; i < len(freshSources); i++ {
refList = refList.Merge(freshSources[i].RefList(), true, false)
}
} else {
refList = deb.NewPackageRefList()
}
snapshot = deb.NewSnapshotFromRefList(b.Name, freshSources, deb.NewPackageRefListFromPackageList(list), b.Description)
// Add any explicitly specified package refs on top
if len(b.PackageRefs) > 0 {
list := deb.NewPackageList()
for _, ref := range b.PackageRefs {
p, err := taskPackageCollection.ByKey([]byte(ref))
if err != nil {
if err == database.ErrNotFound {
return &task.ProcessReturnValue{Code: http.StatusNotFound, Value: nil}, fmt.Errorf("package %s: %s", ref, err)
}
return &task.ProcessReturnValue{Code: http.StatusInternalServerError, Value: nil}, err
}
err = list.Add(p)
if err != nil {
return &task.ProcessReturnValue{Code: http.StatusBadRequest, Value: nil}, err
}
}
refList = refList.Merge(deb.NewPackageRefListFromPackageList(list), true, false)
}
snapshot = deb.NewSnapshotFromRefList(b.Name, freshSources, refList, b.Description)
err = taskSnapshotCollection.Add(snapshot)
if err != nil {
+2 -3
View File
@@ -1207,11 +1207,10 @@ class PublishSwitchAPITestSnapshot(APITest):
self.check_equal(all_repos.status_code, 200)
self.check_in(repo_expected, all_repos.json())
# FIXME: what should exist here ? publish snapshot of snapshot
self.check_not_exists(
"public/" + prefix + "/pool/main/b/boost-defaults/libboost-program-options-dev_1.49.0.1_i386.deb")
self.check_not_exists("public/" + prefix +
"/pool/main/p/pyspi/pyspi-0.6.1-1.3.stripped.dsc")
self.check_exists("public/" + prefix +
"/pool/main/p/pyspi/pyspi-0.6.1-1.3.stripped.dsc")
task = self.delete_task("/api/publish/" + prefix + "/wheezy")
self.check_task(task)