mirror of
https://github.com/mudler/luet.git
synced 2025-09-03 16:25:19 +00:00
Compare commits
15 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
98b01ce00b | ||
|
749a4cb615 | ||
|
57e19c61e7 | ||
|
5ee1e28b9c | ||
|
21bd76af9c | ||
|
89bd7c2281 | ||
|
49d7efa9ea | ||
|
b3e3abec8f | ||
|
92e73051a0 | ||
|
fd7405c2cc | ||
|
2448f3175e | ||
|
101df40eec | ||
|
c22adb3a47 | ||
|
c1fe3278fa | ||
|
2854c68209 |
11
cmd/build.go
11
cmd/build.go
@@ -72,6 +72,7 @@ Build packages specifying multiple definition trees:
|
|||||||
viper.BindPFlag("nodeps", cmd.Flags().Lookup("nodeps"))
|
viper.BindPFlag("nodeps", cmd.Flags().Lookup("nodeps"))
|
||||||
viper.BindPFlag("onlydeps", cmd.Flags().Lookup("onlydeps"))
|
viper.BindPFlag("onlydeps", cmd.Flags().Lookup("onlydeps"))
|
||||||
viper.BindPFlag("values", cmd.Flags().Lookup("values"))
|
viper.BindPFlag("values", cmd.Flags().Lookup("values"))
|
||||||
|
viper.BindPFlag("backend-args", cmd.Flags().Lookup("backend-args"))
|
||||||
|
|
||||||
viper.BindPFlag("image-repository", cmd.Flags().Lookup("image-repository"))
|
viper.BindPFlag("image-repository", cmd.Flags().Lookup("image-repository"))
|
||||||
viper.BindPFlag("push", cmd.Flags().Lookup("push"))
|
viper.BindPFlag("push", cmd.Flags().Lookup("push"))
|
||||||
@@ -83,6 +84,9 @@ Build packages specifying multiple definition trees:
|
|||||||
LuetCfg.Viper.BindPFlag("solver.discount", cmd.Flags().Lookup("solver-discount"))
|
LuetCfg.Viper.BindPFlag("solver.discount", cmd.Flags().Lookup("solver-discount"))
|
||||||
LuetCfg.Viper.BindPFlag("solver.rate", cmd.Flags().Lookup("solver-rate"))
|
LuetCfg.Viper.BindPFlag("solver.rate", cmd.Flags().Lookup("solver-rate"))
|
||||||
LuetCfg.Viper.BindPFlag("solver.max_attempts", cmd.Flags().Lookup("solver-attempts"))
|
LuetCfg.Viper.BindPFlag("solver.max_attempts", cmd.Flags().Lookup("solver-attempts"))
|
||||||
|
LuetCfg.Viper.BindPFlag("general.show_build_output", cmd.Flags().Lookup("live-output"))
|
||||||
|
LuetCfg.Viper.BindPFlag("backend-args", cmd.Flags().Lookup("backend-args"))
|
||||||
|
|
||||||
},
|
},
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
|
||||||
@@ -107,6 +111,7 @@ Build packages specifying multiple definition trees:
|
|||||||
full, _ := cmd.Flags().GetBool("full")
|
full, _ := cmd.Flags().GetBool("full")
|
||||||
concurrent, _ := cmd.Flags().GetBool("solver-concurrent")
|
concurrent, _ := cmd.Flags().GetBool("solver-concurrent")
|
||||||
var results Results
|
var results Results
|
||||||
|
backendArgs := viper.GetStringSlice("backend-args")
|
||||||
|
|
||||||
out, _ := cmd.Flags().GetString("output")
|
out, _ := cmd.Flags().GetString("output")
|
||||||
if out != "terminal" {
|
if out != "terminal" {
|
||||||
@@ -155,6 +160,8 @@ Build packages specifying multiple definition trees:
|
|||||||
LuetCfg.GetSolverOptions().Discount = float32(discount)
|
LuetCfg.GetSolverOptions().Discount = float32(discount)
|
||||||
LuetCfg.GetSolverOptions().MaxAttempts = attempts
|
LuetCfg.GetSolverOptions().MaxAttempts = attempts
|
||||||
|
|
||||||
|
LuetCfg.GetGeneral().ShowBuildOutput = LuetCfg.Viper.GetBool("general.show_build_output")
|
||||||
|
|
||||||
Debug("Solver", LuetCfg.GetSolverOptions().CompactString())
|
Debug("Solver", LuetCfg.GetSolverOptions().CompactString())
|
||||||
|
|
||||||
opts := compiler.NewDefaultCompilerOptions()
|
opts := compiler.NewDefaultCompilerOptions()
|
||||||
@@ -176,6 +183,7 @@ Build packages specifying multiple definition trees:
|
|||||||
}
|
}
|
||||||
|
|
||||||
luetCompiler := compiler.NewLuetCompiler(compilerBackend, generalRecipe.GetDatabase(), opts, solverOpts)
|
luetCompiler := compiler.NewLuetCompiler(compilerBackend, generalRecipe.GetDatabase(), opts, solverOpts)
|
||||||
|
luetCompiler.SetBackendArgs(backendArgs)
|
||||||
luetCompiler.SetConcurrency(concurrency)
|
luetCompiler.SetConcurrency(concurrency)
|
||||||
luetCompiler.SetCompressionType(compiler.CompressionImplementation(compressionType))
|
luetCompiler.SetCompressionType(compiler.CompressionImplementation(compressionType))
|
||||||
if full {
|
if full {
|
||||||
@@ -293,6 +301,7 @@ func init() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
Fatal(err)
|
Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
buildCmd.Flags().StringSliceP("tree", "t", []string{path}, "Path of the tree to use.")
|
buildCmd.Flags().StringSliceP("tree", "t", []string{path}, "Path of the tree to use.")
|
||||||
buildCmd.Flags().String("backend", "docker", "backend used (docker,img)")
|
buildCmd.Flags().String("backend", "docker", "backend used (docker,img)")
|
||||||
buildCmd.Flags().Bool("privileged", false, "Privileged (Keep permissions)")
|
buildCmd.Flags().Bool("privileged", false, "Privileged (Keep permissions)")
|
||||||
@@ -301,6 +310,7 @@ func init() {
|
|||||||
buildCmd.Flags().Bool("all", false, "Build all specfiles in the tree")
|
buildCmd.Flags().Bool("all", false, "Build all specfiles in the tree")
|
||||||
buildCmd.Flags().Bool("full", false, "Build all packages (optimized)")
|
buildCmd.Flags().Bool("full", false, "Build all packages (optimized)")
|
||||||
buildCmd.Flags().String("values", "", "Build values file to interpolate with each package")
|
buildCmd.Flags().String("values", "", "Build values file to interpolate with each package")
|
||||||
|
buildCmd.Flags().StringSliceP("backend-args", "a", []string{}, "Backend args")
|
||||||
|
|
||||||
buildCmd.Flags().String("destination", filepath.Join(path, "build"), "Destination folder")
|
buildCmd.Flags().String("destination", filepath.Join(path, "build"), "Destination folder")
|
||||||
buildCmd.Flags().String("compression", "none", "Compression alg: none, gzip, zstd")
|
buildCmd.Flags().String("compression", "none", "Compression alg: none, gzip, zstd")
|
||||||
@@ -317,6 +327,7 @@ func init() {
|
|||||||
buildCmd.Flags().Float32("solver-discount", 1.0, "Solver discount rate")
|
buildCmd.Flags().Float32("solver-discount", 1.0, "Solver discount rate")
|
||||||
buildCmd.Flags().Int("solver-attempts", 9000, "Solver maximum attempts")
|
buildCmd.Flags().Int("solver-attempts", 9000, "Solver maximum attempts")
|
||||||
buildCmd.Flags().Bool("solver-concurrent", false, "Use concurrent solver (experimental)")
|
buildCmd.Flags().Bool("solver-concurrent", false, "Use concurrent solver (experimental)")
|
||||||
|
buildCmd.Flags().Bool("live-output", LuetCfg.GetGeneral().ShowBuildOutput, "Enable live output of the build phase.")
|
||||||
|
|
||||||
buildCmd.Flags().Bool("pretend", false, "Just print what packages will be compiled")
|
buildCmd.Flags().Bool("pretend", false, "Just print what packages will be compiled")
|
||||||
|
|
||||||
|
@@ -40,7 +40,7 @@ var Verbose bool
|
|||||||
var LockedCommands = []string{"install", "uninstall", "upgrade"}
|
var LockedCommands = []string{"install", "uninstall", "upgrade"}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
LuetCLIVersion = "0.11.0"
|
LuetCLIVersion = "0.11.2"
|
||||||
LuetEnvPrefix = "LUET"
|
LuetEnvPrefix = "LUET"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
2
go.mod
2
go.mod
@@ -7,7 +7,7 @@ require (
|
|||||||
github.com/Sabayon/pkgs-checker v0.7.2
|
github.com/Sabayon/pkgs-checker v0.7.2
|
||||||
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef
|
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef
|
||||||
github.com/asdine/storm v0.0.0-20190418133842-e0f77eada154
|
github.com/asdine/storm v0.0.0-20190418133842-e0f77eada154
|
||||||
github.com/briandowns/spinner v1.7.0
|
github.com/briandowns/spinner v1.12.1-0.20201220203425-e201aaea0a31
|
||||||
github.com/cavaliercoder/grab v1.0.1-0.20201108051000-98a5bfe305ec
|
github.com/cavaliercoder/grab v1.0.1-0.20201108051000-98a5bfe305ec
|
||||||
github.com/containerd/containerd v1.4.1-0.20201117152358-0edc412565dc
|
github.com/containerd/containerd v1.4.1-0.20201117152358-0edc412565dc
|
||||||
github.com/crillab/gophersat v1.3.2-0.20201023142334-3fc2ac466765
|
github.com/crillab/gophersat v1.3.2-0.20201023142334-3fc2ac466765
|
||||||
|
4
go.sum
4
go.sum
@@ -144,8 +144,8 @@ github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJm
|
|||||||
github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||||
github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
|
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
|
||||||
github.com/briandowns/spinner v1.7.0 h1:aan1hBBOoscry2TXAkgtxkJiq7Se0+9pt+TUWaPrB4g=
|
github.com/briandowns/spinner v1.12.1-0.20201220203425-e201aaea0a31 h1:yInAg9pE5qGec5eQ7XdfOTTaGwGxD3bKFVjmD6VKkwc=
|
||||||
github.com/briandowns/spinner v1.7.0/go.mod h1://Zf9tMcxfRUA36V23M6YGEAv+kECGfvpnLTnb8n4XQ=
|
github.com/briandowns/spinner v1.12.1-0.20201220203425-e201aaea0a31/go.mod h1:QOuQk7x+EaDASo80FEXwlwiA+j/PPIcX3FScO+3/ZPQ=
|
||||||
github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=
|
github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=
|
||||||
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
|
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
|
||||||
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
|
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
|
||||||
|
@@ -16,8 +16,14 @@
|
|||||||
package backend
|
package backend
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/google/go-containerregistry/pkg/crane"
|
"os/exec"
|
||||||
|
|
||||||
"github.com/mudler/luet/pkg/compiler"
|
"github.com/mudler/luet/pkg/compiler"
|
||||||
|
"github.com/mudler/luet/pkg/config"
|
||||||
|
. "github.com/mudler/luet/pkg/logger"
|
||||||
|
|
||||||
|
"github.com/google/go-containerregistry/pkg/crane"
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -41,3 +47,40 @@ func NewBackend(s string) compiler.CompilerBackend {
|
|||||||
}
|
}
|
||||||
return compilerBackend
|
return compilerBackend
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func runCommand(cmd *exec.Cmd) error {
|
||||||
|
output := ""
|
||||||
|
buffered := !config.LuetCfg.GetGeneral().ShowBuildOutput
|
||||||
|
writer := NewBackendWriter(buffered)
|
||||||
|
|
||||||
|
cmd.Stdout = writer
|
||||||
|
cmd.Stderr = writer
|
||||||
|
|
||||||
|
if buffered {
|
||||||
|
Spinner(22)
|
||||||
|
defer SpinnerStop()
|
||||||
|
}
|
||||||
|
|
||||||
|
err := cmd.Start()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "Failed starting command")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = cmd.Wait()
|
||||||
|
if err != nil {
|
||||||
|
output = writer.GetCombinedOutput()
|
||||||
|
return errors.Wrapf(err, "Failed running command: %s", output)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func genBuildCommand(opts compiler.CompilerBackendOptions) []string {
|
||||||
|
context := opts.Context
|
||||||
|
|
||||||
|
if context == "" {
|
||||||
|
context = "."
|
||||||
|
}
|
||||||
|
buildarg := append(opts.BackendArgs, "-f", opts.DockerFileName, "-t", opts.ImageName, context)
|
||||||
|
return append([]string{"build"}, buildarg...)
|
||||||
|
}
|
||||||
|
@@ -44,28 +44,24 @@ func NewSimpleDockerBackend() compiler.CompilerBackend {
|
|||||||
// TODO: Missing still: labels, and build args expansion
|
// TODO: Missing still: labels, and build args expansion
|
||||||
func (*SimpleDocker) BuildImage(opts compiler.CompilerBackendOptions) error {
|
func (*SimpleDocker) BuildImage(opts compiler.CompilerBackendOptions) error {
|
||||||
name := opts.ImageName
|
name := opts.ImageName
|
||||||
path := opts.SourcePath
|
|
||||||
dockerfileName := opts.DockerFileName
|
|
||||||
context := opts.Context
|
|
||||||
|
|
||||||
if context == "" {
|
|
||||||
context = "."
|
|
||||||
}
|
|
||||||
buildarg := []string{"build", "-f", dockerfileName, "-t", name, context}
|
|
||||||
|
|
||||||
|
buildarg := genBuildCommand(opts)
|
||||||
Info(":whale2: Building image " + name)
|
Info(":whale2: Building image " + name)
|
||||||
cmd := exec.Command("docker", buildarg...)
|
cmd := exec.Command("docker", buildarg...)
|
||||||
cmd.Dir = path
|
cmd.Dir = opts.SourcePath
|
||||||
out, err := cmd.CombinedOutput()
|
err := runCommand(cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "Failed building image: "+string(out))
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
Info(":whale: Building image " + name + " done")
|
Info(":whale: Building image " + name + " done")
|
||||||
|
|
||||||
if os.Getenv("DOCKER_SQUASH") == "true" {
|
if os.Getenv("DOCKER_SQUASH") == "true" {
|
||||||
Info(":whale: Squashing image " + name)
|
Info(":whale: Squashing image " + name)
|
||||||
var client *docker.Client
|
var client *docker.Client
|
||||||
|
|
||||||
|
Spinner(22)
|
||||||
|
defer SpinnerStop()
|
||||||
client, err = docker.NewClientFromEnv()
|
client, err = docker.NewClientFromEnv()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "could not connect to the Docker daemon")
|
return errors.Wrap(err, "could not connect to the Docker daemon")
|
||||||
@@ -74,13 +70,8 @@ func (*SimpleDocker) BuildImage(opts compiler.CompilerBackendOptions) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "Failed squashing image")
|
return errors.Wrap(err, "Failed squashing image")
|
||||||
}
|
}
|
||||||
Info(":whale: Squashing image " + name + " done")
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.LuetCfg.GetGeneral().ShowBuildOutput {
|
Info(":whale: Squashing image " + name + " done")
|
||||||
Info(string(out))
|
|
||||||
} else {
|
|
||||||
Debug(string(out))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -101,11 +92,16 @@ func (*SimpleDocker) DownloadImage(opts compiler.CompilerBackendOptions) error {
|
|||||||
name := opts.ImageName
|
name := opts.ImageName
|
||||||
buildarg := []string{"pull", name}
|
buildarg := []string{"pull", name}
|
||||||
Debug(":whale: Downloading image " + name)
|
Debug(":whale: Downloading image " + name)
|
||||||
|
|
||||||
|
Spinner(22)
|
||||||
|
defer SpinnerStop()
|
||||||
|
|
||||||
cmd := exec.Command("docker", buildarg...)
|
cmd := exec.Command("docker", buildarg...)
|
||||||
out, err := cmd.CombinedOutput()
|
out, err := cmd.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "Failed pulling image: "+string(out))
|
return errors.Wrap(err, "Failed pulling image: "+string(out))
|
||||||
}
|
}
|
||||||
|
|
||||||
Info(":whale: Downloaded image:", name)
|
Info(":whale: Downloaded image:", name)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -142,6 +138,10 @@ func (*SimpleDocker) RemoveImage(opts compiler.CompilerBackendOptions) error {
|
|||||||
func (*SimpleDocker) Push(opts compiler.CompilerBackendOptions) error {
|
func (*SimpleDocker) Push(opts compiler.CompilerBackendOptions) error {
|
||||||
name := opts.ImageName
|
name := opts.ImageName
|
||||||
pusharg := []string{"push", name}
|
pusharg := []string{"push", name}
|
||||||
|
|
||||||
|
Spinner(22)
|
||||||
|
defer SpinnerStop()
|
||||||
|
|
||||||
out, err := exec.Command("docker", pusharg...).CombinedOutput()
|
out, err := exec.Command("docker", pusharg...).CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "Failed pushing image: "+string(out))
|
return errors.Wrap(err, "Failed pushing image: "+string(out))
|
||||||
@@ -170,6 +170,10 @@ func (*SimpleDocker) ExportImage(opts compiler.CompilerBackendOptions) error {
|
|||||||
|
|
||||||
buildarg := []string{"save", name, "-o", path}
|
buildarg := []string{"save", name, "-o", path}
|
||||||
Debug(":whale: Saving image " + name)
|
Debug(":whale: Saving image " + name)
|
||||||
|
|
||||||
|
Spinner(22)
|
||||||
|
defer SpinnerStop()
|
||||||
|
|
||||||
out, err := exec.Command("docker", buildarg...).CombinedOutput()
|
out, err := exec.Command("docker", buildarg...).CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "Failed exporting image: "+string(out))
|
return errors.Wrap(err, "Failed exporting image: "+string(out))
|
||||||
@@ -194,10 +198,16 @@ func (b *SimpleDocker) ExtractRootfs(opts compiler.CompilerBackendOptions, keepP
|
|||||||
defer os.RemoveAll(tempexport) // clean up
|
defer os.RemoveAll(tempexport) // clean up
|
||||||
|
|
||||||
imageExport := filepath.Join(tempexport, "image.tar")
|
imageExport := filepath.Join(tempexport, "image.tar")
|
||||||
|
|
||||||
|
Spinner(22)
|
||||||
|
defer SpinnerStop()
|
||||||
|
|
||||||
if err := b.ExportImage(compiler.CompilerBackendOptions{ImageName: name, Destination: imageExport}); err != nil {
|
if err := b.ExportImage(compiler.CompilerBackendOptions{ImageName: name, Destination: imageExport}); err != nil {
|
||||||
return errors.Wrap(err, "failed while extracting rootfs for "+name)
|
return errors.Wrap(err, "failed while extracting rootfs for "+name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SpinnerStop()
|
||||||
|
|
||||||
src := imageExport
|
src := imageExport
|
||||||
|
|
||||||
if src == "" && opts.ImageName != "" {
|
if src == "" && opts.ImageName != "" {
|
||||||
|
@@ -35,26 +35,20 @@ func NewSimpleImgBackend() compiler.CompilerBackend {
|
|||||||
// TODO: Missing still: labels, and build args expansion
|
// TODO: Missing still: labels, and build args expansion
|
||||||
func (*SimpleImg) BuildImage(opts compiler.CompilerBackendOptions) error {
|
func (*SimpleImg) BuildImage(opts compiler.CompilerBackendOptions) error {
|
||||||
name := opts.ImageName
|
name := opts.ImageName
|
||||||
path := opts.SourcePath
|
|
||||||
context := opts.Context
|
|
||||||
|
|
||||||
if context == "" {
|
buildarg := genBuildCommand(opts)
|
||||||
context = "."
|
|
||||||
}
|
|
||||||
dockerfileName := opts.DockerFileName
|
|
||||||
|
|
||||||
buildarg := []string{"build", "-f", dockerfileName, "-t", name, context}
|
|
||||||
Spinner(22)
|
|
||||||
defer SpinnerStop()
|
|
||||||
Info(":tea: Building image " + name)
|
Info(":tea: Building image " + name)
|
||||||
cmd := exec.Command("img", buildarg...)
|
|
||||||
cmd.Dir = path
|
|
||||||
out, err := cmd.CombinedOutput()
|
|
||||||
|
|
||||||
|
cmd := exec.Command("img", buildarg...)
|
||||||
|
cmd.Dir = opts.SourcePath
|
||||||
|
err := runCommand(cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "Failed building image: "+string(out))
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
Info(":tea: Building image " + name + " done")
|
Info(":tea: Building image " + name + " done")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,11 +67,13 @@ func (*SimpleImg) RemoveImage(opts compiler.CompilerBackendOptions) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (*SimpleImg) DownloadImage(opts compiler.CompilerBackendOptions) error {
|
func (*SimpleImg) DownloadImage(opts compiler.CompilerBackendOptions) error {
|
||||||
|
|
||||||
name := opts.ImageName
|
name := opts.ImageName
|
||||||
buildarg := []string{"pull", name}
|
buildarg := []string{"pull", name}
|
||||||
|
|
||||||
Debug(":tea: Downloading image " + name)
|
Debug(":tea: Downloading image " + name)
|
||||||
|
|
||||||
|
Spinner(22)
|
||||||
|
defer SpinnerStop()
|
||||||
|
|
||||||
cmd := exec.Command("img", buildarg...)
|
cmd := exec.Command("img", buildarg...)
|
||||||
out, err := cmd.CombinedOutput()
|
out, err := cmd.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -138,6 +134,10 @@ func (*SimpleImg) ExportImage(opts compiler.CompilerBackendOptions) error {
|
|||||||
path := opts.Destination
|
path := opts.Destination
|
||||||
buildarg := []string{"save", "-o", path, name}
|
buildarg := []string{"save", "-o", path, name}
|
||||||
Debug(":tea: Saving image " + name)
|
Debug(":tea: Saving image " + name)
|
||||||
|
|
||||||
|
Spinner(22)
|
||||||
|
defer SpinnerStop()
|
||||||
|
|
||||||
out, err := exec.Command("img", buildarg...).CombinedOutput()
|
out, err := exec.Command("img", buildarg...).CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "Failed exporting image: "+string(out))
|
return errors.Wrap(err, "Failed exporting image: "+string(out))
|
||||||
@@ -158,8 +158,13 @@ func (s *SimpleImg) ExtractRootfs(opts compiler.CompilerBackendOptions, keepPerm
|
|||||||
}
|
}
|
||||||
|
|
||||||
os.RemoveAll(path)
|
os.RemoveAll(path)
|
||||||
|
|
||||||
buildarg := []string{"unpack", "-o", path, name}
|
buildarg := []string{"unpack", "-o", path, name}
|
||||||
Debug(":tea: Extracting image " + name)
|
Debug(":tea: Extracting image " + name)
|
||||||
|
|
||||||
|
Spinner(22)
|
||||||
|
defer SpinnerStop()
|
||||||
|
|
||||||
out, err := exec.Command("img", buildarg...).CombinedOutput()
|
out, err := exec.Command("img", buildarg...).CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "Failed extracting image: "+string(out))
|
return errors.Wrap(err, "Failed extracting image: "+string(out))
|
||||||
|
48
pkg/compiler/backend/writer.go
Normal file
48
pkg/compiler/backend/writer.go
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
// Copyright © 2021 Daniele Rondina <geaaru@sabayonlinux.org>
|
||||||
|
// Ettore Di Giacinto <mudler@sabayonlinux.org>
|
||||||
|
//
|
||||||
|
// 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 2 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 backend
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
|
||||||
|
. "github.com/mudler/luet/pkg/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BackendWriter struct {
|
||||||
|
BufferedOutput bool
|
||||||
|
Buffer *bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBackendWriter(buffered bool) *BackendWriter {
|
||||||
|
return &BackendWriter{
|
||||||
|
BufferedOutput: buffered,
|
||||||
|
Buffer: &bytes.Buffer{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BackendWriter) Write(p []byte) (int, error) {
|
||||||
|
if b.BufferedOutput {
|
||||||
|
return b.Buffer.Write(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
Msg("info", false, false, (string(p)))
|
||||||
|
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BackendWriter) Close() error { return nil }
|
||||||
|
func (b *BackendWriter) GetCombinedOutput() string { return b.Buffer.String() }
|
@@ -51,6 +51,7 @@ type LuetCompiler struct {
|
|||||||
CompressionType CompressionImplementation
|
CompressionType CompressionImplementation
|
||||||
Options CompilerOptions
|
Options CompilerOptions
|
||||||
SolverOptions solver.Options
|
SolverOptions solver.Options
|
||||||
|
BackedArgs []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLuetCompiler(backend CompilerBackend, db pkg.PackageDatabase, opt *CompilerOptions, solvopts solver.Options) Compiler {
|
func NewLuetCompiler(backend CompilerBackend, db pkg.PackageDatabase, opt *CompilerOptions, solvopts solver.Options) Compiler {
|
||||||
@@ -70,7 +71,9 @@ func NewLuetCompiler(backend CompilerBackend, db pkg.PackageDatabase, opt *Compi
|
|||||||
SolverOptions: solvopts,
|
SolverOptions: solvopts,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
func (cs *LuetCompiler) SetBackendArgs(args []string) {
|
||||||
|
cs.BackedArgs = args
|
||||||
|
}
|
||||||
func (cs *LuetCompiler) SetConcurrency(i int) {
|
func (cs *LuetCompiler) SetConcurrency(i int) {
|
||||||
cs.Concurrency = i
|
cs.Concurrency = i
|
||||||
}
|
}
|
||||||
@@ -148,8 +151,6 @@ func (cs *LuetCompiler) CompileWithReverseDeps(keepPermissions bool, ps Compilat
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (cs *LuetCompiler) CompileParallel(keepPermissions bool, ps CompilationSpecs) ([]Artifact, []error) {
|
func (cs *LuetCompiler) CompileParallel(keepPermissions bool, ps CompilationSpecs) ([]Artifact, []error) {
|
||||||
Spinner(22)
|
|
||||||
defer SpinnerStop()
|
|
||||||
all := make(chan CompilationSpec)
|
all := make(chan CompilationSpec)
|
||||||
artifacts := []Artifact{}
|
artifacts := []Artifact{}
|
||||||
mutex := &sync.Mutex{}
|
mutex := &sync.Mutex{}
|
||||||
@@ -383,12 +384,14 @@ func (cs *LuetCompiler) buildPackageImage(image, buildertaggedImage, packageImag
|
|||||||
SourcePath: buildDir,
|
SourcePath: buildDir,
|
||||||
DockerFileName: p.GetPackage().GetFingerPrint() + "-builder.dockerfile",
|
DockerFileName: p.GetPackage().GetFingerPrint() + "-builder.dockerfile",
|
||||||
Destination: p.Rel(p.GetPackage().GetFingerPrint() + "-builder.image.tar"),
|
Destination: p.Rel(p.GetPackage().GetFingerPrint() + "-builder.image.tar"),
|
||||||
|
BackendArgs: cs.BackedArgs,
|
||||||
}
|
}
|
||||||
runnerOpts = CompilerBackendOptions{
|
runnerOpts = CompilerBackendOptions{
|
||||||
ImageName: packageImage,
|
ImageName: packageImage,
|
||||||
SourcePath: buildDir,
|
SourcePath: buildDir,
|
||||||
DockerFileName: p.GetPackage().GetFingerPrint() + ".dockerfile",
|
DockerFileName: p.GetPackage().GetFingerPrint() + ".dockerfile",
|
||||||
Destination: p.Rel(p.GetPackage().GetFingerPrint() + ".image.tar"),
|
Destination: p.Rel(p.GetPackage().GetFingerPrint() + ".image.tar"),
|
||||||
|
BackendArgs: cs.BackedArgs,
|
||||||
}
|
}
|
||||||
|
|
||||||
buildAndPush := func(opts CompilerBackendOptions) error {
|
buildAndPush := func(opts CompilerBackendOptions) error {
|
||||||
|
@@ -34,6 +34,8 @@ type Compiler interface {
|
|||||||
FromDatabase(db pkg.PackageDatabase, minimum bool, dst string) ([]CompilationSpec, error)
|
FromDatabase(db pkg.PackageDatabase, minimum bool, dst string) ([]CompilationSpec, error)
|
||||||
SetBackend(CompilerBackend)
|
SetBackend(CompilerBackend)
|
||||||
GetBackend() CompilerBackend
|
GetBackend() CompilerBackend
|
||||||
|
|
||||||
|
SetBackendArgs([]string)
|
||||||
SetCompressionType(t CompressionImplementation)
|
SetCompressionType(t CompressionImplementation)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,6 +45,7 @@ type CompilerBackendOptions struct {
|
|||||||
DockerFileName string
|
DockerFileName string
|
||||||
Destination string
|
Destination string
|
||||||
Context string
|
Context string
|
||||||
|
BackendArgs []string
|
||||||
}
|
}
|
||||||
|
|
||||||
type CompilerOptions struct {
|
type CompilerOptions struct {
|
||||||
|
@@ -71,6 +71,10 @@ func (c *DockerClient) DownloadArtifact(artifact compiler.Artifact) (compiler.Ar
|
|||||||
//var u *url.URL = nil
|
//var u *url.URL = nil
|
||||||
var err error
|
var err error
|
||||||
var temp string
|
var temp string
|
||||||
|
|
||||||
|
Spinner(22)
|
||||||
|
defer SpinnerStop()
|
||||||
|
|
||||||
var resultingArtifact compiler.Artifact
|
var resultingArtifact compiler.Artifact
|
||||||
artifactName := path.Base(artifact.GetPath())
|
artifactName := path.Base(artifact.GetPath())
|
||||||
cacheFile := filepath.Join(config.LuetCfg.GetSystem().GetSystemPkgsCacheDirPath(), artifactName)
|
cacheFile := filepath.Join(config.LuetCfg.GetSystem().GetSystemPkgsCacheDirPath(), artifactName)
|
||||||
@@ -102,9 +106,9 @@ func (c *DockerClient) DownloadArtifact(artifact compiler.Artifact) (compiler.Ar
|
|||||||
defer os.RemoveAll(temp)
|
defer os.RemoveAll(temp)
|
||||||
|
|
||||||
for _, uri := range c.RepoData.Urls {
|
for _, uri := range c.RepoData.Urls {
|
||||||
Debug("Downloading artifact", artifactName, "from", uri)
|
|
||||||
|
|
||||||
imageName := fmt.Sprintf("%s:%s", uri, artifact.GetCompileSpec().GetPackage().GetFingerPrint())
|
imageName := fmt.Sprintf("%s:%s", uri, artifact.GetCompileSpec().GetPackage().GetFingerPrint())
|
||||||
|
Info("Downloading image", imageName)
|
||||||
|
|
||||||
// imageName := fmt.Sprintf("%s/%s", uri, artifact.GetCompileSpec().GetPackage().GetPackageImageName())
|
// imageName := fmt.Sprintf("%s/%s", uri, artifact.GetCompileSpec().GetPackage().GetPackageImageName())
|
||||||
err = downloadAndExtractDockerImage(imageName, temp)
|
err = downloadAndExtractDockerImage(imageName, temp)
|
||||||
|
@@ -8,6 +8,7 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"sync"
|
||||||
. "github.com/mudler/luet/pkg/config"
|
. "github.com/mudler/luet/pkg/config"
|
||||||
|
|
||||||
"github.com/briandowns/spinner"
|
"github.com/briandowns/spinner"
|
||||||
@@ -20,7 +21,7 @@ import (
|
|||||||
var s *spinner.Spinner = nil
|
var s *spinner.Spinner = nil
|
||||||
var z *zap.Logger = nil
|
var z *zap.Logger = nil
|
||||||
var aurora Aurora = nil
|
var aurora Aurora = nil
|
||||||
|
var spinnerLock = sync.Mutex{}
|
||||||
func NewSpinner() {
|
func NewSpinner() {
|
||||||
if s == nil {
|
if s == nil {
|
||||||
s = spinner.New(
|
s = spinner.New(
|
||||||
@@ -84,6 +85,8 @@ func ZapLogger() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Spinner(i int) {
|
func Spinner(i int) {
|
||||||
|
spinnerLock.Lock()
|
||||||
|
defer spinnerLock.Unlock()
|
||||||
var confLevel int
|
var confLevel int
|
||||||
if LuetCfg.GetGeneral().Debug {
|
if LuetCfg.GetGeneral().Debug {
|
||||||
confLevel = 3
|
confLevel = 3
|
||||||
@@ -120,6 +123,8 @@ func SpinnerText(suffix, prefix string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func SpinnerStop() {
|
func SpinnerStop() {
|
||||||
|
spinnerLock.Lock()
|
||||||
|
defer spinnerLock.Unlock()
|
||||||
var confLevel int
|
var confLevel int
|
||||||
if LuetCfg.GetGeneral().Debug {
|
if LuetCfg.GetGeneral().Debug {
|
||||||
confLevel = 3
|
confLevel = 3
|
||||||
@@ -173,7 +178,7 @@ func level2AtomicLevel(level string) zap.AtomicLevel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func msg(level string, withoutColor bool, msg ...interface{}) {
|
func Msg(level string, withoutColor, ln bool, msg ...interface{}) {
|
||||||
var message string
|
var message string
|
||||||
var confLevel, msgLevel int
|
var confLevel, msgLevel int
|
||||||
|
|
||||||
@@ -219,11 +224,16 @@ func msg(level string, withoutColor bool, msg ...interface{}) {
|
|||||||
log2File(level, message)
|
log2File(level, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ln {
|
||||||
fmt.Println(levelMsg)
|
fmt.Println(levelMsg)
|
||||||
|
} else {
|
||||||
|
fmt.Print(levelMsg)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Warning(mess ...interface{}) {
|
func Warning(mess ...interface{}) {
|
||||||
msg("warning", false, mess...)
|
Msg("warning", false, true, mess...)
|
||||||
if LuetCfg.GetGeneral().FatalWarns {
|
if LuetCfg.GetGeneral().FatalWarns {
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
@@ -235,23 +245,23 @@ func Debug(mess ...interface{}) {
|
|||||||
mess = append([]interface{}{fmt.Sprintf("DEBUG (%s:#%d:%v)",
|
mess = append([]interface{}{fmt.Sprintf("DEBUG (%s:#%d:%v)",
|
||||||
path.Base(file), line, runtime.FuncForPC(pc).Name())}, mess...)
|
path.Base(file), line, runtime.FuncForPC(pc).Name())}, mess...)
|
||||||
}
|
}
|
||||||
msg("debug", false, mess...)
|
Msg("debug", false, true, mess...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func DebugC(mess ...interface{}) {
|
func DebugC(mess ...interface{}) {
|
||||||
msg("debug", true, mess...)
|
Msg("debug", true, true, mess...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Info(mess ...interface{}) {
|
func Info(mess ...interface{}) {
|
||||||
msg("info", false, mess...)
|
Msg("info", false, true, mess...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func InfoC(mess ...interface{}) {
|
func InfoC(mess ...interface{}) {
|
||||||
msg("info", true, mess...)
|
Msg("info", true, true, mess...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Error(mess ...interface{}) {
|
func Error(mess ...interface{}) {
|
||||||
msg("error", false, mess...)
|
Msg("error", false, true, mess...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Fatal(mess ...interface{}) {
|
func Fatal(mess ...interface{}) {
|
||||||
|
@@ -32,6 +32,7 @@ var DBInMemoryInstance = &InMemoryDatabase{
|
|||||||
CacheNoVersion: map[string]map[string]interface{}{},
|
CacheNoVersion: map[string]map[string]interface{}{},
|
||||||
ProvidesDatabase: map[string]map[string]Package{},
|
ProvidesDatabase: map[string]map[string]Package{},
|
||||||
RevDepsDatabase: map[string]map[string]Package{},
|
RevDepsDatabase: map[string]map[string]Package{},
|
||||||
|
cached: map[string]interface{}{},
|
||||||
}
|
}
|
||||||
|
|
||||||
type InMemoryDatabase struct {
|
type InMemoryDatabase struct {
|
||||||
@@ -199,6 +200,10 @@ func (db *InMemoryDatabase) populateCaches(p Package) {
|
|||||||
|
|
||||||
// Create extra cache between package -> []versions
|
// Create extra cache between package -> []versions
|
||||||
db.Lock()
|
db.Lock()
|
||||||
|
if db.cached == nil {
|
||||||
|
db.cached = map[string]interface{}{}
|
||||||
|
}
|
||||||
|
|
||||||
if _, ok := db.cached[p.GetFingerPrint()]; ok {
|
if _, ok := db.cached[p.GetFingerPrint()]; ok {
|
||||||
db.Unlock()
|
db.Unlock()
|
||||||
return
|
return
|
||||||
|
74
tests/integration/01_simple_inmemory.sh
Executable file
74
tests/integration/01_simple_inmemory.sh
Executable file
@@ -0,0 +1,74 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
export LUET_NOLOCK=true
|
||||||
|
|
||||||
|
oneTimeSetUp() {
|
||||||
|
export tmpdir="$(mktemp -d)"
|
||||||
|
}
|
||||||
|
|
||||||
|
oneTimeTearDown() {
|
||||||
|
rm -rf "$tmpdir"
|
||||||
|
}
|
||||||
|
|
||||||
|
testBuild() {
|
||||||
|
mkdir $tmpdir/testbuild
|
||||||
|
luet build --tree "$ROOT_DIR/tests/fixtures/buildableseed" --destination $tmpdir/testbuild --compression gzip test/c > /dev/null
|
||||||
|
buildst=$?
|
||||||
|
assertEquals 'builds successfully' "$buildst" "0"
|
||||||
|
assertTrue 'create package dep B' "[ -e '$tmpdir/testbuild/b-test-1.0.package.tar.gz' ]"
|
||||||
|
assertTrue 'create package' "[ -e '$tmpdir/testbuild/c-test-1.0.package.tar.gz' ]"
|
||||||
|
}
|
||||||
|
|
||||||
|
testRepo() {
|
||||||
|
assertTrue 'no repository' "[ ! -e '$tmpdir/testbuild/repository.yaml' ]"
|
||||||
|
luet create-repo --tree "$ROOT_DIR/tests/fixtures/buildableseed" \
|
||||||
|
--output $tmpdir/testbuild \
|
||||||
|
--packages $tmpdir/testbuild \
|
||||||
|
--name "test" \
|
||||||
|
--descr "Test Repo" \
|
||||||
|
--urls $tmpdir/testrootfs \
|
||||||
|
--type disk > /dev/null
|
||||||
|
|
||||||
|
createst=$?
|
||||||
|
assertEquals 'create repo successfully' "$createst" "0"
|
||||||
|
assertTrue 'create repository' "[ -e '$tmpdir/testbuild/repository.yaml' ]"
|
||||||
|
}
|
||||||
|
|
||||||
|
testConfig() {
|
||||||
|
mkdir $tmpdir/testrootfs
|
||||||
|
cat <<EOF > $tmpdir/luet.yaml
|
||||||
|
general:
|
||||||
|
debug: true
|
||||||
|
system:
|
||||||
|
rootfs: $tmpdir/testrootfs
|
||||||
|
database_engine: "memory"
|
||||||
|
config_from_host: true
|
||||||
|
repositories:
|
||||||
|
- name: "main"
|
||||||
|
type: "disk"
|
||||||
|
enable: true
|
||||||
|
urls:
|
||||||
|
- "$tmpdir/testbuild"
|
||||||
|
EOF
|
||||||
|
luet config --config $tmpdir/luet.yaml
|
||||||
|
res=$?
|
||||||
|
assertEquals 'config test successfully' "$res" "0"
|
||||||
|
}
|
||||||
|
|
||||||
|
testInstall() {
|
||||||
|
luet install -y --config $tmpdir/luet.yaml test/c
|
||||||
|
installst=$?
|
||||||
|
assertEquals 'install test successfully' "$installst" "0"
|
||||||
|
assertTrue 'package installed' "[ -e '$tmpdir/testrootfs/c' ]"
|
||||||
|
}
|
||||||
|
|
||||||
|
testCleanup() {
|
||||||
|
luet cleanup --config $tmpdir/luet.yaml
|
||||||
|
installst=$?
|
||||||
|
assertEquals 'install test successfully' "$installst" "0"
|
||||||
|
assertTrue 'package installed' "[ ! -e '$tmpdir/testrootfs/packages/c-test-1.0.package.tar.gz' ]"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Load shUnit2.
|
||||||
|
. "$ROOT_DIR/tests/integration/shunit2"/shunit2
|
||||||
|
|
7
vendor/github.com/briandowns/spinner/.travis.yml
generated
vendored
7
vendor/github.com/briandowns/spinner/.travis.yml
generated
vendored
@@ -1,7 +1,10 @@
|
|||||||
|
arch:
|
||||||
|
- amd64
|
||||||
|
- ppc64le
|
||||||
language: go
|
language: go
|
||||||
go:
|
go:
|
||||||
- 1.11
|
- 1.13
|
||||||
- 1.12.5
|
- 1.14.1
|
||||||
env:
|
env:
|
||||||
- GOARCH: amd64
|
- GOARCH: amd64
|
||||||
- GOARCH: 386
|
- GOARCH: 386
|
||||||
|
99
vendor/github.com/briandowns/spinner/README.md
generated
vendored
99
vendor/github.com/briandowns/spinner/README.md
generated
vendored
@@ -17,52 +17,52 @@ go get github.com/briandowns/spinner
|
|||||||
## Available Character Sets
|
## Available Character Sets
|
||||||
(Numbered by their slice index)
|
(Numbered by their slice index)
|
||||||
|
|
||||||
index | character set | sample gif
|
| index | character set | sample gif |
|
||||||
------|---------------|---------------
|
| ----- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------- |
|
||||||
0 | ```←↖↑↗→↘↓↙``` | 
|
| 0 | ```←↖↑↗→↘↓↙``` |  |
|
||||||
1 | ```▁▃▄▅▆▇█▇▆▅▄▃▁``` | 
|
| 1 | ```▁▃▄▅▆▇█▇▆▅▄▃▁``` |  |
|
||||||
2 | ```▖▘▝▗``` | 
|
| 2 | ```▖▘▝▗``` |  |
|
||||||
3 | ```┤┘┴└├┌┬┐``` | 
|
| 3 | ```┤┘┴└├┌┬┐``` |  |
|
||||||
4 | ```◢◣◤◥``` | 
|
| 4 | ```◢◣◤◥``` |  |
|
||||||
5 | ```◰◳◲◱``` | 
|
| 5 | ```◰◳◲◱``` |  |
|
||||||
6 | ```◴◷◶◵``` | 
|
| 6 | ```◴◷◶◵``` |  |
|
||||||
7 | ```◐◓◑◒``` | 
|
| 7 | ```◐◓◑◒``` |  |
|
||||||
8 | ```.oO@*``` | 
|
| 8 | ```.oO@*``` |  |
|
||||||
9 | ```|/-\``` | 
|
| 9 | ```\|/-\``` |  |
|
||||||
10 | ```◡◡⊙⊙◠◠``` | 
|
| 10 | ```◡◡⊙⊙◠◠``` |  |
|
||||||
11 | ```⣾⣽⣻⢿⡿⣟⣯⣷``` | 
|
| 11 | ```⣾⣽⣻⢿⡿⣟⣯⣷``` |  |
|
||||||
12 | ```>))'> >))'> >))'> >))'> >))'> <'((< <'((< <'((<``` | 
|
| 12 | ```>))'> >))'> >))'> >))'> >))'> <'((< <'((< <'((<``` |  |
|
||||||
13 | ```⠁⠂⠄⡀⢀⠠⠐⠈``` | 
|
| 13 | ```⠁⠂⠄⡀⢀⠠⠐⠈``` |  |
|
||||||
14 | ```⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏``` | 
|
| 14 | ```⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏``` |  |
|
||||||
15 | ```abcdefghijklmnopqrstuvwxyz``` | 
|
| 15 | ```abcdefghijklmnopqrstuvwxyz``` |  |
|
||||||
16 | ```▉▊▋▌▍▎▏▎▍▌▋▊▉``` | 
|
| 16 | ```▉▊▋▌▍▎▏▎▍▌▋▊▉``` |  |
|
||||||
17 | ```■□▪▫``` | 
|
| 17 | ```■□▪▫``` |  |
|
||||||
18 | ```←↑→↓``` | 
|
| 18 | ```←↑→↓``` |  |
|
||||||
19 | ```╫╪``` | 
|
| 19 | ```╫╪``` |  |
|
||||||
20 | ```⇐⇖⇑⇗⇒⇘⇓⇙``` | 
|
| 20 | ```⇐⇖⇑⇗⇒⇘⇓⇙``` |  |
|
||||||
21 | ```⠁⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈⠈``` | 
|
| 21 | ```⠁⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈⠈``` |  |
|
||||||
22 | ```⠈⠉⠋⠓⠒⠐⠐⠒⠖⠦⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈``` | 
|
| 22 | ```⠈⠉⠋⠓⠒⠐⠐⠒⠖⠦⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈``` |  |
|
||||||
23 | ```⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠴⠲⠒⠂⠂⠒⠚⠙⠉⠁``` | 
|
| 23 | ```⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠴⠲⠒⠂⠂⠒⠚⠙⠉⠁``` |  |
|
||||||
24 | ```⠋⠙⠚⠒⠂⠂⠒⠲⠴⠦⠖⠒⠐⠐⠒⠓⠋``` | 
|
| 24 | ```⠋⠙⠚⠒⠂⠂⠒⠲⠴⠦⠖⠒⠐⠐⠒⠓⠋``` |  |
|
||||||
25 | ```ヲァィゥェォャュョッアイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン``` | 
|
| 25 | ```ヲァィゥェォャュョッアイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン``` |  |
|
||||||
26 | ```. .. ...``` | 
|
| 26 | ```. .. ...``` |  |
|
||||||
27 | ```▁▂▃▄▅▆▇█▉▊▋▌▍▎▏▏▎▍▌▋▊▉█▇▆▅▄▃▂▁``` | 
|
| 27 | ```▁▂▃▄▅▆▇█▉▊▋▌▍▎▏▏▎▍▌▋▊▉█▇▆▅▄▃▂▁``` |  |
|
||||||
28 | ```.oO°Oo.``` | 
|
| 28 | ```.oO°Oo.``` |  |
|
||||||
29 | ```+x``` | 
|
| 29 | ```+x``` |  |
|
||||||
30 | ```v<^>``` | 
|
| 30 | ```v<^>``` |  |
|
||||||
31 | ```>>---> >>---> >>---> >>---> >>---> <---<< <---<< <---<< <---<< <---<<``` | 
|
| 31 | ```>>---> >>---> >>---> >>---> >>---> <---<< <---<< <---<< <---<< <---<<``` |  |
|
||||||
32 | ```| || ||| |||| ||||| |||||| ||||| |||| ||| || |``` | 
|
| 32 | ```\| \|\| \|\|\| \|\|\|\| \|\|\|\|\| \|\|\|\|\|\| \|\|\|\|\| \|\|\|\| \|\|\| \|\| \|``` |  |
|
||||||
33 | ```[] [=] [==] [===] [====] [=====] [======] [=======] [========] [=========] [==========]``` | 
|
| 33 | ```[] [=] [==] [===] [====] [=====] [======] [=======] [========] [=========] [==========]``` |  |
|
||||||
34 | ```(*---------) (-*--------) (--*-------) (---*------) (----*-----) (-----*----) (------*---) (-------*--) (--------*-) (---------*)``` | 
|
| 34 | ```(*---------) (-*--------) (--*-------) (---*------) (----*-----) (-----*----) (------*---) (-------*--) (--------*-) (---------*)``` |  |
|
||||||
35 | ```█▒▒▒▒▒▒▒▒▒ ███▒▒▒▒▒▒▒ █████▒▒▒▒▒ ███████▒▒▒ ██████████``` | 
|
| 35 | ```█▒▒▒▒▒▒▒▒▒ ███▒▒▒▒▒▒▒ █████▒▒▒▒▒ ███████▒▒▒ ██████████``` |  |
|
||||||
36 | ```[ ] [=> ] [===> ] [=====> ] [======> ] [========> ] [==========> ] [============> ] [==============> ] [================> ] [==================> ] [===================>]``` | 
|
| 36 | ```[ ] [=> ] [===> ] [=====> ] [======> ] [========> ] [==========> ] [============> ] [==============> ] [================> ] [==================> ] [===================>]``` |  |
|
||||||
37 | ```🕐 🕑 🕒 🕓 🕔 🕕 🕖 🕗 🕘 🕙 🕚 🕛``` | 
|
| 37 | ```🕐 🕑 🕒 🕓 🕔 🕕 🕖 🕗 🕘 🕙 🕚 🕛``` |  |
|
||||||
38 | ```🕐 🕜 🕑 🕝 🕒 🕞 🕓 🕟 🕔 🕠 🕕 🕡 🕖 🕢 🕗 🕣 🕘 🕤 🕙 🕥 🕚 🕦 🕛 🕧``` | 
|
| 38 | ```🕐 🕜 🕑 🕝 🕒 🕞 🕓 🕟 🕔 🕠 🕕 🕡 🕖 🕢 🕗 🕣 🕘 🕤 🕙 🕥 🕚 🕦 🕛 🕧``` |  |
|
||||||
39 | ```🌍 🌎 🌏``` | 
|
| 39 | ```🌍 🌎 🌏``` |  |
|
||||||
40 | ```◜ ◝ ◞ ◟``` | 
|
| 40 | ```◜ ◝ ◞ ◟``` |  |
|
||||||
41 | ```⬒ ⬔ ⬓ ⬕``` | 
|
| 41 | ```⬒ ⬔ ⬓ ⬕``` |  |
|
||||||
42 | ```⬖ ⬘ ⬗ ⬙``` | 
|
| 42 | ```⬖ ⬘ ⬗ ⬙``` |  |
|
||||||
43 | ```[>>> >] []>>>> [] [] >>>> [] [] >>>> [] [] >>>> [] [] >>>>[] [>> >>]``` | 
|
| 43 | ```[>>> >] []>>>> [] [] >>>> [] [] >>>> [] [] >>>> [] [] >>>>[] [>> >>]``` |  |
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
@@ -139,7 +139,7 @@ s.Prefix = "prefixed text: " // Prefix text before the spinner
|
|||||||
s.Suffix = " :appended text" // Append text after the spinner
|
s.Suffix = " :appended text" // Append text after the spinner
|
||||||
```
|
```
|
||||||
|
|
||||||
## Set or change the color of the spinner. Default color is white. This will restart the spinner with the new color.
|
## Set or change the color of the spinner. Default color is white. The spinner will need to be restarted to pick up the change.
|
||||||
|
|
||||||
```Go
|
```Go
|
||||||
s.Color("red") // Set the spinner color to red
|
s.Color("red") // Set the spinner color to red
|
||||||
@@ -242,10 +242,11 @@ Feature suggested and write up by [dekz](https://github.com/dekz)
|
|||||||
|
|
||||||
Setting the Spinner Writer to Stderr helps show progress to the user, with the enhancement to chain, pipe or redirect the output.
|
Setting the Spinner Writer to Stderr helps show progress to the user, with the enhancement to chain, pipe or redirect the output.
|
||||||
|
|
||||||
|
This is the preferred method of setting a Writer at this time.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
s := spinner.New(spinner.CharSets[11], 100*time.Millisecond)
|
s := spinner.New(spinner.CharSets[11], 100*time.Millisecond, spinner.WithWriter(os.Stderr))
|
||||||
s.Suffix = " Encrypting data..."
|
s.Suffix = " Encrypting data..."
|
||||||
s.Writer = os.Stderr
|
|
||||||
s.Start()
|
s.Start()
|
||||||
// Encrypt the data into ciphertext
|
// Encrypt the data into ciphertext
|
||||||
fmt.Println(os.Stdout, ciphertext)
|
fmt.Println(os.Stdout, ciphertext)
|
||||||
|
2
vendor/github.com/briandowns/spinner/go.mod
generated
vendored
2
vendor/github.com/briandowns/spinner/go.mod
generated
vendored
@@ -1,5 +1,7 @@
|
|||||||
module github.com/briandowns/spinner
|
module github.com/briandowns/spinner
|
||||||
|
|
||||||
|
go 1.14
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/fatih/color v1.7.0
|
github.com/fatih/color v1.7.0
|
||||||
github.com/mattn/go-colorable v0.1.2 // indirect
|
github.com/mattn/go-colorable v0.1.2 // indirect
|
||||||
|
139
vendor/github.com/briandowns/spinner/spinner.go
generated
vendored
139
vendor/github.com/briandowns/spinner/spinner.go
generated
vendored
@@ -14,13 +14,13 @@
|
|||||||
package spinner
|
package spinner
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
@@ -161,7 +161,7 @@ var colorAttributeMap = map[string]color.Attribute{
|
|||||||
"bgHiWhite": color.BgHiWhite,
|
"bgHiWhite": color.BgHiWhite,
|
||||||
}
|
}
|
||||||
|
|
||||||
// validColor will make sure the given color is actually allowed
|
// validColor will make sure the given color is actually allowed.
|
||||||
func validColor(c string) bool {
|
func validColor(c string) bool {
|
||||||
if validColors[c] {
|
if validColors[c] {
|
||||||
return true
|
return true
|
||||||
@@ -169,8 +169,9 @@ func validColor(c string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Spinner struct to hold the provided options
|
// Spinner struct to hold the provided options.
|
||||||
type Spinner struct {
|
type Spinner struct {
|
||||||
|
mu *sync.RWMutex //
|
||||||
Delay time.Duration // Delay is the speed of the indicator
|
Delay time.Duration // Delay is the speed of the indicator
|
||||||
chars []string // chars holds the chosen character set
|
chars []string // chars holds the chosen character set
|
||||||
Prefix string // Prefix is the text preppended to the indicator
|
Prefix string // Prefix is the text preppended to the indicator
|
||||||
@@ -178,20 +179,21 @@ type Spinner struct {
|
|||||||
FinalMSG string // string displayed after Stop() is called
|
FinalMSG string // string displayed after Stop() is called
|
||||||
lastOutput string // last character(set) written
|
lastOutput string // last character(set) written
|
||||||
color func(a ...interface{}) string // default color is white
|
color func(a ...interface{}) string // default color is white
|
||||||
lock *sync.RWMutex //
|
Writer io.Writer // to make testing better, exported so users have access. Use `WithWriter` to update after initialization.
|
||||||
Writer io.Writer // to make testing better, exported so users have access
|
|
||||||
active bool // active holds the state of the spinner
|
active bool // active holds the state of the spinner
|
||||||
stopChan chan struct{} // stopChan is a channel used to stop the indicator
|
stopChan chan struct{} // stopChan is a channel used to stop the indicator
|
||||||
HideCursor bool // hideCursor determines if the cursor is visible
|
HideCursor bool // hideCursor determines if the cursor is visible
|
||||||
|
PreUpdate func(s *Spinner) // will be triggered before every spinner update
|
||||||
|
PostUpdate func(s *Spinner) // will be triggered after every spinner update
|
||||||
}
|
}
|
||||||
|
|
||||||
// New provides a pointer to an instance of Spinner with the supplied options
|
// New provides a pointer to an instance of Spinner with the supplied options.
|
||||||
func New(cs []string, d time.Duration, options ...Option) *Spinner {
|
func New(cs []string, d time.Duration, options ...Option) *Spinner {
|
||||||
s := &Spinner{
|
s := &Spinner{
|
||||||
Delay: d,
|
Delay: d,
|
||||||
chars: cs,
|
chars: cs,
|
||||||
color: color.New(color.FgWhite).SprintFunc(),
|
color: color.New(color.FgWhite).SprintFunc(),
|
||||||
lock: &sync.RWMutex{},
|
mu: &sync.RWMutex{},
|
||||||
Writer: color.Output,
|
Writer: color.Output,
|
||||||
active: false,
|
active: false,
|
||||||
stopChan: make(chan struct{}, 1),
|
stopChan: make(chan struct{}, 1),
|
||||||
@@ -204,10 +206,10 @@ func New(cs []string, d time.Duration, options ...Option) *Spinner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Option is a function that takes a spinner and applies
|
// Option is a function that takes a spinner and applies
|
||||||
// a given configuration
|
// a given configuration.
|
||||||
type Option func(*Spinner)
|
type Option func(*Spinner)
|
||||||
|
|
||||||
// Options contains fields to configure the spinner
|
// Options contains fields to configure the spinner.
|
||||||
type Options struct {
|
type Options struct {
|
||||||
Color string
|
Color string
|
||||||
Suffix string
|
Suffix string
|
||||||
@@ -215,7 +217,7 @@ type Options struct {
|
|||||||
HideCursor bool
|
HideCursor bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithColor adds the given color to the spinner
|
// WithColor adds the given color to the spinner.
|
||||||
func WithColor(color string) Option {
|
func WithColor(color string) Option {
|
||||||
return func(s *Spinner) {
|
return func(s *Spinner) {
|
||||||
s.Color(color)
|
s.Color(color)
|
||||||
@@ -223,7 +225,7 @@ func WithColor(color string) Option {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WithSuffix adds the given string to the spinner
|
// WithSuffix adds the given string to the spinner
|
||||||
// as the suffix
|
// as the suffix.
|
||||||
func WithSuffix(suffix string) Option {
|
func WithSuffix(suffix string) Option {
|
||||||
return func(s *Spinner) {
|
return func(s *Spinner) {
|
||||||
s.Suffix = suffix
|
s.Suffix = suffix
|
||||||
@@ -231,7 +233,7 @@ func WithSuffix(suffix string) Option {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WithFinalMSG adds the given string ot the spinner
|
// WithFinalMSG adds the given string ot the spinner
|
||||||
// as the final message to be written
|
// as the final message to be written.
|
||||||
func WithFinalMSG(finalMsg string) Option {
|
func WithFinalMSG(finalMsg string) Option {
|
||||||
return func(s *Spinner) {
|
return func(s *Spinner) {
|
||||||
s.FinalMSG = finalMsg
|
s.FinalMSG = finalMsg
|
||||||
@@ -239,31 +241,42 @@ func WithFinalMSG(finalMsg string) Option {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WithHiddenCursor hides the cursor
|
// WithHiddenCursor hides the cursor
|
||||||
// if hideCursor = true given
|
// if hideCursor = true given.
|
||||||
func WithHiddenCursor(hideCursor bool) Option {
|
func WithHiddenCursor(hideCursor bool) Option {
|
||||||
return func(s *Spinner) {
|
return func(s *Spinner) {
|
||||||
s.HideCursor = hideCursor
|
s.HideCursor = hideCursor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Active will return whether or not the spinner is currently active
|
// WithWriter adds the given writer to the spinner. This
|
||||||
|
// function should be favored over directly assigning to
|
||||||
|
// the struct value.
|
||||||
|
func WithWriter(w io.Writer) Option {
|
||||||
|
return func(s *Spinner) {
|
||||||
|
s.mu.Lock()
|
||||||
|
s.Writer = w
|
||||||
|
s.mu.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Active will return whether or not the spinner is currently active.
|
||||||
func (s *Spinner) Active() bool {
|
func (s *Spinner) Active() bool {
|
||||||
return s.active
|
return s.active
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start will start the indicator
|
// Start will start the indicator.
|
||||||
func (s *Spinner) Start() {
|
func (s *Spinner) Start() {
|
||||||
s.lock.Lock()
|
s.mu.Lock()
|
||||||
if s.active {
|
if s.active {
|
||||||
s.lock.Unlock()
|
s.mu.Unlock()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if s.HideCursor && runtime.GOOS != "windows" {
|
if s.HideCursor && runtime.GOOS != "windows" {
|
||||||
// hides the cursor
|
// hides the cursor
|
||||||
fmt.Print("\033[?25l")
|
fmt.Fprint(s.Writer, "\033[?25l")
|
||||||
}
|
}
|
||||||
s.active = true
|
s.active = true
|
||||||
s.lock.Unlock()
|
s.mu.Unlock()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
@@ -272,8 +285,17 @@ func (s *Spinner) Start() {
|
|||||||
case <-s.stopChan:
|
case <-s.stopChan:
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
s.lock.Lock()
|
s.mu.Lock()
|
||||||
|
if !s.active {
|
||||||
|
s.mu.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
s.erase()
|
s.erase()
|
||||||
|
|
||||||
|
if s.PreUpdate != nil {
|
||||||
|
s.PreUpdate(s)
|
||||||
|
}
|
||||||
|
|
||||||
var outColor string
|
var outColor string
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
if s.Writer == os.Stderr {
|
if s.Writer == os.Stderr {
|
||||||
@@ -282,14 +304,18 @@ func (s *Spinner) Start() {
|
|||||||
outColor = fmt.Sprintf("\r%s%s%s ", s.Prefix, s.color(s.chars[i]), s.Suffix)
|
outColor = fmt.Sprintf("\r%s%s%s ", s.Prefix, s.color(s.chars[i]), s.Suffix)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
outColor = fmt.Sprintf("%s%s%s ", s.Prefix, s.color(s.chars[i]), s.Suffix)
|
outColor = fmt.Sprintf("\r%s%s%s ", s.Prefix, s.color(s.chars[i]), s.Suffix)
|
||||||
}
|
}
|
||||||
outPlain := fmt.Sprintf("%s%s%s ", s.Prefix, s.chars[i], s.Suffix)
|
outPlain := fmt.Sprintf("\r%s%s%s ", s.Prefix, s.chars[i], s.Suffix)
|
||||||
fmt.Fprint(s.Writer, outColor)
|
fmt.Fprint(s.Writer, outColor)
|
||||||
s.lastOutput = outPlain
|
s.lastOutput = outPlain
|
||||||
delay := s.Delay
|
delay := s.Delay
|
||||||
s.lock.Unlock()
|
|
||||||
|
|
||||||
|
if s.PostUpdate != nil {
|
||||||
|
s.PostUpdate(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.mu.Unlock()
|
||||||
time.Sleep(delay)
|
time.Sleep(delay)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -297,40 +323,41 @@ func (s *Spinner) Start() {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop stops the indicator
|
// Stop stops the indicator.
|
||||||
func (s *Spinner) Stop() {
|
func (s *Spinner) Stop() {
|
||||||
s.lock.Lock()
|
s.mu.Lock()
|
||||||
defer s.lock.Unlock()
|
defer s.mu.Unlock()
|
||||||
if s.active {
|
if s.active {
|
||||||
s.active = false
|
s.active = false
|
||||||
if s.HideCursor && runtime.GOOS != "windows" {
|
if s.HideCursor && runtime.GOOS != "windows" {
|
||||||
// makes the cursor visible
|
// makes the cursor visible
|
||||||
fmt.Print("\033[?25h")
|
fmt.Fprint(s.Writer, "\033[?25h")
|
||||||
}
|
}
|
||||||
s.erase()
|
s.erase()
|
||||||
if s.FinalMSG != "" {
|
if s.FinalMSG != "" {
|
||||||
fmt.Fprintf(s.Writer, s.FinalMSG)
|
fmt.Fprint(s.Writer, s.FinalMSG)
|
||||||
}
|
}
|
||||||
s.stopChan <- struct{}{}
|
s.stopChan <- struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restart will stop and start the indicator
|
// Restart will stop and start the indicator.
|
||||||
func (s *Spinner) Restart() {
|
func (s *Spinner) Restart() {
|
||||||
s.Stop()
|
s.Stop()
|
||||||
s.Start()
|
s.Start()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reverse will reverse the order of the slice assigned to the indicator
|
// Reverse will reverse the order of the slice assigned to the indicator.
|
||||||
func (s *Spinner) Reverse() {
|
func (s *Spinner) Reverse() {
|
||||||
s.lock.Lock()
|
s.mu.Lock()
|
||||||
defer s.lock.Unlock()
|
defer s.mu.Unlock()
|
||||||
for i, j := 0, len(s.chars)-1; i < j; i, j = i+1, j-1 {
|
for i, j := 0, len(s.chars)-1; i < j; i, j = i+1, j-1 {
|
||||||
s.chars[i], s.chars[j] = s.chars[j], s.chars[i]
|
s.chars[i], s.chars[j] = s.chars[j], s.chars[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Color will set the struct field for the given color to be used
|
// Color will set the struct field for the given color to be used. The spinner
|
||||||
|
// will need to be explicitly restarted.
|
||||||
func (s *Spinner) Color(colors ...string) error {
|
func (s *Spinner) Color(colors ...string) error {
|
||||||
colorAttributes := make([]color.Attribute, len(colors))
|
colorAttributes := make([]color.Attribute, len(colors))
|
||||||
|
|
||||||
@@ -342,63 +369,55 @@ func (s *Spinner) Color(colors ...string) error {
|
|||||||
colorAttributes[index] = colorAttributeMap[c]
|
colorAttributes[index] = colorAttributeMap[c]
|
||||||
}
|
}
|
||||||
|
|
||||||
s.lock.Lock()
|
s.mu.Lock()
|
||||||
s.color = color.New(colorAttributes...).SprintFunc()
|
s.color = color.New(colorAttributes...).SprintFunc()
|
||||||
s.lock.Unlock()
|
s.mu.Unlock()
|
||||||
s.Restart()
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateSpeed will set the indicator delay to the given value
|
// UpdateSpeed will set the indicator delay to the given value.
|
||||||
func (s *Spinner) UpdateSpeed(d time.Duration) {
|
func (s *Spinner) UpdateSpeed(d time.Duration) {
|
||||||
s.lock.Lock()
|
s.mu.Lock()
|
||||||
defer s.lock.Unlock()
|
defer s.mu.Unlock()
|
||||||
s.Delay = d
|
s.Delay = d
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateCharSet will change the current character set to the given one
|
// UpdateCharSet will change the current character set to the given one.
|
||||||
func (s *Spinner) UpdateCharSet(cs []string) {
|
func (s *Spinner) UpdateCharSet(cs []string) {
|
||||||
s.lock.Lock()
|
s.mu.Lock()
|
||||||
defer s.lock.Unlock()
|
defer s.mu.Unlock()
|
||||||
s.chars = cs
|
s.chars = cs
|
||||||
}
|
}
|
||||||
|
|
||||||
// erase deletes written characters
|
// erase deletes written characters.
|
||||||
//
|
|
||||||
// Caller must already hold s.lock.
|
// Caller must already hold s.lock.
|
||||||
func (s *Spinner) erase() {
|
func (s *Spinner) erase() {
|
||||||
n := utf8.RuneCountInString(s.lastOutput)
|
n := utf8.RuneCountInString(s.lastOutput)
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
var clearString string
|
clearString := "\r" + strings.Repeat(" ", n) + "\r"
|
||||||
for i := 0; i < n; i++ {
|
fmt.Fprint(s.Writer, clearString)
|
||||||
clearString += " "
|
|
||||||
}
|
|
||||||
clearString += "\r"
|
|
||||||
fmt.Fprintf(s.Writer, clearString)
|
|
||||||
s.lastOutput = ""
|
s.lastOutput = ""
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
del, _ := hex.DecodeString("7f")
|
for _, c := range []string{"\b", "\127", "\b", "\033[K"} { // "\033[K" for macOS Terminal
|
||||||
for _, c := range []string{"\b", string(del), "\b", "\033[K"} { // "\033[K" for macOS Terminal
|
fmt.Fprint(s.Writer, strings.Repeat(c, n))
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
fmt.Fprintf(s.Writer, c)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
fmt.Fprintf(s.Writer, "\r\033[K") // erases to end of line
|
||||||
s.lastOutput = ""
|
s.lastOutput = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lock allows for manual control to lock the spinner
|
// Lock allows for manual control to lock the spinner.
|
||||||
func (s *Spinner) Lock() {
|
func (s *Spinner) Lock() {
|
||||||
s.lock.Lock()
|
s.mu.Lock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unlock allows for manual control to unlock the spinner
|
// Unlock allows for manual control to unlock the spinner.
|
||||||
func (s *Spinner) Unlock() {
|
func (s *Spinner) Unlock() {
|
||||||
s.lock.Unlock()
|
s.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateNumberSequence will generate a slice of integers at the
|
// GenerateNumberSequence will generate a slice of integers at the
|
||||||
// provided length and convert them each to a string
|
// provided length and convert them each to a string.
|
||||||
func GenerateNumberSequence(length int) []string {
|
func GenerateNumberSequence(length int) []string {
|
||||||
numSeq := make([]string, length)
|
numSeq := make([]string, length)
|
||||||
for i := 0; i < length; i++ {
|
for i := 0; i < length; i++ {
|
||||||
|
2
vendor/modules.txt
vendored
2
vendor/modules.txt
vendored
@@ -49,7 +49,7 @@ github.com/asdine/storm/codec/json
|
|||||||
github.com/asdine/storm/index
|
github.com/asdine/storm/index
|
||||||
github.com/asdine/storm/internal
|
github.com/asdine/storm/internal
|
||||||
github.com/asdine/storm/q
|
github.com/asdine/storm/q
|
||||||
# github.com/briandowns/spinner v1.7.0
|
# github.com/briandowns/spinner v1.12.1-0.20201220203425-e201aaea0a31
|
||||||
## explicit
|
## explicit
|
||||||
github.com/briandowns/spinner
|
github.com/briandowns/spinner
|
||||||
# github.com/cavaliercoder/grab v1.0.1-0.20201108051000-98a5bfe305ec
|
# github.com/cavaliercoder/grab v1.0.1-0.20201108051000-98a5bfe305ec
|
||||||
|
Reference in New Issue
Block a user