ALR
/
upgrade.go
131 строка · 3.4 Кб
1/*
2* ALR - Any Linux Repository
3* Copyright (C) 2024 Евгений Храмов
4*
5* This program is free software: you can redistribute it and/or modify
6* it under the terms of the GNU General Public License as published by
7* the Free Software Foundation, either version 3 of the License, or
8* (at your option) any later version.
9*
10* This program is distributed in the hope that it will be useful,
11* but WITHOUT ANY WARRANTY; without even the implied warranty of
12* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13* GNU General Public License for more details.
14*
15* You should have received a copy of the GNU General Public License
16* along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19package main
20
21import (
22"context"
23"fmt"
24
25"github.com/urfave/cli/v2"
26"plemya-x.ru/alr/internal/config"
27"plemya-x.ru/alr/internal/db"
28"plemya-x.ru/alr/internal/types"
29"plemya-x.ru/alr/pkg/build"
30"plemya-x.ru/alr/pkg/distro"
31"plemya-x.ru/alr/pkg/loggerctx"
32"plemya-x.ru/alr/pkg/manager"
33"plemya-x.ru/alr/pkg/repos"
34"go.elara.ws/vercmp"
35"golang.org/x/exp/maps"
36"golang.org/x/exp/slices"
37)
38
39var upgradeCmd = &cli.Command{
40Name: "upgrade",
41Usage: "Upgrade all installed packages",
42Aliases: []string{"up"},
43Flags: []cli.Flag{
44&cli.BoolFlag{
45Name: "clean",
46Aliases: []string{"c"},
47Usage: "Build package from scratch even if there's an already built package available",
48},
49},
50Action: func(c *cli.Context) error {
51ctx := c.Context
52log := loggerctx.From(ctx)
53
54info, err := distro.ParseOSRelease(ctx)
55if err != nil {
56log.Fatal("Error parsing os-release file").Err(err).Send()
57}
58
59mgr := manager.Detect()
60if mgr == nil {
61log.Fatal("Unable to detect a supported package manager on the system").Send()
62}
63
64err = repos.Pull(ctx, config.Config(ctx).Repos)
65if err != nil {
66log.Fatal("Error pulling repos").Err(err).Send()
67}
68
69updates, err := checkForUpdates(ctx, mgr, info)
70if err != nil {
71log.Fatal("Error checking for updates").Err(err).Send()
72}
73
74if len(updates) > 0 {
75build.InstallPkgs(ctx, updates, nil, types.BuildOpts{
76Manager: mgr,
77Clean: c.Bool("clean"),
78Interactive: c.Bool("interactive"),
79})
80} else {
81log.Info("There is nothing to do.").Send()
82}
83
84return nil
85},
86}
87
88func checkForUpdates(ctx context.Context, mgr manager.Manager, info *distro.OSRelease) ([]db.Package, error) {
89installed, err := mgr.ListInstalled(nil)
90if err != nil {
91return nil, err
92}
93
94pkgNames := maps.Keys(installed)
95found, _, err := repos.FindPkgs(ctx, pkgNames)
96if err != nil {
97return nil, err
98}
99
100var out []db.Package
101for pkgName, pkgs := range found {
102if slices.Contains(config.Config(ctx).IgnorePkgUpdates, pkgName) {
103continue
104}
105
106if len(pkgs) > 1 {
107// Puts the element with the highest version first
108slices.SortFunc(pkgs, func(a, b db.Package) int {
109return vercmp.Compare(a.Version, b.Version)
110})
111}
112
113// First element is the package we want to install
114pkg := pkgs[0]
115
116repoVer := pkg.Version
117if pkg.Release != 0 && pkg.Epoch == 0 {
118repoVer = fmt.Sprintf("%s-%d", pkg.Version, pkg.Release)
119} else if pkg.Release != 0 && pkg.Epoch != 0 {
120repoVer = fmt.Sprintf("%d:%s-%d", pkg.Epoch, pkg.Version, pkg.Release)
121}
122
123c := vercmp.Compare(repoVer, installed[pkgName])
124if c == 0 || c == -1 {
125continue
126} else if c == 1 {
127out = append(out, pkg)
128}
129}
130return out, nil
131}
132