forked from Plemya-x/ALR
		
	Initial commit
This commit is contained in:
		
							
								
								
									
										223
									
								
								internal/overrides/overrides.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										223
									
								
								internal/overrides/overrides.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,223 @@
 | 
			
		||||
/*
 | 
			
		||||
 * LURE - Linux User REpository
 | 
			
		||||
 * Copyright (C) 2023 Elara Musayelyan
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package overrides
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"lure.sh/lure/internal/cpu"
 | 
			
		||||
	"lure.sh/lure/internal/db"
 | 
			
		||||
	"lure.sh/lure/pkg/distro"
 | 
			
		||||
	"golang.org/x/exp/slices"
 | 
			
		||||
	"golang.org/x/text/language"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Opts struct {
 | 
			
		||||
	Name         string
 | 
			
		||||
	Overrides    bool
 | 
			
		||||
	LikeDistros  bool
 | 
			
		||||
	Languages    []string
 | 
			
		||||
	LanguageTags []language.Tag
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var DefaultOpts = &Opts{
 | 
			
		||||
	Overrides:   true,
 | 
			
		||||
	LikeDistros: true,
 | 
			
		||||
	Languages:   []string{"en"},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Resolve generates a slice of possible override names in the order that they should be checked
 | 
			
		||||
func Resolve(info *distro.OSRelease, opts *Opts) ([]string, error) {
 | 
			
		||||
	if opts == nil {
 | 
			
		||||
		opts = DefaultOpts
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !opts.Overrides {
 | 
			
		||||
		return []string{opts.Name}, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	langs, err := parseLangs(opts.Languages, opts.LanguageTags)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	architectures, err := cpu.CompatibleArches(cpu.Arch())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	distros := []string{info.ID}
 | 
			
		||||
	if opts.LikeDistros {
 | 
			
		||||
		distros = append(distros, info.Like...)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var out []string
 | 
			
		||||
	for _, lang := range langs {
 | 
			
		||||
		for _, distro := range distros {
 | 
			
		||||
			for _, arch := range architectures {
 | 
			
		||||
				out = append(out, opts.Name+"_"+arch+"_"+distro+"_"+lang)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			out = append(out, opts.Name+"_"+distro+"_"+lang)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, arch := range architectures {
 | 
			
		||||
			out = append(out, opts.Name+"_"+arch+"_"+lang)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		out = append(out, opts.Name+"_"+lang)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, distro := range distros {
 | 
			
		||||
		for _, arch := range architectures {
 | 
			
		||||
			out = append(out, opts.Name+"_"+arch+"_"+distro)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		out = append(out, opts.Name+"_"+distro)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, arch := range architectures {
 | 
			
		||||
		out = append(out, opts.Name+"_"+arch)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	out = append(out, opts.Name)
 | 
			
		||||
 | 
			
		||||
	for index, item := range out {
 | 
			
		||||
		out[index] = strings.TrimPrefix(strings.ReplaceAll(item, "-", "_"), "_")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return out, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *Opts) WithName(name string) *Opts {
 | 
			
		||||
	out := &Opts{}
 | 
			
		||||
	*out = *o
 | 
			
		||||
 | 
			
		||||
	out.Name = name
 | 
			
		||||
	return out
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *Opts) WithOverrides(v bool) *Opts {
 | 
			
		||||
	out := &Opts{}
 | 
			
		||||
	*out = *o
 | 
			
		||||
 | 
			
		||||
	out.Overrides = v
 | 
			
		||||
	return out
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *Opts) WithLikeDistros(v bool) *Opts {
 | 
			
		||||
	out := &Opts{}
 | 
			
		||||
	*out = *o
 | 
			
		||||
 | 
			
		||||
	out.LikeDistros = v
 | 
			
		||||
	return out
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *Opts) WithLanguages(langs []string) *Opts {
 | 
			
		||||
	out := &Opts{}
 | 
			
		||||
	*out = *o
 | 
			
		||||
 | 
			
		||||
	out.Languages = langs
 | 
			
		||||
	return out
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *Opts) WithLanguageTags(langs []string) *Opts {
 | 
			
		||||
	out := &Opts{}
 | 
			
		||||
	*out = *o
 | 
			
		||||
 | 
			
		||||
	out.Languages = langs
 | 
			
		||||
	return out
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ResolvedPackage is a LURE package after its overrides
 | 
			
		||||
// have been resolved
 | 
			
		||||
type ResolvedPackage struct {
 | 
			
		||||
	Name          string   `sh:"name"`
 | 
			
		||||
	Version       string   `sh:"version"`
 | 
			
		||||
	Release       int      `sh:"release"`
 | 
			
		||||
	Epoch         uint     `sh:"epoch"`
 | 
			
		||||
	Description   string   `db:"description"`
 | 
			
		||||
	Homepage      string   `db:"homepage"`
 | 
			
		||||
	Maintainer    string   `db:"maintainer"`
 | 
			
		||||
	Architectures []string `sh:"architectures"`
 | 
			
		||||
	Licenses      []string `sh:"license"`
 | 
			
		||||
	Provides      []string `sh:"provides"`
 | 
			
		||||
	Conflicts     []string `sh:"conflicts"`
 | 
			
		||||
	Replaces      []string `sh:"replaces"`
 | 
			
		||||
	Depends       []string `sh:"deps"`
 | 
			
		||||
	BuildDepends  []string `sh:"build_deps"`
 | 
			
		||||
	OptDepends    []string `sh:"opt_deps"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ResolvePackage(pkg *db.Package, overrides []string) *ResolvedPackage {
 | 
			
		||||
	out := &ResolvedPackage{}
 | 
			
		||||
	outVal := reflect.ValueOf(out).Elem()
 | 
			
		||||
	pkgVal := reflect.ValueOf(pkg).Elem()
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < outVal.NumField(); i++ {
 | 
			
		||||
		fieldVal := outVal.Field(i)
 | 
			
		||||
		fieldType := fieldVal.Type()
 | 
			
		||||
		pkgFieldVal := pkgVal.FieldByName(outVal.Type().Field(i).Name)
 | 
			
		||||
		pkgFieldType := pkgFieldVal.Type()
 | 
			
		||||
 | 
			
		||||
		if strings.HasPrefix(pkgFieldType.String(), "db.JSON") {
 | 
			
		||||
			pkgFieldVal = pkgFieldVal.FieldByName("Val")
 | 
			
		||||
			pkgFieldType = pkgFieldVal.Type()
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if pkgFieldType.AssignableTo(fieldType) {
 | 
			
		||||
			fieldVal.Set(pkgFieldVal)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if pkgFieldVal.Kind() == reflect.Map && pkgFieldType.Elem().AssignableTo(fieldType) {
 | 
			
		||||
			for _, override := range overrides {
 | 
			
		||||
				overrideVal := pkgFieldVal.MapIndex(reflect.ValueOf(override))
 | 
			
		||||
				if !overrideVal.IsValid() {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				fieldVal.Set(overrideVal)
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return out
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parseLangs(langs []string, tags []language.Tag) ([]string, error) {
 | 
			
		||||
	out := make([]string, len(tags)+len(langs))
 | 
			
		||||
	for i, tag := range tags {
 | 
			
		||||
		base, _ := tag.Base()
 | 
			
		||||
		out[i] = base.String()
 | 
			
		||||
	}
 | 
			
		||||
	for i, lang := range langs {
 | 
			
		||||
		tag, err := language.Parse(lang)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		base, _ := tag.Base()
 | 
			
		||||
		out[len(tags)+i] = base.String()
 | 
			
		||||
	}
 | 
			
		||||
	slices.Sort(out)
 | 
			
		||||
	out = slices.Compact(out)
 | 
			
		||||
	return out, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										195
									
								
								internal/overrides/overrides_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										195
									
								
								internal/overrides/overrides_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,195 @@
 | 
			
		||||
/*
 | 
			
		||||
 * LURE - Linux User REpository
 | 
			
		||||
 * Copyright (C) 2023 Elara Musayelyan
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package overrides_test
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"os"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"lure.sh/lure/internal/overrides"
 | 
			
		||||
	"lure.sh/lure/pkg/distro"
 | 
			
		||||
	"golang.org/x/text/language"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var info = &distro.OSRelease{
 | 
			
		||||
	ID:   "centos",
 | 
			
		||||
	Like: []string{"rhel", "fedora"},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestResolve(t *testing.T) {
 | 
			
		||||
	names, err := overrides.Resolve(info, nil)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("Expected no error, got %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	expected := []string{
 | 
			
		||||
		"amd64_centos_en",
 | 
			
		||||
		"centos_en",
 | 
			
		||||
		"amd64_rhel_en",
 | 
			
		||||
		"rhel_en",
 | 
			
		||||
		"amd64_fedora_en",
 | 
			
		||||
		"fedora_en",
 | 
			
		||||
		"amd64_en",
 | 
			
		||||
		"en",
 | 
			
		||||
		"amd64_centos",
 | 
			
		||||
		"centos",
 | 
			
		||||
		"amd64_rhel",
 | 
			
		||||
		"rhel",
 | 
			
		||||
		"amd64_fedora",
 | 
			
		||||
		"fedora",
 | 
			
		||||
		"amd64",
 | 
			
		||||
		"",
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !reflect.DeepEqual(names, expected) {
 | 
			
		||||
		t.Errorf("expected %v, got %v", expected, names)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestResolveName(t *testing.T) {
 | 
			
		||||
	names, err := overrides.Resolve(info, &overrides.Opts{
 | 
			
		||||
		Name:        "deps",
 | 
			
		||||
		Overrides:   true,
 | 
			
		||||
		LikeDistros: true,
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("Expected no error, got %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	expected := []string{
 | 
			
		||||
		"deps_amd64_centos",
 | 
			
		||||
		"deps_centos",
 | 
			
		||||
		"deps_amd64_rhel",
 | 
			
		||||
		"deps_rhel",
 | 
			
		||||
		"deps_amd64_fedora",
 | 
			
		||||
		"deps_fedora",
 | 
			
		||||
		"deps_amd64",
 | 
			
		||||
		"deps",
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !reflect.DeepEqual(names, expected) {
 | 
			
		||||
		t.Errorf("expected %v, got %v", expected, names)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestResolveArch(t *testing.T) {
 | 
			
		||||
	os.Setenv("LURE_ARCH", "arm7")
 | 
			
		||||
	defer os.Setenv("LURE_ARCH", "")
 | 
			
		||||
 | 
			
		||||
	names, err := overrides.Resolve(info, &overrides.Opts{
 | 
			
		||||
		Name:        "deps",
 | 
			
		||||
		Overrides:   true,
 | 
			
		||||
		LikeDistros: true,
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("Expected no error, got %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	expected := []string{
 | 
			
		||||
		"deps_arm7_centos",
 | 
			
		||||
		"deps_arm6_centos",
 | 
			
		||||
		"deps_arm5_centos",
 | 
			
		||||
		"deps_centos",
 | 
			
		||||
		"deps_arm7_rhel",
 | 
			
		||||
		"deps_arm6_rhel",
 | 
			
		||||
		"deps_arm5_rhel",
 | 
			
		||||
		"deps_rhel",
 | 
			
		||||
		"deps_arm7_fedora",
 | 
			
		||||
		"deps_arm6_fedora",
 | 
			
		||||
		"deps_arm5_fedora",
 | 
			
		||||
		"deps_fedora",
 | 
			
		||||
		"deps_arm7",
 | 
			
		||||
		"deps_arm6",
 | 
			
		||||
		"deps_arm5",
 | 
			
		||||
		"deps",
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !reflect.DeepEqual(names, expected) {
 | 
			
		||||
		t.Errorf("expected %v, got %v", expected, names)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestResolveNoLikeDistros(t *testing.T) {
 | 
			
		||||
	names, err := overrides.Resolve(info, &overrides.Opts{
 | 
			
		||||
		Overrides:   true,
 | 
			
		||||
		LikeDistros: false,
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("Expected no error, got %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	expected := []string{
 | 
			
		||||
		"amd64_centos",
 | 
			
		||||
		"centos",
 | 
			
		||||
		"amd64",
 | 
			
		||||
		"",
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !reflect.DeepEqual(names, expected) {
 | 
			
		||||
		t.Errorf("expected %v, got %v", expected, names)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestResolveNoOverrides(t *testing.T) {
 | 
			
		||||
	names, err := overrides.Resolve(info, &overrides.Opts{
 | 
			
		||||
		Name:        "deps",
 | 
			
		||||
		Overrides:   false,
 | 
			
		||||
		LikeDistros: false,
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("Expected no error, got %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	expected := []string{"deps"}
 | 
			
		||||
 | 
			
		||||
	if !reflect.DeepEqual(names, expected) {
 | 
			
		||||
		t.Errorf("expected %v, got %v", expected, names)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestResolveLangs(t *testing.T) {
 | 
			
		||||
	names, err := overrides.Resolve(info, &overrides.Opts{
 | 
			
		||||
		Overrides:    true,
 | 
			
		||||
		Languages:    []string{"ru_RU", "en", "en_US"},
 | 
			
		||||
		LanguageTags: []language.Tag{language.BritishEnglish},
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("Expected no error, got %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	expected := []string{
 | 
			
		||||
		"amd64_centos_en",
 | 
			
		||||
		"centos_en",
 | 
			
		||||
		"amd64_en",
 | 
			
		||||
		"en",
 | 
			
		||||
		"amd64_centos_ru",
 | 
			
		||||
		"centos_ru",
 | 
			
		||||
		"amd64_ru",
 | 
			
		||||
		"ru",
 | 
			
		||||
		"amd64_centos",
 | 
			
		||||
		"centos",
 | 
			
		||||
		"amd64",
 | 
			
		||||
		"",
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !reflect.DeepEqual(names, expected) {
 | 
			
		||||
		t.Errorf("expected %v, got %v", expected, names)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user