Skip to content

Commit

Permalink
Prioritize archives over binaries
Browse files Browse the repository at this point in the history
  • Loading branch information
cardil committed Apr 7, 2023
1 parent ed2734a commit 9d6508b
Show file tree
Hide file tree
Showing 11 changed files with 283 additions and 146 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# **et
# ****et

****et artifacts from GitHub releases. Also, a package manager. And an API.
7 changes: 4 additions & 3 deletions pkg/ghet/download/download.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"strings"

"github.com/1set/gut/yos"
githubapi "github.com/cardil/ghet/pkg/github/api"
"github.com/cardil/ghet/pkg/metadata"
"github.com/cardil/ghet/pkg/output"
"github.com/cardil/ghet/pkg/output/tui"
Expand All @@ -23,7 +24,7 @@ const (
)

type assetInfo struct {
Asset
githubapi.Asset
number int
total int
longestName int
Expand Down Expand Up @@ -86,11 +87,11 @@ func downloadAsset(ctx context.Context, asset assetInfo, args Args) error {
return copyFile(cachePath, asset.Asset, args)
}

func copyFile(cachePath string, asset Asset, args Args) error {
func copyFile(cachePath string, asset githubapi.Asset, args Args) error {
if err := os.MkdirAll(args.Destination, executableMode); err != nil {
return errors.WithStack(err)
}
bin := path.Join(args.Destination, args.Asset.FileName.ToString())
bin := path.Join(args.Destination, asset.Name)
if err := yos.MoveFile(cachePath, bin); err != nil {
return errors.WithStack(err)
}
Expand Down
47 changes: 29 additions & 18 deletions pkg/ghet/download/plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,7 @@ import (
var ErrNoAssetFound = errors.New("no matching asset found")

type Plan struct {
Assets []Asset
}

type Asset struct {
ID int64
Name string
ContentType string
Size int
URL string
Assets []githubapi.Asset
}

func CreatePlan(ctx context.Context, args Args) (*Plan, error) {
Expand Down Expand Up @@ -58,24 +50,27 @@ func CreatePlan(ctx context.Context, args Args) (*Plan, error) {
"release": rr,
}).Trace("Github API response")

assets := make([]Asset, 0, 1)
assets := make([]githubapi.Asset, 0, 1)
log.WithFields(slog.Fields{"assets": namesOf(rr.Assets)}).
Debug("Checking assets")
for _, asset := range rr.Assets {
if assetMatches(asset, args) {
log.WithFields(slog.Fields{"asset": asset}).
Trace("Asset matches")
assets = append(assets, Asset{
a := githubapi.Asset{
ID: asset.GetID(),
Name: asset.GetName(),
ContentType: asset.GetContentType(),
Size: asset.GetSize(),
URL: asset.GetBrowserDownloadURL(),
})
}
log.WithFields(slog.Fields{"asset": a}).Trace("Asset matches")
assets = append(assets, a)
}
}
assets = prioritizeArchives(assets)
if len(assets) == 0 {
return nil, errors.WithStack(ErrNoAssetFound)
}
log.WithFields(slog.Fields{"assets": len(assets)}).
log.WithFields(slog.Fields{"assets": assets}).
Debug("Plan created")
widgets.Printf(ctx, "🎉 Found %s matching assets", color.Cyan.Sprint(len(assets)))
return &Plan{Assets: assets}, nil
Expand Down Expand Up @@ -107,6 +102,22 @@ func (p Plan) Download(ctx context.Context, args Args) error {
return nil
}

func prioritizeArchives(assets []githubapi.Asset) []githubapi.Asset {
idx := githubapi.CreateIndex(assets)
if len(idx.Archives) > 0 {
return append(idx.Archives, idx.Checksums...)
}
return assets
}

func namesOf(assets []*github.ReleaseAsset) []string {
names := make([]string, 0, len(assets))
for _, asset := range assets {
names = append(names, asset.GetName())
}
return names
}

func fetchRelease(
ctx context.Context, args Args,
client *github.Client,
Expand Down Expand Up @@ -137,8 +148,8 @@ func fetchRelease(
func assetMatches(asset *github.ReleaseAsset, args Args) bool {
name := asset.GetName()
return name == args.Checksums.ToString() ||
(strings.Contains(name, args.Asset.BaseName) &&
args.Architecture.Matches(name) &&
args.OperatingSystem.Matches(name))
(strings.HasPrefix(name, args.Asset.BaseName) &&
args.Architecture.Matches(strings.TrimPrefix(name, args.Asset.BaseName)) &&
args.OperatingSystem.Matches(strings.TrimPrefix(name, args.Asset.BaseName)))

}
4 changes: 2 additions & 2 deletions pkg/ghet/download/plan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func TestCreatePlan(t *testing.T) {
testCases := []createPlanTestCase{{
name: "latest release of kn-event",
want: download.Plan{
Assets: []download.Asset{{
Assets: []githubapi.Asset{{
Name: "checksums.txt",
URL: downloadURL("1.9.1", "checksums.txt"),
ContentType: "text/plain",
Expand All @@ -49,7 +49,7 @@ func TestCreatePlan(t *testing.T) {
return args
},
want: download.Plan{
Assets: []download.Asset{{
Assets: []githubapi.Asset{{
Name: "checksums.txt",
URL: downloadURL("1.8.0", "checksums.txt"),
ContentType: "application/octet-stream",
Expand Down
51 changes: 51 additions & 0 deletions pkg/github/api/asset.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package api

import "github.com/cardil/ghet/pkg/match"

type Asset struct {
ID int64
Name string
ContentType string
Size int
URL string
}

type IndexedAssets struct {
Archives []Asset
Checksums []Asset
Other []Asset
}

func CreateIndex(assets []Asset) IndexedAssets {
index := IndexedAssets{}
for _, asset := range assets {
name := asset.Name
if isArchive.Matches(name) {
index.Archives = append(index.Archives, asset)
} else if isChecksum.Matches(name) {
index.Checksums = append(index.Checksums, asset)
} else {
index.Other = append(index.Other, asset)
}
}
return index
}

var (
isArchive = match.Any(
match.EndsWith(".tar.gz"),
match.EndsWith(".tgz"),
match.EndsWith(".tar.bz2"),
match.EndsWith(".tbz2"),
match.EndsWith(".tar.xz"),
match.EndsWith(".txz"),
match.EndsWith(".zip"),
)

isChecksum = match.Any(
match.EndsWith(".sha256"),
match.EndsWith(".sha512"),
match.EndsWith(".md5"),
match.Regex("checksums?\\.txt"),
)
)
50 changes: 50 additions & 0 deletions pkg/github/arch.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package github

import (
"runtime"

"github.com/cardil/ghet/pkg/match"
)

type Architecture string

const (
ArchX86 Architecture = "x86"
ArchAMD64 Architecture = "amd64"
ArchARM Architecture = "arm"
ArchARM64 Architecture = "arm64"
ArchPPC64LE Architecture = "ppc64le"
ArchS390X Architecture = "s390x"
)

func (a Architecture) Matches(name string) bool {
return matchWith(name, archMatchers[a])
}

func CurrentArchitecture() Architecture {
return Architecture(runtime.GOARCH)
}

var archMatchers = map[Architecture]match.Matcher{ //nolint:gochecknoglobals
ArchX86: match.Any(
match.Every(
match.Substr("x86"),
match.Not(match.Substr("x86_64")),
),
match.Regex("i?[3-6]86"),
),
ArchAMD64: match.Any(
match.Substr("amd64"),
match.Substr("x86_64"),
),
ArchARM: match.Any(
match.Substr("arm32"),
match.Every(
match.Substr("arm"),
match.Not(match.Substr("arm64")),
),
),
ArchARM64: match.Any(match.Substr("arm64")),
ArchPPC64LE: match.Any(match.Regex("ppc-?64-?(?:le)?")),
ArchS390X: match.Any(match.Substr("s390x")),
}
17 changes: 17 additions & 0 deletions pkg/github/matching.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package github

import (
"strings"

"github.com/cardil/ghet/pkg/match"
)

var notPackageManagers = match.Every( //nolint:gochecknoglobals
match.Not(match.EndsWith(".deb")),
match.Not(match.EndsWith(".rpm")),
)

func matchWith(name string, matcher match.Matcher) bool {
name = strings.ToLower(name)
return matcher.Matches(name)
}
76 changes: 76 additions & 0 deletions pkg/github/os.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package github

import (
"runtime"
"strings"

"github.com/cardil/ghet/pkg/match"
"github.com/u-root/u-root/pkg/ldd"
)

type OsFamily string

const (
OSFamilyDarwin OsFamily = "darwin"
OSFamilyLinux OsFamily = "linux"
OSFamilyWindows OsFamily = "windows"
)

type OperatingSystem string

const (
OSDarwin OperatingSystem = "darwin"
OSLinuxMusl OperatingSystem = "linux-musl"
OSLinuxGnu OperatingSystem = "linux-gnu"
OSWindows OperatingSystem = "windows"
)

func (os OperatingSystem) Matches(name string) bool {
return matchWith(name, osMatchers[os])
}

func CurrentOS() OperatingSystem {
family := OsFamily(runtime.GOOS)
//goland:noinspection GoBoolExpressions
if family == OSFamilyLinux {
return linuxFlavor()
}
return OperatingSystem(family)
}

var osMatchers = map[OperatingSystem]match.Matcher{ //nolint:gochecknoglobals
OSLinuxMusl: match.Every(
match.Any(match.Substr("linux", "musl")),
notPackageManagers,
),
OSLinuxGnu: match.Every(
match.Any(
match.Substr("linux", "glibc"),
match.Substr("linux", "gnu"),
match.Every(
match.Substr("linux"),
match.Not(match.Substr("musl")),
),
),
notPackageManagers,
),
OSDarwin: match.Any(
match.Substr("darwin"),
match.Substr("mac"),
match.Substr("osx"),
),
OSWindows: match.Any(
match.Substr("win"),
),
}

func linuxFlavor() OperatingSystem {
if fis, err := ldd.Ldd([]string{"/bin/sh"}); err == nil {
for _, fi := range fis {
if strings.Contains(fi.Name(), "musl") {
return OSLinuxMusl
}
}
}
return OSLinuxGnu
}

0 comments on commit 9d6508b

Please sign in to comment.