Various cleanup bits, always use the version from lsb_release instead of hardcoding

ubuntu/plucky
Simon Quigley 1 week ago
parent 4d6176bc13
commit fa0302e5aa

@ -1,12 +1,17 @@
cmake_minimum_required(VERSION 3.5.0) cmake_minimum_required(VERSION 3.5.0)
project(snapd-installation-monitor) project(snapd-installation-monitor)
# Enable CMake features
set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOMOC ON)
# Find required Qt6 components
find_package(Qt6 COMPONENTS Widgets DBus REQUIRED) find_package(Qt6 COMPONENTS Widgets DBus REQUIRED)
# Set C++ standard
set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Add the main application executable
add_executable(snapd-installation-monitor main.cpp) add_executable(snapd-installation-monitor main.cpp)
target_link_libraries(snapd-installation-monitor Qt6::Widgets Qt6::DBus) target_link_libraries(snapd-installation-monitor Qt6::Widgets Qt6::DBus)

@ -10,11 +10,11 @@ import (
) )
// cleanUpFiles removes partial, old, and orphaned snap and assertion files from the download and assertions directories. // cleanUpFiles removes partial, old, and orphaned snap and assertion files from the download and assertions directories.
func cleanUpFiles(snapsDir, assertionsDir, seedYaml string) { func cleanUpFiles(snapsDir string, assertionsDir string) {
verboseLog("Starting cleanup process...") verboseLog("Starting cleanup process...")
// Load the seed.yaml data // Load the seed.yaml data
seedData := loadSeedData(seedYaml) seedData := loadSeedData()
// Create a map of valid snap and assertion files based on seed.yaml // Create a map of valid snap and assertion files based on seed.yaml
validSnaps := make(map[string]bool) validSnaps := make(map[string]bool)

@ -12,15 +12,6 @@ import (
"github.com/snapcore/snapd/store" "github.com/snapcore/snapd/store"
) )
// Seed structure for seed.yaml
type seed struct {
Snaps []struct {
Name string `yaml:"name"`
Channel string `yaml:"channel"`
File string `yaml:"file"`
} `yaml:"snaps"`
}
var ( var (
ctx = context.Background() ctx = context.Background()
storeClient *store.Store storeClient *store.Store
@ -30,6 +21,7 @@ var (
processedSnaps = make(map[string]bool) processedSnaps = make(map[string]bool)
snapSizeMap = make(map[string]float64) snapSizeMap = make(map[string]float64)
totalSnapSize float64 totalSnapSize float64
seedYaml string
) )
type SnapInfo struct { type SnapInfo struct {
@ -61,14 +53,14 @@ func main() {
// Define directories based on the seed directory // Define directories based on the seed directory
snapsDir := filepath.Join(seedDirectory, "snaps") snapsDir := filepath.Join(seedDirectory, "snaps")
assertionsDir := filepath.Join(seedDirectory, "assertions") assertionsDir := filepath.Join(seedDirectory, "assertions")
seedYaml := filepath.Join(seedDirectory, "seed.yaml") seedYaml = filepath.Join(seedDirectory, "seed.yaml")
// Setup directories and seed.yaml // Setup directories and seed.yaml
initializeDirectories(snapsDir, assertionsDir) initializeDirectories(snapsDir, assertionsDir)
initializeSeedYaml(seedYaml) initializeSeedYaml()
// Load existing snaps from seed.yaml // Load existing snaps from seed.yaml
existingSnapsInYaml := loadExistingSnaps(seedYaml) existingSnapsInYaml := loadExistingSnaps()
// Populate currentSnaps based on existing snaps // Populate currentSnaps based on existing snaps
for snapName := range existingSnapsInYaml { for snapName := range existingSnapsInYaml {
@ -133,7 +125,7 @@ func main() {
cleanUpCurrentSnaps(assertionsDir, snapsDir) cleanUpCurrentSnaps(assertionsDir, snapsDir)
// Update seed.yaml with the current required snaps // Update seed.yaml with the current required snaps
if err := updateSeedYaml(snapsDir, seedYaml, currentSnaps); err != nil { if err := updateSeedYaml(snapsDir, currentSnaps); err != nil {
log.Fatalf("Failed to update seed.yaml: %v", err) log.Fatalf("Failed to update seed.yaml: %v", err)
} }
@ -143,7 +135,7 @@ func main() {
if err := validateSeed(seedYaml); err != nil { if err := validateSeed(seedYaml); err != nil {
log.Fatalf("Seed validation failed: %v", err) log.Fatalf("Seed validation failed: %v", err)
} }
cleanUpFiles(snapsDir, assertionsDir, seedYaml) cleanUpFiles(snapsDir, assertionsDir)
// Mark "Finalizing" as complete // Mark "Finalizing" as complete
if progressTracker != nil { if progressTracker != nil {
@ -155,11 +147,21 @@ func main() {
func collectSnapsToProcess(snapsDir, assertionsDir string) ([]SnapDetails, error) { func collectSnapsToProcess(snapsDir, assertionsDir string) ([]SnapDetails, error) {
var snapsToProcess []SnapDetails var snapsToProcess []SnapDetails
versionID, err := getVersionID()
if err != nil {
return nil, err
}
defaultChannel := "latest/stable/ubuntu-" + versionID
if err != nil {
return nil, err
}
fallbackChannel := "latest/stable" fallbackChannel := "latest/stable"
for snapEntry := range requiredSnaps { for snapEntry := range requiredSnaps {
// Extract channel if specified, default to "stable" // Extract channel if specified, default to "stable"
parts := strings.SplitN(snapEntry, "=", 2) parts := strings.SplitN(snapEntry, "=", 2)
channel := "latest/stable/ubuntu-25.04" channel := defaultChannel
if len(parts) == 2 { if len(parts) == 2 {
channel = parts[1] channel = parts[1]
} }
@ -173,9 +175,10 @@ func collectSnapsToProcess(snapsDir, assertionsDir string) ([]SnapDetails, error
// Append only those snaps that need updates // Append only those snaps that need updates
for _, snapDetails := range snapList { for _, snapDetails := range snapList {
verboseLog("Processing snap: %s with result: %v", snapDetails.InstanceName, snapDetails.Result) verboseLog("Processing snap: %s", snapDetails.InstanceName)
if len(snapDetails.Result.Deltas) > 0 { if len(snapDetails.Result.Deltas) > 0 {
for _, delta := range snapDetails.Result.Deltas { for _, delta := range snapDetails.Result.Deltas {
verboseLog("Delta found for %s from %d to %d", snapDetails.InstanceName, delta.FromRevision, delta.ToRevision)
snapSize := float64(delta.Size) snapSize := float64(delta.Size)
snapSizeMap[snapDetails.Result.Info.SuggestedName] = snapSize snapSizeMap[snapDetails.Result.Info.SuggestedName] = snapSize
totalSnapSize += snapSize totalSnapSize += snapSize

@ -32,6 +32,7 @@ func collectSnapDependencies(snapName, channel, fallbackChannel, snapsDir, asser
var result *store.SnapActionResult var result *store.SnapActionResult
var err error var err error
workingChannel := ""
// Fetch or refresh snap information // Fetch or refresh snap information
if oldSnap == nil || oldSnap.SnapID == "" || oldSnap.Revision.N == 0 { if oldSnap == nil || oldSnap.SnapID == "" || oldSnap.Revision.N == 0 {
@ -41,18 +42,25 @@ func collectSnapDependencies(snapName, channel, fallbackChannel, snapsDir, asser
result, err = fetchOrRefreshSnapInfo(snapName, nil, fallbackChannel) result, err = fetchOrRefreshSnapInfo(snapName, nil, fallbackChannel)
if err != nil { if err != nil {
return nil, err return nil, err
} else {
workingChannel = fallbackChannel
} }
} else { } else {
return nil, err return nil, err
} }
} else {
workingChannel = channel
} }
} else { } else {
verboseLog("Old snap info: %s %d", oldSnap.SnapID, oldSnap.Revision.N)
result, err = fetchOrRefreshSnapInfo(snapName, oldSnap, channel) result, err = fetchOrRefreshSnapInfo(snapName, oldSnap, channel)
if err != nil { if err != nil {
if strings.Contains(err.Error(), "snap has no updates available") { if strings.Contains(err.Error(), "snap has no updates available") {
result, err = fetchOrRefreshSnapInfo(snapName, nil, channel) result, err = fetchOrRefreshSnapInfo(snapName, nil, channel)
if err != nil { if err != nil {
return nil, err return nil, err
} else {
workingChannel = channel
} }
} else if strings.Contains(err.Error(), "no snap revision available as specified") { } else if strings.Contains(err.Error(), "no snap revision available as specified") {
result, err = fetchOrRefreshSnapInfo(snapName, oldSnap, fallbackChannel) result, err = fetchOrRefreshSnapInfo(snapName, oldSnap, fallbackChannel)
@ -61,14 +69,20 @@ func collectSnapDependencies(snapName, channel, fallbackChannel, snapsDir, asser
result, err = fetchOrRefreshSnapInfo(snapName, nil, fallbackChannel) result, err = fetchOrRefreshSnapInfo(snapName, nil, fallbackChannel)
if err != nil { if err != nil {
return nil, err return nil, err
} else {
workingChannel = fallbackChannel
} }
} else { } else {
return nil, err return nil, err
} }
} else {
workingChannel = fallbackChannel
} }
} else { } else {
return nil, err return nil, err
} }
} else {
workingChannel = channel
} }
} }
@ -78,9 +92,10 @@ func collectSnapDependencies(snapName, channel, fallbackChannel, snapsDir, asser
info := result.Info info := result.Info
newSnap := &store.CurrentSnap{ newSnap := &store.CurrentSnap{
InstanceName: snapName, InstanceName: snapName,
SnapID: info.SnapID, SnapID: info.SnapID,
Revision: snap.Revision{N: info.Revision.N}, Revision: snap.Revision{N: info.Revision.N},
TrackingChannel: workingChannel,
} }
snapInCurrentSnaps, oldRevision := isSnapInCurrentSnaps(snapName) snapInCurrentSnaps, oldRevision := isSnapInCurrentSnaps(snapName)
if snapInCurrentSnaps { if snapInCurrentSnaps {
@ -176,7 +191,7 @@ func fetchOrRefreshSnapInfo(snapName string, currentSnap *store.CurrentSnap, cha
results, _, err := storeClient.SnapAction(ctx, includeSnap, actions, nil, nil, nil) results, _, err := storeClient.SnapAction(ctx, includeSnap, actions, nil, nil, nil)
if err != nil { if err != nil {
verboseLog("SnapAction error for %s: %v", snapName, err) verboseLog("SnapAction error for %s: %v", snapName, err)
if strings.Contains(err.Error(), "snap has no updates available") && currentSnap != nil { if (strings.Contains(err.Error(), "snap has no updates available") || strings.Contains(err.Error(), "no snap revision available as specified")) && currentSnap != nil {
return nil, err return nil, err
} }
return nil, fmt.Errorf("snap action failed for %s: %w", snapName, err) return nil, fmt.Errorf("snap action failed for %s: %w", snapName, err)
@ -232,6 +247,12 @@ func findPreviousSnap(downloadDir, assertionsDir, snapName string) (string, *sto
assertFilePath := filepath.Join(assertionsDir, strings.Replace(file.Name(), ".snap", ".assert", 1)) assertFilePath := filepath.Join(assertionsDir, strings.Replace(file.Name(), ".snap", ".assert", 1))
currentSnap = parseSnapInfo(assertFilePath, snapName) currentSnap = parseSnapInfo(assertFilePath, snapName)
currentSnap.Revision.N = revision currentSnap.Revision.N = revision
trackingChannel, err := getChannelName(snapName)
if err != nil {
verboseLog("Failed to get existing channel name for %s", snapName)
continue
}
currentSnap.TrackingChannel = "latest/" + trackingChannel
} }
} }
} }

@ -5,13 +5,42 @@ import (
"io/ioutil" "io/ioutil"
"log" "log"
"os" "os"
"strings"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"github.com/snapcore/snapd/store" "github.com/snapcore/snapd/store"
) )
type seed struct {
Snaps []struct {
Name string `yaml:"name"`
Channel string `yaml:"channel"`
File string `yaml:"file"`
} `yaml:"snaps"`
}
// getChannelName returns the channel name for a specific snap name
func getChannelName(snapName string) (string, error) {
file, err := ioutil.ReadFile(seedYaml)
if err != nil {
return "", fmt.Errorf("failed to read seed.yaml: %w", err)
}
var seedData seed
if err := yaml.Unmarshal(file, &seedData); err != nil {
return "", fmt.Errorf("failed to parse seed.yaml: %w", err)
}
for _, snap := range seedData.Snaps {
if snap.Name == snapName {
return snap.Channel, nil
}
}
return "", fmt.Errorf("snap %s not found in seed.yaml", snapName)
}
// initializeSeedYaml ensures that seed.yaml exists; if not, creates it. // initializeSeedYaml ensures that seed.yaml exists; if not, creates it.
func initializeSeedYaml(seedYaml string) { func initializeSeedYaml() {
if _, err := os.Stat(seedYaml); os.IsNotExist(err) { if _, err := os.Stat(seedYaml); os.IsNotExist(err) {
file, err := os.Create(seedYaml) file, err := os.Create(seedYaml)
if err != nil { if err != nil {
@ -23,7 +52,7 @@ func initializeSeedYaml(seedYaml string) {
} }
// loadSeedData loads seed data from seed.yaml // loadSeedData loads seed data from seed.yaml
func loadSeedData(seedYaml string) seed { func loadSeedData() seed {
file, err := ioutil.ReadFile(seedYaml) file, err := ioutil.ReadFile(seedYaml)
if err != nil { if err != nil {
log.Fatalf("Failed to read seed.yaml: %v", err) log.Fatalf("Failed to read seed.yaml: %v", err)
@ -38,7 +67,7 @@ func loadSeedData(seedYaml string) seed {
} }
// loadExistingSnaps loads snaps from seed.yaml into a map // loadExistingSnaps loads snaps from seed.yaml into a map
func loadExistingSnaps(seedYaml string) map[string]bool { func loadExistingSnaps() map[string]bool {
file, err := ioutil.ReadFile(seedYaml) file, err := ioutil.ReadFile(seedYaml)
if err != nil { if err != nil {
log.Fatalf("Failed to read seed.yaml: %v", err) log.Fatalf("Failed to read seed.yaml: %v", err)
@ -58,7 +87,7 @@ func loadExistingSnaps(seedYaml string) map[string]bool {
} }
// updateSeedYaml updates the seed.yaml file with the current required snaps // updateSeedYaml updates the seed.yaml file with the current required snaps
func updateSeedYaml(snapsDir, seedYaml string, currentSnaps []*store.CurrentSnap) error { func updateSeedYaml(snapsDir string, currentSnaps []*store.CurrentSnap) error {
// Log the snaps to be written // Log the snaps to be written
verboseLog("CurrentSnaps to be written to seed.yaml:") verboseLog("CurrentSnaps to be written to seed.yaml:")
for _, snapInfo := range currentSnaps { for _, snapInfo := range currentSnaps {
@ -92,7 +121,7 @@ func updateSeedYaml(snapsDir, seedYaml string, currentSnaps []*store.CurrentSnap
File string `yaml:"file"` File string `yaml:"file"`
}{ }{
Name: snapInfo.InstanceName, Name: snapInfo.InstanceName,
Channel: "stable", // Assuming 'stable' channel; modify as needed Channel: strings.Replace(snapInfo.TrackingChannel, "latest/", "", -1),
File: snapFileName, File: snapFileName,
} }
seedData.Snaps = append(seedData.Snaps, snapData) seedData.Snaps = append(seedData.Snaps, snapData)

@ -148,3 +148,28 @@ func verifySnapIntegrity(filePath, expectedChecksum string) bool {
} }
return checksumMatches return checksumMatches
} }
// Get the raw VERSION_ID from /etc/os-release to use for branch detection
func getVersionID() (string, error) {
file, err := os.Open("/etc/os-release")
if err != nil {
return "", fmt.Errorf("failed to open /etc/os-release: %w", err)
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
if strings.HasPrefix(line, "VERSION_ID=") {
// Remove the prefix and any surrounding quotes
versionID := strings.Trim(strings.SplitN(line, "=", 2)[1], `"`)
return versionID, nil
}
}
if err := scanner.Err(); err != nil {
return "", fmt.Errorf("error reading /etc/os-release: %w", err)
}
return "", fmt.Errorf("VERSION_ID not found in /etc/os-release")
}

Loading…
Cancel
Save