Make the cli colorful and informational (#4525)

This commit include a few things that improves the progresbars and other info messages are printed out during the CLI run.

![2023-10-20-17-17-09](https://github.com/alcionai/corso/assets/14259816/30a25832-24cb-48ef-944d-6aaced1cd62f)

---

#### Does this PR need a docs update or release note?

- [ ]  Yes, it's included
- [x] 🕐 Yes, but in a later PR
- [ ]  No

#### Type of change

<!--- Please check the type of change your PR introduces: --->
- [x] 🌻 Feature
- [ ] 🐛 Bugfix
- [ ] 🗺️ Documentation
- [ ] 🤖 Supportability/Tests
- [ ] 💻 CI/Deployment
- [ ] 🧹 Tech Debt/Cleanup

#### Issue(s)

<!-- Can reference multiple issues. Use one of the following "magic words" - "closes, fixes" to auto-close the Github issue. -->
* #<issue>

#### Test Plan

<!-- How will this be tested prior to merging.-->
- [x] 💪 Manual
- [ ]  Unit test
- [ ] 💚 E2E
This commit is contained in:
Abin Simon 2023-11-16 17:41:37 +05:30 committed by GitHub
parent f2102e55f6
commit b46f242bc4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 290 additions and 74 deletions

View File

@ -2,6 +2,7 @@ package backup
import (
"context"
"encoding/json"
"fmt"
"strings"
@ -12,8 +13,10 @@ import (
"github.com/alcionai/corso/src/cli/flags"
. "github.com/alcionai/corso/src/cli/print"
"github.com/alcionai/corso/src/cli/utils"
"github.com/alcionai/corso/src/internal/common/color"
"github.com/alcionai/corso/src/internal/common/idname"
"github.com/alcionai/corso/src/internal/data"
"github.com/alcionai/corso/src/internal/observe"
"github.com/alcionai/corso/src/pkg/backup"
"github.com/alcionai/corso/src/pkg/backup/details"
"github.com/alcionai/corso/src/pkg/control"
@ -187,8 +190,15 @@ func genericCreateCommand(
bo, err := r.NewBackupWithLookup(ictx, discSel, ins)
if err != nil {
errs = append(errs, clues.WrapWC(ictx, err, owner))
Errf(ictx, "%v\n", err)
cerr := clues.WrapWC(ictx, err, owner)
errs = append(errs, cerr)
meta, err := json.Marshal(cerr.Core().Values)
if err != nil {
meta = []byte("Unable to marshal error metadata")
}
Errf(ictx, "%s\nMessage: %v\nMetadata:%s", "Unable to complete backup", err, meta)
continue
}
@ -208,8 +218,15 @@ func genericCreateCommand(
continue
}
errs = append(errs, clues.WrapWC(ictx, err, owner))
Errf(ictx, "%v\n", err)
cerr := clues.WrapWC(ictx, err, owner)
errs = append(errs, cerr)
meta, err := json.Marshal(cerr.Core().Values)
if err != nil {
meta = []byte("Unable to marshal error metadata")
}
Errf(ictx, "%s\nMessage: %v\nMetadata:%s", "Unable to complete backup", err, meta)
continue
}
@ -217,10 +234,10 @@ func genericCreateCommand(
bIDs = append(bIDs, string(bo.Results.BackupID))
if !DisplayJSONFormat() {
Infof(ctx, "Done\n")
Infof(ctx, fmt.Sprintf("Backup complete %s %s", observe.Bullet, color.BlueOutput(bo.Results.BackupID)))
printBackupStats(ctx, r, string(bo.Results.BackupID))
} else {
Infof(ctx, "Done - ID: %v\n", bo.Results.BackupID)
Infof(ctx, "Backup complete - ID: %v\n", bo.Results.BackupID)
}
}
@ -231,9 +248,8 @@ func genericCreateCommand(
if len(bups) > 0 {
Info(ctx, "Completed Backups:")
}
backup.PrintAll(ctx, bups)
}
if len(errs) > 0 {
sb := fmt.Sprintf("%d of %d backups failed:\n", len(errs), len(selectorSet))

View File

@ -73,7 +73,7 @@ func preRun(cc *cobra.Command, args []string) error {
func handleMailBoxFlag(ctx context.Context, c *cobra.Command, flagNames []string) {
if !slices.Contains(flagNames, "user") && !slices.Contains(flagNames, "mailbox") {
print.Errf(ctx, "either --user or --mailbox flag is required")
print.Err(ctx, "either --user or --mailbox flag is required")
os.Exit(1)
}

View File

@ -107,7 +107,7 @@ func runExport(
// It would be better to give a progressbar than a spinner, but we
// have any way of knowing how many files are available as of now.
diskWriteComplete := observe.MessageWithCompletion(ctx, "Writing data to disk")
diskWriteComplete := observe.MessageWithCompletion(ctx, observe.ProgressCfg{}, "Writing data to disk")
err = export.ConsumeExportCollections(ctx, exportLocation, expColl, eo.Errors)

View File

@ -10,6 +10,7 @@ import (
"github.com/tidwall/pretty"
"github.com/tomlazar/table"
"github.com/alcionai/corso/src/internal/common/color"
"github.com/alcionai/corso/src/internal/observe"
)
@ -83,16 +84,21 @@ func Only(ctx context.Context, e error) error {
// Err prints the params to cobra's error writer (stdErr by default)
// if s is nil, prints nothing.
// Prepends the message with "Error: "
func Err(ctx context.Context, s ...any) {
out(ctx, getRootCmd(ctx).ErrOrStderr(), s...)
cw := color.NewColorableWriter(color.Red, getRootCmd(ctx).ErrOrStderr())
s = append([]any{"Error:"}, s...)
out(ctx, cw, s...)
}
// Errf prints the params to cobra's error writer (stdErr by default)
// if s is nil, prints nothing.
// Prepends the message with "Error: "
// You should ideally be using SimpleError or OperationError.
func Errf(ctx context.Context, tmpl string, s ...any) {
outf(ctx, getRootCmd(ctx).ErrOrStderr(), "\nError: \n\t"+tmpl+"\n", s...)
cw := color.NewColorableWriter(color.Red, getRootCmd(ctx).ErrOrStderr())
tmpl = "Error: " + tmpl
outf(ctx, cw, tmpl, s...)
}
// Out prints the params to cobra's output writer (stdOut by default)

View File

@ -10,6 +10,7 @@ require (
github.com/armon/go-metrics v0.4.1
github.com/aws/aws-xray-sdk-go v1.8.3
github.com/cenkalti/backoff/v4 v4.2.1
github.com/fatih/color v1.15.0
github.com/golang-jwt/jwt/v5 v5.1.0
github.com/google/uuid v1.4.0
github.com/h2non/gock v1.2.0
@ -51,7 +52,6 @@ require (
github.com/aws/aws-sdk-go v1.47.9 // indirect
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/go-test/deep v1.1.0 // indirect
github.com/gofrs/flock v0.8.1 // indirect
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f // indirect
github.com/google/go-cmp v0.5.9 // indirect

View File

@ -119,6 +119,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=

View File

@ -0,0 +1,36 @@
package color
import (
"io"
"github.com/fatih/color"
)
var (
Red = color.FgRed
Blue = color.FgBlue
Magenta = color.FgMagenta
Cyan = color.FgCyan
Green = color.FgGreen
White = color.FgWhite
RedOutput = color.New(Red).SprintFunc()
BlueOutput = color.New(Blue).SprintFunc()
MagentaOutput = color.New(Magenta).SprintFunc()
CyanOutput = color.New(Cyan).SprintFunc()
GreenOutput = color.New(Green).SprintFunc()
GreyOutput = color.New(White).SprintFunc()
)
type colorableWriter struct {
color color.Attribute
writer io.Writer
}
func NewColorableWriter(clr color.Attribute, writer io.Writer) io.Writer {
return &colorableWriter{clr, writer}
}
func (cw *colorableWriter) Write(p []byte) (n int, err error) {
return color.New(cw.color).Fprint(cw.writer, string(p))
}

View File

@ -651,6 +651,7 @@ func (w Wrapper) RepoMaintenance(
if len(params.Owner) == 0 || (params.Owner != currentOwner && opts.Force) {
observe.Message(
ctx,
observe.ProgressCfg{},
"updating maintenance user@host to ",
clues.Hide(currentOwner))

View File

@ -3,7 +3,6 @@ package drive
import (
"context"
"encoding/json"
"fmt"
"io"
"strings"
@ -18,7 +17,6 @@ import (
"github.com/alcionai/corso/src/internal/m365/collection/drive/metadata"
odConsts "github.com/alcionai/corso/src/internal/m365/service/onedrive/consts"
"github.com/alcionai/corso/src/internal/m365/support"
"github.com/alcionai/corso/src/internal/observe"
bupMD "github.com/alcionai/corso/src/pkg/backup/metadata"
"github.com/alcionai/corso/src/pkg/control"
"github.com/alcionai/corso/src/pkg/fault"
@ -283,11 +281,6 @@ func (c *Collections) Get(
driveTombstones[driveID] = struct{}{}
}
progressBar := observe.MessageWithCompletion(
ctx,
observe.Bulletf(path.FilesCategory.HumanString()))
defer close(progressBar)
// Enumerate drives for the specified resourceOwner
pager := c.handler.NewDrivePager(c.protectedResource.ID(), nil)
@ -456,8 +449,6 @@ func (c *Collections) Get(
}
}
observe.Message(ctx, fmt.Sprintf("Discovered %d items to backup", c.NumItems))
collections := []data.BackupCollection{}
// add all the drives we found

View File

@ -2,6 +2,7 @@ package exchange
import (
"context"
"fmt"
"github.com/alcionai/clues"
@ -42,6 +43,8 @@ func CreateCollections(
ProtectedResource: bpc.ProtectedResource,
TenantID: tenantID,
}
collections map[string]data.BackupCollection
err error
)
handler, ok := handlers[category]
@ -49,9 +52,15 @@ func CreateCollections(
return nil, clues.NewWC(ctx, "unsupported backup category type")
}
pcfg := observe.ProgressCfg{
Indent: 1,
CompletionMessage: func() string { return fmt.Sprintf("(found %d folders)", len(collections)) },
}
foldersComplete := observe.MessageWithCompletion(
ctx,
observe.Bulletf("%s", qp.Category.HumanString()))
pcfg,
qp.Category.HumanString())
defer close(foldersComplete)
rootFolder, cc := handler.NewContainerCache(bpc.ProtectedResource.ID())
@ -60,7 +69,7 @@ func CreateCollections(
return nil, clues.Wrap(err, "populating container cache")
}
collections, err := populateCollections(
collections, err = populateCollections(
ctx,
qp,
handler,

View File

@ -2,6 +2,8 @@ package site
import (
"context"
"fmt"
stdpath "path"
"github.com/alcionai/clues"
@ -10,6 +12,7 @@ import (
"github.com/alcionai/corso/src/internal/m365/collection/drive"
betaAPI "github.com/alcionai/corso/src/internal/m365/service/sharepoint/api"
"github.com/alcionai/corso/src/internal/m365/support"
"github.com/alcionai/corso/src/internal/observe"
"github.com/alcionai/corso/src/internal/operations/inject"
"github.com/alcionai/corso/src/pkg/account"
"github.com/alcionai/corso/src/pkg/count"
@ -44,6 +47,18 @@ func CollectLibraries(
bpc.Options)
)
msg := fmt.Sprintf(
"%s (%s)",
path.LibrariesCategory.HumanString(),
stdpath.Base(bpc.ProtectedResource.Name()))
pcfg := observe.ProgressCfg{
Indent: 1,
CompletionMessage: func() string { return fmt.Sprintf("(found %d items)", colls.NumItems) },
}
progressBar := observe.MessageWithCompletion(ctx, pcfg, msg)
close(progressBar)
odcs, canUsePreviousBackup, err := colls.Get(ctx, bpc.MetadataCollections, ssmb, errs)
if err != nil {
return nil, false, graph.Wrap(ctx, err, "getting library")

View File

@ -2,6 +2,7 @@ package groups
import (
"context"
"fmt"
"github.com/alcionai/clues"
"github.com/kopia/kopia/repo/manifest"
@ -70,11 +71,6 @@ func ProduceBackupCollections(
break
}
progressBar := observe.MessageWithCompletion(
ctx,
observe.Bulletf("%s", scope.Category().PathType().HumanString()))
defer close(progressBar)
var dbcs []data.BackupCollection
switch scope.Category().PathType() {
@ -134,15 +130,27 @@ func ProduceBackupCollections(
dbcs = append(dbcs, cs...)
}
case path.ChannelMessagesCategory:
var (
cs []data.BackupCollection
canUsePreviousBackup bool
err error
)
pcfg := observe.ProgressCfg{
Indent: 1,
// TODO(meain): Use number of messages and not channels
CompletionMessage: func() string { return fmt.Sprintf("(found %d channels)", len(cs)) },
}
progressBar := observe.MessageWithCompletion(ctx, pcfg, scope.Category().PathType().HumanString())
if !isTeam {
continue
}
bh := groups.NewChannelBackupHandler(bpc.ProtectedResource.ID(), ac.Channels())
cs, canUsePreviousBackup, err := groups.CreateCollections(
cs, canUsePreviousBackup, err = groups.CreateCollections(
ctx,
bpc,
bh,
@ -165,6 +173,8 @@ func ProduceBackupCollections(
}
dbcs = append(dbcs, cs...)
close(progressBar)
}
collections = append(collections, dbcs...)

View File

@ -2,6 +2,7 @@ package onedrive
import (
"context"
"fmt"
"github.com/alcionai/clues"
@ -9,6 +10,7 @@ import (
"github.com/alcionai/corso/src/internal/data"
"github.com/alcionai/corso/src/internal/m365/collection/drive"
"github.com/alcionai/corso/src/internal/m365/support"
"github.com/alcionai/corso/src/internal/observe"
"github.com/alcionai/corso/src/internal/operations/inject"
"github.com/alcionai/corso/src/internal/version"
"github.com/alcionai/corso/src/pkg/fault"
@ -55,6 +57,14 @@ func ProduceBackupCollections(
su,
bpc.Options)
pcfg := observe.ProgressCfg{
Indent: 1,
CompletionMessage: func() string { return fmt.Sprintf("(found %d files)", nc.NumFiles) },
}
progressBar := observe.MessageWithCompletion(ctx, pcfg, path.FilesCategory.HumanString())
defer close(progressBar)
odcs, canUsePreviousBackup, err = nc.Get(ctx, bpc.MetadataCollections, ssmb, errs)
if err != nil {
el.AddRecoverable(ctx, clues.Stack(err).Label(fault.LabelForceNoBackupCreation))

View File

@ -10,7 +10,6 @@ import (
"github.com/alcionai/corso/src/internal/m365/collection/drive"
"github.com/alcionai/corso/src/internal/m365/collection/site"
"github.com/alcionai/corso/src/internal/m365/support"
"github.com/alcionai/corso/src/internal/observe"
"github.com/alcionai/corso/src/internal/operations/inject"
"github.com/alcionai/corso/src/pkg/account"
"github.com/alcionai/corso/src/pkg/count"
@ -52,11 +51,6 @@ func ProduceBackupCollections(
break
}
progressBar := observe.MessageWithCompletion(
ctx,
observe.Bulletf("%s", scope.Category().PathType().HumanString()))
defer close(progressBar)
var spcs []data.BackupCollection
switch scope.Category().PathType() {

View File

@ -15,13 +15,14 @@ import (
"github.com/vbauerster/mpb/v8"
"github.com/vbauerster/mpb/v8/decor"
"github.com/alcionai/corso/src/internal/common/color"
"github.com/alcionai/corso/src/pkg/logger"
)
const (
hideProgressBarsFN = "hide-progress"
retainProgressBarsFN = "retain-progress"
progressBarWidth = 32
progressBarWidth = 40
)
func init() {
@ -170,8 +171,15 @@ const (
// Progress Updates
// ---------------------------------------------------------------------------
type ProgressCfg struct {
NewSection bool
SectionIdentifier any
Indent int
CompletionMessage func() string
}
// Message is used to display a progress message
func Message(ctx context.Context, msgs ...any) {
func Message(ctx context.Context, cfg ProgressCfg, msgs ...any) {
var (
obs = getObserver(ctx)
plainSl = make([]string, 0, len(msgs))
@ -186,18 +194,37 @@ func Message(ctx context.Context, msgs ...any) {
plain := strings.Join(plainSl, " ")
loggable := strings.Join(loggableSl, " ")
if cfg.SectionIdentifier != nil {
plain = fmt.Sprintf(
"%s %s %s",
plain,
Bullet,
color.MagentaOutput(plainString(cfg.SectionIdentifier)))
loggable = fmt.Sprintf("%s - %v", loggable, cfg.SectionIdentifier)
}
logger.Ctx(ctx).Info(loggable)
if obs.hidden() {
return
}
if cfg.NewSection {
// Empty bar to separate out section
obs.wg.Add(1)
empty := obs.mp.New(-1, mpb.NopStyle())
empty.SetTotal(-1, true)
waitAndCloseBar(ctx, empty, obs.wg, func() {})()
}
obs.wg.Add(1)
bar := obs.mp.New(
-1,
mpb.NopStyle(),
mpb.PrependDecorators(decor.Name(
mpb.PrependDecorators(
decor.Name("", decor.WC{W: cfg.Indent * 2}),
decor.Name(
plain,
decor.WC{
W: len(plain) + 1,
@ -213,16 +240,38 @@ func Message(ctx context.Context, msgs ...any) {
// that switches to "done" when the completion channel is signalled
func MessageWithCompletion(
ctx context.Context,
msg any,
cfg ProgressCfg,
msgs ...any,
) chan<- struct{} {
var (
obs = getObserver(ctx)
plain = plainString(msg)
loggable = fmt.Sprintf("%v", msg)
log = logger.Ctx(ctx)
plainSl = make([]string, 0, len(msgs))
loggableSl = make([]string, 0, len(msgs))
ch = make(chan struct{}, 1)
)
for _, m := range msgs {
plainSl = append(plainSl, plainString(m))
loggableSl = append(loggableSl, fmt.Sprintf("%v", m))
}
plain := strings.Join(plainSl, " ")
loggable := strings.Join(loggableSl, " ")
if cfg.SectionIdentifier != nil {
plain = fmt.Sprintf(
"%s %s %s",
plain,
Bullet,
color.MagentaOutput(plainString(cfg.SectionIdentifier)))
loggable = fmt.Sprintf("%s - %v", loggable, cfg.SectionIdentifier)
}
if cfg.Indent > 0 {
plain = color.CyanOutput(plain)
}
log.Info(loggable)
if obs.hidden() {
@ -230,17 +279,45 @@ func MessageWithCompletion(
return ch
}
if cfg.NewSection {
// Empty bar to separate out section
obs.wg.Add(1)
empty := obs.mp.New(-1, mpb.NopStyle())
empty.SetTotal(-1, true)
waitAndCloseBar(ctx, empty, obs.wg, func() {})()
}
obs.wg.Add(1)
frames := []string{"∙∙∙", "●∙∙", "∙●∙", "∙∙●", "∙∙∙"}
// https://github.com/vbauerster/mpb/issues/71
bfoc := mpb.BarFillerOnComplete(color.GreenOutput("done"))
if cfg.CompletionMessage != nil {
bfoc = mpb.BarFillerMiddleware(func(base mpb.BarFiller) mpb.BarFiller {
filler := func(w io.Writer, st decor.Statistics) error {
if st.Completed {
msg := fmt.Sprintf("%s %s", color.GreenOutput("done"), color.GreyOutput(cfg.CompletionMessage()))
_, err := io.WriteString(w, msg)
return err
}
return base.Fill(w, st)
}
return mpb.BarFillerFunc(filler)
})
}
bar := obs.mp.New(
-1,
mpb.SpinnerStyle(frames...).PositionLeft(),
mpb.PrependDecorators(
decor.Name(plain+":"),
decor.Name("", decor.WC{W: cfg.Indent * 2}),
decor.Name(plain, decor.WC{W: progressBarWidth - cfg.Indent*2, C: decor.DidentRight}),
decor.Elapsed(decor.ET_STYLE_GO, decor.WC{W: 8})),
mpb.BarFillerOnComplete("done"))
bfoc)
go listen(
ctx,
@ -520,7 +597,9 @@ func CollectionProgress(
obs.wg.Add(1)
barOpts := []mpb.BarOption{
mpb.PrependDecorators(decor.Name(string(category))),
mpb.PrependDecorators(
decor.Name("", decor.WC{W: 2}),
decor.Name(color.CyanOutput(string(category)))),
mpb.AppendDecorators(
decor.CurrentNoUnit("%d - ", decor.WCSyncSpace),
decor.Name(plain)),

View File

@ -127,6 +127,30 @@ func (suite *ObserveProgressUnitSuite) TestCollectionProgress_unblockOnChannelCl
}()
}
func (suite *ObserveProgressUnitSuite) TestObserve_section() {
t := suite.T()
ctx, flush := tester.NewContext(t)
defer flush()
recorder := strings.Builder{}
ctx = SeedObserver(ctx, &recorder, config{})
process := uuid.NewString()[:8]
target := uuid.NewString()[:8]
pcfg := ProgressCfg{
NewSection: true,
SectionIdentifier: target,
}
Message(ctx, pcfg, process)
Flush(ctx)
assert.NotEmpty(t, recorder)
assert.Contains(t, recorder.String(), process)
assert.Contains(t, recorder.String(), target)
}
func (suite *ObserveProgressUnitSuite) TestObserve_message() {
t := suite.T()
@ -138,7 +162,7 @@ func (suite *ObserveProgressUnitSuite) TestObserve_message() {
message := uuid.NewString()[:8]
Message(ctx, message)
Message(ctx, ProgressCfg{}, message)
Flush(ctx)
assert.NotEmpty(t, recorder)
assert.Contains(t, recorder.String(), message)
@ -155,7 +179,7 @@ func (suite *ObserveProgressUnitSuite) TestObserve_progressWithChannelClosed() {
message := uuid.NewString()[:8]
ch := MessageWithCompletion(ctx, message)
ch := MessageWithCompletion(ctx, ProgressCfg{}, message)
// Close channel without completing
close(ch)
@ -180,7 +204,7 @@ func (suite *ObserveProgressUnitSuite) TestObserve_progressWithContextCancelled(
message := uuid.NewString()[:8]
_ = MessageWithCompletion(ctx, message)
_ = MessageWithCompletion(ctx, ProgressCfg{}, message)
// cancel context
cancel()

View File

@ -279,7 +279,11 @@ func (op *BackupOperation) Run(ctx context.Context) (err error) {
// Execution
// -----
observe.Message(ctx, "Backing Up", observe.Bullet, clues.Hide(op.ResourceOwner.Name()))
pcfg := observe.ProgressCfg{
NewSection: true,
SectionIdentifier: clues.Hide(op.ResourceOwner.Name()),
}
observe.Message(ctx, pcfg, "Backing Up")
deets, err := op.do(
ctx,
@ -528,7 +532,7 @@ func produceBackupDataCollections(
counter *count.Bus,
errs *fault.Bus,
) ([]data.BackupCollection, prefixmatcher.StringSetReader, bool, error) {
progressBar := observe.MessageWithCompletion(ctx, "Discovering items to backup")
progressBar := observe.MessageWithCompletion(ctx, observe.ProgressCfg{}, "Discovering items to backup")
defer close(progressBar)
bpc := inject.BackupProducerConfig{
@ -565,7 +569,7 @@ func consumeBackupCollections(
"collection_source", "operations",
"snapshot_type", "item data")
progressBar := observe.MessageWithCompletion(ctx, "Backing up data")
progressBar := observe.MessageWithCompletion(ctx, observe.ProgressCfg{}, "Backing up data")
defer close(progressBar)
tags := map[string]string{

View File

@ -223,7 +223,11 @@ func (op *ExportOperation) do(
return nil, clues.Wrap(err, "getting backup and details")
}
observe.Message(ctx, "Exporting", observe.Bullet, clues.Hide(bup.Selector.DiscreteOwner))
pcfg := observe.ProgressCfg{
NewSection: true,
SectionIdentifier: clues.Hide(bup.Selector.DiscreteOwner),
}
observe.Message(ctx, pcfg, "Exporting")
paths, err := formatDetailsForRestoration(ctx, bup.Version, op.Selectors, deets, op.ec, op.Errors)
if err != nil {
@ -239,9 +243,12 @@ func (op *ExportOperation) do(
"backup_snapshot_id", bup.SnapshotID,
"backup_version", bup.Version)
observe.Message(ctx, fmt.Sprintf("Discovered %d items in backup %s to export", len(paths), op.BackupID))
observe.Message(
ctx,
observe.ProgressCfg{},
fmt.Sprintf("Discovered %d items in backup %s to export", len(paths), op.BackupID))
kopiaComplete := observe.MessageWithCompletion(ctx, "Enumerating items in repository")
kopiaComplete := observe.MessageWithCompletion(ctx, observe.ProgressCfg{}, "Enumerating items in repository")
defer close(kopiaComplete)
dcs, err := op.kopia.ProduceRestoreCollections(ctx, bup.SnapshotID, paths, opStats.bytesRead, op.Errors)
@ -332,7 +339,7 @@ func produceExportCollections(
exportStats *data.ExportStats,
errs *fault.Bus,
) ([]export.Collectioner, error) {
complete := observe.MessageWithCompletion(ctx, "Preparing export")
complete := observe.MessageWithCompletion(ctx, observe.ProgressCfg{}, "Preparing export")
defer func() {
complete <- struct{}{}
close(complete)

View File

@ -250,7 +250,11 @@ func (op *RestoreOperation) do(
return nil, clues.WrapWC(ctx, graph.ErrServiceNotEnabled, "service not enabled for restore")
}
observe.Message(ctx, "Restoring", observe.Bullet, clues.Hide(restoreToProtectedResource.Name()))
pcfg := observe.ProgressCfg{
NewSection: true,
SectionIdentifier: clues.Hide(restoreToProtectedResource.Name()),
}
observe.Message(ctx, pcfg, "Restoring")
paths, err := formatDetailsForRestoration(
ctx,
@ -270,9 +274,16 @@ func (op *RestoreOperation) do(
"backup_snapshot_id", bup.SnapshotID,
"backup_version", bup.Version)
observe.Message(ctx, fmt.Sprintf("Discovered %d items in backup %s to restore", len(paths), op.BackupID))
if len(paths) == 0 {
return nil, clues.New("no items match the provided filters")
}
progressBar := observe.MessageWithCompletion(ctx, "Enumerating items in repository")
observe.Message(
ctx,
observe.ProgressCfg{},
fmt.Sprintf("Discovered %d items in backup %s to restore", len(paths), op.BackupID))
progressBar := observe.MessageWithCompletion(ctx, observe.ProgressCfg{}, "Enumerating items in repository")
defer close(progressBar)
dcs, err := op.kopia.ProduceRestoreCollections(
@ -380,7 +391,7 @@ func consumeRestoreCollections(
errs *fault.Bus,
ctr *count.Bus,
) (*details.Details, error) {
progressBar := observe.MessageWithCompletion(ctx, "Restoring data")
progressBar := observe.MessageWithCompletion(ctx, observe.ProgressCfg{}, "Restoring data")
defer close(progressBar)
rcc := inject.RestoreConsumerConfig{

View File

@ -89,7 +89,7 @@ func connectToM365(
return ctrl, nil
}
progressBar := observe.MessageWithCompletion(ctx, "Connecting to M365")
progressBar := observe.MessageWithCompletion(ctx, observe.ProgressCfg{}, "Connecting to M365")
defer close(progressBar)
ctrl, err := m365.NewController(

View File

@ -153,7 +153,7 @@ func (r *repository) Initialize(ctx context.Context, cfg InitConfig) (err error)
return clues.Stack(err)
}
observe.Message(ctx, "Initializing repository")
observe.Message(ctx, observe.ProgressCfg{}, "Initializing repository")
if err := r.setupKopia(ctx, cfg.RetentionOpts, true); err != nil {
return err
@ -187,7 +187,8 @@ func (r *repository) Connect(ctx context.Context, cfg ConnConfig) (err error) {
return clues.Stack(err)
}
observe.Message(ctx, "Connecting to repository")
progressBar := observe.MessageWithCompletion(ctx, observe.ProgressCfg{}, "Connecting to repository")
defer close(progressBar)
if err := r.setupKopia(ctx, ctrlRepo.Retention{}, false); err != nil {
return clues.Stack(err)
@ -209,7 +210,7 @@ func (r *repository) UpdatePassword(ctx context.Context, password string) (err e
}
}()
progressBar := observe.MessageWithCompletion(ctx, "Connecting to repository")
progressBar := observe.MessageWithCompletion(ctx, observe.ProgressCfg{}, "Connecting to repository")
defer close(progressBar)
repoNameHash, err := r.GenerateHashForRepositoryConfigFileName()