This repository has been archived by the owner on Aug 23, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathupdate.go
160 lines (140 loc) · 5.07 KB
/
update.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
// update.go // This file holds the implementation for the "update" functionality - (parallel) //>
package main
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"io"
"os"
"path/filepath"
"sync"
"sync/atomic"
)
// update checks for updates to the valid programs and installs any that have changed.
func update(programsToUpdate []string) error {
// 'Configure' external functions
UseProgressBar = false
InstallUseCache = false
// Initialize counters
var (
skipped, updated, errors, toBeChecked uint32
checked uint32
errorMessages string
padding = " "
)
// Call validateProgramsFrom with InstallDir and programsToUpdate
programsToUpdate, err := validateProgramsFrom(InstallDir, programsToUpdate)
if err != nil {
fmt.Println("Error validating programs:", err)
return err
}
// Calculate toBeChecked
toBeChecked = uint32(len(programsToUpdate))
// Use a mutex for thread-safe updates to the progress
var progressMutex sync.Mutex
// Use a wait group to wait for all programs to finish updating
var wg sync.WaitGroup
// Iterate over programsToUpdate and download/update each one concurrently
for _, program := range programsToUpdate {
// Increment the WaitGroup counter
wg.Add(1)
// Launch a goroutine to update the program
go func(program string) {
defer wg.Done()
installPath := filepath.Join(InstallDir, program)
if !fileExists(installPath) {
progressMutex.Lock()
atomic.AddUint32(&checked, 1)
atomic.AddUint32(&skipped, 1)
truncatePrintf("\033[2K\r<%d/%d> %s | Warning: Tried to update a non-existent program %s. Skipping.", atomic.LoadUint32(&checked), toBeChecked, padding, program)
progressMutex.Unlock()
return
}
localSHA256, err := getLocalSHA256(installPath)
if err != nil {
progressMutex.Lock()
atomic.AddUint32(&checked, 1)
atomic.AddUint32(&skipped, 1)
truncatePrintf("\033[2K\r<%d/%d> %s | Warning: Failed to get SHA256 for %s. Skipping.", atomic.LoadUint32(&checked), toBeChecked, padding, program)
progressMutex.Unlock()
return
}
binaryInfo, err := getBinaryInfo(program)
if err != nil {
progressMutex.Lock()
atomic.AddUint32(&checked, 1)
atomic.AddUint32(&skipped, 1)
truncatePrintf("\033[2K\r<%d/%d> %s | Warning: Failed to get metadata for %s. Skipping.", atomic.LoadUint32(&checked), toBeChecked, padding, program)
progressMutex.Unlock()
return
}
// Skip if the SHA field is null
if binaryInfo.SHA256 == "" {
progressMutex.Lock()
atomic.AddUint32(&checked, 1)
atomic.AddUint32(&skipped, 1)
truncatePrintf("\033[2K\r<%d/%d> %s | Skipping %s because the SHA256 field is null.", atomic.LoadUint32(&checked), toBeChecked, padding, program)
progressMutex.Unlock()
return
}
// Start update process
truncatePrintf("\033[2K\r<%d/%d> %s | Looking for differences in %s against the repo's...", atomic.LoadUint32(&checked), toBeChecked, padding, program)
if checkDifferences(localSHA256, binaryInfo.SHA256) == 1 {
truncatePrintf("\033[2K\r<%d/%d> %s | The repo's version of %s differs from yours. Updating...", atomic.LoadUint32(&checked), toBeChecked, padding, program)
err := installCommand(true, program)
if err != nil {
progressMutex.Lock()
atomic.AddUint32(&errors, 1)
errorMessages += sanitizeString(fmt.Sprintf("Failed to update '%s', please check this file's properties, etc\n", program))
progressMutex.Unlock()
return
}
progressMutex.Lock()
atomic.AddUint32(&checked, 1)
atomic.AddUint32(&updated, 1)
truncatePrintf("\033[2K\r<%d/%d> %s | Successfully updated %s.", atomic.LoadUint32(&checked), toBeChecked, padding, program)
progressMutex.Unlock()
} else {
progressMutex.Lock()
atomic.AddUint32(&checked, 1)
truncatePrintf("\033[2K\r<%d/%d> %s | No updates available for %s.", atomic.LoadUint32(&checked), toBeChecked, padding, program)
progressMutex.Unlock()
}
}(program)
}
// Wait for all goroutines to finish
wg.Wait()
// Prepare final counts
finalCounts := fmt.Sprintf("\033[2K\rSkipped: %d\tUpdated: %d\tChecked: %d", atomic.LoadUint32(&skipped), atomic.LoadUint32(&updated), uint32(int(atomic.LoadUint32(&checked))))
if errors > 0 {
finalCounts += fmt.Sprintf("\tErrors: %d", atomic.LoadUint32(&errors))
}
// Print final counts
fmt.Println(finalCounts)
for error := range errorMessages {
fmt.Println(error)
}
return nil
}
// getLocalSHA256 calculates the SHA256 checksum of the local file.
func getLocalSHA256(filePath string) (string, error) {
// Open the file for reading
file, err := os.Open(filePath)
if err != nil {
return "", fmt.Errorf("failed to open file: %v", err)
}
defer file.Close()
// Calculate SHA256 checksum
hasher := sha256.New()
if _, err := io.Copy(hasher, file); err != nil {
return "", fmt.Errorf("failed to calculate SHA256: %v", err)
}
sha256Checksum := hex.EncodeToString(hasher.Sum(nil))
return sha256Checksum, nil
}
func checkDifferences(localSHA256, remoteSHA256 string) int {
if localSHA256 != remoteSHA256 {
return 1
}
return 0
}