refactor(cli): move cli+logger to own module
This commit is contained in:
parent
c8faae8637
commit
a5c7242045
6 changed files with 64 additions and 57 deletions
|
@ -1,7 +1,8 @@
|
||||||
package main
|
package cli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
logger "v/logger"
|
||||||
state "v/state"
|
state "v/state"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -69,10 +70,10 @@ func (c CLI) Run(args []string, currentState state.State) error {
|
||||||
// Prints autogenerated help documentation specifying command usage
|
// Prints autogenerated help documentation specifying command usage
|
||||||
// and descriptions based on registered commands (see: AddCommand).
|
// and descriptions based on registered commands (see: AddCommand).
|
||||||
func (c CLI) Help() {
|
func (c CLI) Help() {
|
||||||
InfoLogger.Printf("v: A simple version manager. (v%s)\n---", Version)
|
logger.InfoLogger.Printf("v: A simple version manager. (v%s)\n---", c.Metadata["Version"])
|
||||||
for _, commandLabel := range c.OrderedCommands {
|
for _, commandLabel := range c.OrderedCommands {
|
||||||
command := c.Commands[commandLabel]
|
command := c.Commands[commandLabel]
|
||||||
InfoLogger.Printf("\033[1m%-30s\033[0m%s\n", command.Usage, command.Description)
|
logger.InfoLogger.Printf("\033[1m%-30s\033[0m%s\n", command.Usage, command.Description)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
36
commands.go
36
commands.go
|
@ -3,6 +3,8 @@ package main
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"slices"
|
"slices"
|
||||||
|
cli "v/cli"
|
||||||
|
logger "v/logger"
|
||||||
state "v/state"
|
state "v/state"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -30,9 +32,9 @@ func writeShim(shimPath string) error {
|
||||||
|
|
||||||
// Sets up directories and files used to store downloaded archives,
|
// Sets up directories and files used to store downloaded archives,
|
||||||
// installed runtimes and metadata.
|
// installed runtimes and metadata.
|
||||||
func Initialize(args []string, flags Flags, currentState state.State) error {
|
func Initialize(args []string, flags cli.Flags, currentState state.State) error {
|
||||||
if flags.AddPath {
|
if flags.AddPath {
|
||||||
InfoLogger.Printf("export PATH=%s:$PATH\n", state.GetStatePath("shims"))
|
logger.InfoLogger.Printf("export PATH=%s:$PATH\n", state.GetStatePath("shims"))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,19 +49,19 @@ func Initialize(args []string, flags Flags, currentState state.State) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func UninstallPython(args []string, flags Flags, currentState state.State) error {
|
func UninstallPython(args []string, flags cli.Flags, currentState state.State) error {
|
||||||
runtimePath := state.GetStatePath("runtimes", "py-"+args[1])
|
runtimePath := state.GetStatePath("runtimes", "py-"+args[1])
|
||||||
err := os.RemoveAll(runtimePath)
|
err := os.RemoveAll(runtimePath)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func InstallPython(args []string, flags Flags, currentState state.State) error {
|
func InstallPython(args []string, flags cli.Flags, currentState state.State) error {
|
||||||
version := args[1]
|
version := args[1]
|
||||||
|
|
||||||
return InstallPythonDistribution(version, flags.NoCache, flags.Verbose)
|
return InstallPythonDistribution(version, flags.NoCache, flags.Verbose)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Use(args []string, flags Flags, currentState state.State) error {
|
func Use(args []string, flags cli.Flags, currentState state.State) error {
|
||||||
version := args[1]
|
version := args[1]
|
||||||
if err := ValidateVersion(version); err != nil {
|
if err := ValidateVersion(version); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -76,16 +78,16 @@ func Use(args []string, flags Flags, currentState state.State) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !found {
|
if !found {
|
||||||
InfoLogger.Println("Version not installed. Installing it first.")
|
logger.InfoLogger.Println("Version not installed. Installing it first.")
|
||||||
InstallPythonDistribution(version, flags.NoCache, flags.Verbose)
|
InstallPythonDistribution(version, flags.NoCache, flags.Verbose)
|
||||||
}
|
}
|
||||||
|
|
||||||
state.WriteState(version)
|
state.WriteState(version)
|
||||||
InfoLogger.Printf("Now using Python %s\n", version)
|
logger.InfoLogger.Printf("Now using Python %s\n", version)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func ListVersions(args []string, flags Flags, currentState state.State) error {
|
func ListVersions(args []string, flags cli.Flags, currentState state.State) error {
|
||||||
installedVersions, err := ListInstalledVersions()
|
installedVersions, err := ListInstalledVersions()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -93,19 +95,19 @@ func ListVersions(args []string, flags Flags, currentState state.State) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(installedVersions) == 0 {
|
if len(installedVersions) == 0 {
|
||||||
InfoLogger.Println("No versions installed!")
|
logger.InfoLogger.Println("No versions installed!")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, d := range installedVersions {
|
for _, d := range installedVersions {
|
||||||
InfoLogger.Println(d)
|
logger.InfoLogger.Println(d)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Which prints out the system path to the executable being used by `python`.
|
// Which prints out the system path to the executable being used by `python`.
|
||||||
func Which(args []string, flags Flags, currentState state.State) error {
|
func Which(args []string, flags cli.Flags, currentState state.State) error {
|
||||||
selectedVersion, _ := DetermineSelectedPythonVersion(currentState)
|
selectedVersion, _ := DetermineSelectedPythonVersion(currentState)
|
||||||
installedVersions, _ := ListInstalledVersions()
|
installedVersions, _ := ListInstalledVersions()
|
||||||
isInstalled := slices.Contains(installedVersions, selectedVersion.Version)
|
isInstalled := slices.Contains(installedVersions, selectedVersion.Version)
|
||||||
|
@ -119,7 +121,7 @@ func Which(args []string, flags Flags, currentState state.State) error {
|
||||||
tag := VersionStringToStruct(selectedVersion.Version)
|
tag := VersionStringToStruct(selectedVersion.Version)
|
||||||
printedPath = state.GetStatePath("runtimes", "py-"+selectedVersion.Version, "bin", "python"+tag.MajorMinor())
|
printedPath = state.GetStatePath("runtimes", "py-"+selectedVersion.Version, "bin", "python"+tag.MajorMinor())
|
||||||
} else {
|
} else {
|
||||||
InfoLogger.Printf("The desired version (%s) is not installed.\n", selectedVersion.Version)
|
logger.InfoLogger.Printf("The desired version (%s) is not installed.\n", selectedVersion.Version)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,27 +133,27 @@ func Which(args []string, flags Flags, currentState state.State) error {
|
||||||
printedPath = Bold(printedPath)
|
printedPath = Bold(printedPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
InfoLogger.Printf("%s%s\n", prefix, printedPath)
|
logger.InfoLogger.Printf("%s%s\n", prefix, printedPath)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CurrentVersion (called via `v version`) outputs the currently selected version
|
// CurrentVersion (called via `v version`) outputs the currently selected version
|
||||||
// and what configures it. If the version is configured by a file, the file is returned
|
// and what configures it. If the version is configured by a file, the file is returned
|
||||||
// under "source", if the system Python is used, "system" is returned as a source.
|
// under "source", if the system Python is used, "system" is returned as a source.
|
||||||
func CurrentVersion(args []string, flags Flags, currentState state.State) error {
|
func CurrentVersion(args []string, flags cli.Flags, currentState state.State) error {
|
||||||
selectedVersion, _ := DetermineSelectedPythonVersion(currentState)
|
selectedVersion, _ := DetermineSelectedPythonVersion(currentState)
|
||||||
installedVersions, _ := ListInstalledVersions()
|
installedVersions, _ := ListInstalledVersions()
|
||||||
isInstalled := slices.Contains(installedVersions, selectedVersion.Version)
|
isInstalled := slices.Contains(installedVersions, selectedVersion.Version)
|
||||||
|
|
||||||
if !isInstalled {
|
if !isInstalled {
|
||||||
InfoLogger.Println(Bold(Yellow("WARNING: This version is not installed.")))
|
logger.InfoLogger.Println(Bold(Yellow("WARNING: This version is not installed.")))
|
||||||
}
|
}
|
||||||
|
|
||||||
if flags.RawOutput {
|
if flags.RawOutput {
|
||||||
InfoLogger.Println(selectedVersion.Version)
|
logger.InfoLogger.Println(selectedVersion.Version)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
InfoLogger.Printf("Python version: %s\nSource: %s\n", Bold(selectedVersion.Version), Bold(selectedVersion.Source))
|
logger.InfoLogger.Printf("Python version: %s\nSource: %s\n", Bold(selectedVersion.Version), Bold(selectedVersion.Source))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
cli "v/cli"
|
||||||
|
logger "v/logger"
|
||||||
state "v/state"
|
state "v/state"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -14,10 +16,10 @@ func TestListVersionOutputsNoticeIfNoVersionsInstalled(t *testing.T) {
|
||||||
os.Mkdir(state.GetStatePath("runtimes"), 0750)
|
os.Mkdir(state.GetStatePath("runtimes"), 0750)
|
||||||
var out bytes.Buffer
|
var out bytes.Buffer
|
||||||
|
|
||||||
InfoLogger.SetOutput(&out)
|
logger.InfoLogger.SetOutput(&out)
|
||||||
defer InfoLogger.SetOutput(os.Stdout)
|
defer logger.InfoLogger.SetOutput(os.Stdout)
|
||||||
|
|
||||||
ListVersions([]string{}, Flags{}, state.State{})
|
ListVersions([]string{}, cli.Flags{}, state.State{})
|
||||||
|
|
||||||
captured := out.String()
|
captured := out.String()
|
||||||
if captured != "No versions installed!\n" {
|
if captured != "No versions installed!\n" {
|
||||||
|
@ -31,10 +33,10 @@ func TestListVersionOutputsVersionsInstalled(t *testing.T) {
|
||||||
os.MkdirAll(state.GetStatePath("runtimes", "py-1.2.3"), 0750)
|
os.MkdirAll(state.GetStatePath("runtimes", "py-1.2.3"), 0750)
|
||||||
var out bytes.Buffer
|
var out bytes.Buffer
|
||||||
|
|
||||||
InfoLogger.SetOutput(&out)
|
logger.InfoLogger.SetOutput(&out)
|
||||||
defer InfoLogger.SetOutput(os.Stdout)
|
defer logger.InfoLogger.SetOutput(os.Stdout)
|
||||||
|
|
||||||
ListVersions([]string{}, Flags{}, state.State{})
|
ListVersions([]string{}, cli.Flags{}, state.State{})
|
||||||
|
|
||||||
captured := out.String()
|
captured := out.String()
|
||||||
if captured != "1.2.3\n" {
|
if captured != "1.2.3\n" {
|
||||||
|
@ -47,10 +49,10 @@ func TestListVersionReturnsErrorOnFailure(t *testing.T) {
|
||||||
|
|
||||||
var out bytes.Buffer
|
var out bytes.Buffer
|
||||||
|
|
||||||
InfoLogger.SetOutput(&out)
|
logger.InfoLogger.SetOutput(&out)
|
||||||
defer InfoLogger.SetOutput(os.Stdout)
|
defer logger.InfoLogger.SetOutput(os.Stdout)
|
||||||
|
|
||||||
err := ListVersions([]string{}, Flags{}, state.State{})
|
err := ListVersions([]string{}, cli.Flags{}, state.State{})
|
||||||
|
|
||||||
captured := out.String()
|
captured := out.String()
|
||||||
if captured != "" {
|
if captured != "" {
|
||||||
|
@ -67,10 +69,10 @@ func TestListVersionOutputsVersionSelectedAndWarnsNotInstalled(t *testing.T) {
|
||||||
|
|
||||||
var out bytes.Buffer
|
var out bytes.Buffer
|
||||||
|
|
||||||
InfoLogger.SetOutput(&out)
|
logger.InfoLogger.SetOutput(&out)
|
||||||
defer InfoLogger.SetOutput(os.Stdout)
|
defer logger.InfoLogger.SetOutput(os.Stdout)
|
||||||
|
|
||||||
Which([]string{}, Flags{}, state.State{GlobalVersion: "1.2.3"})
|
Which([]string{}, cli.Flags{}, state.State{GlobalVersion: "1.2.3"})
|
||||||
|
|
||||||
captured := out.String()
|
captured := out.String()
|
||||||
if captured != "The desired version (1.2.3) is not installed.\n" {
|
if captured != "The desired version (1.2.3) is not installed.\n" {
|
||||||
|
@ -83,11 +85,11 @@ func TestWhichOutputsVersionSelectedIfInstalled(t *testing.T) {
|
||||||
|
|
||||||
var out bytes.Buffer
|
var out bytes.Buffer
|
||||||
|
|
||||||
InfoLogger.SetOutput(&out)
|
logger.InfoLogger.SetOutput(&out)
|
||||||
defer InfoLogger.SetOutput(os.Stdout)
|
defer logger.InfoLogger.SetOutput(os.Stdout)
|
||||||
|
|
||||||
os.MkdirAll(state.GetStatePath("runtimes", "py-1.2.3"), 0750)
|
os.MkdirAll(state.GetStatePath("runtimes", "py-1.2.3"), 0750)
|
||||||
Which([]string{}, Flags{}, state.State{GlobalVersion: "1.2.3"})
|
Which([]string{}, cli.Flags{}, state.State{GlobalVersion: "1.2.3"})
|
||||||
|
|
||||||
captured := strings.TrimSpace(out.String())
|
captured := strings.TrimSpace(out.String())
|
||||||
expected := state.GetStatePath("runtimes", "py-1.2.3", "bin", "python1.2")
|
expected := state.GetStatePath("runtimes", "py-1.2.3", "bin", "python1.2")
|
||||||
|
@ -101,10 +103,10 @@ func TestWhichOutputsSystemVersionIfNoneSelected(t *testing.T) {
|
||||||
|
|
||||||
var out bytes.Buffer
|
var out bytes.Buffer
|
||||||
|
|
||||||
InfoLogger.SetOutput(&out)
|
logger.InfoLogger.SetOutput(&out)
|
||||||
defer InfoLogger.SetOutput(os.Stdout)
|
defer logger.InfoLogger.SetOutput(os.Stdout)
|
||||||
|
|
||||||
Which([]string{}, Flags{RawOutput: true}, state.State{})
|
Which([]string{}, cli.Flags{RawOutput: true}, state.State{})
|
||||||
|
|
||||||
captured := strings.TrimSpace(out.String())
|
captured := strings.TrimSpace(out.String())
|
||||||
|
|
||||||
|
@ -118,11 +120,11 @@ func TestWhichOutputsVersionWithoutPrefixesIfRawOutput(t *testing.T) {
|
||||||
|
|
||||||
var out bytes.Buffer
|
var out bytes.Buffer
|
||||||
|
|
||||||
InfoLogger.SetOutput(&out)
|
logger.InfoLogger.SetOutput(&out)
|
||||||
defer InfoLogger.SetOutput(os.Stdout)
|
defer logger.InfoLogger.SetOutput(os.Stdout)
|
||||||
|
|
||||||
os.MkdirAll(state.GetStatePath("runtimes", "py-1.2.3"), 0750)
|
os.MkdirAll(state.GetStatePath("runtimes", "py-1.2.3"), 0750)
|
||||||
Which([]string{}, Flags{RawOutput: true}, state.State{GlobalVersion: "1.2.3"})
|
Which([]string{}, cli.Flags{RawOutput: true}, state.State{GlobalVersion: "1.2.3"})
|
||||||
|
|
||||||
captured := strings.TrimSpace(out.String())
|
captured := strings.TrimSpace(out.String())
|
||||||
expected := state.GetStatePath("runtimes", "py-1.2.3", "bin", "python1.2")
|
expected := state.GetStatePath("runtimes", "py-1.2.3", "bin", "python1.2")
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
logger "v/logger"
|
||||||
state "v/state"
|
state "v/state"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -57,15 +58,15 @@ func downloadSource(version string, skipCache bool) (PackageMetadata, error) {
|
||||||
|
|
||||||
client := http.Client{}
|
client := http.Client{}
|
||||||
|
|
||||||
InfoLogger.Println(Bold("Downloading source for Python " + version))
|
logger.InfoLogger.Println(Bold("Downloading source for Python " + version))
|
||||||
InfoLogger.SetPrefix(" ")
|
logger.InfoLogger.SetPrefix(" ")
|
||||||
defer InfoLogger.SetPrefix("")
|
defer logger.InfoLogger.SetPrefix("")
|
||||||
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
_, err := os.Stat(archivePath)
|
_, err := os.Stat(archivePath)
|
||||||
|
|
||||||
if errors.Is(err, os.ErrNotExist) || skipCache {
|
if errors.Is(err, os.ErrNotExist) || skipCache {
|
||||||
InfoLogger.Println("Fetching from " + sourceUrl)
|
logger.InfoLogger.Println("Fetching from " + sourceUrl)
|
||||||
|
|
||||||
resp, err := client.Get(sourceUrl)
|
resp, err := client.Get(sourceUrl)
|
||||||
|
|
||||||
|
@ -79,21 +80,21 @@ func downloadSource(version string, skipCache bool) (PackageMetadata, error) {
|
||||||
|
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
} else {
|
} else {
|
||||||
InfoLogger.Println("Found in cache: " + archivePath)
|
logger.InfoLogger.Println("Found in cache: " + archivePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
InfoLogger.Printf("✅ Done (%s)\n", time.Since(start))
|
logger.InfoLogger.Printf("✅ Done (%s)\n", time.Since(start))
|
||||||
return PackageMetadata{ArchivePath: archivePath, Version: version}, nil
|
return PackageMetadata{ArchivePath: archivePath, Version: version}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildFromSource(pkgMeta PackageMetadata, verbose bool) (PackageMetadata, error) {
|
func buildFromSource(pkgMeta PackageMetadata, verbose bool) (PackageMetadata, error) {
|
||||||
InfoLogger.Println(Bold("Building from source"))
|
logger.InfoLogger.Println(Bold("Building from source"))
|
||||||
InfoLogger.SetPrefix(" ")
|
logger.InfoLogger.SetPrefix(" ")
|
||||||
defer InfoLogger.SetPrefix("")
|
defer logger.InfoLogger.SetPrefix("")
|
||||||
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
InfoLogger.Println("Unpacking source for " + pkgMeta.ArchivePath)
|
logger.InfoLogger.Println("Unpacking source for " + pkgMeta.ArchivePath)
|
||||||
|
|
||||||
_, untarErr := RunCommand([]string{"tar", "zxvf", pkgMeta.ArchivePath}, state.GetStatePath("cache"), !verbose)
|
_, untarErr := RunCommand([]string{"tar", "zxvf", pkgMeta.ArchivePath}, state.GetStatePath("cache"), !verbose)
|
||||||
|
|
||||||
|
@ -103,7 +104,7 @@ func buildFromSource(pkgMeta PackageMetadata, verbose bool) (PackageMetadata, er
|
||||||
|
|
||||||
unzippedRoot := strings.TrimSuffix(pkgMeta.ArchivePath, path.Ext(pkgMeta.ArchivePath))
|
unzippedRoot := strings.TrimSuffix(pkgMeta.ArchivePath, path.Ext(pkgMeta.ArchivePath))
|
||||||
|
|
||||||
InfoLogger.Println("Configuring installer")
|
logger.InfoLogger.Println("Configuring installer")
|
||||||
|
|
||||||
targetDirectory := state.GetStatePath("runtimes", "py-"+pkgMeta.Version)
|
targetDirectory := state.GetStatePath("runtimes", "py-"+pkgMeta.Version)
|
||||||
|
|
||||||
|
@ -113,7 +114,7 @@ func buildFromSource(pkgMeta PackageMetadata, verbose bool) (PackageMetadata, er
|
||||||
return pkgMeta, configureErr
|
return pkgMeta, configureErr
|
||||||
}
|
}
|
||||||
|
|
||||||
InfoLogger.Println("Building")
|
logger.InfoLogger.Println("Building")
|
||||||
_, buildErr := RunCommand([]string{"make", "altinstall", "-j4"}, unzippedRoot, !verbose)
|
_, buildErr := RunCommand([]string{"make", "altinstall", "-j4"}, unzippedRoot, !verbose)
|
||||||
|
|
||||||
if buildErr != nil {
|
if buildErr != nil {
|
||||||
|
@ -126,7 +127,7 @@ func buildFromSource(pkgMeta PackageMetadata, verbose bool) (PackageMetadata, er
|
||||||
|
|
||||||
pkgMeta.InstallPath = targetDirectory
|
pkgMeta.InstallPath = targetDirectory
|
||||||
|
|
||||||
InfoLogger.Printf("Installed Python %s at %s\n", pkgMeta.Version, pkgMeta.InstallPath)
|
logger.InfoLogger.Printf("Installed Python %s at %s\n", pkgMeta.Version, pkgMeta.InstallPath)
|
||||||
InfoLogger.Printf("✅ Done (%s)\n", time.Since(start))
|
logger.InfoLogger.Printf("✅ Done (%s)\n", time.Since(start))
|
||||||
return pkgMeta, nil
|
return pkgMeta, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package main
|
package logger
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
3
v.go
3
v.go
|
@ -2,6 +2,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
cli "v/cli"
|
||||||
state "v/state"
|
state "v/state"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -16,7 +17,7 @@ func main() {
|
||||||
args := os.Args[1:]
|
args := os.Args[1:]
|
||||||
currentState := state.ReadState()
|
currentState := state.ReadState()
|
||||||
|
|
||||||
cli := CLI{
|
cli := cli.CLI{
|
||||||
Metadata: map[string]string{
|
Metadata: map[string]string{
|
||||||
"Version": Version,
|
"Version": Version,
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue