vendor github.com/containers/image@v2.0.1
* progress bar: use spinners for unknown blob sizes * use 'containers_image_ostree' as build tag * ostree: default is no OStree support * Add "Env" to ImageInspectInfo * config.go: improve debug message * config.go: log where credentials come from * Fix typo in docs/containers-registries.conf.5.md * docker: delete: support all MIME types * Try harder in storageImageDestination.TryReusingBlob * docker: allow deleting OCI images * ostree: improve error message Signed-off-by: Valentin Rothberg <rothberg@redhat.com> Closes: #1730 Approved by: TomSweeneyRedHat
This commit is contained in:
		
							parent
							
								
									4ccb343cef
								
							
						
					
					
						commit
						5bab9b0651
					
				
							
								
								
									
										4
									
								
								go.mod
								
								
								
								
							
							
						
						
									
										4
									
								
								go.mod
								
								
								
								
							|  | @ -7,7 +7,7 @@ require ( | |||
| 	github.com/blang/semver v3.5.0+incompatible // indirect | ||||
| 	github.com/containerd/continuity v0.0.0-20181203112020-004b46473808 // indirect | ||||
| 	github.com/containernetworking/cni v0.7.0-rc2 | ||||
| 	github.com/containers/image v2.0.0+incompatible | ||||
| 	github.com/containers/image v2.0.1+incompatible | ||||
| 	github.com/containers/storage v1.12.15 | ||||
| 	github.com/cyphar/filepath-securejoin v0.2.1 | ||||
| 	github.com/docker/distribution v0.0.0-20170817175659-5f6282db7d65 | ||||
|  | @ -44,7 +44,7 @@ require ( | |||
| 	github.com/spf13/pflag v1.0.3 | ||||
| 	github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 | ||||
| 	github.com/ulikunitz/xz v0.5.5 // indirect | ||||
| 	github.com/vbauerster/mpb v3.3.4+incompatible // indirect | ||||
| 	github.com/vbauerster/mpb v3.4.0+incompatible // indirect | ||||
| 	github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect | ||||
| 	github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect | ||||
| 	github.com/xeipuuv/gojsonschema v1.1.0 // indirect | ||||
|  |  | |||
							
								
								
									
										4
									
								
								go.sum
								
								
								
								
							
							
						
						
									
										4
									
								
								go.sum
								
								
								
								
							|  | @ -27,6 +27,8 @@ github.com/containernetworking/cni v0.7.0-rc2 h1:2GGDhbwdWPY53iT7LXy+LBP76Ch2D/h | |||
| github.com/containernetworking/cni v0.7.0-rc2/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= | ||||
| github.com/containers/image v2.0.0+incompatible h1:FTr6Br7jlIKNCKMjSOMbAxKp2keQ0//jzJaYNTVhauk= | ||||
| github.com/containers/image v2.0.0+incompatible/go.mod h1:8Vtij257IWSanUQKe1tAeNOm2sRVkSqQTVQ1IlwI3+M= | ||||
| github.com/containers/image v2.0.1+incompatible h1:w39mlElA/aSFZ6moFa5N+A4MWu9c8hgdMiMMYnH94Hs= | ||||
| github.com/containers/image v2.0.1+incompatible/go.mod h1:8Vtij257IWSanUQKe1tAeNOm2sRVkSqQTVQ1IlwI3+M= | ||||
| github.com/containers/storage v1.12.10-0.20190725063046-8038df61d6f6 h1:c7Fq9bbRl0Ua6swRHAH8rkrK2fSt6K+ZBrXHD50kDR4= | ||||
| github.com/containers/storage v1.12.10-0.20190725063046-8038df61d6f6/go.mod h1:QsZp4XMJjyPNNbQHZeyNW3OmhwsWviI+7S6iOcu6a4c= | ||||
| github.com/containers/storage v1.12.13 h1:GtaLCY8p1Drlk1Oew581jGvB137UaO+kpz0HII67T0A= | ||||
|  | @ -169,6 +171,8 @@ github.com/vbatts/tar-split v0.11.1 h1:0Odu65rhcZ3JZaPHxl7tCI3V/C/Q9Zf82UFravl02 | |||
| github.com/vbatts/tar-split v0.11.1/go.mod h1:LEuURwDEiWjRjwu46yU3KVGuUdVv/dcnpcEPSzR8z6g= | ||||
| github.com/vbauerster/mpb v3.3.4+incompatible h1:DDIhnwmgTQIDZo+SWlEr5d6mJBxkOLBwCXPzunhEfJ4= | ||||
| github.com/vbauerster/mpb v3.3.4+incompatible/go.mod h1:zAHG26FUhVKETRu+MWqYXcI70POlC6N8up9p1dID7SU= | ||||
| github.com/vbauerster/mpb v3.4.0+incompatible h1:mfiiYw87ARaeRW6x5gWwYRUawxaW1tLAD8IceomUCNw= | ||||
| github.com/vbauerster/mpb v3.4.0+incompatible/go.mod h1:zAHG26FUhVKETRu+MWqYXcI70POlC6N8up9p1dID7SU= | ||||
| github.com/vishvananda/netlink v1.0.0/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= | ||||
| github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= | ||||
| github.com/vrothberg/storage v0.0.0-20190724065215-a1e42fd78930 h1:/LeIxi2kj5UYTJR9W35t5Pq2gqz03ZNoTURchTH3vc0= | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| #!/bin/bash | ||||
| if pkg-config ostree-1 2> /dev/null ; then | ||||
| 	echo constainers_image_ostree | ||||
| 	echo containers_image_ostree | ||||
| else | ||||
| 	echo containers_image_ostree_stub | ||||
| fi | ||||
|  |  | |||
|  | @ -597,7 +597,11 @@ func (c *copier) createProgressBar(pool *mpb.Progress, info types.BlobInfo, kind | |||
| 		prefix = prefix[:maxPrefixLen] | ||||
| 	} | ||||
| 
 | ||||
| 	bar := pool.AddBar(info.Size, | ||||
| 	// Use a normal progress bar when we know the size (i.e., size > 0).
 | ||||
| 	// Otherwise, use a spinner to indicate that something's happening.
 | ||||
| 	var bar *mpb.Bar | ||||
| 	if info.Size > 0 { | ||||
| 		bar = pool.AddBar(info.Size, | ||||
| 			mpb.BarClearOnComplete(), | ||||
| 			mpb.PrependDecorators( | ||||
| 				decor.Name(prefix), | ||||
|  | @ -606,6 +610,19 @@ func (c *copier) createProgressBar(pool *mpb.Progress, info types.BlobInfo, kind | |||
| 				decor.OnComplete(decor.CountersKibiByte("%.1f / %.1f"), " "+onComplete), | ||||
| 			), | ||||
| 		) | ||||
| 	} else { | ||||
| 		bar = pool.AddSpinner(info.Size, | ||||
| 			mpb.SpinnerOnLeft, | ||||
| 			mpb.BarClearOnComplete(), | ||||
| 			mpb.SpinnerStyle([]string{".", "..", "...", "....", ""}), | ||||
| 			mpb.PrependDecorators( | ||||
| 				decor.Name(prefix), | ||||
| 			), | ||||
| 			mpb.AppendDecorators( | ||||
| 				decor.OnComplete(decor.Name(""), " "+onComplete), | ||||
| 			), | ||||
| 		) | ||||
| 	} | ||||
| 	if c.progressOutput == ioutil.Discard { | ||||
| 		c.Printf("Copying %s %s\n", kind, info.Digest) | ||||
| 	} | ||||
|  |  | |||
|  | @ -138,8 +138,9 @@ func (s *dockerImageSource) GetManifest(ctx context.Context, instanceDigest *dig | |||
| 
 | ||||
| func (s *dockerImageSource) fetchManifest(ctx context.Context, tagOrDigest string) ([]byte, string, error) { | ||||
| 	path := fmt.Sprintf(manifestPath, reference.Path(s.ref.ref), tagOrDigest) | ||||
| 	headers := make(map[string][]string) | ||||
| 	headers["Accept"] = manifest.DefaultRequestedManifestMIMETypes | ||||
| 	headers := map[string][]string{ | ||||
| 		"Accept": manifest.DefaultRequestedManifestMIMETypes, | ||||
| 	} | ||||
| 	res, err := s.c.makeRequest(ctx, "GET", path, headers, nil, v2Auth, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, "", err | ||||
|  | @ -381,11 +382,9 @@ func deleteImage(ctx context.Context, sys *types.SystemContext, ref dockerRefere | |||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// When retrieving the digest from a registry >= 2.3 use the following header:
 | ||||
| 	//   "Accept": "application/vnd.docker.distribution.manifest.v2+json"
 | ||||
| 	headers := make(map[string][]string) | ||||
| 	headers["Accept"] = []string{manifest.DockerV2Schema2MediaType} | ||||
| 
 | ||||
| 	headers := map[string][]string{ | ||||
| 		"Accept": manifest.DefaultRequestedManifestMIMETypes, | ||||
| 	} | ||||
| 	refTail, err := ref.tagOrDigest() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
|  |  | |||
|  | @ -15,7 +15,7 @@ import ( | |||
| 	"github.com/containers/image/manifest" | ||||
| 	"github.com/containers/image/pkg/compression" | ||||
| 	"github.com/containers/image/types" | ||||
| 	"github.com/opencontainers/go-digest" | ||||
| 	digest "github.com/opencontainers/go-digest" | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
| 
 | ||||
|  | @ -23,9 +23,7 @@ import ( | |||
| type Source struct { | ||||
| 	tarPath              string | ||||
| 	removeTarPathOnClose bool // Remove temp file on close if true
 | ||||
| 	cacheDataLock        sync.Once // Atomic way to ensure that ensureCachedDataIsPresent is only invoked once
 | ||||
| 	// The following data is only available after ensureCachedDataIsPresent() succeeds
 | ||||
| 	cacheDataResult   error         // The return value of ensureCachedDataIsPresent, since it should be as safe to cache as the side effects
 | ||||
| 	tarManifest       *ManifestItem // nil if not available yet.
 | ||||
| 	configBytes       []byte | ||||
| 	configDigest      digest.Digest | ||||
|  | @ -33,6 +31,8 @@ type Source struct { | |||
| 	knownLayers       map[digest.Digest]*layerInfo | ||||
| 	// Other state
 | ||||
| 	generatedManifest []byte    // Private cache for GetManifest(), nil if not set yet.
 | ||||
| 	cacheDataLock     sync.Once // Private state for ensureCachedDataIsPresent to make it concurrency-safe
 | ||||
| 	cacheDataResult   error     // Private state for ensureCachedDataIsPresent
 | ||||
| } | ||||
| 
 | ||||
| type layerInfo struct { | ||||
|  | @ -201,37 +201,41 @@ func (s *Source) readTarComponent(path string) ([]byte, error) { | |||
| } | ||||
| 
 | ||||
| // ensureCachedDataIsPresent loads data necessary for any of the public accessors.
 | ||||
| // It is safe to call this from multi-threaded code.
 | ||||
| func (s *Source) ensureCachedDataIsPresent() error { | ||||
| 	s.cacheDataLock.Do(func() { | ||||
| 		s.cacheDataResult = s.ensureCachedDataIsPresentPrivate() | ||||
| 	}) | ||||
| 	return s.cacheDataResult | ||||
| } | ||||
| 
 | ||||
| // ensureCachedDataIsPresentPrivate is a private implementation detail of ensureCachedDataIsPresent.
 | ||||
| // Call ensureCachedDataIsPresent instead.
 | ||||
| func (s *Source) ensureCachedDataIsPresentPrivate() error { | ||||
| 	// Read and parse manifest.json
 | ||||
| 	tarManifest, err := s.loadTarManifest() | ||||
| 	if err != nil { | ||||
| 			s.cacheDataResult = err | ||||
| 			return | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// Check to make sure length is 1
 | ||||
| 	if len(tarManifest) != 1 { | ||||
| 			s.cacheDataResult = errors.Errorf("Unexpected tar manifest.json: expected 1 item, got %d", len(tarManifest)) | ||||
| 			return | ||||
| 		return errors.Errorf("Unexpected tar manifest.json: expected 1 item, got %d", len(tarManifest)) | ||||
| 	} | ||||
| 
 | ||||
| 	// Read and parse config.
 | ||||
| 	configBytes, err := s.readTarComponent(tarManifest[0].Config) | ||||
| 	if err != nil { | ||||
| 			s.cacheDataResult = err | ||||
| 			return | ||||
| 		return err | ||||
| 	} | ||||
| 	var parsedConfig manifest.Schema2Image // There's a lot of info there, but we only really care about layer DiffIDs.
 | ||||
| 	if err := json.Unmarshal(configBytes, &parsedConfig); err != nil { | ||||
| 			s.cacheDataResult = errors.Wrapf(err, "Error decoding tar config %s", tarManifest[0].Config) | ||||
| 			return | ||||
| 		return errors.Wrapf(err, "Error decoding tar config %s", tarManifest[0].Config) | ||||
| 	} | ||||
| 
 | ||||
| 	knownLayers, err := s.prepareLayerData(&tarManifest[0], &parsedConfig) | ||||
| 	if err != nil { | ||||
| 			s.cacheDataResult = err | ||||
| 			return | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// Success; commit.
 | ||||
|  | @ -240,8 +244,7 @@ func (s *Source) ensureCachedDataIsPresent() error { | |||
| 	s.configDigest = digest.FromBytes(configBytes) | ||||
| 	s.orderedDiffIDList = parsedConfig.RootFS.DiffIDs | ||||
| 	s.knownLayers = knownLayers | ||||
| 	}) | ||||
| 	return s.cacheDataResult | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // loadTarManifest loads and decodes the manifest.json.
 | ||||
|  |  | |||
|  | @ -226,6 +226,7 @@ func (m *Schema1) Inspect(_ func(types.BlobInfo) ([]byte, error)) (*types.ImageI | |||
| 	} | ||||
| 	if s1.Config != nil { | ||||
| 		i.Labels = s1.Config.Labels | ||||
| 		i.Env = s1.Config.Env | ||||
| 	} | ||||
| 	return i, nil | ||||
| } | ||||
|  |  | |||
|  | @ -241,6 +241,7 @@ func (m *Schema2) Inspect(configGetter func(types.BlobInfo) ([]byte, error)) (*t | |||
| 	} | ||||
| 	if s2.Config != nil { | ||||
| 		i.Labels = s2.Config.Labels | ||||
| 		i.Env = s2.Config.Env | ||||
| 	} | ||||
| 	return i, nil | ||||
| } | ||||
|  |  | |||
|  | @ -116,6 +116,7 @@ func (m *OCI1) Inspect(configGetter func(types.BlobInfo) ([]byte, error)) (*type | |||
| 		Architecture:  v1.Architecture, | ||||
| 		Os:            v1.OS, | ||||
| 		Layers:        layerInfosToStrings(m.LayerInfos()), | ||||
| 		Env:           d1.Config.Env, | ||||
| 	} | ||||
| 	return i, nil | ||||
| } | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| // +build !containers_image_ostree_stub
 | ||||
| // +build containers_image_ostree
 | ||||
| 
 | ||||
| package ostree | ||||
| 
 | ||||
|  | @ -218,7 +218,7 @@ func fixFiles(selinuxHnd *C.struct_selabel_handle, root string, dir string, user | |||
| 				defer C.free(unsafe.Pointer(fullpathC)) | ||||
| 				res, err = C.lsetfilecon_raw(fullpathC, context) | ||||
| 				if int(res) < 0 { | ||||
| 					return errors.Wrapf(err, "cannot setfilecon_raw %s", fullpath) | ||||
| 					return errors.Wrapf(err, "cannot setfilecon_raw %s to %s", fullpath, C.GoString(context)) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| // +build !containers_image_ostree_stub
 | ||||
| // +build containers_image_ostree
 | ||||
| 
 | ||||
| package ostree | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| // +build !containers_image_ostree_stub
 | ||||
| // +build containers_image_ostree
 | ||||
| 
 | ||||
| package ostree | ||||
| 
 | ||||
|  |  | |||
|  | @ -56,6 +56,7 @@ func SetAuthentication(sys *types.SystemContext, registry, username, password st | |||
| // If an entry is not found empty strings are returned for the username and password
 | ||||
| func GetAuthentication(sys *types.SystemContext, registry string) (string, string, error) { | ||||
| 	if sys != nil && sys.DockerAuthConfig != nil { | ||||
| 		logrus.Debug("Returning credentials from DockerAuthConfig") | ||||
| 		return sys.DockerAuthConfig.Username, sys.DockerAuthConfig.Password, nil | ||||
| 	} | ||||
| 
 | ||||
|  | @ -76,12 +77,15 @@ func GetAuthentication(sys *types.SystemContext, registry string) (string, strin | |||
| 		legacyFormat := path == dockerLegacyPath | ||||
| 		username, password, err := findAuthentication(registry, path, legacyFormat) | ||||
| 		if err != nil { | ||||
| 			logrus.Debugf("Credentials not found") | ||||
| 			return "", "", err | ||||
| 		} | ||||
| 		if username != "" && password != "" { | ||||
| 			logrus.Debugf("Returning credentials from %s", path) | ||||
| 			return username, password, nil | ||||
| 		} | ||||
| 	} | ||||
| 	logrus.Debugf("Credentials not found") | ||||
| 	return "", "", nil | ||||
| } | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										24
									
								
								vendor/github.com/containers/image/pkg/sysregistriesv2/system_registries_v2.go
								
								
									generated
								
								
									vendored
								
								
							
							
						
						
									
										24
									
								
								vendor/github.com/containers/image/pkg/sysregistriesv2/system_registries_v2.go
								
								
									generated
								
								
									vendored
								
								
							|  | @ -30,10 +30,10 @@ const builtinRegistriesConfPath = "/etc/containers/registries.conf" | |||
| // Endpoint describes a remote location of a registry.
 | ||||
| type Endpoint struct { | ||||
| 	// The endpoint's remote location.
 | ||||
| 	Location string `toml:"location"` | ||||
| 	Location string `toml:"location,omitempty"` | ||||
| 	// If true, certs verification will be skipped and HTTP (non-TLS)
 | ||||
| 	// connections will be allowed.
 | ||||
| 	Insecure bool `toml:"insecure"` | ||||
| 	Insecure bool `toml:"insecure,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // rewriteReference will substitute the provided reference `prefix` to the
 | ||||
|  | @ -56,22 +56,22 @@ func (e *Endpoint) rewriteReference(ref reference.Named, prefix string) (referen | |||
| 
 | ||||
| // Registry represents a registry.
 | ||||
| type Registry struct { | ||||
| 	// A registry is an Endpoint too
 | ||||
| 	Endpoint | ||||
| 	// The registry's mirrors.
 | ||||
| 	Mirrors []Endpoint `toml:"mirror"` | ||||
| 	// If true, pulling from the registry will be blocked.
 | ||||
| 	Blocked bool `toml:"blocked"` | ||||
| 	// If true, mirrors will only be used for digest pulls. Pulling images by
 | ||||
| 	// tag can potentially yield different images, depending on which endpoint
 | ||||
| 	// we pull from.  Forcing digest-pulls for mirrors avoids that issue.
 | ||||
| 	MirrorByDigestOnly bool `toml:"mirror-by-digest-only"` | ||||
| 	// Prefix is used for matching images, and to translate one namespace to
 | ||||
| 	// another.  If `Prefix="example.com/bar"`, `location="example.com/foo/bar"`
 | ||||
| 	// and we pull from "example.com/bar/myimage:latest", the image will
 | ||||
| 	// effectively be pulled from "example.com/foo/bar/myimage:latest".
 | ||||
| 	// If no Prefix is specified, it defaults to the specified location.
 | ||||
| 	Prefix string `toml:"prefix"` | ||||
| 	// A registry is an Endpoint too
 | ||||
| 	Endpoint | ||||
| 	// The registry's mirrors.
 | ||||
| 	Mirrors []Endpoint `toml:"mirror,omitempty"` | ||||
| 	// If true, pulling from the registry will be blocked.
 | ||||
| 	Blocked bool `toml:"blocked,omitempty"` | ||||
| 	// If true, mirrors will only be used for digest pulls. Pulling images by
 | ||||
| 	// tag can potentially yield different images, depending on which endpoint
 | ||||
| 	// we pull from.  Forcing digest-pulls for mirrors avoids that issue.
 | ||||
| 	MirrorByDigestOnly bool `toml:"mirror-by-digest-only,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // PullSource consists of an Endpoint and a Reference. Note that the reference is
 | ||||
|  |  | |||
|  | @ -491,14 +491,21 @@ func (s *storageImageDestination) TryReusingBlob(ctx context.Context, blobinfo t | |||
| 
 | ||||
| 	// Does the blob correspond to a known DiffID which we already have available?
 | ||||
| 	// Because we must return the size, which is unknown for unavailable compressed blobs, the returned BlobInfo refers to the
 | ||||
| 	// uncompressed layer, and that can happen only if canSubstitute.
 | ||||
| 	if canSubstitute { | ||||
| 	// uncompressed layer, and that can happen only if canSubstitute, or if the incoming manifest already specifies the size.
 | ||||
| 	if canSubstitute || blobinfo.Size != -1 { | ||||
| 		if uncompressedDigest := cache.UncompressedDigest(blobinfo.Digest); uncompressedDigest != "" && uncompressedDigest != blobinfo.Digest { | ||||
| 			layers, err := s.imageRef.transport.store.LayersByUncompressedDigest(uncompressedDigest) | ||||
| 			if err != nil && errors.Cause(err) != storage.ErrLayerUnknown { | ||||
| 				return false, types.BlobInfo{}, errors.Wrapf(err, `Error looking for layers with digest %q`, uncompressedDigest) | ||||
| 			} | ||||
| 			if len(layers) > 0 { | ||||
| 				if blobinfo.Size != -1 { | ||||
| 					s.blobDiffIDs[blobinfo.Digest] = layers[0].UncompressedDigest | ||||
| 					return true, blobinfo, nil | ||||
| 				} | ||||
| 				if !canSubstitute { | ||||
| 					return false, types.BlobInfo{}, fmt.Errorf("Internal error: canSubstitute was expected to be true for blobInfo %v", blobinfo) | ||||
| 				} | ||||
| 				s.blobDiffIDs[uncompressedDigest] = layers[0].UncompressedDigest | ||||
| 				return true, types.BlobInfo{ | ||||
| 					Digest:    uncompressedDigest, | ||||
|  | @ -627,7 +634,7 @@ func (s *storageImageDestination) Commit(ctx context.Context) error { | |||
| 		if !ok { | ||||
| 			// Try to find the layer with contents matching that blobsum.
 | ||||
| 			layer := "" | ||||
| 			layers, err2 := s.imageRef.transport.store.LayersByUncompressedDigest(blob.Digest) | ||||
| 			layers, err2 := s.imageRef.transport.store.LayersByUncompressedDigest(diffID) | ||||
| 			if err2 == nil && len(layers) > 0 { | ||||
| 				layer = layers[0].ID | ||||
| 			} else { | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| // +build !containers_image_ostree_stub,linux
 | ||||
| // +build containers_image_ostree,linux
 | ||||
| 
 | ||||
| package alltransports | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| // +build containers_image_ostree_stub !linux
 | ||||
| // +build !containers_image_ostree !linux
 | ||||
| 
 | ||||
| package alltransports | ||||
| 
 | ||||
|  |  | |||
|  | @ -398,6 +398,7 @@ type ImageInspectInfo struct { | |||
| 	Architecture  string | ||||
| 	Os            string | ||||
| 	Layers        []string | ||||
| 	Env           []string | ||||
| } | ||||
| 
 | ||||
| // DockerAuthConfig contains authorization information for connecting to a registry.
 | ||||
|  |  | |||
|  | @ -8,7 +8,7 @@ const ( | |||
| 	// VersionMinor is for functionality in a backwards-compatible manner
 | ||||
| 	VersionMinor = 0 | ||||
| 	// VersionPatch is for backwards-compatible bug fixes
 | ||||
| 	VersionPatch = 0 | ||||
| 	VersionPatch = 1 | ||||
| 
 | ||||
| 	// VersionDev indicates development branch. Releases will be empty string.
 | ||||
| 	VersionDev = "" | ||||
|  |  | |||
|  | @ -1,8 +1,8 @@ | |||
| language: go | ||||
| sudo: false | ||||
| go: | ||||
|   - 1.8.x | ||||
|   - 1.9.x | ||||
|   - 1.10.x | ||||
|   - tip | ||||
| 
 | ||||
| before_install: | ||||
|   - go get -t -v ./... | ||||
|  |  | |||
|  | @ -31,8 +31,6 @@ _Note:_ it is preferable to go get from github.com, rather than gopkg.in. See is | |||
|     p := mpb.New( | ||||
|         // override default (80) width | ||||
|         mpb.WithWidth(64), | ||||
|         // override default "[=>-]" format | ||||
|         mpb.WithFormat("╢▌▌░╟"), | ||||
|         // override default 120ms refresh rate | ||||
|         mpb.WithRefreshRate(180*time.Millisecond), | ||||
|     ) | ||||
|  | @ -41,6 +39,8 @@ _Note:_ it is preferable to go get from github.com, rather than gopkg.in. See is | |||
|     name := "Single Bar:" | ||||
|     // adding a single bar | ||||
|     bar := p.AddBar(int64(total), | ||||
|         // override default "[=>-]" style | ||||
|         mpb.BarStyle("╢▌▌░╟"), | ||||
|         mpb.PrependDecorators( | ||||
|             // display our name with one space on the right | ||||
|             decor.Name(name, decor.WC{W: len(name) + 1, C: decor.DidentRight}), | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ package mpb | |||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"io/ioutil" | ||||
|  | @ -11,21 +12,8 @@ import ( | |||
| 	"unicode/utf8" | ||||
| 
 | ||||
| 	"github.com/vbauerster/mpb/decor" | ||||
| 	"github.com/vbauerster/mpb/internal" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	rLeft = iota | ||||
| 	rFill | ||||
| 	rTip | ||||
| 	rEmpty | ||||
| 	rRight | ||||
| ) | ||||
| 
 | ||||
| const formatLen = 5 | ||||
| 
 | ||||
| type barRunes [formatLen]rune | ||||
| 
 | ||||
| // Bar represents a progress Bar
 | ||||
| type Bar struct { | ||||
| 	priority int | ||||
|  | @ -45,15 +33,30 @@ type Bar struct { | |||
| 	shutdown chan struct{} | ||||
| } | ||||
| 
 | ||||
| // Filler interface.
 | ||||
| // Bar renders by calling Filler's Fill method. You can literally have
 | ||||
| // any bar kind, by implementing this interface and passing it to the
 | ||||
| // Add method.
 | ||||
| type Filler interface { | ||||
| 	Fill(w io.Writer, width int, s *decor.Statistics) | ||||
| } | ||||
| 
 | ||||
| // FillerFunc is function type adapter to convert function into Filler.
 | ||||
| type FillerFunc func(w io.Writer, width int, stat *decor.Statistics) | ||||
| 
 | ||||
| func (f FillerFunc) Fill(w io.Writer, width int, stat *decor.Statistics) { | ||||
| 	f(w, width, stat) | ||||
| } | ||||
| 
 | ||||
| type ( | ||||
| 	bState struct { | ||||
| 		filler             Filler | ||||
| 		id                 int | ||||
| 		width              int | ||||
| 		alignment          int | ||||
| 		total              int64 | ||||
| 		current            int64 | ||||
| 		runes              barRunes | ||||
| 		trimLeftSpace      bool | ||||
| 		trimRightSpace     bool | ||||
| 		trimSpace          bool | ||||
| 		toComplete         bool | ||||
| 		removeOnComplete   bool | ||||
| 		barClearOnComplete bool | ||||
|  | @ -73,8 +76,8 @@ type ( | |||
| 		runningBar *Bar | ||||
| 	} | ||||
| 	refill struct { | ||||
| 		char rune | ||||
| 		till int64 | ||||
| 		r     rune | ||||
| 		limit int64 | ||||
| 	} | ||||
| 	frameReader struct { | ||||
| 		io.Reader | ||||
|  | @ -84,14 +87,20 @@ type ( | |||
| 	} | ||||
| ) | ||||
| 
 | ||||
| func newBar(wg *sync.WaitGroup, id int, total int64, cancel <-chan struct{}, options ...BarOption) *Bar { | ||||
| 	if total <= 0 { | ||||
| 		total = time.Now().Unix() | ||||
| 	} | ||||
| func newBar( | ||||
| 	ctx context.Context, | ||||
| 	wg *sync.WaitGroup, | ||||
| 	filler Filler, | ||||
| 	id, width int, | ||||
| 	total int64, | ||||
| 	options ...BarOption, | ||||
| ) *Bar { | ||||
| 
 | ||||
| 	s := &bState{ | ||||
| 		filler:   filler, | ||||
| 		id:       id, | ||||
| 		priority: id, | ||||
| 		width:    width, | ||||
| 		total:    total, | ||||
| 	} | ||||
| 
 | ||||
|  | @ -104,6 +113,9 @@ func newBar(wg *sync.WaitGroup, id int, total int64, cancel <-chan struct{}, opt | |||
| 	s.bufP = bytes.NewBuffer(make([]byte, 0, s.width)) | ||||
| 	s.bufB = bytes.NewBuffer(make([]byte, 0, s.width)) | ||||
| 	s.bufA = bytes.NewBuffer(make([]byte, 0, s.width)) | ||||
| 	if s.newLineExtendFn != nil { | ||||
| 		s.bufNL = bytes.NewBuffer(make([]byte, 0, s.width)) | ||||
| 	} | ||||
| 
 | ||||
| 	b := &Bar{ | ||||
| 		priority:      s.priority, | ||||
|  | @ -121,11 +133,7 @@ func newBar(wg *sync.WaitGroup, id int, total int64, cancel <-chan struct{}, opt | |||
| 		b.priority = b.runningBar.priority | ||||
| 	} | ||||
| 
 | ||||
| 	if s.newLineExtendFn != nil { | ||||
| 		s.bufNL = bytes.NewBuffer(make([]byte, 0, s.width)) | ||||
| 	} | ||||
| 
 | ||||
| 	go b.serve(wg, s, cancel) | ||||
| 	go b.serve(ctx, wg, s) | ||||
| 	return b | ||||
| } | ||||
| 
 | ||||
|  | @ -178,37 +186,27 @@ func (b *Bar) Current() int64 { | |||
| } | ||||
| 
 | ||||
| // SetTotal sets total dynamically.
 | ||||
| // Set final to true, when total is known, it will trigger bar complete event.
 | ||||
| func (b *Bar) SetTotal(total int64, final bool) bool { | ||||
| // Set complete to true, to trigger bar complete event now.
 | ||||
| func (b *Bar) SetTotal(total int64, complete bool) { | ||||
| 	select { | ||||
| 	case b.operateState <- func(s *bState) { | ||||
| 		if total > 0 { | ||||
| 		s.total = total | ||||
| 		} | ||||
| 		if final { | ||||
| 		if complete && !s.toComplete { | ||||
| 			s.current = s.total | ||||
| 			s.toComplete = true | ||||
| 		} | ||||
| 	}: | ||||
| 		return true | ||||
| 	case <-b.done: | ||||
| 		return false | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // SetRefill sets fill rune to r, up until n.
 | ||||
| func (b *Bar) SetRefill(n int, r rune) { | ||||
| 	if n <= 0 { | ||||
| 		return | ||||
| 	} | ||||
| // SetRefill sets refill, if supported by underlying Filler.
 | ||||
| func (b *Bar) SetRefill(amount int64) { | ||||
| 	b.operateState <- func(s *bState) { | ||||
| 		s.refill = &refill{r, int64(n)} | ||||
| 		if f, ok := s.filler.(interface{ SetRefill(int64) }); ok { | ||||
| 			f.SetRefill(amount) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // RefillBy is deprecated, use SetRefill
 | ||||
| func (b *Bar) RefillBy(n int, r rune) { | ||||
| 	b.SetRefill(n, r) | ||||
| } | ||||
| 
 | ||||
| // Increment is a shorthand for b.IncrBy(1).
 | ||||
|  | @ -217,13 +215,13 @@ func (b *Bar) Increment() { | |||
| } | ||||
| 
 | ||||
| // IncrBy increments progress bar by amount of n.
 | ||||
| // wdd is optional work duration i.e. time.Since(start),
 | ||||
| // which expected to be provided, if any ewma based decorator is used.
 | ||||
| // wdd is optional work duration i.e. time.Since(start), which expected
 | ||||
| // to be provided, if any ewma based decorator is used.
 | ||||
| func (b *Bar) IncrBy(n int, wdd ...time.Duration) { | ||||
| 	select { | ||||
| 	case b.operateState <- func(s *bState) { | ||||
| 		s.current += int64(n) | ||||
| 		if s.current >= s.total { | ||||
| 		if s.total > 0 && s.current >= s.total { | ||||
| 			s.current = s.total | ||||
| 			s.toComplete = true | ||||
| 		} | ||||
|  | @ -238,9 +236,9 @@ func (b *Bar) IncrBy(n int, wdd ...time.Duration) { | |||
| // Completed reports whether the bar is in completed state.
 | ||||
| func (b *Bar) Completed() bool { | ||||
| 	// omit select here, because primary usage of the method is for loop
 | ||||
| 	// condition, like 	for !bar.Completed() {...}
 | ||||
| 	// so when toComplete=true it is called once (at which time, the bar is still alive),
 | ||||
| 	// then quits the loop and never suppose to be called afterwards.
 | ||||
| 	// condition, like for !bar.Completed() {...} so when toComplete=true
 | ||||
| 	// it is called once (at which time, the bar is still alive), then
 | ||||
| 	// quits the loop and never suppose to be called afterwards.
 | ||||
| 	return <-b.boolCh | ||||
| } | ||||
| 
 | ||||
|  | @ -253,8 +251,9 @@ func (b *Bar) wSyncTable() [][]chan int { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (b *Bar) serve(wg *sync.WaitGroup, s *bState, cancel <-chan struct{}) { | ||||
| func (b *Bar) serve(ctx context.Context, wg *sync.WaitGroup, s *bState) { | ||||
| 	defer wg.Done() | ||||
| 	cancel := ctx.Done() | ||||
| 	for { | ||||
| 		select { | ||||
| 		case op := <-b.operateState: | ||||
|  | @ -322,8 +321,6 @@ func (b *Bar) render(debugOut io.Writer, tw int) { | |||
| } | ||||
| 
 | ||||
| func (s *bState) draw(termWidth int) io.Reader { | ||||
| 	defer s.bufA.WriteByte('\n') | ||||
| 
 | ||||
| 	if s.panicMsg != "" { | ||||
| 		return strings.NewReader(fmt.Sprintf(fmt.Sprintf("%%.%ds\n", termWidth), s.panicMsg)) | ||||
| 	} | ||||
|  | @ -338,77 +335,32 @@ func (s *bState) draw(termWidth int) io.Reader { | |||
| 		s.bufA.WriteString(d.Decor(stat)) | ||||
| 	} | ||||
| 
 | ||||
| 	prependCount := utf8.RuneCount(s.bufP.Bytes()) | ||||
| 	appendCount := utf8.RuneCount(s.bufA.Bytes()) | ||||
| 
 | ||||
| 	if s.barClearOnComplete && s.completeFlushed { | ||||
| 		s.bufA.WriteByte('\n') | ||||
| 		return io.MultiReader(s.bufP, s.bufA) | ||||
| 	} | ||||
| 
 | ||||
| 	s.fillBar(s.width) | ||||
| 	barCount := utf8.RuneCount(s.bufB.Bytes()) | ||||
| 	totalCount := prependCount + barCount + appendCount | ||||
| 	if spaceCount := 0; totalCount > termWidth { | ||||
| 		if !s.trimLeftSpace { | ||||
| 			spaceCount++ | ||||
| 		} | ||||
| 		if !s.trimRightSpace { | ||||
| 			spaceCount++ | ||||
| 		} | ||||
| 		s.fillBar(termWidth - prependCount - appendCount - spaceCount) | ||||
| 	} | ||||
| 	prependCount := utf8.RuneCount(s.bufP.Bytes()) | ||||
| 	appendCount := utf8.RuneCount(s.bufA.Bytes()) | ||||
| 
 | ||||
| 	return io.MultiReader(s.bufP, s.bufB, s.bufA) | ||||
| } | ||||
| 
 | ||||
| func (s *bState) fillBar(width int) { | ||||
| 	defer func() { | ||||
| 		s.bufB.WriteRune(s.runes[rRight]) | ||||
| 		if !s.trimRightSpace { | ||||
| 	if !s.trimSpace { | ||||
| 		// reserve space for edge spaces
 | ||||
| 		termWidth -= 2 | ||||
| 		s.bufB.WriteByte(' ') | ||||
| 	} | ||||
| 	}() | ||||
| 
 | ||||
| 	s.bufB.Reset() | ||||
| 	if !s.trimLeftSpace { | ||||
| 		s.bufB.WriteByte(' ') | ||||
| 	} | ||||
| 	s.bufB.WriteRune(s.runes[rLeft]) | ||||
| 	if width <= 2 { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	// bar s.width without leftEnd and rightEnd runes
 | ||||
| 	barWidth := width - 2 | ||||
| 
 | ||||
| 	completedWidth := internal.Percentage(s.total, s.current, int64(barWidth)) | ||||
| 
 | ||||
| 	if s.refill != nil { | ||||
| 		till := internal.Percentage(s.total, s.refill.till, int64(barWidth)) | ||||
| 		// append refill rune
 | ||||
| 		var i int64 | ||||
| 		for i = 0; i < till; i++ { | ||||
| 			s.bufB.WriteRune(s.refill.char) | ||||
| 		} | ||||
| 		for i = till; i < completedWidth; i++ { | ||||
| 			s.bufB.WriteRune(s.runes[rFill]) | ||||
| 		} | ||||
| 	if prependCount+s.width+appendCount > termWidth { | ||||
| 		s.filler.Fill(s.bufB, termWidth-prependCount-appendCount, stat) | ||||
| 	} else { | ||||
| 		var i int64 | ||||
| 		for i = 0; i < completedWidth; i++ { | ||||
| 			s.bufB.WriteRune(s.runes[rFill]) | ||||
| 		} | ||||
| 		s.filler.Fill(s.bufB, s.width, stat) | ||||
| 	} | ||||
| 
 | ||||
| 	if completedWidth < int64(barWidth) && completedWidth > 0 { | ||||
| 		_, size := utf8.DecodeLastRune(s.bufB.Bytes()) | ||||
| 		s.bufB.Truncate(s.bufB.Len() - size) | ||||
| 		s.bufB.WriteRune(s.runes[rTip]) | ||||
| 	if !s.trimSpace { | ||||
| 		s.bufB.WriteByte(' ') | ||||
| 	} | ||||
| 
 | ||||
| 	for i := completedWidth; i < int64(barWidth); i++ { | ||||
| 		s.bufB.WriteRune(s.runes[rEmpty]) | ||||
| 	} | ||||
| 	s.bufA.WriteByte('\n') | ||||
| 	return io.MultiReader(s.bufP, s.bufB, s.bufA) | ||||
| } | ||||
| 
 | ||||
| func (s *bState) wSyncTable() [][]chan int { | ||||
|  | @ -442,14 +394,6 @@ func newStatistics(s *bState) *decor.Statistics { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| func strToBarRunes(format string) (array barRunes) { | ||||
| 	for i, n := 0, 0; len(format) > 0; i++ { | ||||
| 		array[i], n = utf8.DecodeRuneInString(format) | ||||
| 		format = format[n:] | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func countLines(b []byte) int { | ||||
| 	return bytes.Count(b, []byte("\n")) | ||||
| } | ||||
|  |  | |||
|  | @ -0,0 +1,111 @@ | |||
| package mpb | ||||
| 
 | ||||
| import ( | ||||
| 	"io" | ||||
| 	"unicode/utf8" | ||||
| 
 | ||||
| 	"github.com/vbauerster/mpb/decor" | ||||
| 	"github.com/vbauerster/mpb/internal" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	rLeft = iota | ||||
| 	rFill | ||||
| 	rTip | ||||
| 	rEmpty | ||||
| 	rRight | ||||
| 	rRevTip | ||||
| 	rRefill | ||||
| ) | ||||
| 
 | ||||
| var defaultBarStyle = "[=>-]<+" | ||||
| 
 | ||||
| type barFiller struct { | ||||
| 	format       [][]byte | ||||
| 	refillAmount int64 | ||||
| 	reverse      bool | ||||
| } | ||||
| 
 | ||||
| func newDefaultBarFiller() Filler { | ||||
| 	bf := &barFiller{ | ||||
| 		format: make([][]byte, utf8.RuneCountInString(defaultBarStyle)), | ||||
| 	} | ||||
| 	bf.setStyle(defaultBarStyle) | ||||
| 	return bf | ||||
| } | ||||
| 
 | ||||
| func (s *barFiller) setStyle(style string) { | ||||
| 	if !utf8.ValidString(style) { | ||||
| 		return | ||||
| 	} | ||||
| 	src := make([][]byte, 0, utf8.RuneCountInString(style)) | ||||
| 	for _, r := range style { | ||||
| 		src = append(src, []byte(string(r))) | ||||
| 	} | ||||
| 	copy(s.format, src) | ||||
| } | ||||
| 
 | ||||
| func (s *barFiller) setReverse() { | ||||
| 	s.reverse = true | ||||
| } | ||||
| 
 | ||||
| func (s *barFiller) SetRefill(amount int64) { | ||||
| 	s.refillAmount = amount | ||||
| } | ||||
| 
 | ||||
| func (s *barFiller) Fill(w io.Writer, width int, stat *decor.Statistics) { | ||||
| 
 | ||||
| 	// don't count rLeft and rRight [brackets]
 | ||||
| 	width -= 2 | ||||
| 	if width < 2 { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	w.Write(s.format[rLeft]) | ||||
| 	if width == 2 { | ||||
| 		w.Write(s.format[rRight]) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	bb := make([][]byte, width) | ||||
| 
 | ||||
| 	cwidth := int(internal.Percentage(stat.Total, stat.Current, int64(width))) | ||||
| 
 | ||||
| 	for i := 0; i < cwidth; i++ { | ||||
| 		bb[i] = s.format[rFill] | ||||
| 	} | ||||
| 
 | ||||
| 	if s.refillAmount > 0 { | ||||
| 		var rwidth int | ||||
| 		if s.refillAmount > stat.Current { | ||||
| 			rwidth = cwidth | ||||
| 		} else { | ||||
| 			rwidth = int(internal.Percentage(stat.Total, int64(s.refillAmount), int64(width))) | ||||
| 		} | ||||
| 		for i := 0; i < rwidth; i++ { | ||||
| 			bb[i] = s.format[rRefill] | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if cwidth > 0 && cwidth < width { | ||||
| 		bb[cwidth-1] = s.format[rTip] | ||||
| 	} | ||||
| 
 | ||||
| 	for i := cwidth; i < width; i++ { | ||||
| 		bb[i] = s.format[rEmpty] | ||||
| 	} | ||||
| 
 | ||||
| 	if s.reverse { | ||||
| 		if cwidth > 0 && cwidth < width { | ||||
| 			bb[cwidth-1] = s.format[rRevTip] | ||||
| 		} | ||||
| 		for i := len(bb) - 1; i >= 0; i-- { | ||||
| 			w.Write(bb[i]) | ||||
| 		} | ||||
| 	} else { | ||||
| 		for i := 0; i < len(bb); i++ { | ||||
| 			w.Write(bb[i]) | ||||
| 		} | ||||
| 	} | ||||
| 	w.Write(s.format[rRight]) | ||||
| } | ||||
|  | @ -6,11 +6,10 @@ import ( | |||
| 	"github.com/vbauerster/mpb/decor" | ||||
| ) | ||||
| 
 | ||||
| // BarOption is a function option which changes the default behavior of a bar,
 | ||||
| // if passed to p.AddBar(int64, ...BarOption)
 | ||||
| // BarOption is a function option which changes the default behavior of a bar.
 | ||||
| type BarOption func(*bState) | ||||
| 
 | ||||
| // AppendDecorators let you inject decorators to the bar's right side
 | ||||
| // AppendDecorators let you inject decorators to the bar's right side.
 | ||||
| func AppendDecorators(appenders ...decor.Decorator) BarOption { | ||||
| 	return func(s *bState) { | ||||
| 		for _, decorator := range appenders { | ||||
|  | @ -25,7 +24,7 @@ func AppendDecorators(appenders ...decor.Decorator) BarOption { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| // PrependDecorators let you inject decorators to the bar's left side
 | ||||
| // PrependDecorators let you inject decorators to the bar's left side.
 | ||||
| func PrependDecorators(prependers ...decor.Decorator) BarOption { | ||||
| 	return func(s *bState) { | ||||
| 		for _, decorator := range prependers { | ||||
|  | @ -40,85 +39,155 @@ func PrependDecorators(prependers ...decor.Decorator) BarOption { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| // BarTrimLeft trims left side space of the bar
 | ||||
| func BarTrimLeft() BarOption { | ||||
| 	return func(s *bState) { | ||||
| 		s.trimLeftSpace = true | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // BarTrimRight trims right space of the bar
 | ||||
| func BarTrimRight() BarOption { | ||||
| 	return func(s *bState) { | ||||
| 		s.trimRightSpace = true | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // BarTrim trims both left and right spaces of the bar
 | ||||
| func BarTrim() BarOption { | ||||
| 	return func(s *bState) { | ||||
| 		s.trimLeftSpace = true | ||||
| 		s.trimRightSpace = true | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // BarID overwrites internal bar id
 | ||||
| // BarID sets bar id.
 | ||||
| func BarID(id int) BarOption { | ||||
| 	return func(s *bState) { | ||||
| 		s.id = id | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // BarRemoveOnComplete is a flag, if set whole bar line will be removed on complete event.
 | ||||
| // If both BarRemoveOnComplete and BarClearOnComplete are set, first bar section gets cleared
 | ||||
| // and then whole bar line gets removed completely.
 | ||||
| // BarWidth sets bar width independent of the container.
 | ||||
| func BarWidth(width int) BarOption { | ||||
| 	return func(s *bState) { | ||||
| 		s.width = width | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // BarRemoveOnComplete is a flag, if set whole bar line will be removed
 | ||||
| // on complete event. If both BarRemoveOnComplete and BarClearOnComplete
 | ||||
| // are set, first bar section gets cleared and then whole bar line
 | ||||
| // gets removed completely.
 | ||||
| func BarRemoveOnComplete() BarOption { | ||||
| 	return func(s *bState) { | ||||
| 		s.removeOnComplete = true | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // BarReplaceOnComplete is indicator for delayed bar start, after the `runningBar` is complete.
 | ||||
| // To achieve bar replacement effect, `runningBar` should has its `BarRemoveOnComplete` option set.
 | ||||
| // BarReplaceOnComplete is indicator for delayed bar start, after the
 | ||||
| // `runningBar` is complete. To achieve bar replacement effect,
 | ||||
| // `runningBar` should has its `BarRemoveOnComplete` option set.
 | ||||
| func BarReplaceOnComplete(runningBar *Bar) BarOption { | ||||
| 	return BarParkTo(runningBar) | ||||
| } | ||||
| 
 | ||||
| // BarParkTo same as BarReplaceOnComplete
 | ||||
| func BarParkTo(runningBar *Bar) BarOption { | ||||
| 	return func(s *bState) { | ||||
| 		s.runningBar = runningBar | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // BarClearOnComplete is a flag, if set will clear bar section on complete event.
 | ||||
| // If you need to remove a whole bar line, refer to BarRemoveOnComplete.
 | ||||
| // BarClearOnComplete is a flag, if set will clear bar section on
 | ||||
| // complete event. If you need to remove a whole bar line, refer to
 | ||||
| // BarRemoveOnComplete.
 | ||||
| func BarClearOnComplete() BarOption { | ||||
| 	return func(s *bState) { | ||||
| 		s.barClearOnComplete = true | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // BarPriority sets bar's priority.
 | ||||
| // Zero is highest priority, i.e. bar will be on top.
 | ||||
| // If `BarReplaceOnComplete` option is supplied, this option is ignored.
 | ||||
| // BarPriority sets bar's priority. Zero is highest priority, i.e. bar
 | ||||
| // will be on top. If `BarReplaceOnComplete` option is supplied, this
 | ||||
| // option is ignored.
 | ||||
| func BarPriority(priority int) BarOption { | ||||
| 	return func(s *bState) { | ||||
| 		s.priority = priority | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // BarNewLineExtend takes user defined efn, which gets called each render cycle.
 | ||||
| // Any write to provided writer of efn, will appear on new line of respective bar.
 | ||||
| // BarNewLineExtend takes user defined efn, which gets called each
 | ||||
| // render cycle. Any write to provided writer of efn, will appear on
 | ||||
| // new line of respective bar.
 | ||||
| func BarNewLineExtend(efn func(io.Writer, *decor.Statistics)) BarOption { | ||||
| 	return func(s *bState) { | ||||
| 		s.newLineExtendFn = efn | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func barWidth(w int) BarOption { | ||||
| // TrimSpace trims bar's edge spaces.
 | ||||
| func TrimSpace() BarOption { | ||||
| 	return func(s *bState) { | ||||
| 		s.width = w | ||||
| 		s.trimSpace = true | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func barFormat(format string) BarOption { | ||||
| // BarStyle sets custom bar style, default one is "[=>-]<+".
 | ||||
| //
 | ||||
| //	'[' left bracket rune
 | ||||
| //
 | ||||
| //	'=' fill rune
 | ||||
| //
 | ||||
| //	'>' tip rune
 | ||||
| //
 | ||||
| //	'-' empty rune
 | ||||
| //
 | ||||
| //	']' right bracket rune
 | ||||
| //
 | ||||
| //	'<' reverse tip rune, used when BarReverse option is set
 | ||||
| //
 | ||||
| //	'+' refill rune, used when *Bar.SetRefill(int64) is called
 | ||||
| //
 | ||||
| // It's ok to provide first five runes only, for example mpb.BarStyle("╢▌▌░╟")
 | ||||
| func BarStyle(style string) BarOption { | ||||
| 	chk := func(filler Filler) (interface{}, bool) { | ||||
| 		if style == "" { | ||||
| 			return nil, false | ||||
| 		} | ||||
| 		t, ok := filler.(*barFiller) | ||||
| 		return t, ok | ||||
| 	} | ||||
| 	cb := func(t interface{}) { | ||||
| 		t.(*barFiller).setStyle(style) | ||||
| 	} | ||||
| 	return MakeFillerTypeSpecificBarOption(chk, cb) | ||||
| } | ||||
| 
 | ||||
| // BarReverse reverse mode, bar will progress from right to left.
 | ||||
| func BarReverse() BarOption { | ||||
| 	chk := func(filler Filler) (interface{}, bool) { | ||||
| 		t, ok := filler.(*barFiller) | ||||
| 		return t, ok | ||||
| 	} | ||||
| 	cb := func(t interface{}) { | ||||
| 		t.(*barFiller).setReverse() | ||||
| 	} | ||||
| 	return MakeFillerTypeSpecificBarOption(chk, cb) | ||||
| } | ||||
| 
 | ||||
| // SpinnerStyle sets custom spinner style.
 | ||||
| // Effective when Filler type is spinner.
 | ||||
| func SpinnerStyle(frames []string) BarOption { | ||||
| 	chk := func(filler Filler) (interface{}, bool) { | ||||
| 		if len(frames) == 0 { | ||||
| 			return nil, false | ||||
| 		} | ||||
| 		t, ok := filler.(*spinnerFiller) | ||||
| 		return t, ok | ||||
| 	} | ||||
| 	cb := func(t interface{}) { | ||||
| 		t.(*spinnerFiller).frames = frames | ||||
| 	} | ||||
| 	return MakeFillerTypeSpecificBarOption(chk, cb) | ||||
| } | ||||
| 
 | ||||
| // MakeFillerTypeSpecificBarOption makes BarOption specific to Filler's
 | ||||
| // actual type. If you implement your own Filler, so most probably
 | ||||
| // you'll need this. See BarStyle or SpinnerStyle for example.
 | ||||
| func MakeFillerTypeSpecificBarOption( | ||||
| 	typeChecker func(Filler) (interface{}, bool), | ||||
| 	cb func(interface{}), | ||||
| ) BarOption { | ||||
| 	return func(s *bState) { | ||||
| 		s.runes = strToBarRunes(format) | ||||
| 		if t, ok := typeChecker(s.filler); ok { | ||||
| 			cb(t) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // OptionOnCondition returns option when condition evaluates to true.
 | ||||
| func OptionOnCondition(option BarOption, condition func() bool) BarOption { | ||||
| 	if condition() { | ||||
| 		return option | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  |  | |||
|  | @ -22,8 +22,8 @@ var ( | |||
| 	clearCursorAndLine = cursorUp + clearLine | ||||
| ) | ||||
| 
 | ||||
| // Writer is a buffered the writer that updates the terminal.
 | ||||
| // The contents of writer will be flushed when Flush is called.
 | ||||
| // Writer is a buffered the writer that updates the terminal.  The
 | ||||
| // contents of writer will be flushed when Flush is called.
 | ||||
| type Writer struct { | ||||
| 	out        io.Writer | ||||
| 	buf        bytes.Buffer | ||||
|  | @ -64,11 +64,13 @@ func (w *Writer) WriteString(s string) (n int, err error) { | |||
| 	return w.buf.WriteString(s) | ||||
| } | ||||
| 
 | ||||
| // ReadFrom reads from the provided io.Reader and writes to the underlying buffer.
 | ||||
| // ReadFrom reads from the provided io.Reader and writes to the
 | ||||
| // underlying buffer.
 | ||||
| func (w *Writer) ReadFrom(r io.Reader) (n int64, err error) { | ||||
| 	return w.buf.ReadFrom(r) | ||||
| } | ||||
| 
 | ||||
| // GetWidth returns width of underlying terminal.
 | ||||
| func (w *Writer) GetWidth() (int, error) { | ||||
| 	if w.isTerminal { | ||||
| 		tw, _, err := terminal.GetSize(w.fd) | ||||
|  |  | |||
|  | @ -8,7 +8,7 @@ import ( | |||
| 	"syscall" | ||||
| 	"unsafe" | ||||
| 
 | ||||
| 	"github.com/mattn/go-isatty" | ||||
| 	isatty "github.com/mattn/go-isatty" | ||||
| ) | ||||
| 
 | ||||
| var kernel32 = syscall.NewLazyDLL("kernel32.dll") | ||||
|  |  | |||
|  | @ -141,12 +141,14 @@ func CountersNoUnit(pairFormat string, wcc ...WC) Decorator { | |||
| 	return Counters(0, pairFormat, wcc...) | ||||
| } | ||||
| 
 | ||||
| // CountersKibiByte is a wrapper around Counters with predefined unit UnitKiB (bytes/1024).
 | ||||
| // CountersKibiByte is a wrapper around Counters with predefined unit
 | ||||
| // UnitKiB (bytes/1024).
 | ||||
| func CountersKibiByte(pairFormat string, wcc ...WC) Decorator { | ||||
| 	return Counters(UnitKiB, pairFormat, wcc...) | ||||
| } | ||||
| 
 | ||||
| // CountersKiloByte is a wrapper around Counters with predefined unit UnitKB (bytes/1000).
 | ||||
| // CountersKiloByte is a wrapper around Counters with predefined unit
 | ||||
| // UnitKB (bytes/1000).
 | ||||
| func CountersKiloByte(pairFormat string, wcc ...WC) Decorator { | ||||
| 	return Counters(UnitKB, pairFormat, wcc...) | ||||
| } | ||||
|  |  | |||
|  | @ -31,8 +31,12 @@ const ( | |||
| 	DSyncSpaceR = DSyncWidth | DextraSpace | DidentRight | ||||
| ) | ||||
| 
 | ||||
| // TimeStyle enum.
 | ||||
| type TimeStyle int | ||||
| 
 | ||||
| // TimeStyle kinds.
 | ||||
| const ( | ||||
| 	ET_STYLE_GO = iota | ||||
| 	ET_STYLE_GO TimeStyle = iota | ||||
| 	ET_STYLE_HHMMSS | ||||
| 	ET_STYLE_HHMM | ||||
| 	ET_STYLE_MMSS | ||||
|  | @ -47,35 +51,37 @@ type Statistics struct { | |||
| } | ||||
| 
 | ||||
| // Decorator interface.
 | ||||
| // A decorator must implement this interface, in order to be used with mpb library.
 | ||||
| // A decorator must implement this interface, in order to be used with
 | ||||
| // mpb library.
 | ||||
| type Decorator interface { | ||||
| 	Decor(*Statistics) string | ||||
| 	Syncable | ||||
| } | ||||
| 
 | ||||
| // Syncable interface.
 | ||||
| // All decorators implement this interface implicitly.
 | ||||
| // Its Syncable method exposes width sync channel, if sync is enabled.
 | ||||
| // All decorators implement this interface implicitly. Its Syncable
 | ||||
| // method exposes width sync channel, if sync is enabled.
 | ||||
| type Syncable interface { | ||||
| 	Syncable() (bool, chan int) | ||||
| } | ||||
| 
 | ||||
| // OnCompleteMessenger interface.
 | ||||
| // Decorators implementing this interface suppose to return provided string on complete event.
 | ||||
| // Decorators implementing this interface suppose to return provided
 | ||||
| // string on complete event.
 | ||||
| type OnCompleteMessenger interface { | ||||
| 	OnCompleteMessage(string) | ||||
| } | ||||
| 
 | ||||
| // AmountReceiver interface.
 | ||||
| // If decorator needs to receive increment amount,
 | ||||
| // so this is the right interface to implement.
 | ||||
| // If decorator needs to receive increment amount, so this is the right
 | ||||
| // interface to implement.
 | ||||
| type AmountReceiver interface { | ||||
| 	NextAmount(int, ...time.Duration) | ||||
| } | ||||
| 
 | ||||
| // ShutdownListener interface.
 | ||||
| // If decorator needs to be notified once upon bar shutdown event,
 | ||||
| // so this is the right interface to implement.
 | ||||
| // If decorator needs to be notified once upon bar shutdown event, so
 | ||||
| // this is the right interface to implement.
 | ||||
| type ShutdownListener interface { | ||||
| 	Shutdown() | ||||
| } | ||||
|  | @ -90,6 +96,7 @@ var ( | |||
| 
 | ||||
| // WC is a struct with two public fields W and C, both of int type.
 | ||||
| // W represents width and C represents bit set of width related config.
 | ||||
| // A decorator should embed WC, in order to become Syncable.
 | ||||
| type WC struct { | ||||
| 	W      int | ||||
| 	C      int | ||||
|  | @ -126,12 +133,13 @@ func (wc *WC) Init() { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Syncable is implementation of Syncable interface.
 | ||||
| func (wc *WC) Syncable() (bool, chan int) { | ||||
| 	return (wc.C & DSyncWidth) != 0, wc.wsync | ||||
| } | ||||
| 
 | ||||
| // OnComplete returns decorator, which wraps provided decorator, with sole
 | ||||
| // purpose to display provided message on complete event.
 | ||||
| // OnComplete returns decorator, which wraps provided decorator, with
 | ||||
| // sole purpose to display provided message on complete event.
 | ||||
| //
 | ||||
| //	`decorator` Decorator to wrap
 | ||||
| //
 | ||||
|  |  | |||
|  | @ -10,7 +10,7 @@ import ( | |||
| //	`style` one of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS]
 | ||||
| //
 | ||||
| //	`wcc` optional WC config
 | ||||
| func Elapsed(style int, wcc ...WC) Decorator { | ||||
| func Elapsed(style TimeStyle, wcc ...WC) Decorator { | ||||
| 	var wc WC | ||||
| 	for _, widthConf := range wcc { | ||||
| 		wc = widthConf | ||||
|  | @ -26,7 +26,7 @@ func Elapsed(style int, wcc ...WC) Decorator { | |||
| 
 | ||||
| type elapsedDecorator struct { | ||||
| 	WC | ||||
| 	style       int | ||||
| 	style       TimeStyle | ||||
| 	startTime   time.Time | ||||
| 	msg         string | ||||
| 	completeMsg *string | ||||
|  |  | |||
|  | @ -6,7 +6,6 @@ import ( | |||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/VividCortex/ewma" | ||||
| 	"github.com/vbauerster/mpb/internal" | ||||
| ) | ||||
| 
 | ||||
| type TimeNormalizer func(time.Duration) time.Duration | ||||
|  | @ -18,7 +17,7 @@ type TimeNormalizer func(time.Duration) time.Duration | |||
| //	`age` is the previous N samples to average over.
 | ||||
| //
 | ||||
| //	`wcc` optional WC config
 | ||||
| func EwmaETA(style int, age float64, wcc ...WC) Decorator { | ||||
| func EwmaETA(style TimeStyle, age float64, wcc ...WC) Decorator { | ||||
| 	return MovingAverageETA(style, ewma.NewMovingAverage(age), NopNormalizer(), wcc...) | ||||
| } | ||||
| 
 | ||||
|  | @ -31,7 +30,7 @@ func EwmaETA(style int, age float64, wcc ...WC) Decorator { | |||
| //	`normalizer` available implementations are [NopNormalizer|FixedIntervalTimeNormalizer|MaxTolerateTimeNormalizer]
 | ||||
| //
 | ||||
| //	`wcc` optional WC config
 | ||||
| func MovingAverageETA(style int, average MovingAverage, normalizer TimeNormalizer, wcc ...WC) Decorator { | ||||
| func MovingAverageETA(style TimeStyle, average MovingAverage, normalizer TimeNormalizer, wcc ...WC) Decorator { | ||||
| 	var wc WC | ||||
| 	for _, widthConf := range wcc { | ||||
| 		wc = widthConf | ||||
|  | @ -48,7 +47,7 @@ func MovingAverageETA(style int, average MovingAverage, normalizer TimeNormalize | |||
| 
 | ||||
| type movingAverageETA struct { | ||||
| 	WC | ||||
| 	style       int | ||||
| 	style       TimeStyle | ||||
| 	average     ewma.MovingAverage | ||||
| 	completeMsg *string | ||||
| 	normalizer  TimeNormalizer | ||||
|  | @ -59,7 +58,7 @@ func (d *movingAverageETA) Decor(st *Statistics) string { | |||
| 		return d.FormatMsg(*d.completeMsg) | ||||
| 	} | ||||
| 
 | ||||
| 	v := internal.Round(d.average.Value()) | ||||
| 	v := math.Round(d.average.Value()) | ||||
| 	remaining := d.normalizer(time.Duration((st.Total - st.Current) * int64(v))) | ||||
| 	hours := int64((remaining / time.Hour) % 60) | ||||
| 	minutes := int64((remaining / time.Minute) % 60) | ||||
|  | @ -105,7 +104,7 @@ func (d *movingAverageETA) OnCompleteMessage(msg string) { | |||
| //	`style` one of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS]
 | ||||
| //
 | ||||
| //	`wcc` optional WC config
 | ||||
| func AverageETA(style int, wcc ...WC) Decorator { | ||||
| func AverageETA(style TimeStyle, wcc ...WC) Decorator { | ||||
| 	var wc WC | ||||
| 	for _, widthConf := range wcc { | ||||
| 		wc = widthConf | ||||
|  | @ -121,7 +120,7 @@ func AverageETA(style int, wcc ...WC) Decorator { | |||
| 
 | ||||
| type averageETA struct { | ||||
| 	WC | ||||
| 	style       int | ||||
| 	style       TimeStyle | ||||
| 	startTime   time.Time | ||||
| 	completeMsg *string | ||||
| } | ||||
|  | @ -133,7 +132,7 @@ func (d *averageETA) Decor(st *Statistics) string { | |||
| 
 | ||||
| 	var str string | ||||
| 	timeElapsed := time.Since(d.startTime) | ||||
| 	v := internal.Round(float64(timeElapsed) / float64(st.Current)) | ||||
| 	v := math.Round(float64(timeElapsed) / float64(st.Current)) | ||||
| 	if math.IsInf(v, 0) || math.IsNaN(v) { | ||||
| 		v = 0 | ||||
| 	} | ||||
|  |  | |||
|  | @ -6,9 +6,9 @@ import ( | |||
| 	"github.com/VividCortex/ewma" | ||||
| ) | ||||
| 
 | ||||
| // MovingAverage is the interface that computes a moving average over a time-
 | ||||
| // series stream of numbers. The average may be over a window or exponentially
 | ||||
| // decaying.
 | ||||
| // MovingAverage is the interface that computes a moving average over
 | ||||
| // a time-series stream of numbers. The average may be over a window
 | ||||
| // or exponentially decaying.
 | ||||
| type MovingAverage interface { | ||||
| 	Add(float64) | ||||
| 	Value() float64 | ||||
|  | @ -57,7 +57,8 @@ func (s *medianEwma) Add(v float64) { | |||
| 	s.count++ | ||||
| } | ||||
| 
 | ||||
| // NewMedianEwma is ewma based MovingAverage, which gets its values from median MovingAverage.
 | ||||
| // NewMedianEwma is ewma based MovingAverage, which gets its values
 | ||||
| // from median MovingAverage.
 | ||||
| func NewMedianEwma(age ...float64) MovingAverage { | ||||
| 	return &medianEwma{ | ||||
| 		MovingAverage: ewma.NewMovingAverage(age...), | ||||
|  |  | |||
|  | @ -137,7 +137,8 @@ func EwmaSpeed(unit int, unitFormat string, age float64, wcc ...WC) Decorator { | |||
| 	return MovingAverageSpeed(unit, unitFormat, ewma.NewMovingAverage(age), wcc...) | ||||
| } | ||||
| 
 | ||||
| // MovingAverageSpeed decorator relies on MovingAverage implementation to calculate its average.
 | ||||
| // MovingAverageSpeed decorator relies on MovingAverage implementation
 | ||||
| // to calculate its average.
 | ||||
| //
 | ||||
| //	`unit` one of [0|UnitKiB|UnitKB] zero for no unit
 | ||||
| //
 | ||||
|  |  | |||
|  | @ -1,10 +1,12 @@ | |||
| package internal | ||||
| 
 | ||||
| import "math" | ||||
| 
 | ||||
| // Percentage is a helper function, to calculate percentage.
 | ||||
| func Percentage(total, current, width int64) int64 { | ||||
| 	if total <= 0 { | ||||
| 		return 0 | ||||
| 	} | ||||
| 	p := float64(width*current) / float64(total) | ||||
| 	return int64(Round(p)) | ||||
| 	return int64(math.Round(p)) | ||||
| } | ||||
|  |  | |||
|  | @ -1,49 +0,0 @@ | |||
| package internal | ||||
| 
 | ||||
| import "math" | ||||
| 
 | ||||
| const ( | ||||
| 	uvone    = 0x3FF0000000000000 | ||||
| 	mask     = 0x7FF | ||||
| 	shift    = 64 - 11 - 1 | ||||
| 	bias     = 1023 | ||||
| 	signMask = 1 << 63 | ||||
| 	fracMask = 1<<shift - 1 | ||||
| ) | ||||
| 
 | ||||
| // Round returns the nearest integer, rounding half away from zero.
 | ||||
| //
 | ||||
| // Special cases are:
 | ||||
| //	Round(±0) = ±0
 | ||||
| //	Round(±Inf) = ±Inf
 | ||||
| //	Round(NaN) = NaN
 | ||||
| func Round(x float64) float64 { | ||||
| 	// Round is a faster implementation of:
 | ||||
| 	//
 | ||||
| 	// func Round(x float64) float64 {
 | ||||
| 	//   t := Trunc(x)
 | ||||
| 	//   if Abs(x-t) >= 0.5 {
 | ||||
| 	//     return t + Copysign(1, x)
 | ||||
| 	//   }
 | ||||
| 	//   return t
 | ||||
| 	// }
 | ||||
| 	bits := math.Float64bits(x) | ||||
| 	e := uint(bits>>shift) & mask | ||||
| 	if e < bias { | ||||
| 		// Round abs(x) < 1 including denormals.
 | ||||
| 		bits &= signMask // +-0
 | ||||
| 		if e == bias-1 { | ||||
| 			bits |= uvone // +-1
 | ||||
| 		} | ||||
| 	} else if e < bias+shift { | ||||
| 		// Round any abs(x) >= 1 containing a fractional component [0,1).
 | ||||
| 		//
 | ||||
| 		// Numbers with larger exponents are returned unchanged since they
 | ||||
| 		// must be either an integer, infinity, or NaN.
 | ||||
| 		const half = 1 << (shift - 1) | ||||
| 		e -= bias | ||||
| 		bits += half >> e | ||||
| 		bits &^= fracMask >> e | ||||
| 	} | ||||
| 	return math.Float64frombits(bits) | ||||
| } | ||||
|  | @ -1,29 +1,30 @@ | |||
| package mpb | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"io" | ||||
| 	"sync" | ||||
| 	"time" | ||||
| 	"unicode/utf8" | ||||
| 
 | ||||
| 	"github.com/vbauerster/mpb/cwriter" | ||||
| ) | ||||
| 
 | ||||
| // ProgressOption is a function option which changes the default behavior of
 | ||||
| // progress pool, if passed to mpb.New(...ProgressOption)
 | ||||
| // ProgressOption is a function option which changes the default
 | ||||
| // behavior of progress pool, if passed to mpb.New(...ProgressOption).
 | ||||
| type ProgressOption func(*pState) | ||||
| 
 | ||||
| // WithWaitGroup provides means to have a single joint point.
 | ||||
| // If *sync.WaitGroup is provided, you can safely call just p.Wait()
 | ||||
| // without calling Wait() on provided *sync.WaitGroup.
 | ||||
| // Makes sense when there are more than one bar to render.
 | ||||
| // WithWaitGroup provides means to have a single joint point. If
 | ||||
| // *sync.WaitGroup is provided, you can safely call just p.Wait()
 | ||||
| // without calling Wait() on provided *sync.WaitGroup. Makes sense
 | ||||
| // when there are more than one bar to render.
 | ||||
| func WithWaitGroup(wg *sync.WaitGroup) ProgressOption { | ||||
| 	return func(s *pState) { | ||||
| 		s.uwg = wg | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // WithWidth overrides default width 80
 | ||||
| // WithWidth sets container width. Default is 80. Bars inherit this
 | ||||
| // width, as long as no BarWidth is applied.
 | ||||
| func WithWidth(w int) ProgressOption { | ||||
| 	return func(s *pState) { | ||||
| 		if w >= 0 { | ||||
|  | @ -32,16 +33,7 @@ func WithWidth(w int) ProgressOption { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| // WithFormat overrides default bar format "[=>-]"
 | ||||
| func WithFormat(format string) ProgressOption { | ||||
| 	return func(s *pState) { | ||||
| 		if utf8.RuneCountInString(format) == formatLen { | ||||
| 			s.format = format | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // WithRefreshRate overrides default 120ms refresh rate
 | ||||
| // WithRefreshRate overrides default 120ms refresh rate.
 | ||||
| func WithRefreshRate(d time.Duration) ProgressOption { | ||||
| 	return func(s *pState) { | ||||
| 		if d < 10*time.Millisecond { | ||||
|  | @ -59,22 +51,25 @@ func WithManualRefresh(ch <-chan time.Time) ProgressOption { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| // WithCancel provide your cancel channel,
 | ||||
| // which you plan to close at some point.
 | ||||
| func WithCancel(ch <-chan struct{}) ProgressOption { | ||||
| // WithContext provided context will be used for cancellation purposes.
 | ||||
| func WithContext(ctx context.Context) ProgressOption { | ||||
| 	return func(s *pState) { | ||||
| 		s.cancel = ch | ||||
| 		if ctx == nil { | ||||
| 			return | ||||
| 		} | ||||
| 		s.ctx = ctx | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // WithShutdownNotifier provided chanel will be closed, after all bars have been rendered.
 | ||||
| // WithShutdownNotifier provided chanel will be closed, after all bars
 | ||||
| // have been rendered.
 | ||||
| func WithShutdownNotifier(ch chan struct{}) ProgressOption { | ||||
| 	return func(s *pState) { | ||||
| 		s.shutdownNotifier = ch | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // WithOutput overrides default output os.Stdout
 | ||||
| // WithOutput overrides default output os.Stdout.
 | ||||
| func WithOutput(w io.Writer) ProgressOption { | ||||
| 	return func(s *pState) { | ||||
| 		if w == nil { | ||||
|  |  | |||
|  | @ -1,15 +0,0 @@ | |||
| //+build go1.7
 | ||||
| 
 | ||||
| package mpb | ||||
| 
 | ||||
| import "context" | ||||
| 
 | ||||
| // WithContext provided context will be used for cancellation purposes
 | ||||
| func WithContext(ctx context.Context) ProgressOption { | ||||
| 	return func(s *pState) { | ||||
| 		if ctx == nil { | ||||
| 			panic("ctx must not be nil") | ||||
| 		} | ||||
| 		s.cancel = ctx.Done() | ||||
| 	} | ||||
| } | ||||
|  | @ -2,6 +2,7 @@ package mpb | |||
| 
 | ||||
| import ( | ||||
| 	"container/heap" | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"io/ioutil" | ||||
|  | @ -17,8 +18,6 @@ const ( | |||
| 	prr = 120 * time.Millisecond | ||||
| 	// default width
 | ||||
| 	pwidth = 80 | ||||
| 	// default format
 | ||||
| 	pformat = "[=>-]" | ||||
| ) | ||||
| 
 | ||||
| // Progress represents the container that renders Progress bars
 | ||||
|  | @ -42,24 +41,24 @@ type pState struct { | |||
| 	pMatrix         map[int][]chan int | ||||
| 	aMatrix         map[int][]chan int | ||||
| 
 | ||||
| 	// following are provided by user
 | ||||
| 	// following are provided/overrided by user
 | ||||
| 	ctx              context.Context | ||||
| 	uwg              *sync.WaitGroup | ||||
| 	manualRefreshCh  <-chan time.Time | ||||
| 	cancel           <-chan struct{} | ||||
| 	shutdownNotifier chan struct{} | ||||
| 	waitBars         map[*Bar]*Bar | ||||
| 	debugOut         io.Writer | ||||
| } | ||||
| 
 | ||||
| // New creates new Progress instance, which orchestrates bars rendering process.
 | ||||
| // Accepts mpb.ProgressOption funcs for customization.
 | ||||
| // New creates new Progress instance, which orchestrates bars rendering
 | ||||
| // process. Accepts mpb.ProgressOption funcs for customization.
 | ||||
| func New(options ...ProgressOption) *Progress { | ||||
| 	pq := make(priorityQueue, 0) | ||||
| 	heap.Init(&pq) | ||||
| 	s := &pState{ | ||||
| 		ctx:      context.Background(), | ||||
| 		bHeap:    &pq, | ||||
| 		width:    pwidth, | ||||
| 		format:   pformat, | ||||
| 		cw:       cwriter.New(os.Stdout), | ||||
| 		rr:       prr, | ||||
| 		waitBars: make(map[*Bar]*Bar), | ||||
|  | @ -84,12 +83,28 @@ func New(options ...ProgressOption) *Progress { | |||
| 
 | ||||
| // AddBar creates a new progress bar and adds to the container.
 | ||||
| func (p *Progress) AddBar(total int64, options ...BarOption) *Bar { | ||||
| 	return p.Add(total, newDefaultBarFiller(), options...) | ||||
| } | ||||
| 
 | ||||
| // AddSpinner creates a new spinner bar and adds to the container.
 | ||||
| func (p *Progress) AddSpinner(total int64, alignment SpinnerAlignment, options ...BarOption) *Bar { | ||||
| 	filler := &spinnerFiller{ | ||||
| 		frames:    defaultSpinnerStyle, | ||||
| 		alignment: alignment, | ||||
| 	} | ||||
| 	return p.Add(total, filler, options...) | ||||
| } | ||||
| 
 | ||||
| // Add creates a bar which renders itself by provided filler.
 | ||||
| func (p *Progress) Add(total int64, filler Filler, options ...BarOption) *Bar { | ||||
| 	if filler == nil { | ||||
| 		filler = newDefaultBarFiller() | ||||
| 	} | ||||
| 	p.wg.Add(1) | ||||
| 	result := make(chan *Bar) | ||||
| 	select { | ||||
| 	case p.operateState <- func(s *pState) { | ||||
| 		options = append(options, barWidth(s.width), barFormat(s.format)) | ||||
| 		b := newBar(p.wg, s.idCounter, total, s.cancel, options...) | ||||
| 		b := newBar(s.ctx, p.wg, filler, s.idCounter, s.width, total, options...) | ||||
| 		if b.runningBar != nil { | ||||
| 			s.waitBars[b.runningBar] = b | ||||
| 		} else { | ||||
|  | @ -106,10 +121,10 @@ func (p *Progress) AddBar(total int64, options ...BarOption) *Bar { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Abort is only effective while bar progress is running,
 | ||||
| // it means remove bar now without waiting for its completion.
 | ||||
| // If bar is already completed, there is nothing to abort.
 | ||||
| // If you need to remove bar after completion, use BarRemoveOnComplete BarOption.
 | ||||
| // Abort is only effective while bar progress is running, it means
 | ||||
| // remove bar now without waiting for its completion. If bar is already
 | ||||
| // completed, there is nothing to abort. If you need to remove bar
 | ||||
| // after completion, use BarRemoveOnComplete BarOption.
 | ||||
| func (p *Progress) Abort(b *Bar, remove bool) { | ||||
| 	select { | ||||
| 	case p.operateState <- func(s *pState) { | ||||
|  | @ -145,9 +160,10 @@ func (p *Progress) BarCount() int { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Wait first waits for user provided *sync.WaitGroup, if any,
 | ||||
| // then waits far all bars to complete and finally shutdowns master goroutine.
 | ||||
| // After this method has been called, there is no way to reuse *Progress instance.
 | ||||
| // Wait first waits for user provided *sync.WaitGroup, if any, then
 | ||||
| // waits far all bars to complete and finally shutdowns master goroutine.
 | ||||
| // After this method has been called, there is no way to reuse *Progress
 | ||||
| // instance.
 | ||||
| func (p *Progress) Wait() { | ||||
| 	if p.uwg != nil { | ||||
| 		p.uwg.Wait() | ||||
|  | @ -205,8 +221,8 @@ func (s *pState) flush(lineCount int) error { | |||
| 		defer func() { | ||||
| 			if frameReader.toShutdown { | ||||
| 				// shutdown at next flush, in other words decrement underlying WaitGroup
 | ||||
| 				// only after the bar with completed state has been flushed.
 | ||||
| 				// this ensures no bar ends up with less than 100% rendered.
 | ||||
| 				// only after the bar with completed state has been flushed. this
 | ||||
| 				// ensures no bar ends up with less than 100% rendered.
 | ||||
| 				s.shutdownPending = append(s.shutdownPending, bar) | ||||
| 				if replacementBar, ok := s.waitBars[bar]; ok { | ||||
| 					heap.Push(s.bHeap, replacementBar) | ||||
|  |  | |||
|  | @ -0,0 +1,48 @@ | |||
| package mpb | ||||
| 
 | ||||
| import ( | ||||
| 	"io" | ||||
| 	"strings" | ||||
| 	"unicode/utf8" | ||||
| 
 | ||||
| 	"github.com/vbauerster/mpb/decor" | ||||
| ) | ||||
| 
 | ||||
| // SpinnerAlignment enum.
 | ||||
| type SpinnerAlignment int | ||||
| 
 | ||||
| // SpinnerAlignment kinds.
 | ||||
| const ( | ||||
| 	SpinnerOnLeft SpinnerAlignment = iota | ||||
| 	SpinnerOnMiddle | ||||
| 	SpinnerOnRight | ||||
| ) | ||||
| 
 | ||||
| var defaultSpinnerStyle = []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"} | ||||
| 
 | ||||
| type spinnerFiller struct { | ||||
| 	frames    []string | ||||
| 	count     uint | ||||
| 	alignment SpinnerAlignment | ||||
| } | ||||
| 
 | ||||
| func (s *spinnerFiller) Fill(w io.Writer, width int, stat *decor.Statistics) { | ||||
| 
 | ||||
| 	frame := s.frames[s.count%uint(len(s.frames))] | ||||
| 	frameWidth := utf8.RuneCountInString(frame) | ||||
| 
 | ||||
| 	if width < frameWidth { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	switch rest := width - frameWidth; s.alignment { | ||||
| 	case SpinnerOnLeft: | ||||
| 		io.WriteString(w, frame+strings.Repeat(" ", rest)) | ||||
| 	case SpinnerOnMiddle: | ||||
| 		str := strings.Repeat(" ", rest/2) + frame + strings.Repeat(" ", rest/2+rest%2) | ||||
| 		io.WriteString(w, str) | ||||
| 	case SpinnerOnRight: | ||||
| 		io.WriteString(w, strings.Repeat(" ", rest)+frame) | ||||
| 	} | ||||
| 	s.count++ | ||||
| } | ||||
|  | @ -38,7 +38,7 @@ github.com/containernetworking/cni/pkg/invoke | |||
| github.com/containernetworking/cni/pkg/types | ||||
| github.com/containernetworking/cni/pkg/types/020 | ||||
| github.com/containernetworking/cni/pkg/types/current | ||||
| # github.com/containers/image v2.0.0+incompatible | ||||
| # github.com/containers/image v2.0.1+incompatible | ||||
| github.com/containers/image/copy | ||||
| github.com/containers/image/directory | ||||
| github.com/containers/image/docker | ||||
|  | @ -318,7 +318,7 @@ github.com/ulikunitz/xz/internal/hash | |||
| github.com/vbatts/tar-split/tar/asm | ||||
| github.com/vbatts/tar-split/tar/storage | ||||
| github.com/vbatts/tar-split/archive/tar | ||||
| # github.com/vbauerster/mpb v3.3.4+incompatible | ||||
| # github.com/vbauerster/mpb v3.4.0+incompatible | ||||
| github.com/vbauerster/mpb | ||||
| github.com/vbauerster/mpb/decor | ||||
| github.com/vbauerster/mpb/cwriter | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue