updater: support multiple versions of a package in an apt repo

Some apt repos (google-cloud-sdk) have multiple versions of the same
package, so we should support that. Now we do. Also added the
google-cloud-cli-bin package.
This commit is contained in:
Jared Allard 2024-05-15 17:35:36 -07:00
parent e70f1f9936
commit 1ee7f3f308
Signed by: jaredallard
SSH key fingerprint: SHA256:wyRyyv28jBYw8Yp/oABNPUYvbGd6hyZj23XVXEm5G/U
5 changed files with 97 additions and 7 deletions

View file

@ -8,6 +8,7 @@ require (
github.com/docker/docker v25.0.3+incompatible github.com/docker/docker v25.0.3+incompatible
github.com/egym-playground/go-prefix-writer v0.0.0-20180609083313-7326ea162eca github.com/egym-playground/go-prefix-writer v0.0.0-20180609083313-7326ea162eca
github.com/fatih/color v1.15.0 github.com/fatih/color v1.15.0
github.com/jamespfennell/xz v0.1.2
github.com/minio/minio-go/v7 v7.0.70 github.com/minio/minio-go/v7 v7.0.70
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/spf13/cobra v1.8.0 github.com/spf13/cobra v1.8.0

View file

@ -54,6 +54,8 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 h1:Wqo399gCIufwto+VfwCSvsnfGpF
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYpldc7gVz2KMQwJ/QYCDIa7XU= github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYpldc7gVz2KMQwJ/QYCDIa7XU=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jamespfennell/xz v0.1.2 h1:iCw5kScLfGCceOKgQaGuj5RilAAlV4iiwauYntak2oU=
github.com/jamespfennell/xz v0.1.2/go.mod h1:DhpWvZY1xDkK/6BREFl3c3R/fZh7IBdYq2m7xh4uLl0=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI= github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI=

View file

@ -23,12 +23,25 @@ import (
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"os"
"path/filepath"
"strconv" "strconv"
"strings" "strings"
"github.com/blang/semver/v4"
logger "github.com/charmbracelet/log"
"github.com/jamespfennell/xz"
"pault.ag/go/debian/control" "pault.ag/go/debian/control"
) )
// log is the logger for this package.
var log = logger.NewWithOptions(os.Stderr, logger.Options{
ReportCaller: true,
ReportTimestamp: true,
Level: logger.DebugLevel,
})
// Repository is a parsed version of a sources.list entry. // Repository is a parsed version of a sources.list entry.
type Repository struct { type Repository struct {
// URL is the URL of the repository. // URL is the URL of the repository.
@ -79,14 +92,14 @@ func GetPackageVersion(l Lookup) (string, error) {
var i *index var i *index
for _, index := range rel.Indexes { for _, index := range rel.Indexes {
for _, comp := range rel.Components { for _, comp := range rel.Components {
if index.Path == fmt.Sprintf("%s/binary-%s/Packages.gz", comp, l.Architecture) { if strings.HasPrefix(index.Path, fmt.Sprintf("%s/binary-%s/Packages", comp, l.Architecture)) {
i = &index i = &index
break break
} }
} }
} }
if i == nil { if i == nil {
return "", fmt.Errorf("failed to find Packages.gz index") return "", fmt.Errorf("failed to find Packages index")
} }
// Find the package in the index. // Find the package in the index.
@ -95,13 +108,39 @@ func GetPackageVersion(l Lookup) (string, error) {
return "", fmt.Errorf("failed to parse packages: %w", err) return "", fmt.Errorf("failed to parse packages: %w", err)
} }
var latestVersion *semver.Version
for _, p := range packages { for _, p := range packages {
if p.Name == l.Package { if p.Name != l.Package {
return p.Version, nil continue
}
plog := log.With("package", p.Name, "version", p.Version)
plog.Debug("found package")
sv, err := semver.ParseTolerant(p.Version)
if err != nil {
plog.With("error", err).Warn("failed to parse version, skipping")
// Can't compare it, skip it.
continue
}
// Start w/ this version if it's the first one.
if latestVersion == nil {
latestVersion = &sv
continue
}
// If this version is greater than the latest, update it.
if sv.GT(*latestVersion) {
latestVersion = &sv
} }
} }
return "", nil if latestVersion == nil {
return "", fmt.Errorf("failed to find package: %s", l.Package)
}
return latestVersion.String(), nil
} }
// getRepositoryFromSourcesEntry returns a repository from a sources // getRepositoryFromSourcesEntry returns a repository from a sources
@ -132,7 +171,7 @@ func getRepositoryFromSourcesEntry(entry string) (*Repository, error) {
} }
// parseRelease parses the Release file for the given repository. // parseRelease parses the Release file for the given repository.
func parseRelease(r *Repository, l Lookup) (*release, error) { func parseRelease(r *Repository, _ Lookup) (*release, error) {
// TODO(jaredallard): InRelease? // TODO(jaredallard): InRelease?
releaseURL := fmt.Sprintf("%s/dists/%s/Release", r.URL, r.Distribution) releaseURL := fmt.Sprintf("%s/dists/%s/Release", r.URL, r.Distribution)
@ -230,11 +269,21 @@ func parsePackages(p *index) ([]Package, error) {
defer resp.Body.Close() defer resp.Body.Close()
r := resp.Body r := resp.Body
if strings.HasSuffix(p.Path, ".gz") { switch filepath.Ext(p.Path) {
case ".gz":
r, err = gzip.NewReader(r) r, err = gzip.NewReader(r)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to create gzip reader: %w", err) return nil, fmt.Errorf("failed to create gzip reader: %w", err)
} }
case ".xz":
r = xz.NewReader(r)
case "":
// We don't need to handle compression if there is none.
break
default:
return nil, fmt.Errorf("unsupported extension for index: %s", filepath.Ext(p.Path))
} }
// Parse the index. // Parse the index.

View file

@ -0,0 +1,33 @@
# Copyright 2020-2023 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
EAPI=8
DESCRIPTION="Interact with the Google Cloud Platform"
HOMEPAGE="https://cloud.google.com/cli"
SITE="https://dl.google.com/dl/cloudsdk/channels/rapid/downloads"
SRC_URI="
amd64? ( ${SITE}/google-cloud-cli-${PV}-linux-x86_64.tar.gz )
arm64? ( ${SITE}/google-cloud-cli-${PV}-linux-arm.tar.gz )
"
LICENSE="Apache-2.0"
SLOT="0"
KEYWORDS="amd64 arm64 arm"
QA_PREBUILT="
google-cloud-sdk/bin/anothoscli
google-cloud-sdk/bin/gcloud-crc32c
"
RESTRICT="bindist mirror"
S="${WORKDIR}"
src_install() {
mkdir -p "${D}/opt/google-cloud-sdk"
cp -r "${S}/google-cloud-sdk/"* "${D}/opt/google-cloud-sdk" || die "Install failed!"
dosym /opt/google-cloud-sdk/bin/gcloud /usr/bin/gcloud
dosym /opt/google-cloud-sdk/bin/gsutil /usr/bin/gsutil
chmod 4755 /opt/google-cloud-sdk/bin/gsutil
chmod 4755 /opt/google-cloud-sdk/bin/gcloud
}

View file

@ -8,6 +8,11 @@ app-admin/1password:
options: options:
repository: "deb https://downloads.1password.com/linux/debian/amd64 stable main" repository: "deb https://downloads.1password.com/linux/debian/amd64 stable main"
package: 1password package: 1password
app-admin/google-cloud-cli-bin:
resolver: apt
options:
repository: "deb https://packages.cloud.google.com/apt cloud-sdk main"
package: google-cloud-cli
app-admin/op-cli-bin: app-admin/op-cli-bin:
resolver: apt resolver: apt
options: options: