mirror of
https://github.com/safing/portbase
synced 2025-09-01 18:19:57 +00:00
Merge pull request #38 from safing/fix/updates
Fix update not marked as available, add comments and notification helper
This commit is contained in:
commit
0a53e78e8a
4 changed files with 142 additions and 18 deletions
|
@ -67,6 +67,48 @@ func Get(id string) *Notification {
|
|||
return nil
|
||||
}
|
||||
|
||||
// NotifyInfo is a helper method for quickly showing a info
|
||||
// notification. The notification is already shown. If id is
|
||||
// an empty string a new UUIDv4 will be generated.
|
||||
func NotifyInfo(id, msg string, actions ...Action) *Notification {
|
||||
return notify(Info, id, msg, actions...)
|
||||
}
|
||||
|
||||
// NotifyWarn is a helper method for quickly showing a warning
|
||||
// notification. The notification is already shown. If id is
|
||||
// an empty string a new UUIDv4 will be generated.
|
||||
func NotifyWarn(id, msg string, actions ...Action) *Notification {
|
||||
return notify(Warning, id, msg, actions...)
|
||||
}
|
||||
|
||||
// NotifyPrompt is a helper method for quickly showing a prompt
|
||||
// notification. The notification is already shown. If id is
|
||||
// an empty string a new UUIDv4 will be generated.
|
||||
func NotifyPrompt(id, msg string, actions ...Action) *Notification {
|
||||
return notify(Prompt, id, msg, actions...)
|
||||
}
|
||||
|
||||
func notify(nType uint8, id string, msg string, actions ...Action) *Notification {
|
||||
acts := make([]*Action, len(actions))
|
||||
for idx := range actions {
|
||||
a := actions[idx]
|
||||
acts[idx] = &a
|
||||
}
|
||||
|
||||
if id == "" {
|
||||
id = uuid.NewV4().String()
|
||||
}
|
||||
|
||||
n := Notification{
|
||||
ID: id,
|
||||
Message: msg,
|
||||
Type: nType,
|
||||
AvailableActions: acts,
|
||||
}
|
||||
|
||||
return n.Save()
|
||||
}
|
||||
|
||||
// Save saves the notification and returns it.
|
||||
func (n *Notification) Save() *Notification {
|
||||
notsLock.Lock()
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package updater
|
||||
|
||||
import "github.com/safing/portbase/log"
|
||||
|
||||
// File represents a file from the update system.
|
||||
type File struct {
|
||||
resource *Resource
|
||||
|
@ -36,6 +38,7 @@ func (file *File) markActiveWithLocking() {
|
|||
|
||||
// update last used version
|
||||
if file.resource.ActiveVersion != file.version {
|
||||
log.Debugf("updater: setting active version of resource %s from %s to %s", file.resource.Identifier, file.resource.ActiveVersion, file.version.VersionNumber)
|
||||
file.resource.ActiveVersion = file.version
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,37 +19,90 @@ type Resource struct {
|
|||
registry *ResourceRegistry
|
||||
notifier *notifier
|
||||
|
||||
// Identifier is the unique identifier for that resource.
|
||||
// It forms a file path using a forward-slash as the
|
||||
// path separator.
|
||||
Identifier string
|
||||
Versions []*ResourceVersion
|
||||
|
||||
ActiveVersion *ResourceVersion
|
||||
// Versions holds all available resource versions.
|
||||
Versions []*ResourceVersion
|
||||
|
||||
// ActiveVersion is the last version of the resource
|
||||
// that someone requested using GetFile().
|
||||
ActiveVersion *ResourceVersion
|
||||
|
||||
// SelectedVersion is newest, selectable version of
|
||||
// that resource that is available. A version
|
||||
// is selectable if it's not blacklisted by the user.
|
||||
// Note that it's not guaranteed that the selected version
|
||||
// is available locally. In that case, GetFile will attempt
|
||||
// to download the latest version from the updates servers
|
||||
// specified in the resource registry.
|
||||
SelectedVersion *ResourceVersion
|
||||
ForceDownload bool
|
||||
}
|
||||
|
||||
// ResourceVersion represents a single version of a resource.
|
||||
type ResourceVersion struct {
|
||||
resource *Resource
|
||||
|
||||
// VersionNumber is the string representation of the resource
|
||||
// version.
|
||||
VersionNumber string
|
||||
semVer *semver.Version
|
||||
Available bool
|
||||
|
||||
// Available indicates if this version is available locally.
|
||||
Available bool
|
||||
|
||||
// StableRelease indicates that this version is part of
|
||||
// a stable release index file.
|
||||
StableRelease bool
|
||||
BetaRelease bool
|
||||
Blacklisted bool
|
||||
|
||||
// BetaRelease indicates that this version is part of
|
||||
// a beta release index file.
|
||||
BetaRelease bool
|
||||
|
||||
// Blacklisted may be set to true if this version should
|
||||
// be skipped and not used. This is useful if the version
|
||||
// is known to be broken.
|
||||
Blacklisted bool
|
||||
}
|
||||
|
||||
// Len is the number of elements in the collection. (sort.Interface for Versions)
|
||||
func (rv *ResourceVersion) String() string {
|
||||
return rv.VersionNumber
|
||||
}
|
||||
|
||||
// isSelectable returns true if the version represented by rv is selectable.
|
||||
// A version is selectable if it's not blacklisted and either already locally
|
||||
// available or ready to be downloaded.
|
||||
func (rv *ResourceVersion) isSelectable() bool {
|
||||
return !rv.Blacklisted && (rv.Available || rv.resource.registry.Online)
|
||||
}
|
||||
|
||||
// isBetaVersionNumber checks if rv is marked as a beta version by checking
|
||||
// the version string. It does not honor the BetaRelease field of rv!
|
||||
func (rv *ResourceVersion) isBetaVersionNumber() bool {
|
||||
// "b" suffix check if for backwards compatibility
|
||||
// new versions should use the pre-release suffix as
|
||||
// declared by https://semver.org
|
||||
// i.e. 1.2.3-beta
|
||||
return strings.HasSuffix(rv.VersionNumber, "b") || strings.Contains(rv.semVer.Prerelease(), "beta")
|
||||
}
|
||||
|
||||
// Len is the number of elements in the collection.
|
||||
// It implements sort.Interface for ResourceVersion.
|
||||
func (res *Resource) Len() int {
|
||||
return len(res.Versions)
|
||||
}
|
||||
|
||||
// Less reports whether the element with index i should sort before the element with index j. (sort.Interface for Versions)
|
||||
// Less reports whether the element with index i should
|
||||
// sort before the element with index j.
|
||||
// It implements sort.Interface for ResourceVersions.
|
||||
func (res *Resource) Less(i, j int) bool {
|
||||
return res.Versions[i].semVer.GreaterThan(res.Versions[j].semVer)
|
||||
}
|
||||
|
||||
// Swap swaps the elements with indexes i and j. (sort.Interface for Versions)
|
||||
// Swap swaps the elements with indexes i and j.
|
||||
// It implements sort.Interface for ResourceVersions.
|
||||
func (res *Resource) Swap(i, j int) {
|
||||
res.Versions[i], res.Versions[j] = res.Versions[j], res.Versions[i]
|
||||
}
|
||||
|
@ -64,6 +117,20 @@ func (res *Resource) available() bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// inUse returns true if the resource is currently in use.
|
||||
func (res *Resource) inUse() bool {
|
||||
return res.ActiveVersion != nil
|
||||
}
|
||||
|
||||
// AnyVersionAvailable returns true if any version of
|
||||
// res is locally available.
|
||||
func (res *Resource) AnyVersionAvailable() bool {
|
||||
res.Lock()
|
||||
defer res.Unlock()
|
||||
|
||||
return res.available()
|
||||
}
|
||||
|
||||
func (reg *ResourceRegistry) newResource(identifier string) *Resource {
|
||||
return &Resource{
|
||||
registry: reg,
|
||||
|
@ -154,18 +221,24 @@ func (res *Resource) GetFile() *File {
|
|||
}
|
||||
}
|
||||
|
||||
//nolint:gocognit // function already kept as simlpe as possible
|
||||
//nolint:gocognit // function already kept as simple as possible
|
||||
func (res *Resource) selectVersion() {
|
||||
sort.Sort(res)
|
||||
|
||||
// export after we finish
|
||||
defer func() {
|
||||
if res.ActiveVersion != nil && // resource has already been used
|
||||
log.Debugf("updater: selected version %s for resource %s", res.SelectedVersion, res.Identifier)
|
||||
|
||||
if res.inUse() &&
|
||||
res.SelectedVersion != res.ActiveVersion && // new selected version does not match previously selected version
|
||||
res.notifier != nil {
|
||||
|
||||
res.notifier.markAsUpgradeable()
|
||||
res.notifier = nil
|
||||
|
||||
log.Debugf("updater: active version of %s is %s, update available", res.Identifier, res.ActiveVersion.VersionNumber)
|
||||
}
|
||||
|
||||
}()
|
||||
|
||||
if len(res.Versions) == 0 {
|
||||
|
@ -190,7 +263,7 @@ func (res *Resource) selectVersion() {
|
|||
if res.registry.Beta {
|
||||
for _, rv := range res.Versions {
|
||||
if rv.BetaRelease {
|
||||
if !rv.Blacklisted && (rv.Available || rv.resource.registry.Online) {
|
||||
if rv.isSelectable() {
|
||||
res.SelectedVersion = rv
|
||||
return
|
||||
}
|
||||
|
@ -202,7 +275,7 @@ func (res *Resource) selectVersion() {
|
|||
// 3) Stable release
|
||||
for _, rv := range res.Versions {
|
||||
if rv.StableRelease {
|
||||
if !rv.Blacklisted && (rv.Available || rv.resource.registry.Online) {
|
||||
if rv.isSelectable() {
|
||||
res.SelectedVersion = rv
|
||||
return
|
||||
}
|
||||
|
@ -212,7 +285,7 @@ func (res *Resource) selectVersion() {
|
|||
|
||||
// 4) Latest stable release
|
||||
for _, rv := range res.Versions {
|
||||
if !strings.HasSuffix(rv.VersionNumber, "b") && !rv.Blacklisted && (rv.Available || rv.resource.registry.Online) {
|
||||
if !rv.isBetaVersionNumber() && rv.isSelectable() {
|
||||
res.SelectedVersion = rv
|
||||
return
|
||||
}
|
||||
|
@ -220,7 +293,7 @@ func (res *Resource) selectVersion() {
|
|||
|
||||
// 5) Latest of any type
|
||||
for _, rv := range res.Versions {
|
||||
if !rv.Blacklisted && (rv.Available || rv.resource.registry.Online) {
|
||||
if rv.isSelectable() {
|
||||
res.SelectedVersion = rv
|
||||
return
|
||||
}
|
||||
|
@ -228,6 +301,7 @@ func (res *Resource) selectVersion() {
|
|||
|
||||
// 6) Default to newest
|
||||
res.SelectedVersion = res.Versions[0]
|
||||
log.Warningf("updater: falling back to version %s for %s because we failed to find a selectable one", res.SelectedVersion, res.Identifier)
|
||||
}
|
||||
|
||||
// Blacklist blacklists the specified version and selects a new version.
|
||||
|
@ -235,7 +309,7 @@ func (res *Resource) Blacklist(version string) error {
|
|||
res.Lock()
|
||||
defer res.Unlock()
|
||||
|
||||
// count already blacklisted entries
|
||||
// count available and valid versions
|
||||
valid := 0
|
||||
for _, rv := range res.Versions {
|
||||
if rv.VersionNumber == "0" {
|
||||
|
@ -262,7 +336,11 @@ func (res *Resource) Blacklist(version string) error {
|
|||
return errors.New("could not find version")
|
||||
}
|
||||
|
||||
// Purge deletes old updates, retaining a certain amount, specified by the keep parameter. Will at least keep 2 updates per resource. After purging, new versions will be selected.
|
||||
// Purge deletes old updates, retaining a certain amount, specified by
|
||||
// the keep parameter. Purge will always keep at least 2 versions so
|
||||
// specifying a smaller keep value will have no effect. Note that
|
||||
// blacklisted versions are not counted for the keep parameter.
|
||||
// After purging a new version will be selected.
|
||||
func (res *Resource) Purge(keep int) {
|
||||
res.Lock()
|
||||
defer res.Unlock()
|
||||
|
|
|
@ -77,7 +77,7 @@ func (reg *ResourceRegistry) DownloadUpdates(ctx context.Context) error {
|
|||
res.Lock()
|
||||
|
||||
// check if we want to download
|
||||
if res.ActiveVersion != nil || // resource is currently being used
|
||||
if res.inUse() ||
|
||||
res.available() || // resource was used in the past
|
||||
utils.StringInSlice(reg.MandatoryUpdates, res.Identifier) { // resource is mandatory
|
||||
|
||||
|
@ -111,6 +111,7 @@ func (reg *ResourceRegistry) DownloadUpdates(ctx context.Context) error {
|
|||
for tries := 0; tries < 3; tries++ {
|
||||
err = reg.fetchFile(rv, tries)
|
||||
if err == nil {
|
||||
rv.Available = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue