diff --git a/.github/workflows/unit-tests.yaml b/.github/workflows/unit-tests.yaml index 382b733b3e..ffef514122 100644 --- a/.github/workflows/unit-tests.yaml +++ b/.github/workflows/unit-tests.yaml @@ -21,10 +21,10 @@ jobs: strategy: matrix: os: [macos-latest, ubuntu-latest] - + name: unit-test-${{ matrix.os }} runs-on: ${{ matrix.os }} - + steps: - name: Set up Go uses: actions/setup-go@v5 diff --git a/cmd/init.go b/cmd/init.go index aea903b88c..b9874cbd6e 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -35,7 +35,7 @@ import ( "github.com/loft-sh/devspace/pkg/util/fsutil" "github.com/loft-sh/devspace/pkg/util/log" "github.com/loft-sh/devspace/pkg/util/survey" - "github.com/loft-sh/loft-util/pkg/command" + "github.com/loft-sh/utils/pkg/command" "github.com/mgutz/ansi" "github.com/pkg/errors" "github.com/spf13/cobra" diff --git a/cmd/run.go b/cmd/run.go index 1692bf7c0f..bbec9a5e3a 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -20,7 +20,7 @@ import ( "github.com/loft-sh/devspace/pkg/util/exit" "github.com/loft-sh/devspace/pkg/util/interrupt" "github.com/loft-sh/devspace/pkg/util/log" - "github.com/loft-sh/loft-util/pkg/command" + "github.com/loft-sh/utils/pkg/command" "mvdan.cc/sh/v3/interp" "github.com/loft-sh/devspace/cmd/flags" diff --git a/e2e/tests/render/testdata/helm/rendered.txt b/e2e/tests/render/testdata/helm/rendered.txt index 9ddbae8358..f9c5e5b2a4 100644 --- a/e2e/tests/render/testdata/helm/rendered.txt +++ b/e2e/tests/render/testdata/helm/rendered.txt @@ -9,7 +9,7 @@ metadata: "app.kubernetes.io/component": "test" "app.kubernetes.io/managed-by": "Helm" annotations: - "helm.sh/chart": "component-chart-0.9.1" + "helm.sh/chart": "component-chart-0.9.2" spec: replicas: 1 strategy: @@ -26,7 +26,7 @@ spec: "app.kubernetes.io/component": "test" "app.kubernetes.io/managed-by": "Helm" annotations: - "helm.sh/chart": "component-chart-0.9.1" + "helm.sh/chart": "component-chart-0.9.2" spec: imagePullSecrets: nodeSelector: @@ -78,7 +78,6 @@ spec: volumeMounts: initContainers: volumes: - volumeClaimTemplates: --- # Source: component-chart/templates/deployment.yaml # Create headless service for StatefulSet diff --git a/go.mod b/go.mod index cec030d25a..428ba7f7a0 100644 --- a/go.mod +++ b/go.mod @@ -31,7 +31,6 @@ require ( github.com/json-iterator/go v1.1.12 github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 github.com/loft-sh/go-github-selfupdate v1.0.0 - github.com/loft-sh/loft-util v0.0.9-alpha github.com/loft-sh/notify v0.0.0-20210827094439-0720dcc7feee github.com/loft-sh/programming-language-detection v0.0.5 github.com/loft-sh/utils v0.0.16 diff --git a/go.sum b/go.sum index ca69031e04..0705c5405e 100644 --- a/go.sum +++ b/go.sum @@ -343,8 +343,6 @@ github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhn github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/loft-sh/go-github-selfupdate v1.0.0 h1:YS8iSsIWXw3BygBdPK2xDO4K84XYu2YuYgVS7eQNtik= github.com/loft-sh/go-github-selfupdate v1.0.0/go.mod h1:LDkR6J2QpqQLIMcYvNaSinVwvjPAkg8278oZBPGnrb8= -github.com/loft-sh/loft-util v0.0.9-alpha h1:kGcyTQWxWHWy7bbjhS8Hsq/JRdlSztAU++anV6P+sqk= -github.com/loft-sh/loft-util v0.0.9-alpha/go.mod h1:lsjG5Exh5iEf7Z/87nqwkxx3GRQTizFRLGuS1knF6Cg= github.com/loft-sh/notify v0.0.0-20210827094439-0720dcc7feee h1:hZ79+pKEbCBrH1dVmgZ4jtFrrDPxgM4zqEP1lHlSnvI= github.com/loft-sh/notify v0.0.0-20210827094439-0720dcc7feee/go.mod h1:pq83B8lgfCY7tKdegTTXU6DZxGQkcWMowUTOTpTQmqk= github.com/loft-sh/programming-language-detection v0.0.5 h1:XiWlxtrf4t6Z7SQiob0JMKaCeMHCP3kWhB80wLt+EMY= diff --git a/hack/build-all.bash b/hack/build-all.bash index c537502347..535937b6a1 100755 --- a/hack/build-all.bash +++ b/hack/build-all.bash @@ -41,13 +41,32 @@ fi # Create the release directory mkdir -p "${DEVSPACE_ROOT}/release" -# Install Helm 3 +# Install Helm 4 echo "Installing helm" -curl -s https://get.helm.sh/helm-v3.3.4-darwin-amd64.tar.gz > helm3.tar.gz && tar -zxvf helm3.tar.gz darwin-amd64/helm && chmod +x darwin-amd64/helm +HELM_OS=$(uname -s | tr '[:upper:]' '[:lower:]') +HELM_ARCH=$(uname -m) +case "${HELM_ARCH}" in + x86_64) + HELM_ARCH="amd64" + ;; + aarch64|arm64) + HELM_ARCH="arm64" + ;; + i386|i686) + HELM_ARCH="386" + ;; +esac +HELM_PLATFORM="${HELM_OS}-${HELM_ARCH}" +HELM_VERSION=$(sed -nE 's/^const helmVersion = "([^"]+)"/\1/p' pkg/util/downloader/commands/helm_v4.go) +if [[ -z "${HELM_VERSION}" ]]; then + echo "unable to determine Helm version" 1>&2 + exit 1 +fi +curl -s "https://get.helm.sh/helm-${HELM_VERSION}-${HELM_PLATFORM}.tar.gz" > helm4.tar.gz && tar -zxvf helm4.tar.gz "${HELM_PLATFORM}/helm" && chmod +x "${HELM_PLATFORM}/helm" # Pull the component chart COMPONENT_CHART_VERSION=$(cat pkg/devspace/deploy/deployer/helm/client.go | grep 'Version: "' | sed -nE 's/[^"]+"(.+)",\s*/\1/p') -darwin-amd64/helm pull component-chart --repo https://charts.devspace.sh --version $COMPONENT_CHART_VERSION +"${HELM_PLATFORM}/helm" pull component-chart --repo https://charts.devspace.sh --version $COMPONENT_CHART_VERSION # Move ui.tar.gz to releases echo "Moving ui" @@ -74,28 +93,28 @@ for OS in ${DEVSPACE_BUILD_PLATFORMS[@]}; do if [[ "${OS}" == "windows" ]]; then NAME="${NAME}.exe" fi - + # darwin 386 is deprecated and shouldn't be used anymore if [[ "${ARCH}" == "386" && "${OS}" == "darwin" ]]; then echo "Building for ${OS}/${ARCH} not supported." continue fi - + # arm64 build is only supported for darwin if [[ "${ARCH}" == "arm64" && "${OS}" == "windows" ]]; then echo "Building for ${OS}/${ARCH} not supported." continue fi - + echo "Building for ${OS}/${ARCH}" - + # build darwin with CGO_ENABLED=1 if [[ "${OS}" == "darwin" ]]; then CGO_ENABLED=1 else - CGO_ENABLED=0 + CGO_ENABLED=0 fi - + # build the DevSpace binary CGO_ENABLED=${CGO_ENABLED} GOARCH=${ARCH} GOOS=${OS} ${GO_BUILD_CMD} -ldflags "${GO_BUILD_LDFLAGS}"\ -o "${DEVSPACE_ROOT}/release/${NAME}" . diff --git a/pkg/devspace/config/loader/variable/predefined_variable.go b/pkg/devspace/config/loader/variable/predefined_variable.go index ddeccc027d..cf7a1db372 100644 --- a/pkg/devspace/config/loader/variable/predefined_variable.go +++ b/pkg/devspace/config/loader/variable/predefined_variable.go @@ -6,26 +6,25 @@ import ( "encoding/json" "errors" "fmt" - "github.com/loft-sh/devspace/pkg/devspace/config/constants" "os" "path/filepath" "strconv" "strings" "time" + "github.com/loft-sh/devspace/pkg/devspace/config/constants" + "github.com/loft-sh/devspace/pkg/devspace/config/versions/latest" "github.com/loft-sh/devspace/pkg/devspace/context/values" "github.com/loft-sh/devspace/pkg/devspace/kubectl" - "github.com/loft-sh/devspace/pkg/util/log" - "github.com/loft-sh/utils/pkg/downloader" - "github.com/loft-sh/utils/pkg/downloader/commands" - "github.com/sirupsen/logrus" - - "github.com/loft-sh/devspace/pkg/devspace/config/versions/latest" "github.com/loft-sh/devspace/pkg/devspace/plugin" "github.com/loft-sh/devspace/pkg/devspace/upgrade" "github.com/loft-sh/devspace/pkg/util/git" + "github.com/loft-sh/devspace/pkg/util/log" "github.com/loft-sh/devspace/pkg/util/randutil" + "github.com/loft-sh/utils/pkg/downloader" + "github.com/loft-sh/utils/pkg/downloader/commands" "github.com/mitchellh/go-homedir" + "github.com/sirupsen/logrus" ) // PredefinedVariableOptions holds the options for a predefined variable to load @@ -54,8 +53,8 @@ var predefinedVars = map[string]PredefinedVariableFunction{ } return ex, nil }, - "DEVSPACE_KUBECTL_EXECUTABLE": func(ctx context.Context, options *PredefinedVariableOptions, log log.Logger) (interface{}, error) { - debugLog := log.WithLevel(logrus.DebugLevel) + "DEVSPACE_KUBECTL_EXECUTABLE": func(ctx context.Context, options *PredefinedVariableOptions, logger log.Logger) (interface{}, error) { + debugLog := logger.WithLevel(logrus.DebugLevel) path, err := downloader.NewDownloader(commands.NewKubectlCommand(), debugLog, constants.DefaultHomeDevSpaceFolder).EnsureCommand(ctx) if err != nil { debugLog.Debugf("Error downloading kubectl: %v", err) diff --git a/pkg/devspace/deploy/deployer/helm/client.go b/pkg/devspace/deploy/deployer/helm/client.go index 6effb7c756..bc30bb2a3b 100644 --- a/pkg/devspace/deploy/deployer/helm/client.go +++ b/pkg/devspace/deploy/deployer/helm/client.go @@ -23,7 +23,7 @@ const ComponentChartFolder = "component-chart" // DevSpaceChartConfig is the config that holds the devspace chart information var DevSpaceChartConfig = &latest.ChartConfig{ Name: "component-chart", - Version: "0.9.1", + Version: "0.9.2", RepoURL: "https://charts.devspace.sh", } diff --git a/pkg/devspace/helm/client.go b/pkg/devspace/helm/client.go index 9f6be99ebe..284fb3dbb6 100644 --- a/pkg/devspace/helm/client.go +++ b/pkg/devspace/helm/client.go @@ -2,11 +2,11 @@ package helm import ( "github.com/loft-sh/devspace/pkg/devspace/helm/types" - v3 "github.com/loft-sh/devspace/pkg/devspace/helm/v3" + v4 "github.com/loft-sh/devspace/pkg/devspace/helm/v4" "github.com/loft-sh/devspace/pkg/util/log" ) // NewClient creates a new helm client based on the config func NewClient(log log.Logger) (types.Client, error) { - return v3.NewClient(log) + return v4.NewClient(log) } diff --git a/pkg/devspace/helm/generic/generic.go b/pkg/devspace/helm/generic/generic.go index 65b6f66cc8..c2bca15864 100644 --- a/pkg/devspace/helm/generic/generic.go +++ b/pkg/devspace/helm/generic/generic.go @@ -28,13 +28,13 @@ type Client interface { WriteValues(values map[string]interface{}) (string, error) } -func NewGenericClient(command commands.Command, log log.Logger) Client { +func NewGenericClient(command commands.Command, logger log.Logger) Client { c := &client{ - log: log, + log: logger, extract: extract.NewExtractor(), } - c.downloader = downloader.NewDownloader(command, log, constants.DefaultHomeDevSpaceFolder) + c.downloader = downloader.NewDownloader(command, logger, constants.DefaultHomeDevSpaceFolder) return c } diff --git a/pkg/devspace/helm/v3/client.go b/pkg/devspace/helm/v4/client.go similarity index 96% rename from pkg/devspace/helm/v3/client.go rename to pkg/devspace/helm/v4/client.go index 69b62f326f..0dd501edf9 100644 --- a/pkg/devspace/helm/v3/client.go +++ b/pkg/devspace/helm/v4/client.go @@ -1,4 +1,4 @@ -package v3 +package v4 import ( "net/url" @@ -12,8 +12,8 @@ import ( dependencyutil "github.com/loft-sh/devspace/pkg/devspace/dependency/util" "github.com/loft-sh/devspace/pkg/devspace/helm/generic" "github.com/loft-sh/devspace/pkg/devspace/helm/types" + devspacecommands "github.com/loft-sh/devspace/pkg/util/downloader/commands" "github.com/loft-sh/devspace/pkg/util/log" - "github.com/loft-sh/utils/pkg/downloader/commands" "github.com/pkg/errors" "github.com/sirupsen/logrus" "sigs.k8s.io/yaml" @@ -23,10 +23,10 @@ type client struct { genericHelm generic.Client } -// NewClient creates a new helm v3 Client +// NewClient creates a new helm v4 Client func NewClient(log log.Logger) (types.Client, error) { c := &client{} - c.genericHelm = generic.NewGenericClient(commands.NewHelmV3Command(), log) + c.genericHelm = generic.NewGenericClient(devspacecommands.NewHelmV4Command(), log) return c, nil } @@ -38,7 +38,7 @@ func (c *client) DownloadChart(ctx devspacecontext.Context, helmConfig *latest.H return filepath.Dir(chartName), nil } -// InstallChart installs the given chart via helm v3 +// InstallChart installs the given chart via helm v4 func (c *client) InstallChart(ctx devspacecontext.Context, releaseName string, releaseNamespace string, values map[string]interface{}, helmConfig *latest.HelmConfig) (*types.Release, error) { valuesFile, err := c.genericHelm.WriteValues(values) if err != nil { diff --git a/pkg/devspace/pipeline/engine/basichandler/handler.go b/pkg/devspace/pipeline/engine/basichandler/handler.go index d6437f6de4..c3c41b69cb 100644 --- a/pkg/devspace/pipeline/engine/basichandler/handler.go +++ b/pkg/devspace/pipeline/engine/basichandler/handler.go @@ -9,6 +9,7 @@ import ( "github.com/loft-sh/devspace/pkg/devspace/config/constants" enginecommands "github.com/loft-sh/devspace/pkg/devspace/pipeline/engine/basichandler/commands" "github.com/loft-sh/devspace/pkg/devspace/pipeline/engine/types" + devspacecommands "github.com/loft-sh/devspace/pkg/util/downloader/commands" "github.com/loft-sh/devspace/pkg/util/log" "github.com/loft-sh/utils/pkg/downloader" "github.com/loft-sh/utils/pkg/downloader/commands" @@ -79,7 +80,7 @@ var EnsureCommands = map[string]func(ctx context.Context, args []string) (string }, "helm": func(ctx context.Context, args []string) (string, error) { hc := interp.HandlerCtx(ctx) - path, err := downloader.NewDownloader(commands.NewHelmV3Command(), log.GetFileLogger("shell"), constants.DefaultHomeDevSpaceFolder).EnsureCommand(ctx) + path, err := downloader.NewDownloader(devspacecommands.NewHelmV4Command(), log.GetFileLogger("shell"), constants.DefaultHomeDevSpaceFolder).EnsureCommand(ctx) if err != nil { _, _ = fmt.Fprintln(hc.Stderr, err) return "", interp.NewExitStatus(127) diff --git a/pkg/devspace/pipeline/engine/engine_test.go b/pkg/devspace/pipeline/engine/engine_test.go index ea535feaaf..d3cb85f71b 100644 --- a/pkg/devspace/pipeline/engine/engine_test.go +++ b/pkg/devspace/pipeline/engine/engine_test.go @@ -3,13 +3,13 @@ package engine import ( "bytes" "context" - "mvdan.cc/sh/v3/expand" "os" "path/filepath" "strings" "testing" "gotest.tools/assert" + "mvdan.cc/sh/v3/expand" ) type testCaseShell struct { @@ -132,5 +132,6 @@ func TestHelmDownload(t *testing.T) { if err != nil { t.Fatal(err) } - assert.Assert(t, strings.Contains(stdout1.String(), `Version:"v3`)) + + assert.Assert(t, strings.Contains(stdout1.String(), `Version:"v4`)) } diff --git a/pkg/util/downloader/commands/helm_v4.go b/pkg/util/downloader/commands/helm_v4.go new file mode 100644 index 0000000000..1e3b952b5f --- /dev/null +++ b/pkg/util/downloader/commands/helm_v4.go @@ -0,0 +1,120 @@ +package commands + +import ( + "context" + "errors" + "io" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + + utilscommand "github.com/loft-sh/utils/pkg/command" + downloadercommands "github.com/loft-sh/utils/pkg/downloader/commands" + "github.com/loft-sh/utils/pkg/extract" + "github.com/mitchellh/go-homedir" + pkgerrors "github.com/pkg/errors" + "mvdan.cc/sh/v3/expand" +) + +const helmVersion = "v4.0.4" + +var helmDownload = "https://get.helm.sh/helm-" + helmVersion + "-" + runtime.GOOS + "-" + runtime.GOARCH + +func NewHelmV4Command() downloadercommands.Command { + return &helmCommand{} +} + +type helmCommand struct{} + +func (h *helmCommand) Name() string { + return "helm" +} + +func (h *helmCommand) InstallPath(toolHomeFolder string) (string, error) { + home, err := homedir.Dir() + if err != nil { + return "", err + } + + installPath := filepath.Join(home, toolHomeFolder, "bin", h.Name()) + if runtime.GOOS == "windows" { + installPath += ".exe" + } + + return installPath, nil +} + +func (h *helmCommand) DownloadURL() string { + if runtime.GOOS == "windows" { + return helmDownload + ".zip" + } + + return helmDownload + ".tar.gz" +} + +func (h *helmCommand) IsValid(ctx context.Context, path string) (bool, error) { + out, err := utilscommand.Output(ctx, "", expand.ListEnviron(os.Environ()...), path, "version") + if err != nil { + if errors.Is(err, exec.ErrNotFound) || os.IsNotExist(err) { + return false, nil + } + + return false, err + } + + return strings.Contains(string(out), `:"v4.`), nil +} + +func (h *helmCommand) Install(toolHomeFolder, archiveFile string) error { + installPath, err := h.InstallPath(toolHomeFolder) + if err != nil { + return err + } + + return installHelmBinary(archiveFile, installPath, h.DownloadURL()) +} + +func installHelmBinary(archiveFile, installPath, installFromURL string) error { + targetDir := filepath.Dir(archiveFile) + extractor := extract.NewExtractor() + + if strings.HasSuffix(installFromURL, ".tar.gz") { + if err := extractor.UntarGz(archiveFile, targetDir); err != nil { + return pkgerrors.Wrap(err, "extract tar.gz") + } + } else if strings.HasSuffix(installFromURL, ".zip") { + if err := extractor.Unzip(archiveFile, targetDir); err != nil { + return pkgerrors.Wrap(err, "extract zip") + } + } + + binaryName := "helm" + if runtime.GOOS == "windows" { + binaryName = "helm.exe" + } + + sourcePath := filepath.Join(targetDir, runtime.GOOS+"-"+runtime.GOARCH, binaryName) + return copyFile(sourcePath, installPath) +} + +func copyFile(sourcePath, targetPath string) error { + source, err := os.Open(sourcePath) + if err != nil { + return err + } + defer source.Close() + + target, err := os.Create(targetPath) + if err != nil { + return err + } + + if _, err := io.Copy(target, source); err != nil { + _ = target.Close() + return err + } + + return target.Close() +} diff --git a/pkg/util/log/file_logger.go b/pkg/util/log/file_logger.go index 0b29a30c81..8df4b0d283 100644 --- a/pkg/util/log/file_logger.go +++ b/pkg/util/log/file_logger.go @@ -408,3 +408,15 @@ func (f *fileLogger) WithPrefixColor(prefix, color string) Logger { func (f *fileLogger) ErrorStreamOnly() Logger { return f } + +func (f *fileLogger) Children() []Logger { + return nil +} + +func (f *fileLogger) Fail(args ...interface{}) { + f.Error(args...) +} + +func (f *fileLogger) Failf(format string, args ...interface{}) { + f.Errorf(format, args...) +} diff --git a/pkg/util/log/global.go b/pkg/util/log/global.go index f03d556c72..2e925d7745 100644 --- a/pkg/util/log/global.go +++ b/pkg/util/log/global.go @@ -2,8 +2,9 @@ package log import ( "fmt" - "github.com/loft-sh/devspace/pkg/util/randutil" "sync" + + "github.com/loft-sh/devspace/pkg/util/randutil" ) var ( diff --git a/pkg/util/log/log.go b/pkg/util/log/log.go index 8fcd804936..85e65eae5f 100644 --- a/pkg/util/log/log.go +++ b/pkg/util/log/log.go @@ -1,13 +1,14 @@ package log import ( + "io" + "os" + "runtime" + "github.com/loft-sh/devspace/pkg/util/scanner" "github.com/mgutz/ansi" "github.com/olekukonko/tablewriter" "github.com/sirupsen/logrus" - "io" - "os" - "runtime" ) var baseLog = NewStdoutLogger(os.Stdin, stdout, stderr, logrus.InfoLevel) diff --git a/pkg/util/log/logger.go b/pkg/util/log/logger.go index 1db6aac089..c86c36b859 100644 --- a/pkg/util/log/logger.go +++ b/pkg/util/log/logger.go @@ -4,7 +4,6 @@ import ( "io" "github.com/loft-sh/devspace/pkg/util/survey" - "github.com/loft-sh/utils/pkg/log" "github.com/sirupsen/logrus" ) @@ -22,7 +21,29 @@ const ( // Logger defines the devspace common logging interface type Logger interface { - log.Logger + Debug(args ...interface{}) + Debugf(format string, args ...interface{}) + Info(args ...interface{}) + Infof(format string, args ...interface{}) + Warn(args ...interface{}) + Warnf(format string, args ...interface{}) + Error(args ...interface{}) + Errorf(format string, args ...interface{}) + Fatal(args ...interface{}) + Fatalf(format string, args ...interface{}) + Done(args ...interface{}) + Donef(format string, args ...interface{}) + Fail(args ...interface{}) + Failf(format string, args ...interface{}) + Print(level logrus.Level, args ...interface{}) + Printf(level logrus.Level, format string, args ...interface{}) + StartWait(message string) + StopWait() + SetLevel(level logrus.Level) + GetLevel() logrus.Level + Children() []Logger + Write(message []byte) (int, error) + // WithLevel creates a new logger with the given level WithLevel(level logrus.Level) Logger Question(params *survey.QuestionOptions) (string, error) diff --git a/pkg/util/log/stream_logger.go b/pkg/util/log/stream_logger.go index b3a6281dae..d3544dd2de 100644 --- a/pkg/util/log/stream_logger.go +++ b/pkg/util/log/stream_logger.go @@ -415,6 +415,14 @@ func (s *StreamLogger) Donef(format string, args ...interface{}) { s.writeMessage(doneFn, fmt.Sprintf(format, args...)+"\n") } +func (s *StreamLogger) Fail(args ...interface{}) { + s.Error(args...) +} + +func (s *StreamLogger) Failf(format string, args ...interface{}) { + s.Errorf(format, args...) +} + func (s *StreamLogger) Print(level logrus.Level, args ...interface{}) { switch level { case logrus.InfoLevel: @@ -459,6 +467,19 @@ func (s *StreamLogger) GetLevel() logrus.Level { return s.level } +func (s *StreamLogger) StartWait(message string) { + if message == "" { + return + } + + s.Info(message) +} + +func (s *StreamLogger) StopWait() { + // StartWait writes a complete log line instead of starting a spinner, so + // there is no terminal state to clean up. +} + func (s *StreamLogger) Writer(level logrus.Level, raw bool) io.WriteCloser { s.m.Lock() defer s.m.Unlock() @@ -496,6 +517,13 @@ func (s *StreamLogger) WriteString(level logrus.Level, message string) { _, _ = s.write(level, []byte(message)) } +func (s *StreamLogger) Write(message []byte) (int, error) { + s.m.Lock() + defer s.m.Unlock() + + return s.write(logrus.InfoLevel, message) +} + func (s *StreamLogger) write(level logrus.Level, message []byte) (int, error) { var ( n int diff --git a/pkg/util/log/stream_logger_test.go b/pkg/util/log/stream_logger_test.go new file mode 100644 index 0000000000..c9dd54ca96 --- /dev/null +++ b/pkg/util/log/stream_logger_test.go @@ -0,0 +1,25 @@ +package log + +import ( + "bytes" + "testing" + + "github.com/sirupsen/logrus" +) + +func TestStreamLoggerStartWaitFallsBackToInfo(t *testing.T) { + stdout := &bytes.Buffer{} + stderr := &bytes.Buffer{} + logger := NewStreamLoggerWithFormat(stdout, stderr, logrus.InfoLevel, RawFormat) + + logger.StartWait("Downloading helm...") + logger.StopWait() + + expected := "Downloading helm...\n" + if stdout.String() != expected { + t.Fatalf("expected %q, got %q", expected, stdout.String()) + } + if stderr.Len() != 0 { + t.Fatalf("expected no stderr output, got %q", stderr.String()) + } +} diff --git a/pkg/util/log/testing/fake.go b/pkg/util/log/testing/fake.go index c493a426c3..46ab6726de 100644 --- a/pkg/util/log/testing/fake.go +++ b/pkg/util/log/testing/fake.go @@ -2,9 +2,10 @@ package testing import ( "fmt" - "github.com/loft-sh/devspace/pkg/util/log" "io" + "github.com/loft-sh/devspace/pkg/util/log" + "github.com/loft-sh/devspace/pkg/util/survey" fakesurvey "github.com/loft-sh/devspace/pkg/util/survey/testing" "github.com/sirupsen/logrus" @@ -143,3 +144,7 @@ func (d *FakeLogger) WithPrefixColor(prefix, color string) log.Logger { func (d *FakeLogger) ErrorStreamOnly() log.Logger { return d } + +func (d *FakeLogger) Children() []log.Logger { + return nil +} diff --git a/ui/package-lock.json b/ui/package-lock.json index 7af1211183..111d24079f 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -128,6 +128,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz", "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", "license": "MIT", + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", @@ -2160,6 +2161,30 @@ "node": ">=0.1.95" } }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "license": "MIT", + "optional": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@emnapi/core": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", @@ -2228,6 +2253,7 @@ "integrity": "sha512-YUcsLQKYb6DmaJjIHdDWpBIGCcyE/W+p/LMGvjQem55Mm2XWVAP5kWTMKWLv9lwpCVjpLxPyOMOyUocP1GxrtA==", "hasInstallScript": true, "license": "MIT", + "peer": true, "dependencies": { "@fortawesome/fontawesome-common-types": "^0.2.36" }, @@ -5423,6 +5449,34 @@ "string.prototype.matchall": "^4.0.6" } }, + "node_modules/@tsconfig/node10": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", + "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==", + "license": "MIT", + "optional": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "license": "MIT", + "optional": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "license": "MIT", + "optional": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "license": "MIT", + "optional": true + }, "node_modules/@tybys/wasm-util": { "version": "0.10.1", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", @@ -5689,7 +5743,8 @@ "version": "10.17.60", "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@types/node-fetch": { "version": "2.6.13", @@ -5745,6 +5800,7 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-16.14.65.tgz", "integrity": "sha512-Guc3kE+W8LrQB9I3bF3blvNH15dXFIVIHIJTqrF8cp5XI/3IJcHGo4C3sJNPb8Zx49aofXKnAGIKyonE4f7XWg==", "license": "MIT", + "peer": true, "dependencies": { "@types/prop-types": "*", "@types/scheduler": "^0.16", @@ -6387,6 +6443,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -6406,6 +6463,19 @@ "acorn": "^8.14.0" } }, + "node_modules/acorn-walk": { + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz", + "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==", + "license": "MIT", + "optional": true, + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/address": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/address/-/address-1.1.2.tgz", @@ -6420,6 +6490,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -7892,6 +7963,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.1.0.tgz", "integrity": "sha512-9EWmD0cQAbcXSc+31RIoYgEHx3KQ2CCSMDBhnXrShWvo45TMw+3/55KVxlhkG53kw9tl87DqINgHDgFVhZJV/Q==", "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.0.0", "@babel/generator": "^7.0.0", @@ -8463,6 +8535,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", @@ -9172,6 +9245,13 @@ "node": ">=0.10.0" } }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "license": "MIT", + "optional": true + }, "node_modules/cross-env": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-6.0.3.tgz", @@ -13159,6 +13239,23 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, + "node_modules/jest-circus/node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "license": "MIT", + "optional": true, + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/jest-circus/node_modules/dedent": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz", @@ -13226,6 +13323,28 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/jest-circus/node_modules/resolve": { + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "license": "MIT", + "optional": true, + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/jest-circus/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -16824,6 +16943,7 @@ "resolved": "https://registry.npmjs.org/jsep/-/jsep-1.4.0.tgz", "integrity": "sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw==", "license": "MIT", + "peer": true, "engines": { "node": ">= 10.16.0" } @@ -17408,6 +17528,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -19131,6 +19252,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", @@ -19574,6 +19696,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz", "integrity": "sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", @@ -20280,6 +20403,7 @@ "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.80.0.tgz", "integrity": "sha512-cIFJOD1DESzpjOBl763Kp1AH7UE/0fcdHe6rZXUdQ9c50uvgigvW97u3IcSeBwOkgqL/PXPBktBCh0KEu5L8XQ==", "license": "MIT", + "peer": true, "bin": { "rollup": "dist/bin/rollup" }, @@ -22264,6 +22388,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -23023,6 +23148,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.3.tgz", "integrity": "sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==", "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -23578,6 +23704,13 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "license": "MIT", + "optional": true + }, "node_modules/v8-to-istanbul": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", @@ -23659,6 +23792,7 @@ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.104.1.tgz", "integrity": "sha512-Qphch25abbMNtekmEGJmeRUhLDbe+QfiWTiqpKYkpCOWY64v9eyl+KRRLmqOFA2AvKPpc9DC6+u2n76tQLBoaA==", "license": "MIT", + "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -23730,6 +23864,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -23842,6 +23977,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -24112,6 +24248,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -24592,6 +24729,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -25092,6 +25230,7 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz", "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==", "license": "MIT", + "peer": true, "engines": { "node": ">=10.0.0" }, diff --git a/vendor/github.com/loft-sh/loft-util/LICENSE b/vendor/github.com/loft-sh/loft-util/LICENSE deleted file mode 100644 index 261eeb9e9f..0000000000 --- a/vendor/github.com/loft-sh/loft-util/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/vendor/github.com/loft-sh/loft-util/pkg/command/command.go b/vendor/github.com/loft-sh/loft-util/pkg/command/command.go deleted file mode 100644 index 1f03d3d8f7..0000000000 --- a/vendor/github.com/loft-sh/loft-util/pkg/command/command.go +++ /dev/null @@ -1,160 +0,0 @@ -package command - -import ( - "bytes" - "context" - "fmt" - "io" - "mvdan.cc/sh/v3/expand" - "os" - "os/exec" - "runtime" - "strings" - "time" -) - -// streamCommand is the command whose output is streamed to a log -type streamCommand struct { - cmd *exec.Cmd - killTimeout time.Duration -} - -// newStreamCommand creates a new stream command -func newStreamCommand(command string, args []string) *streamCommand { - return &streamCommand{ - cmd: exec.Command(command, args...), - killTimeout: time.Second * 2, - } -} - -func ListVars(environ expand.Environ) map[string]string { - variables := map[string]string{} - environ.Each(func(name string, vr expand.Variable) bool { - if vr.Kind == expand.String && vr.Str != "" { - variables[name] = vr.Str - } - return true - }) - return variables -} - -// RunWithEnv runs a stream command -func (s *streamCommand) RunWithEnv(ctx context.Context, dir string, environ expand.Environ, stdout io.Writer, stderr io.Writer, stdin io.Reader) error { - s.cmd.Dir = dir - env := []string{} - for k, v := range ListVars(environ) { - env = append(env, k+"="+v) - } - - s.cmd.Env = env - if stdout != nil { - s.cmd.Stdout = stdout - } - - var defaultStderr *prefixSuffixSaver - if stderr != nil { - s.cmd.Stderr = stderr - } else { - defaultStderr = &prefixSuffixSaver{N: 32 << 10} - s.cmd.Stderr = defaultStderr - } - - if stdin != nil { - s.cmd.Stdin = stdin - } - - var err error - err = s.cmd.Start() - if err == nil { - if done := ctx.Done(); done != nil { - go func() { - <-done - - if s.killTimeout <= 0 || runtime.GOOS == "windows" { - _ = s.cmd.Process.Signal(os.Kill) - return - } - - // TODO: don't temporarily leak this goroutine - // if the program stops itself with the - // interrupt. - go func() { - time.Sleep(s.killTimeout) - _ = s.cmd.Process.Signal(os.Kill) - }() - _ = s.cmd.Process.Signal(os.Interrupt) - }() - } - - err = s.cmd.Wait() - } - if err != nil { - if exitErr, ok := err.(*exec.ExitError); ok && defaultStderr != nil { - exitErr.Stderr = defaultStderr.Bytes() - } - - return err - } - - return nil -} - -// Run runs a stream command -func (s *streamCommand) Run(ctx context.Context, dir string, stdout io.Writer, stderr io.Writer, stdin io.Reader) error { - return s.RunWithEnv(ctx, dir, expand.ListEnviron(os.Environ()...), stdout, stderr, stdin) -} - -func ShouldExecuteOnOS(os string) bool { - // if the operating system is set and the current is not specified - // we skip the hook - if os != "" { - found := false - oss := strings.Split(os, ",") - for _, os := range oss { - if strings.TrimSpace(os) == runtime.GOOS { - found = true - break - } - } - if !found { - return false - } - } - - return true -} - -func Command(ctx context.Context, dir string, environ expand.Environ, stdout io.Writer, stderr io.Writer, stdin io.Reader, cmd string, args ...string) error { - err := newStreamCommand(cmd, args).RunWithEnv(ctx, dir, environ, stdout, stderr, stdin) - if err != nil { - if errr, ok := err.(*exec.ExitError); ok { - return fmt.Errorf("error executing '%s %s': %s", cmd, strings.Join(args, " "), string(errr.Stderr)) - } - - return err - } - - return nil -} - -func CombinedOutput(ctx context.Context, dir string, environ expand.Environ, cmd string, args ...string) ([]byte, error) { - stdout := &bytes.Buffer{} - err := Command(ctx, dir, environ, stdout, stdout, nil, cmd, args...) - return stdout.Bytes(), err -} - -func Output(ctx context.Context, dir string, environ expand.Environ, cmd string, args ...string) ([]byte, error) { - stdout := &bytes.Buffer{} - err := Command(ctx, dir, environ, stdout, nil, nil, cmd, args...) - return stdout.Bytes(), err -} - -func FormatCommandName(cmd string, args []string) string { - commandString := strings.TrimSpace(cmd + " " + strings.Join(args, " ")) - splitted := strings.Split(commandString, "\n") - if len(splitted) > 1 { - return splitted[0] + "..." - } - - return commandString -} diff --git a/vendor/github.com/loft-sh/loft-util/pkg/command/fake.go b/vendor/github.com/loft-sh/loft-util/pkg/command/fake.go deleted file mode 100644 index 8df07ebc23..0000000000 --- a/vendor/github.com/loft-sh/loft-util/pkg/command/fake.go +++ /dev/null @@ -1,28 +0,0 @@ -package command - -import "io" - -// FakeCommand is used for testing -type FakeCommand struct { - OutputBytes []byte -} - -// CombinedOutput runs the command and returns the stdout and stderr -func (f *FakeCommand) CombinedOutput() ([]byte, error) { - return f.OutputBytes, nil -} - -// Output runs the command and returns the stdout -func (f *FakeCommand) Output() ([]byte, error) { - return f.OutputBytes, nil -} - -// RunWithEnv Run implements interface -func (f *FakeCommand) RunWithEnv(stdout io.Writer, stderr io.Writer, stdin io.Reader, dir string, extraEnvVars map[string]string) error { - return nil -} - -// Run implements interface -func (f *FakeCommand) Run(workingDirectory string, stdout io.Writer, stderr io.Writer, stdin io.Reader) error { - return nil -} diff --git a/vendor/github.com/loft-sh/loft-util/pkg/command/prefixed_saver.go b/vendor/github.com/loft-sh/loft-util/pkg/command/prefixed_saver.go deleted file mode 100644 index bfb4ac7a2e..0000000000 --- a/vendor/github.com/loft-sh/loft-util/pkg/command/prefixed_saver.go +++ /dev/null @@ -1,83 +0,0 @@ -package command - -import ( - "bytes" - "strconv" -) - -// prefixSuffixSaver is an io.Writer which retains the first N bytes -// and the last N bytes written to it. The Bytes() methods reconstructs -// it with a pretty error message. -type prefixSuffixSaver struct { - N int // max size of prefix or suffix - prefix []byte - suffix []byte // ring buffer once len(suffix) == N - suffixOff int // offset to write into suffix - skipped int64 - - // TODO(bradfitz): we could keep one large []byte and use part of it for - // the prefix, reserve space for the '... Omitting N bytes ...' message, - // then the ring buffer suffix, and just rearrange the ring buffer - // suffix when Bytes() is called, but it doesn't seem worth it for - // now just for error messages. It's only ~64KB anyway. -} - -func (w *prefixSuffixSaver) Write(p []byte) (n int, err error) { - lenp := len(p) - p = w.fill(&w.prefix, p) - - // Only keep the last w.N bytes of suffix data. - if overage := len(p) - w.N; overage > 0 { - p = p[overage:] - w.skipped += int64(overage) - } - p = w.fill(&w.suffix, p) - - // w.suffix is full now if p is non-empty. Overwrite it in a circle. - for len(p) > 0 { // 0, 1, or 2 iterations. - n := copy(w.suffix[w.suffixOff:], p) - p = p[n:] - w.skipped += int64(n) - w.suffixOff += n - if w.suffixOff == w.N { - w.suffixOff = 0 - } - } - return lenp, nil -} - -// fill appends up to len(p) bytes of p to *dst, such that *dst does not -// grow larger than w.N. It returns the un-appended suffix of p. -func (w *prefixSuffixSaver) fill(dst *[]byte, p []byte) (pRemain []byte) { - if remain := w.N - len(*dst); remain > 0 { - add := minInt(len(p), remain) - *dst = append(*dst, p[:add]...) - p = p[add:] - } - return p -} - -func (w *prefixSuffixSaver) Bytes() []byte { - if w.suffix == nil { - return w.prefix - } - if w.skipped == 0 { - return append(w.prefix, w.suffix...) - } - var buf bytes.Buffer - buf.Grow(len(w.prefix) + len(w.suffix) + 50) - buf.Write(w.prefix) - buf.WriteString("\n... omitting ") - buf.WriteString(strconv.FormatInt(w.skipped, 10)) - buf.WriteString(" bytes ...\n") - buf.Write(w.suffix[w.suffixOff:]) - buf.Write(w.suffix[:w.suffixOff]) - return buf.Bytes() -} - -func minInt(a, b int) int { - if a < b { - return a - } - return b -} diff --git a/vendor/modules.txt b/vendor/modules.txt index 6377307506..58c43b679f 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -422,9 +422,6 @@ github.com/liggitt/tabwriter # github.com/loft-sh/go-github-selfupdate v1.0.0 ## explicit; go 1.13 github.com/loft-sh/go-github-selfupdate/selfupdate -# github.com/loft-sh/loft-util v0.0.9-alpha -## explicit; go 1.19 -github.com/loft-sh/loft-util/pkg/command # github.com/loft-sh/notify v0.0.0-20210827094439-0720dcc7feee ## explicit; go 1.11 github.com/loft-sh/notify