mirror of
https://github.com/wagoodman/dive
synced 2026-03-14 14:25:50 +01:00
Reintegrate fork joschi/dive (#570)
* chore: configure Renovate (#1) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(deps): update github artifact actions to v4 (#20) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(deps): update docker/login-action action to v3 (#19) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(deps): update actions/setup-go action to v5 (#18) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(deps): update actions/checkout action to v4 (#17) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(deps): update actions/cache action to v4 (#16) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * build: use Go 1.23.x to build project * chore(deps): update build tools - golangci-lint v1.16.0: https://github.com/golangci/golangci-lint/releases/tag/v1.61.0 - GoReleaser v1.61.0: https://github.com/goreleaser/goreleaser/releases/tag/v1.26.2 - Chronicle v0.8.0: https://github.com/anchore/chronicle/releases/tag/v0.8.0 - Glow v1.5.1: https://github.com/charmbracelet/glow/releases/tag/v1.5.1 * chore: temporarily lower coverage threshold to 30% Old coverage: ``` go test -race -coverprofile ./.tmp/unit-coverage-details.txt ./... ? github.com/wagoodman/dive [no test files] ? github.com/wagoodman/dive/cmd [no test files] ? github.com/wagoodman/dive/dive [no test files] ? github.com/wagoodman/dive/dive/image [no test files] ok github.com/wagoodman/dive/dive/filetree 0.032s coverage: 58.0% of statements ? github.com/wagoodman/dive/dive/image/podman [no test files] ok github.com/wagoodman/dive/dive/image/docker 0.076s coverage: 43.6% of statements ? github.com/wagoodman/dive/runtime/ui [no test files] ? github.com/wagoodman/dive/runtime/ui/format [no test files] ? github.com/wagoodman/dive/runtime/ui/key [no test files] ok github.com/wagoodman/dive/runtime 0.531s coverage: 53.3% of statements ok github.com/wagoodman/dive/runtime/ci 0.087s coverage: 62.1% of statements ok github.com/wagoodman/dive/runtime/export 0.096s coverage: 100.0% of statements ? github.com/wagoodman/dive/runtime/ui/layout/compound [no test files] ? github.com/wagoodman/dive/runtime/ui/view [no test files] ok github.com/wagoodman/dive/runtime/ui/layout 0.021s coverage: 82.6% of statements ? github.com/wagoodman/dive/utils [no test files] ok github.com/wagoodman/dive/runtime/ui/viewmodel 1.202s coverage: 55.3% of statements Coverage: 57.5% ``` New coverage: ``` go test -race -coverprofile ./.tmp/unit-coverage-details.txt ./... github.com/wagoodman/dive/dive coverage: 0.0% of statements github.com/wagoodman/dive/cmd coverage: 0.0% of statements github.com/wagoodman/dive coverage: 0.0% of statements github.com/wagoodman/dive/dive/image coverage: 0.0% of statements github.com/wagoodman/dive/dive/image/podman coverage: 0.0% of statements ok github.com/wagoodman/dive/dive/filetree 1.027s coverage: 58.0% of statements ok github.com/wagoodman/dive/dive/image/docker 1.064s coverage: 43.6% of statements github.com/wagoodman/dive/runtime/ui coverage: 0.0% of statements github.com/wagoodman/dive/runtime/ui/format coverage: 0.0% of statements github.com/wagoodman/dive/runtime/ui/key coverage: 0.0% of statements github.com/wagoodman/dive/runtime/ui/layout/compound coverage: 0.0% of statements github.com/wagoodman/dive/runtime/ui/view coverage: 0.0% of statements ok github.com/wagoodman/dive/runtime 1.382s coverage: 53.3% of statements ok github.com/wagoodman/dive/runtime/ci 1.055s coverage: 62.1% of statements github.com/wagoodman/dive/utils coverage: 0.0% of statements ok github.com/wagoodman/dive/runtime/export 1.048s coverage: 100.0% of statements ok github.com/wagoodman/dive/runtime/ui/layout 1.012s coverage: 82.6% of statements ok github.com/wagoodman/dive/runtime/ui/viewmodel 2.202s coverage: 55.3% of statements Coverage: 31.8% Coverage below threshold of 55.0% ``` * chore(deps): update fountainhead/action-wait-for-check action to v1.2.0 (#25) https://github.com/fountainhead/action-wait-for-check/releases/tag/v1.2.0 * ci: validate Renovate configuration file on build * ci: add gomodTidy and gomodUpdateImportPaths post update options to Renovate * fix(deps): update module github.com/dustin/go-humanize to v1.0.1 (#5) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(deps): update alpine docker tag to v3.20 (#7) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * fix(deps): update github.com/awesome-gocui/keybinding digest to 8602903 (#2) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * fix(deps): update module github.com/spf13/cobra to v0.0.7 (#6) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * fix(deps): update module github.com/fatih/color to v1.18.0 (#9) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * fix(deps): update module github.com/google/uuid to v1.6.0 (#10) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * fix(deps): update module github.com/sergi/go-diff to v1.3.1 (#11) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * fix(deps): update module github.com/cespare/xxhash to v2 (#21) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * fix(deps): update module github.com/docker/docker to v24.0.9+incompatible (#3) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * fix(deps): update module github.com/spf13/viper to v1.19.0 (#14) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * fix(deps): update module github.com/sirupsen/logrus to v1.9.3 (#12) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * fix(deps): update module golang.org/x/net to v0.30.0 (#15) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * fix(deps): update module github.com/spf13/cobra to v1 (#23) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * fix(deps): update module github.com/docker/docker to v27 (#22) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * fix(deps): update module github.com/docker/cli to v27.3.1+incompatible (#27) * ci: run Linux acceptance tests in parallel (#28) * chore(deps): update module logrusorgru/aurora to v4 (major) (#29) github.com/logrusorgru/aurora changed the license from the WTFPL to the Unlicense due to pkg.go.dev restrictions. Refs304bc2c7ed* build: squash Docker image layers (#30) Refs wagoodman/dive#535 * chore(deps): update Docker CLI version to 27.3.1 (#31) * ci: update GoReleaser configuration (#33) * chore(deps): update GoReleaser to v2.4.4 (major) (#32) * revert: run Linux acceptance tests in parallel This reverts commit19714728c9. * ci: fix image name, actions/checkout@v4, docker/login-action name * fix: create `~/.docker` directory in Docker images (#34) Error: ``` ❯ docker run -ti --rm -v /var/run/docker.sock:/var/run/docker.sock joschi/dive:0.13.0-alpha.1 busybox:latest Image Source: docker://busybox:latest Extracting image from docker-engine... (this can take a while for large images) > could not determine docker host: stat /root/.docker: no such file or directory cannot fetch image unable to parse docker host `` ``` * build: added ppc64le support (wagoodman/dive#551) Co-authored-by: Pooja Shah <53046887+pooja0805@users.noreply.github.com> * fix: fix OCI format, GZIP file can be <1024 bytes (wagoodman/dive#511) Fixes wagoodman/dive#507 Fixes wagoodman/dive#510 Fixes wagoodman/dive#526 Fixes wagoodman/dive#534 Co-authored-by: Maddog2050 <17902029+Maddog2050@users.noreply.github.com> * docs: fix typos (wagoodman/dive#531) Found via `codespell -L ot,te` and `typos --hidden --format brief` Closes wagoodman/dive#464 Co-authored-by: Kian-Meng Ang <kianmeng@cpan.org> * docs: add more Windows installation options to the README (wagoodman/dive#470) Fixes wagoodman/dive#346 Co-authored-by: Nikolas Grottendieck <git@nikolasgrottendieck.com> * docs: Update README.md (wagoodman/dive#506) Made copy and paste easy for zsh users - auto escape character issue Co-authored-by: YóUnǎi <c0d3r.nodiru.gaji@gmail.com> * docs: update the install command to the latest Go version (wagoodman/dive#509) `go get` no longer works with recent versions of `go`. Co-authored-by: Trevor Gross <tmgross@umich.edu> * chore: remove repetitive words (wagoodman/dive#515) Co-authored-by: thirdkeyword <fliterdashen@gmail.com> Signed-off-by: thirdkeyword <fliterdashen@gmail.com> * fix: close tmp files (wagoodman/dive#517) Co-authored-by: guoguangwu <guoguangwug@gmail.com> Signed-off-by: guoguangwu <guoguangwug@gmail.com> * chore: fix phony ci-release target (wagoodman/dive#530) Co-authored-by: Richard Steinmetz <richard@steinmetz.cloud> * docs: warning message for Snap approach on Ubuntu/Debian (wagoodman/dive#552) https://github.com/wagoodman/dive/issues/546 demonstrates the trouble it may cause. Co-authored-by: Zhang Yuanfeng <71358306+YuanfengZhang@users.noreply.github.com> * docs: update curl commands in README (wagoodman/dive#533) Co-authored-by: Ali Afsharzadeh <afsharzadeh8@gmail.com> * feat: improve "Fetching" message (wagoodman/dive#482) The Fetching... message was confusing. This replaces it with a clearer messages to avoid confusion. Additional fix: show original error unless image is not found Only try doing a pull if the image isn't found. Everything else should just generate the error so the user can fix it. Fixes wagoodman/dive#360 Co-authored-by: Christian Höltje <docwhat@gerf.org> * fix: line wrap toggle now updates status bar indicator (wagoodman/dive#497) Fixes wagoodman/dive#496 Co-authored-by: Scott Moore <scott.moore@viavisolutions.com> * feat: show setuid, setgid and sticky attributes (wagoodman/dive#524) See https://en.wikipedia.org/wiki/File-system_permissions#Notation_of_traditional_Unix_permissions Co-authored-by: Alexander Yastrebov <alexander.yastrebov@zalando.de> * feat(docker): Honor the host specified in current docker context (wagoodman/dive#490) This patch adds support for detecting the "docker host" to connect to which is set in the current docker context. One can have multiple such contexts and one of them can be activated via `docker context use <context-name>`. Fixes wagoodman/dive#397 Fixes wagoodman/dive#408 Fixes wagoodman/dive#412 Fixes wagoodman/dive#463 Fixes wagoodman/dive#495 Co-authored-by: Rajiv Kushwaha <raj25by10@gmail.com> * fix: can't inspect ubuntu:24.04 with Podman (wagoodman/dive#476) The problem was caused by `net/url.Parse()`: ``` panic: parse "podman://ubuntu:24.04": invalid port ":24.04" after host ``` Failure: ``` $ ./dive podman://ubuntu:24.04 Image Source: docker://podman://ubuntu:24.04 Fetching image... (this can take a while for large images) Handler not available locally. Trying to pull 'podman://ubuntu:24.04'... cannot fetch image cannot find docker client executable ``` Success: ``` $ ./dive podman://ubuntu:24.04 Image Source: podman://ubuntu:24.04 ... $ ./dive ubuntu:24.04 --source podman Image Source: podman://ubuntu:24.04 ... $ ./dive podman://ubuntu:24.04 --source docker Image Source: podman://ubuntu:24.04 ... ``` Fixes wagoodman/dive#475 Co-authored-by: Anatoli Babenia <anatoli@rainforce.org> * fix: enable layer scrolling (wagoodman/dive#521) Fixes wagoodman/dive#469 Fixes wagoodman/dive#494 Fixes wagoodman/dive#540 Refs wagoodman/dive#473 Refs wagoodman/dive#478 Refs wagoodman/dive#520 Co-authored-by: st-gr <38470677+st-gr@users.noreply.github.com> * feat: add layer-wise filesystem information to the analysis json file (wagoodman/dive#458) Add layer-wise filesystem information to the analysis which is written to a JSON file when running dive with `-j` or `--json` flag. Co-authored-by: Akash Nayak <akash19nayak@gmail.com> * feat: add size to Layer Details view (wagoodman/dive#522) Refs https://github.com/wagoodman/dive/issues/469#issuecomment-1685322270 Co-authored-by: st-gr <38470677+st-gr@users.noreply.github.com> * feat: add CTRL+e for extracting current focused file Refs wagoodman/dive#224 Co-authored-by: kaedwen <kaedwen@heinrich.blue> * feat: vim-like arrow, scroll, and close filtering motions (wagoodman/dive#501) * Adding configurable keybindings for up/down arrows (`k` and `j` vim motions can be used as alternative to up/down arrows). Thanks to @gwendolyngoetz for implementing this feature [Adding configurable keybindings for up/down arrows #499](https://github.com/wagoodman/dive/pull/499) * Add configurable keybindings for left/right arrows (`h` and `l` vim motions can be used as alternative to left/right arrows) * Add `u` and `d` keys for page up/down alternatives (I didn't want to replace default `ctrl+u` toggle-unmodified-files keybinding so I used`u` and `d` like `Vimium` extension ) * Add `esc` key to close filtering (Implemented a new method by utilizing the existing toggle filter method, without touching its current behavior) Refs wagoodman/dive#129 Refs wagoodman/dive#415 Refs wagoodman/dive#499 Co-authored-by: Gwendolyn Goetz <gwendolyngoetz@users.noreply.github.com> Co-authored-by: Mehmet Ümit Özden <ozdenmehmetumit@gmail.com> * fix: gracefully check for Docker configuration (#37) Refs https://github.com/jesseduffield/lazydocker/pull/489 * refactor!: migrate Go module from wagoodman/dive to joschi/dive (#36) * refactor!: migrate Go module from wagoodman/dive to joschi/dive * fix: bring back :latest Docker image * feat: create multi-arch container images with AMD64 and ARM64 (#38) * feat: create multi-arch container images with AMD64 and ARM64 * fix: use joschi/dive:latest-amd64 in CI * ci: use correct container registry in `ci-test-docker-image` (#39) * docs: fix Homebrew instructions in README * fix(deps): update module golang.org/x/net to v0.31.0 (#40) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(deps): update suzuki-shunsuke/github-action-renovate-config-validator action to v1.1.1 (#43) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * fix(deps): update module golang.org/x/net to v0.32.0 (#44) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(deps): update alpine docker tag to v3.21 (#45) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * fix(deps): update module github.com/docker/cli to v27.4.1+incompatible (#46) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * fix(deps): update module golang.org/x/net to v0.33.0 (#48) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * fix(deps): update module github.com/docker/docker to v27.4.1+incompatible (#47) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * fix(deps): update module golang.org/x/net to v0.34.0 (#49) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * fix(deps): update module github.com/spf13/afero to v1.12.0 (#50) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * fix(deps): update module github.com/docker/cli to v27.5.0+incompatible (#51) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * fix(deps): update module github.com/docker/docker to v27.5.0+incompatible (#52) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * fix(deps): update module github.com/docker/cli to v27.5.1+incompatible (#54) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * fix(deps): update module github.com/docker/docker to v27.5.1+incompatible (#55) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * feat: support oci zstd compression (#53) Co-authored-by: Jochen Schalanda <jochen@schalanda.name> * fix(deps): update module github.com/klauspost/compress to v1.17.11 (#57) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * fix(deps): update module golang.org/x/net to v0.35.0 (#59) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * fix(deps): update module github.com/spf13/cobra to v1.9.0 (#60) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * fix(deps): update module github.com/spf13/cobra to v1.9.1 (#62) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * fix(deps): update module github.com/klauspost/compress to v1.18.0 (#63) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * fix(deps): update module github.com/docker/cli to v28 (#64) * fix(deps): update module github.com/docker/cli to v28 * Replace deprecated function in engineResolver.fetchArchive() --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Jochen Schalanda <jochen@schalanda.name> * build: bump Go toolchain from 1.23.x to 1.24.x (#61) * build: bump Go toolchain from 1.23.x to 1.24.x * chore(deps): bump golangci-lint from v1.61.0 to v1.64.5 * fix: non-constant format string in call to (*testing.common).Errorf * test: add layer_set_state_test * fix(deps): update module github.com/docker/cli to v28.0.1+incompatible (#66) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * fix(deps): update module github.com/docker/docker to v28.0.1+incompatible (#65) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * fix(deps): update module golang.org/x/net to v0.36.0 (#73) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * fix(deps): update module golang.org/x/net to v0.37.0 (#74) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * fix(deps): update module github.com/spf13/viper to v1.20.0 (#76) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * fix(deps): update module github.com/spf13/afero to v1.14.0 (#75) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * refactor!: revert module name back to github.com/wagoodman/dive Partially revertse46f931a8a* chore: remove Renovate configuration Revertsaa75fbf36f* Revert "ci: validate Renovate configuration file on build" This reverts commit8d938774e2. --------- Signed-off-by: thirdkeyword <fliterdashen@gmail.com> Signed-off-by: guoguangwu <guoguangwug@gmail.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Pooja Shah <53046887+pooja0805@users.noreply.github.com> Co-authored-by: Maddog2050 <17902029+Maddog2050@users.noreply.github.com> Co-authored-by: Kian-Meng Ang <kianmeng@cpan.org> Co-authored-by: Nikolas Grottendieck <git@nikolasgrottendieck.com> Co-authored-by: YóUnǎi <c0d3r.nodiru.gaji@gmail.com> Co-authored-by: Trevor Gross <tmgross@umich.edu> Co-authored-by: thirdkeyword <fliterdashen@gmail.com> Co-authored-by: guoguangwu <guoguangwug@gmail.com> Co-authored-by: Richard Steinmetz <richard@steinmetz.cloud> Co-authored-by: Zhang Yuanfeng <71358306+YuanfengZhang@users.noreply.github.com> Co-authored-by: Ali Afsharzadeh <afsharzadeh8@gmail.com> Co-authored-by: Christian Höltje <docwhat@gerf.org> Co-authored-by: Scott Moore <scott.moore@viavisolutions.com> Co-authored-by: Alexander Yastrebov <alexander.yastrebov@zalando.de> Co-authored-by: Rajiv Kushwaha <raj25by10@gmail.com> Co-authored-by: Anatoli Babenia <anatoli@rainforce.org> Co-authored-by: st-gr <38470677+st-gr@users.noreply.github.com> Co-authored-by: Akash Nayak <akash19nayak@gmail.com> Co-authored-by: kaedwen <kaedwen@heinrich.blue> Co-authored-by: Gwendolyn Goetz <gwendolyngoetz@users.noreply.github.com> Co-authored-by: Mehmet Ümit Özden <ozdenmehmetumit@gmail.com> Co-authored-by: steven-halaka <steven.halaka@twosixtech.com>
This commit is contained in:
parent
925cdd8648
commit
e65b32c4d7
69 changed files with 5545 additions and 460 deletions
|
|
@ -5,6 +5,7 @@ permit:
|
||||||
- MPL.*
|
- MPL.*
|
||||||
- ISC
|
- ISC
|
||||||
- WTFPL
|
- WTFPL
|
||||||
|
- Unlicense
|
||||||
|
|
||||||
ignore-packages:
|
ignore-packages:
|
||||||
# crypto/internal/boring is released under the openSSL license as a part of the Golang Standard Library
|
# crypto/internal/boring is released under the openSSL license as a part of the Golang Standard Library
|
||||||
|
|
|
||||||
2
.data/Dockerfile.minimal
Normal file
2
.data/Dockerfile.minimal
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
FROM scratch
|
||||||
|
COPY README.md /README.md
|
||||||
BIN
.data/test-estargz-image.tar
Normal file
BIN
.data/test-estargz-image.tar
Normal file
Binary file not shown.
BIN
.data/test-gzip-image.tar
Normal file
BIN
.data/test-gzip-image.tar
Normal file
Binary file not shown.
BIN
.data/test-oci-estargz-image.tar
Normal file
BIN
.data/test-oci-estargz-image.tar
Normal file
Binary file not shown.
BIN
.data/test-oci-gzip-image.tar
Normal file
BIN
.data/test-oci-gzip-image.tar
Normal file
Binary file not shown.
BIN
.data/test-oci-uncompressed-image.tar
Normal file
BIN
.data/test-oci-uncompressed-image.tar
Normal file
Binary file not shown.
BIN
.data/test-oci-zstd-image.tar
Normal file
BIN
.data/test-oci-zstd-image.tar
Normal file
Binary file not shown.
BIN
.data/test-uncompressed-image.tar
Normal file
BIN
.data/test-uncompressed-image.tar
Normal file
Binary file not shown.
BIN
.data/test-zstd-image.tar
Normal file
BIN
.data/test-zstd-image.tar
Normal file
Binary file not shown.
10
.github/actions/bootstrap/action.yaml
vendored
10
.github/actions/bootstrap/action.yaml
vendored
|
|
@ -4,7 +4,7 @@ inputs:
|
||||||
go-version:
|
go-version:
|
||||||
description: "Go version to install"
|
description: "Go version to install"
|
||||||
required: true
|
required: true
|
||||||
default: "1.20.x"
|
default: "1.24.x"
|
||||||
use-go-cache:
|
use-go-cache:
|
||||||
description: "Restore go cache"
|
description: "Restore go cache"
|
||||||
required: true
|
required: true
|
||||||
|
|
@ -24,13 +24,13 @@ inputs:
|
||||||
runs:
|
runs:
|
||||||
using: "composite"
|
using: "composite"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/setup-go@v3
|
- uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ${{ inputs.go-version }}
|
go-version: ${{ inputs.go-version }}
|
||||||
|
|
||||||
- name: Restore tool cache
|
- name: Restore tool cache
|
||||||
id: tool-cache
|
id: tool-cache
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ${{ github.workspace }}/.tmp
|
path: ${{ github.workspace }}/.tmp
|
||||||
key: ${{ inputs.cache-key-prefix }}-${{ runner.os }}-tool-${{ hashFiles('Makefile') }}
|
key: ${{ inputs.cache-key-prefix }}-${{ runner.os }}-tool-${{ hashFiles('Makefile') }}
|
||||||
|
|
@ -40,7 +40,7 @@ runs:
|
||||||
- name: Restore go module cache
|
- name: Restore go module cache
|
||||||
id: go-mod-cache
|
id: go-mod-cache
|
||||||
if: inputs.use-go-cache == 'true'
|
if: inputs.use-go-cache == 'true'
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/go/pkg/mod
|
~/go/pkg/mod
|
||||||
|
|
@ -56,7 +56,7 @@ runs:
|
||||||
- name: Restore go build cache
|
- name: Restore go build cache
|
||||||
id: go-cache
|
id: go-cache
|
||||||
if: inputs.use-go-cache == 'true'
|
if: inputs.use-go-cache == 'true'
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/.cache/go-build
|
~/.cache/go-build
|
||||||
|
|
|
||||||
36
.github/workflows/release.yaml
vendored
36
.github/workflows/release.yaml
vendored
|
|
@ -11,7 +11,7 @@ jobs:
|
||||||
environment: release
|
environment: release
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Check if tag already exists
|
- name: Check if tag already exists
|
||||||
# note: this will fail if the tag already exists
|
# note: this will fail if the tag already exists
|
||||||
|
|
@ -20,7 +20,7 @@ jobs:
|
||||||
git tag ${{ github.event.inputs.version }}
|
git tag ${{ github.event.inputs.version }}
|
||||||
|
|
||||||
- name: Check static analysis results
|
- name: Check static analysis results
|
||||||
uses: fountainhead/action-wait-for-check@v1.1.0
|
uses: fountainhead/action-wait-for-check@v1.2.0
|
||||||
id: static-analysis
|
id: static-analysis
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
@ -29,7 +29,7 @@ jobs:
|
||||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||||
|
|
||||||
- name: Check unit test results
|
- name: Check unit test results
|
||||||
uses: fountainhead/action-wait-for-check@v1.1.0
|
uses: fountainhead/action-wait-for-check@v1.2.0
|
||||||
id: unit
|
id: unit
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
@ -38,7 +38,7 @@ jobs:
|
||||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||||
|
|
||||||
- name: Check acceptance test results (linux)
|
- name: Check acceptance test results (linux)
|
||||||
uses: fountainhead/action-wait-for-check@v1.1.0
|
uses: fountainhead/action-wait-for-check@v1.2.0
|
||||||
id: acceptance-linux
|
id: acceptance-linux
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
@ -47,7 +47,7 @@ jobs:
|
||||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||||
|
|
||||||
- name: Check acceptance test results (mac)
|
- name: Check acceptance test results (mac)
|
||||||
uses: fountainhead/action-wait-for-check@v1.1.0
|
uses: fountainhead/action-wait-for-check@v1.2.0
|
||||||
id: acceptance-mac
|
id: acceptance-mac
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
@ -56,7 +56,7 @@ jobs:
|
||||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||||
|
|
||||||
- name: Check acceptance test results (windows)
|
- name: Check acceptance test results (windows)
|
||||||
uses: fountainhead/action-wait-for-check@v1.1.0
|
uses: fountainhead/action-wait-for-check@v1.2.0
|
||||||
id: acceptance-windows
|
id: acceptance-windows
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
@ -64,7 +64,6 @@ jobs:
|
||||||
checkName: "Acceptance tests (Windows)"
|
checkName: "Acceptance tests (Windows)"
|
||||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||||
|
|
||||||
|
|
||||||
- name: Quality gate
|
- name: Quality gate
|
||||||
if: steps.static-analysis.outputs.conclusion != 'success' || steps.unit.outputs.conclusion != 'success' || steps.acceptance-linux.outputs.conclusion != 'success' || steps.acceptance-mac.outputs.conclusion != 'success' || steps.acceptance-windows.outputs.conclusion != 'success'
|
if: steps.static-analysis.outputs.conclusion != 'success' || steps.unit.outputs.conclusion != 'success' || steps.acceptance-linux.outputs.conclusion != 'success' || steps.acceptance-mac.outputs.conclusion != 'success' || steps.acceptance-windows.outputs.conclusion != 'success'
|
||||||
run: |
|
run: |
|
||||||
|
|
@ -82,15 +81,25 @@ jobs:
|
||||||
permissions:
|
permissions:
|
||||||
# for tagging
|
# for tagging
|
||||||
contents: write
|
contents: write
|
||||||
|
# for pushing container images
|
||||||
|
packages: write
|
||||||
|
env:
|
||||||
|
REGISTRY: ghcr.io
|
||||||
|
IMAGE_NAME: ${{ github.repository }}
|
||||||
steps:
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/checkout@v3
|
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Bootstrap environment
|
- name: Bootstrap environment
|
||||||
uses: ./.github/actions/bootstrap
|
uses: ./.github/actions/bootstrap
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
- name: Tag release
|
- name: Tag release
|
||||||
run: |
|
run: |
|
||||||
git tag ${{ github.event.inputs.version }}
|
git tag ${{ github.event.inputs.version }}
|
||||||
|
|
@ -98,11 +107,12 @@ jobs:
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Login to Docker Hub
|
- name: Login to container registry
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
registry: ${{ env.REGISTRY }}
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Build & publish release artifacts
|
- name: Build & publish release artifacts
|
||||||
run: make ci-release
|
run: make ci-release
|
||||||
|
|
|
||||||
44
.github/workflows/validations.yaml
vendored
44
.github/workflows/validations.yaml
vendored
|
|
@ -7,13 +7,12 @@ on:
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
Static-Analysis:
|
Static-Analysis:
|
||||||
# Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline
|
# Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline
|
||||||
name: "Static analysis"
|
name: "Static analysis"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Bootstrap environment
|
- name: Bootstrap environment
|
||||||
uses: ./.github/actions/bootstrap
|
uses: ./.github/actions/bootstrap
|
||||||
|
|
@ -21,21 +20,19 @@ jobs:
|
||||||
- name: Run static analysis
|
- name: Run static analysis
|
||||||
run: make static-analysis
|
run: make static-analysis
|
||||||
|
|
||||||
|
|
||||||
Unit-Test:
|
Unit-Test:
|
||||||
# Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline
|
# Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline
|
||||||
name: "Unit tests"
|
name: "Unit tests"
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
platform:
|
platform:
|
||||||
- ubuntu-latest
|
- ubuntu-latest
|
||||||
# - macos-latest # todo: mac runners are expensive minute-wise
|
# - macos-latest # todo: mac runners are expensive minute-wise
|
||||||
# - windows-latest # todo: support windows
|
# - windows-latest # todo: support windows
|
||||||
|
|
||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
steps:
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Bootstrap environment
|
- name: Bootstrap environment
|
||||||
uses: ./.github/actions/bootstrap
|
uses: ./.github/actions/bootstrap
|
||||||
|
|
@ -43,16 +40,21 @@ jobs:
|
||||||
- name: Run unit tests
|
- name: Run unit tests
|
||||||
run: make unit
|
run: make unit
|
||||||
|
|
||||||
|
|
||||||
Build-Snapshot-Artifacts:
|
Build-Snapshot-Artifacts:
|
||||||
name: "Build snapshot artifacts"
|
name: "Build snapshot artifacts"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Bootstrap environment
|
- name: Bootstrap environment
|
||||||
uses: ./.github/actions/bootstrap
|
uses: ./.github/actions/bootstrap
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
- name: Build snapshot artifacts
|
- name: Build snapshot artifacts
|
||||||
run: make snapshot
|
run: make snapshot
|
||||||
|
|
||||||
|
|
@ -65,28 +67,26 @@ jobs:
|
||||||
# why not use actions/upload-artifact? It is very slow (3 minutes to upload ~600MB of data, vs 10 seconds with this approach).
|
# why not use actions/upload-artifact? It is very slow (3 minutes to upload ~600MB of data, vs 10 seconds with this approach).
|
||||||
# see https://github.com/actions/upload-artifact/issues/199 for more info
|
# see https://github.com/actions/upload-artifact/issues/199 for more info
|
||||||
- name: Upload snapshot artifacts
|
- name: Upload snapshot artifacts
|
||||||
uses: actions/cache/save@v3
|
uses: actions/cache/save@v4
|
||||||
with:
|
with:
|
||||||
path: snapshot
|
path: snapshot
|
||||||
key: snapshot-build-${{ github.run_id }}
|
key: snapshot-build-${{ github.run_id }}
|
||||||
|
|
||||||
# ... however the cache trick doesn't work on windows :(
|
# ... however the cache trick doesn't work on windows :(
|
||||||
- uses: actions/upload-artifact@v3
|
- uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: windows-artifacts
|
name: windows-artifacts
|
||||||
path: snapshot/dive_windows_amd64_v1/dive.exe
|
path: snapshot/dive_windows_amd64_v1/dive.exe
|
||||||
|
|
||||||
|
|
||||||
Acceptance-Linux:
|
Acceptance-Linux:
|
||||||
name: "Acceptance tests (Linux)"
|
name: "Acceptance tests (Linux)"
|
||||||
needs: [ Build-Snapshot-Artifacts ]
|
needs: [Build-Snapshot-Artifacts]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
- uses: actions/checkout@master
|
- uses: actions/checkout@master
|
||||||
|
|
||||||
- name: Download snapshot build
|
- name: Download snapshot build
|
||||||
uses: actions/cache/restore@v3
|
uses: actions/cache/restore@v4
|
||||||
with:
|
with:
|
||||||
path: snapshot
|
path: snapshot
|
||||||
key: snapshot-build-${{ github.run_id }}
|
key: snapshot-build-${{ github.run_id }}
|
||||||
|
|
@ -100,17 +100,15 @@ jobs:
|
||||||
- name: Test RPM package installation
|
- name: Test RPM package installation
|
||||||
run: make ci-test-rpm-package-install
|
run: make ci-test-rpm-package-install
|
||||||
|
|
||||||
|
|
||||||
Acceptance-Mac:
|
Acceptance-Mac:
|
||||||
name: "Acceptance tests (Mac)"
|
name: "Acceptance tests (Mac)"
|
||||||
needs: [ Build-Snapshot-Artifacts ]
|
needs: [Build-Snapshot-Artifacts]
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
- uses: actions/checkout@master
|
- uses: actions/checkout@master
|
||||||
|
|
||||||
- name: Download snapshot build
|
- name: Download snapshot build
|
||||||
uses: actions/cache/restore@v3
|
uses: actions/cache/restore@v4
|
||||||
with:
|
with:
|
||||||
path: snapshot
|
path: snapshot
|
||||||
key: snapshot-build-${{ github.run_id }}
|
key: snapshot-build-${{ github.run_id }}
|
||||||
|
|
@ -118,16 +116,14 @@ jobs:
|
||||||
- name: Test darwin run
|
- name: Test darwin run
|
||||||
run: make ci-test-mac-run
|
run: make ci-test-mac-run
|
||||||
|
|
||||||
|
|
||||||
Acceptance-Windows:
|
Acceptance-Windows:
|
||||||
name: "Acceptance tests (Windows)"
|
name: "Acceptance tests (Windows)"
|
||||||
needs: [ Build-Snapshot-Artifacts ]
|
needs: [Build-Snapshot-Artifacts]
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
- uses: actions/checkout@master
|
- uses: actions/checkout@master
|
||||||
|
|
||||||
- uses: actions/download-artifact@v3
|
- uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: windows-artifacts
|
name: windows-artifacts
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -67,8 +67,8 @@ linters-settings:
|
||||||
# - prealloc # following this rule isn't consistently a good idea, as it sometimes forces unnecessary allocations that result in less idiomatic code
|
# - prealloc # following this rule isn't consistently a good idea, as it sometimes forces unnecessary allocations that result in less idiomatic code
|
||||||
# - scopelint # deprecated
|
# - scopelint # deprecated
|
||||||
# - testpackage
|
# - testpackage
|
||||||
# - wsl # this doens't have an auto-fixer yet and is pretty noisy (https://github.com/bombsimon/wsl/issues/90)
|
# - wsl # this doesn't have an auto-fixer yet and is pretty noisy (https://github.com/bombsimon/wsl/issues/90)
|
||||||
# - varcheck # deprecated (since v1.49.0) due to: The owner seems to have abandoned the linter. Replaced by unused.
|
# - varcheck # deprecated (since v1.49.0) due to: The owner seems to have abandoned the linter. Replaced by unused.
|
||||||
# - deadcode # deprecated (since v1.49.0) due to: The owner seems to have abandoned the linter. Replaced by unused.
|
# - deadcode # deprecated (since v1.49.0) due to: The owner seems to have abandoned the linter. Replaced by unused.
|
||||||
# - structcheck # deprecated (since v1.49.0) due to: The owner seems to have abandoned the linter. Replaced by unused.
|
# - structcheck # deprecated (since v1.49.0) due to: The owner seems to have abandoned the linter. Replaced by unused.
|
||||||
# - rowserrcheck # we're not using sql.Rows at all in the codebase
|
# - rowserrcheck # we're not using sql.Rows at all in the codebase
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
version: 2
|
||||||
|
|
||||||
release:
|
release:
|
||||||
# If set to auto, will mark the release as not ready for production in case there is an indicator for this in the
|
# If set to auto, will mark the release as not ready for production in case there is an indicator for this in the
|
||||||
# tag e.g. v1.0.0-rc1 .If set to true, will mark the release as not ready for production.
|
# tag e.g. v1.0.0-rc1 .If set to true, will mark the release as not ready for production.
|
||||||
|
|
@ -17,6 +19,7 @@ builds:
|
||||||
goarch:
|
goarch:
|
||||||
- amd64
|
- amd64
|
||||||
- arm64
|
- arm64
|
||||||
|
- ppc64le
|
||||||
ldflags: -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.buildTime={{.Date}}`.
|
ldflags: -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.buildTime={{.Date}}`.
|
||||||
|
|
||||||
brews:
|
brews:
|
||||||
|
|
@ -43,15 +46,44 @@ nfpms:
|
||||||
- deb
|
- deb
|
||||||
|
|
||||||
dockers:
|
dockers:
|
||||||
-
|
- id: docker-amd64
|
||||||
ids:
|
ids:
|
||||||
- dive
|
- dive
|
||||||
dockerfile: Dockerfile
|
use: buildx
|
||||||
# todo: on 1.0 remove 'v' prefix
|
goarch: amd64
|
||||||
image_templates:
|
image_templates:
|
||||||
- "wagoodman/dive:latest"
|
- '{{ envOrDefault "REGISTRY" "docker.io" }}/wagoodman/dive:latest-amd64'
|
||||||
- "wagoodman/dive:{{ .Tag }}"
|
- '{{ envOrDefault "REGISTRY" "docker.io" }}/wagoodman/dive:{{ .Version }}-amd64'
|
||||||
- "wagoodman/dive:v{{ .Major }}"
|
|
||||||
- "wagoodman/dive:v{{ .Major }}.{{ .Minor }}"
|
|
||||||
build_flag_templates:
|
build_flag_templates:
|
||||||
- "--build-arg=DOCKER_CLI_VERSION={{.Env.DOCKER_CLI_VERSION}}"
|
- "--build-arg=DOCKER_CLI_VERSION={{.Env.DOCKER_CLI_VERSION}}"
|
||||||
|
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||||
|
- "--label=org.opencontainers.image.title={{.ProjectName}}"
|
||||||
|
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
|
||||||
|
- "--label=org.opencontainers.image.version={{.Version}}"
|
||||||
|
- "--label=org.opencontainers.image.source={{.GitURL}}"
|
||||||
|
- "--platform=linux/amd64"
|
||||||
|
- id: docker-arm64
|
||||||
|
ids:
|
||||||
|
- dive
|
||||||
|
use: buildx
|
||||||
|
goarch: arm64
|
||||||
|
image_templates:
|
||||||
|
- '{{ envOrDefault "REGISTRY" "docker.io" }}/wagoodman/dive:latest-arm64'
|
||||||
|
- '{{ envOrDefault "REGISTRY" "docker.io" }}/wagoodman/dive:{{ .Version }}-arm64'
|
||||||
|
build_flag_templates:
|
||||||
|
- "--build-arg=DOCKER_CLI_VERSION={{.Env.DOCKER_CLI_VERSION}}"
|
||||||
|
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||||
|
- "--label=org.opencontainers.image.title={{.ProjectName}}"
|
||||||
|
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
|
||||||
|
- "--label=org.opencontainers.image.version={{.Version}}"
|
||||||
|
- "--label=org.opencontainers.image.source={{.GitURL}}"
|
||||||
|
- "--platform=linux/arm64/v8"
|
||||||
|
docker_manifests:
|
||||||
|
- name_template: '{{ envOrDefault "REGISTRY" "docker.io" }}/wagoodman/dive:latest'
|
||||||
|
image_templates:
|
||||||
|
- '{{ envOrDefault "REGISTRY" "docker.io" }}/wagoodman/dive:latest-amd64'
|
||||||
|
- '{{ envOrDefault "REGISTRY" "docker.io" }}/wagoodman/dive:latest-arm64'
|
||||||
|
- name_template: '{{ envOrDefault "REGISTRY" "docker.io" }}/wagoodman/dive:{{ .Version }}'
|
||||||
|
image_templates:
|
||||||
|
- '{{ envOrDefault "REGISTRY" "docker.io" }}/wagoodman/dive:{{ .Version }}-amd64'
|
||||||
|
- '{{ envOrDefault "REGISTRY" "docker.io" }}/wagoodman/dive:{{ .Version }}-arm64'
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
FROM alpine:3.18
|
FROM alpine:3.21 AS base
|
||||||
|
|
||||||
ARG DOCKER_CLI_VERSION=${DOCKER_CLI_VERSION}
|
ARG DOCKER_CLI_VERSION=${DOCKER_CLI_VERSION}
|
||||||
RUN wget -O- https://download.docker.com/linux/static/stable/$(uname -m)/docker-${DOCKER_CLI_VERSION}.tgz | \
|
RUN wget -O- https://download.docker.com/linux/static/stable/$(uname -m)/docker-${DOCKER_CLI_VERSION}.tgz | \
|
||||||
tar -xzf - docker/docker --strip-component=1 && \
|
tar -xzf - docker/docker --strip-component=1 -C /usr/local/bin
|
||||||
mv docker /usr/local/bin
|
|
||||||
|
|
||||||
COPY dive /usr/local/bin/
|
COPY dive /usr/local/bin/
|
||||||
|
|
||||||
|
FROM scratch
|
||||||
|
COPY --from=base /usr/local/bin /usr/local/bin
|
||||||
|
|
||||||
ENTRYPOINT ["/usr/local/bin/dive"]
|
ENTRYPOINT ["/usr/local/bin/dive"]
|
||||||
|
|
|
||||||
66
Makefile
66
Makefile
|
|
@ -1,24 +1,24 @@
|
||||||
BIN = dive
|
BIN = dive
|
||||||
TEMP_DIR = ./.tmp
|
TEMP_DIR = ./.tmp
|
||||||
PWD := ${CURDIR}
|
PWD := ${CURDIR}
|
||||||
PRODUCTION_REGISTRY = docker.io
|
REGISTRY ?= docker.io
|
||||||
SHELL = /bin/bash -o pipefail
|
SHELL = /bin/bash -o pipefail
|
||||||
TEST_IMAGE = busybox:latest
|
TEST_IMAGE = busybox:latest
|
||||||
|
|
||||||
# Tool versions #################################
|
# Tool versions #################################
|
||||||
GOLANG_CI_VERSION = v1.52.2
|
GOLANG_CI_VERSION = v1.64.5
|
||||||
GOBOUNCER_VERSION = v0.4.0
|
GOBOUNCER_VERSION = v0.4.0
|
||||||
GORELEASER_VERSION = v1.19.1
|
GORELEASER_VERSION = v2.4.4
|
||||||
GOSIMPORTS_VERSION = v0.3.8
|
GOSIMPORTS_VERSION = v0.3.8
|
||||||
CHRONICLE_VERSION = v0.6.0
|
CHRONICLE_VERSION = v0.8.0
|
||||||
GLOW_VERSION = v1.5.0
|
GLOW_VERSION = v1.5.1
|
||||||
DOCKER_CLI_VERSION = 23.0.6
|
DOCKER_CLI_VERSION = 28.0.0
|
||||||
|
|
||||||
# Command templates #################################
|
# Command templates #################################
|
||||||
LINT_CMD = $(TEMP_DIR)/golangci-lint run --tests=false --timeout=2m --config .golangci.yaml
|
LINT_CMD = $(TEMP_DIR)/golangci-lint run --tests=false --timeout=2m --config .golangci.yaml
|
||||||
GOIMPORTS_CMD = $(TEMP_DIR)/gosimports -local github.com/wagoodman
|
GOIMPORTS_CMD = $(TEMP_DIR)/gosimports -local github.com/wagoodman
|
||||||
RELEASE_CMD = DOCKER_CLI_VERSION=$(DOCKER_CLI_VERSION) $(TEMP_DIR)/goreleaser release --clean
|
RELEASE_CMD = DOCKER_CLI_VERSION=$(DOCKER_CLI_VERSION) $(TEMP_DIR)/goreleaser release --clean
|
||||||
SNAPSHOT_CMD = $(RELEASE_CMD) --skip-publish --snapshot --skip-sign
|
SNAPSHOT_CMD = $(RELEASE_CMD) --skip=publish --skip=sign --snapshot
|
||||||
CHRONICLE_CMD = $(TEMP_DIR)/chronicle
|
CHRONICLE_CMD = $(TEMP_DIR)/chronicle
|
||||||
GLOW_CMD = $(TEMP_DIR)/glow
|
GLOW_CMD = $(TEMP_DIR)/glow
|
||||||
|
|
||||||
|
|
@ -34,7 +34,7 @@ SUCCESS := $(BOLD)$(GREEN)
|
||||||
|
|
||||||
# Test variables #################################
|
# Test variables #################################
|
||||||
# the quality gate lower threshold for unit test total % coverage (by function statements)
|
# the quality gate lower threshold for unit test total % coverage (by function statements)
|
||||||
COVERAGE_THRESHOLD := 55
|
COVERAGE_THRESHOLD := 30
|
||||||
|
|
||||||
## Build variables #################################
|
## Build variables #################################
|
||||||
DIST_DIR = dist
|
DIST_DIR = dist
|
||||||
|
|
@ -86,7 +86,7 @@ bootstrap-tools: $(TEMP_DIR)
|
||||||
curl -sSfL https://raw.githubusercontent.com/anchore/chronicle/main/install.sh | sh -s -- -b $(TEMP_DIR)/ $(CHRONICLE_VERSION)
|
curl -sSfL https://raw.githubusercontent.com/anchore/chronicle/main/install.sh | sh -s -- -b $(TEMP_DIR)/ $(CHRONICLE_VERSION)
|
||||||
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(TEMP_DIR)/ $(GOLANG_CI_VERSION)
|
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(TEMP_DIR)/ $(GOLANG_CI_VERSION)
|
||||||
curl -sSfL https://raw.githubusercontent.com/wagoodman/go-bouncer/master/bouncer.sh | sh -s -- -b $(TEMP_DIR)/ $(GOBOUNCER_VERSION)
|
curl -sSfL https://raw.githubusercontent.com/wagoodman/go-bouncer/master/bouncer.sh | sh -s -- -b $(TEMP_DIR)/ $(GOBOUNCER_VERSION)
|
||||||
GOBIN="$(realpath $(TEMP_DIR))" go install github.com/goreleaser/goreleaser@$(GORELEASER_VERSION)
|
GOBIN="$(realpath $(TEMP_DIR))" go install github.com/goreleaser/goreleaser/v2@$(GORELEASER_VERSION)
|
||||||
GOBIN="$(realpath $(TEMP_DIR))" go install github.com/rinchsan/gosimports/cmd/gosimports@$(GOSIMPORTS_VERSION)
|
GOBIN="$(realpath $(TEMP_DIR))" go install github.com/rinchsan/gosimports/cmd/gosimports@$(GOSIMPORTS_VERSION)
|
||||||
GOBIN="$(realpath $(TEMP_DIR))" go install github.com/charmbracelet/glow@$(GLOW_VERSION)
|
GOBIN="$(realpath $(TEMP_DIR))" go install github.com/charmbracelet/glow@$(GLOW_VERSION)
|
||||||
|
|
||||||
|
|
@ -125,7 +125,34 @@ bootstrap: bootstrap-go bootstrap-tools ## Download and install all go dependenc
|
||||||
|
|
||||||
.PHONY: generate-test-data
|
.PHONY: generate-test-data
|
||||||
generate-test-data:
|
generate-test-data:
|
||||||
docker build -t dive-test:latest -f .data/Dockerfile.test-image . && docker image save -o .data/test-docker-image.tar dive-test:latest && echo 'Exported test data!'
|
docker build -t dive-test:latest -f .data/Dockerfile.test-image . && \
|
||||||
|
docker image save -o .data/test-docker-image.tar dive-test:latest && \
|
||||||
|
echo 'Exported test data!'
|
||||||
|
|
||||||
|
.PHONY: generate-compressed-test-images
|
||||||
|
generate-compressed-test-images:
|
||||||
|
for alg in uncompressed gzip estargz zstd; do \
|
||||||
|
for exporter in docker image; do \
|
||||||
|
docker buildx build \
|
||||||
|
-f .data/Dockerfile.minimal \
|
||||||
|
--tag test-dive-$${exporter}:$${alg} \
|
||||||
|
--output type=$${exporter},force-compression=true,compression=$${alg} . ; \
|
||||||
|
done ; \
|
||||||
|
done && \
|
||||||
|
echo 'Exported test data!'
|
||||||
|
|
||||||
|
.PHONY: generate-compressed-test-data
|
||||||
|
generate-compressed-test-data:
|
||||||
|
for alg in uncompressed gzip estargz zstd; \
|
||||||
|
do \
|
||||||
|
docker buildx build \
|
||||||
|
-f .data/Dockerfile.minimal \
|
||||||
|
--output type=tar,dest=.data/test-$${alg}-image.tar,force-compression=true,compression=$${alg} . ; \
|
||||||
|
docker buildx build \
|
||||||
|
-f .data/Dockerfile.minimal \
|
||||||
|
--output type=oci,dest=.data/test-oci-$${alg}-image.tar,force-compression=true,compression=$${alg} . ; \
|
||||||
|
done && \
|
||||||
|
echo 'Exported test data!'
|
||||||
|
|
||||||
|
|
||||||
## Static analysis targets #################################
|
## Static analysis targets #################################
|
||||||
|
|
@ -186,7 +213,7 @@ ci-test-docker-image:
|
||||||
--rm \
|
--rm \
|
||||||
-t \
|
-t \
|
||||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||||
'${PRODUCTION_REGISTRY}/wagoodman/dive:latest' \
|
'${REGISTRY}/wagoodman/dive:latest-amd64' \
|
||||||
'${TEST_IMAGE}' \
|
'${TEST_IMAGE}' \
|
||||||
--ci
|
--ci
|
||||||
|
|
||||||
|
|
@ -227,12 +254,18 @@ ci-test-rpm-package-install:
|
||||||
"
|
"
|
||||||
|
|
||||||
.PHONY: ci-test-linux-run
|
.PHONY: ci-test-linux-run
|
||||||
ci-test-linux-run:
|
ci-test-linux-run: generate-compressed-test-images
|
||||||
ls -la $(SNAPSHOT_DIR)
|
ls -la $(SNAPSHOT_DIR)
|
||||||
ls -la $(SNAPSHOT_DIR)/dive_linux_amd64_v1
|
ls -la $(SNAPSHOT_DIR)/dive_linux_amd64_v1
|
||||||
chmod 755 $(SNAPSHOT_DIR)/dive_linux_amd64_v1/dive && \
|
chmod 755 $(SNAPSHOT_DIR)/dive_linux_amd64_v1/dive && \
|
||||||
$(SNAPSHOT_DIR)/dive_linux_amd64_v1/dive '${TEST_IMAGE}' --ci && \
|
$(SNAPSHOT_DIR)/dive_linux_amd64_v1/dive '${TEST_IMAGE}' --ci && \
|
||||||
$(SNAPSHOT_DIR)/dive_linux_amd64_v1/dive --source docker-archive .data/test-kaniko-image.tar --ci --ci-config .data/.dive-ci
|
$(SNAPSHOT_DIR)/dive_linux_amd64_v1/dive --source docker-archive .data/test-kaniko-image.tar --ci --ci-config .data/.dive-ci
|
||||||
|
for alg in uncompressed gzip estargz zstd; do \
|
||||||
|
for exporter in docker image; do \
|
||||||
|
$(SNAPSHOT_DIR)/dive_linux_amd64_v1/dive "test-dive-$${exporter}:$${alg}" --ci ; \
|
||||||
|
done && \
|
||||||
|
$(SNAPSHOT_DIR)/dive_linux_amd64_v1/dive --source docker-archive .data/test-oci-$${alg}-image.tar --ci --ci-config .data/.dive-ci; \
|
||||||
|
done
|
||||||
|
|
||||||
# we're not attempting to test docker, just our ability to run on these systems. This avoids setting up docker in CI.
|
# we're not attempting to test docker, just our ability to run on these systems. This avoids setting up docker in CI.
|
||||||
.PHONY: ci-test-mac-run
|
.PHONY: ci-test-mac-run
|
||||||
|
|
@ -282,7 +315,7 @@ $(CHANGELOG):
|
||||||
release: ## Cut a new release
|
release: ## Cut a new release
|
||||||
@.github/scripts/trigger-release.sh
|
@.github/scripts/trigger-release.sh
|
||||||
|
|
||||||
.PHONY: release
|
.PHONY: ci-release
|
||||||
ci-release: ci-check clean-dist $(CHANGELOG)
|
ci-release: ci-check clean-dist $(CHANGELOG)
|
||||||
$(call title,Publishing release artifacts)
|
$(call title,Publishing release artifacts)
|
||||||
|
|
||||||
|
|
@ -315,9 +348,8 @@ clean-changelog:
|
||||||
rm -f $(CHANGELOG) VERSION
|
rm -f $(CHANGELOG) VERSION
|
||||||
|
|
||||||
|
|
||||||
## Halp! #################################
|
## Help! #################################
|
||||||
|
|
||||||
.PHONY: help
|
.PHONY: help
|
||||||
help:
|
help:
|
||||||
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "$(BOLD)$(CYAN)%-25s$(RESET)%s\n", $$1, $$2}'
|
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "$(BOLD)$(CYAN)%-25s$(RESET)%s\n", $$1, $$2}'
|
||||||
|
|
||||||
|
|
|
||||||
81
README.md
81
README.md
|
|
@ -5,7 +5,7 @@
|
||||||
[](https://github.com/wagoodman/dive/blob/main/LICENSE)
|
[](https://github.com/wagoodman/dive/blob/main/LICENSE)
|
||||||
[](https://www.paypal.me/wagoodman)
|
[](https://www.paypal.me/wagoodman)
|
||||||
|
|
||||||
**A tool for exploring a docker image, layer contents, and discovering ways to shrink the size of your Docker/OCI image.**
|
**A tool for exploring a Docker image, layer contents, and discovering ways to shrink the size of your Docker/OCI image.**
|
||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
|
@ -15,9 +15,9 @@ To analyze a Docker image simply run dive with an image tag/id/digest:
|
||||||
dive <your-image-tag>
|
dive <your-image-tag>
|
||||||
```
|
```
|
||||||
|
|
||||||
or you can dive with docker command directly
|
or you can dive with Docker directly:
|
||||||
```
|
```
|
||||||
alias dive="docker run -ti --rm -v /var/run/docker.sock:/var/run/docker.sock wagoodman/dive"
|
alias dive="docker run -ti --rm -v /var/run/docker.sock:/var/run/docker.sock ghcr.io/wagoodman/dive"
|
||||||
dive <your-image-tag>
|
dive <your-image-tag>
|
||||||
|
|
||||||
# for example
|
# for example
|
||||||
|
|
@ -29,7 +29,7 @@ or if you want to build your image then jump straight into analyzing it:
|
||||||
dive build -t <some-tag> .
|
dive build -t <some-tag> .
|
||||||
```
|
```
|
||||||
|
|
||||||
Building on Macbook (supporting only the Docker container engine)
|
Building on macOS (supporting only the Docker container engine):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run --rm -it \
|
docker run --rm -it \
|
||||||
|
|
@ -37,7 +37,7 @@ docker run --rm -it \
|
||||||
-v "$(pwd)":"$(pwd)" \
|
-v "$(pwd)":"$(pwd)" \
|
||||||
-w "$(pwd)" \
|
-w "$(pwd)" \
|
||||||
-v "$HOME/.dive.yaml":"$HOME/.dive.yaml" \
|
-v "$HOME/.dive.yaml":"$HOME/.dive.yaml" \
|
||||||
wagoodman/dive:latest build -t <some-tag> .
|
ghcr.io/wagoodman/dive:latest build -t <some-tag> .
|
||||||
```
|
```
|
||||||
|
|
||||||
Additionally you can run this in your CI pipeline to ensure you're keeping wasted space to a minimum (this skips the UI):
|
Additionally you can run this in your CI pipeline to ensure you're keeping wasted space to a minimum (this skips the UI):
|
||||||
|
|
@ -98,7 +98,7 @@ With valid `source` options as such:
|
||||||
Using debs:
|
Using debs:
|
||||||
```bash
|
```bash
|
||||||
DIVE_VERSION=$(curl -sL "https://api.github.com/repos/wagoodman/dive/releases/latest" | grep '"tag_name":' | sed -E 's/.*"v([^"]+)".*/\1/')
|
DIVE_VERSION=$(curl -sL "https://api.github.com/repos/wagoodman/dive/releases/latest" | grep '"tag_name":' | sed -E 's/.*"v([^"]+)".*/\1/')
|
||||||
curl -OL https://github.com/wagoodman/dive/releases/download/v${DIVE_VERSION}/dive_${DIVE_VERSION}_linux_amd64.deb
|
curl -fOL "https://github.com/wagoodman/dive/releases/download/v${DIVE_VERSION}/dive_${DIVE_VERSION}_linux_amd64.deb"
|
||||||
sudo apt install ./dive_${DIVE_VERSION}_linux_amd64.deb
|
sudo apt install ./dive_${DIVE_VERSION}_linux_amd64.deb
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -110,10 +110,16 @@ sudo snap connect dive:docker-executables docker:docker-executables
|
||||||
sudo snap connect dive:docker-daemon docker:docker-daemon
|
sudo snap connect dive:docker-daemon docker:docker-daemon
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> [!CAUTION]
|
||||||
|
> The Snap method is not recommended if you installed Docker via `apt-get`, since it might break your existing Docker daemon.
|
||||||
|
>
|
||||||
|
> See also: https://github.com/wagoodman/dive/issues/546
|
||||||
|
|
||||||
|
|
||||||
**RHEL/Centos**
|
**RHEL/Centos**
|
||||||
```bash
|
```bash
|
||||||
DIVE_VERSION=$(curl -sL "https://api.github.com/repos/wagoodman/dive/releases/latest" | grep '"tag_name":' | sed -E 's/.*"v([^"]+)".*/\1/')
|
DIVE_VERSION=$(curl -sL "https://api.github.com/repos/wagoodman/dive/releases/latest" | grep '"tag_name":' | sed -E 's/.*"v([^"]+)".*/\1/')
|
||||||
curl -OL https://github.com/wagoodman/dive/releases/download/v${DIVE_VERSION}/dive_${DIVE_VERSION}_linux_amd64.rpm
|
curl -fOL "https://github.com/wagoodman/dive/releases/download/v${DIVE_VERSION}/dive_${DIVE_VERSION}_linux_amd64.rpm"
|
||||||
rpm -i dive_${DIVE_VERSION}_linux_amd64.rpm
|
rpm -i dive_${DIVE_VERSION}_linux_amd64.rpm
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -130,7 +136,8 @@ pacman -S dive
|
||||||
If you use [Homebrew](https://brew.sh):
|
If you use [Homebrew](https://brew.sh):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
brew install dive
|
brew tap wagoodman/dive
|
||||||
|
brew install wagoodman/dive/dive
|
||||||
```
|
```
|
||||||
|
|
||||||
If you use [MacPorts](https://www.macports.org):
|
If you use [MacPorts](https://www.macports.org):
|
||||||
|
|
@ -143,13 +150,31 @@ Or download the latest Darwin build from the [releases page](https://github.com/
|
||||||
|
|
||||||
**Windows**
|
**Windows**
|
||||||
|
|
||||||
Download the [latest release](https://github.com/wagoodman/dive/releases/latest).
|
If you use [Chocolatey](https://chocolatey.org)
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
choco install dive
|
||||||
|
```
|
||||||
|
|
||||||
|
If you use [scoop](https://scoop.sh/)
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
scoop install main/dive
|
||||||
|
```
|
||||||
|
|
||||||
|
If you use [winget](https://learn.microsoft.com/en-gb/windows/package-manager/):
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
winget install --id wagoodman.dive
|
||||||
|
```
|
||||||
|
|
||||||
|
Or download the latest Windows build from the [releases page](https://github.com/wagoodman/dive/releases/latest).
|
||||||
|
|
||||||
**Go tools**
|
**Go tools**
|
||||||
Requires Go version 1.10 or higher.
|
Requires Go version 1.10 or higher.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
go get github.com/wagoodman/dive
|
go install github.com/wagoodman/dive@latest
|
||||||
```
|
```
|
||||||
*Note*: installing in this way you will not see a proper version when running `dive -v`.
|
*Note*: installing in this way you will not see a proper version when running `dive -v`.
|
||||||
|
|
||||||
|
|
@ -166,27 +191,21 @@ nix-env -iA nixpkgs.dive
|
||||||
|
|
||||||
**Docker**
|
**Docker**
|
||||||
```bash
|
```bash
|
||||||
docker pull wagoodman/dive
|
docker pull ghcr.io/wagoodman/dive
|
||||||
```
|
```
|
||||||
|
|
||||||
or
|
When running you'll need to include the Docker socket file:
|
||||||
|
|
||||||
```bash
|
|
||||||
docker pull quay.io/wagoodman/dive
|
|
||||||
```
|
|
||||||
|
|
||||||
When running you'll need to include the docker socket file:
|
|
||||||
```bash
|
```bash
|
||||||
docker run --rm -it \
|
docker run --rm -it \
|
||||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||||
wagoodman/dive:latest <dive arguments...>
|
ghcr.io/wagoodman/dive:latest <dive arguments...>
|
||||||
```
|
```
|
||||||
|
|
||||||
Docker for Windows (showing PowerShell compatible line breaks; collapse to a single line for Command Prompt compatibility)
|
Docker for Windows (showing PowerShell compatible line breaks; collapse to a single line for Command Prompt compatibility)
|
||||||
```bash
|
```bash
|
||||||
docker run --rm -it `
|
docker run --rm -it `
|
||||||
-v /var/run/docker.sock:/var/run/docker.sock `
|
-v /var/run/docker.sock:/var/run/docker.sock `
|
||||||
wagoodman/dive:latest <dive arguments...>
|
ghcr.io/wagoodman/dive:latest <dive arguments...>
|
||||||
```
|
```
|
||||||
|
|
||||||
**Note:** depending on the version of docker you are running locally you may need to specify the docker API version as an environment variable:
|
**Note:** depending on the version of docker you are running locally you may need to specify the docker API version as an environment variable:
|
||||||
|
|
@ -198,7 +217,7 @@ or if you are running with a docker image:
|
||||||
docker run --rm -it \
|
docker run --rm -it \
|
||||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||||
-e DOCKER_API_VERSION=1.37 \
|
-e DOCKER_API_VERSION=1.37 \
|
||||||
wagoodman/dive:latest <dive arguments...>
|
ghcr.io/wagoodman/dive:latest <dive arguments...>
|
||||||
```
|
```
|
||||||
|
|
||||||
## CI Integration
|
## CI Integration
|
||||||
|
|
@ -228,8 +247,11 @@ Key Binding | Description
|
||||||
<kbd>Ctrl + C</kbd> or <kbd>Q</kbd> | Exit
|
<kbd>Ctrl + C</kbd> or <kbd>Q</kbd> | Exit
|
||||||
<kbd>Tab</kbd> | Switch between the layer and filetree views
|
<kbd>Tab</kbd> | Switch between the layer and filetree views
|
||||||
<kbd>Ctrl + F</kbd> | Filter files
|
<kbd>Ctrl + F</kbd> | Filter files
|
||||||
<kbd>PageUp</kbd> | Scroll up a page
|
<kbd>ESC</kbd> | Close filter files
|
||||||
<kbd>PageDown</kbd> | Scroll down a page
|
<kbd>PageUp</kbd> or <kbd>U</kbd> | Scroll up a page
|
||||||
|
<kbd>PageDown</kbd> or <kbd>D</kbd> | Scroll down a page
|
||||||
|
<kbd>Up</kbd> or <kbd>K</kbd> | Move up one line within a page
|
||||||
|
<kbd>Down</kbd> or <kbd>J</kbd> | Move down one line within a page
|
||||||
<kbd>Ctrl + A</kbd> | Layer view: see aggregated image modifications
|
<kbd>Ctrl + A</kbd> | Layer view: see aggregated image modifications
|
||||||
<kbd>Ctrl + L</kbd> | Layer view: see current layer modifications
|
<kbd>Ctrl + L</kbd> | Layer view: see current layer modifications
|
||||||
<kbd>Space</kbd> | Filetree view: collapse/uncollapse a directory
|
<kbd>Space</kbd> | Filetree view: collapse/uncollapse a directory
|
||||||
|
|
@ -239,8 +261,8 @@ Key Binding | Description
|
||||||
<kbd>Ctrl + M</kbd> | Filetree view: show/hide modified files
|
<kbd>Ctrl + M</kbd> | Filetree view: show/hide modified files
|
||||||
<kbd>Ctrl + U</kbd> | Filetree view: show/hide unmodified files
|
<kbd>Ctrl + U</kbd> | Filetree view: show/hide unmodified files
|
||||||
<kbd>Ctrl + B</kbd> | Filetree view: show/hide file attributes
|
<kbd>Ctrl + B</kbd> | Filetree view: show/hide file attributes
|
||||||
<kbd>PageUp</kbd> | Filetree view: scroll up a page
|
<kbd>PageUp</kbd> or <kbd>U</kbd> | Filetree view: scroll up a page
|
||||||
<kbd>PageDown</kbd> | Filetree view: scroll down a page
|
<kbd>PageDown</kbd> or <kbd>D</kbd> | Filetree view: scroll down a page
|
||||||
|
|
||||||
## UI Configuration
|
## UI Configuration
|
||||||
|
|
||||||
|
|
@ -262,6 +284,11 @@ keybinding:
|
||||||
quit: ctrl+c
|
quit: ctrl+c
|
||||||
toggle-view: tab
|
toggle-view: tab
|
||||||
filter-files: ctrl+f, ctrl+slash
|
filter-files: ctrl+f, ctrl+slash
|
||||||
|
close-filter-files: esc
|
||||||
|
up: up,k
|
||||||
|
down: down,j
|
||||||
|
left: left,h
|
||||||
|
right: right,l
|
||||||
|
|
||||||
# Layer view specific bindings
|
# Layer view specific bindings
|
||||||
compare-all: ctrl+a
|
compare-all: ctrl+a
|
||||||
|
|
@ -275,8 +302,8 @@ keybinding:
|
||||||
toggle-modified-files: ctrl+m
|
toggle-modified-files: ctrl+m
|
||||||
toggle-unmodified-files: ctrl+u
|
toggle-unmodified-files: ctrl+u
|
||||||
toggle-filetree-attributes: ctrl+b
|
toggle-filetree-attributes: ctrl+b
|
||||||
page-up: pgup
|
page-up: pgup,u
|
||||||
page-down: pgdn
|
page-down: pgdn,d
|
||||||
|
|
||||||
diff:
|
diff:
|
||||||
# You can change the default files shown in the filetree (right pane). All diff types are shown by default.
|
# You can change the default files shown in the filetree (right pane). All diff types are shown by default.
|
||||||
|
|
|
||||||
10
cmd/root.go
10
cmd/root.go
|
|
@ -80,6 +80,7 @@ func initConfig() {
|
||||||
viper.SetDefault("keybinding.quit", "ctrl+c,q")
|
viper.SetDefault("keybinding.quit", "ctrl+c,q")
|
||||||
viper.SetDefault("keybinding.toggle-view", "tab")
|
viper.SetDefault("keybinding.toggle-view", "tab")
|
||||||
viper.SetDefault("keybinding.filter-files", "ctrl+f, ctrl+slash")
|
viper.SetDefault("keybinding.filter-files", "ctrl+f, ctrl+slash")
|
||||||
|
viper.SetDefault("keybinding.close-filter-files", "esc")
|
||||||
// keybindings: layer view
|
// keybindings: layer view
|
||||||
viper.SetDefault("keybinding.compare-all", "ctrl+a")
|
viper.SetDefault("keybinding.compare-all", "ctrl+a")
|
||||||
viper.SetDefault("keybinding.compare-layer", "ctrl+l")
|
viper.SetDefault("keybinding.compare-layer", "ctrl+l")
|
||||||
|
|
@ -93,8 +94,13 @@ func initConfig() {
|
||||||
viper.SetDefault("keybinding.toggle-modified-files", "ctrl+m")
|
viper.SetDefault("keybinding.toggle-modified-files", "ctrl+m")
|
||||||
viper.SetDefault("keybinding.toggle-unmodified-files", "ctrl+u")
|
viper.SetDefault("keybinding.toggle-unmodified-files", "ctrl+u")
|
||||||
viper.SetDefault("keybinding.toggle-wrap-tree", "ctrl+p")
|
viper.SetDefault("keybinding.toggle-wrap-tree", "ctrl+p")
|
||||||
viper.SetDefault("keybinding.page-up", "pgup")
|
viper.SetDefault("keybinding.extract-file", "ctrl+e")
|
||||||
viper.SetDefault("keybinding.page-down", "pgdn")
|
viper.SetDefault("keybinding.page-up", "pgup,u")
|
||||||
|
viper.SetDefault("keybinding.page-down", "pgdn,d")
|
||||||
|
viper.SetDefault("keybinding.up", "up,k")
|
||||||
|
viper.SetDefault("keybinding.down", "down,j")
|
||||||
|
viper.SetDefault("keybinding.left", "left,h")
|
||||||
|
viper.SetDefault("keybinding.right", "right,l")
|
||||||
|
|
||||||
viper.SetDefault("diff.hide", "")
|
viper.SetDefault("diff.hide", "")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ func Efficiency(trees []*FileTree) (float64, EfficiencySlice) {
|
||||||
stackedTree, failedPaths, err := StackTreeRange(trees, 0, currentTree-1)
|
stackedTree, failedPaths, err := StackTreeRange(trees, 0, currentTree-1)
|
||||||
if len(failedPaths) > 0 {
|
if len(failedPaths) > 0 {
|
||||||
for _, path := range failedPaths {
|
for _, path := range failedPaths {
|
||||||
logrus.Errorf(path.String())
|
logrus.Errorf("%s", path.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ func checkError(t *testing.T, err error, message string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEfficency(t *testing.T) {
|
func TestEfficiency(t *testing.T) {
|
||||||
trees := make([]*FileTree, 3)
|
trees := make([]*FileTree, 3)
|
||||||
for idx := range trees {
|
for idx := range trees {
|
||||||
trees[idx] = NewFileTree()
|
trees[idx] = NewFileTree()
|
||||||
|
|
@ -56,7 +56,7 @@ func TestEfficency(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEfficency_ScratchImage(t *testing.T) {
|
func TestEfficiency_ScratchImage(t *testing.T) {
|
||||||
trees := make([]*FileTree, 3)
|
trees := make([]*FileTree, 3)
|
||||||
for idx := range trees {
|
for idx := range trees {
|
||||||
trees[idx] = NewFileTree()
|
trees[idx] = NewFileTree()
|
||||||
|
|
|
||||||
|
|
@ -5,21 +5,21 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/cespare/xxhash"
|
"github.com/cespare/xxhash/v2"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// FileInfo contains tar metadata for a specific FileNode
|
// FileInfo contains tar metadata for a specific FileNode
|
||||||
type FileInfo struct {
|
type FileInfo struct {
|
||||||
Path string
|
Path string `json:"path"`
|
||||||
TypeFlag byte
|
TypeFlag byte `json:"typeFlag"`
|
||||||
Linkname string
|
Linkname string `json:"linkName"`
|
||||||
hash uint64
|
hash uint64 //`json:"hash"`
|
||||||
Size int64
|
Size int64 `json:"size"`
|
||||||
Mode os.FileMode
|
Mode os.FileMode `json:"fileMode"`
|
||||||
Uid int
|
Uid int `json:"uid"`
|
||||||
Gid int
|
Gid int `json:"gid"`
|
||||||
IsDir bool
|
IsDir bool `json:"isDir"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFileInfoFromTarHeader extracts the metadata from a tar header and file contents and generates a new FileInfo object.
|
// NewFileInfoFromTarHeader extracts the metadata from a tar header and file contents and generates a new FileInfo object.
|
||||||
|
|
|
||||||
|
|
@ -141,11 +141,33 @@ func (node *FileNode) MetadataString() string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
fileMode := permbits.FileMode(node.Data.FileInfo.Mode).String()
|
|
||||||
dir := "-"
|
dir := "-"
|
||||||
if node.Data.FileInfo.IsDir {
|
if node.Data.FileInfo.IsDir {
|
||||||
dir = "d"
|
dir = "d"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fm := permbits.FileMode(node.Data.FileInfo.Mode)
|
||||||
|
var fileMode strings.Builder
|
||||||
|
fileMode.Grow(9)
|
||||||
|
cond := func(c bool, x, y byte) byte {
|
||||||
|
if c {
|
||||||
|
return x
|
||||||
|
} else {
|
||||||
|
return y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fileMode.WriteByte(cond(fm.UserRead(), 'r', '-'))
|
||||||
|
fileMode.WriteByte(cond(fm.UserWrite(), 'w', '-'))
|
||||||
|
fileMode.WriteByte(cond(fm.UserExecute(), cond(fm.Setuid(), 's', 'x'), cond(fm.Setuid(), 'S', '-')))
|
||||||
|
|
||||||
|
fileMode.WriteByte(cond(fm.GroupRead(), 'r', '-'))
|
||||||
|
fileMode.WriteByte(cond(fm.GroupWrite(), 'w', '-'))
|
||||||
|
fileMode.WriteByte(cond(fm.GroupExecute(), cond(fm.Setgid(), 's', 'x'), cond(fm.Setgid(), 'S', '-')))
|
||||||
|
|
||||||
|
fileMode.WriteByte(cond(fm.OtherRead(), 'r', '-'))
|
||||||
|
fileMode.WriteByte(cond(fm.OtherWrite(), 'w', '-'))
|
||||||
|
fileMode.WriteByte(cond(fm.OtherExecute(), cond(fm.Sticky(), 't', 'x'), cond(fm.Sticky(), 'T', '-')))
|
||||||
|
|
||||||
user := node.Data.FileInfo.Uid
|
user := node.Data.FileInfo.Uid
|
||||||
group := node.Data.FileInfo.Gid
|
group := node.Data.FileInfo.Gid
|
||||||
userGroup := fmt.Sprintf("%d:%d", user, group)
|
userGroup := fmt.Sprintf("%d:%d", user, group)
|
||||||
|
|
@ -156,7 +178,7 @@ func (node *FileNode) MetadataString() string {
|
||||||
|
|
||||||
size := humanize.Bytes(uint64(sizeBytes))
|
size := humanize.Bytes(uint64(sizeBytes))
|
||||||
|
|
||||||
return diffTypeColor[node.Data.DiffType].Sprint(fmt.Sprintf(AttributeFormat, dir, fileMode, userGroup, size))
|
return diffTypeColor[node.Data.DiffType].Sprint(fmt.Sprintf(AttributeFormat, dir, fileMode.String(), userGroup, size))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (node *FileNode) GetSize() int64 {
|
func (node *FileNode) GetSize() int64 {
|
||||||
|
|
|
||||||
|
|
@ -269,7 +269,7 @@ func (tree *FileTree) AddPath(filepath string, data FileInfo) (*FileNode, []*Fil
|
||||||
|
|
||||||
if node == nil {
|
if node == nil {
|
||||||
// the child could not be added
|
// the child could not be added
|
||||||
return node, addedNodes, fmt.Errorf(fmt.Sprintf("could not add child node: '%s' (path:'%s')", name, filepath))
|
return node, addedNodes, fmt.Errorf("could not add child node: '%s' (path:'%s')", name, filepath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ var GlobalFileTreeCollapse bool
|
||||||
// NodeData is the payload for a FileNode
|
// NodeData is the payload for a FileNode
|
||||||
type NodeData struct {
|
type NodeData struct {
|
||||||
ViewInfo ViewInfo
|
ViewInfo ViewInfo
|
||||||
FileInfo FileInfo
|
FileInfo FileInfo `json:"fileInfo"`
|
||||||
DiffType DiffType
|
DiffType DiffType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,13 +21,13 @@ func TestMergeDiffTypes(t *testing.T) {
|
||||||
b := Unmodified
|
b := Unmodified
|
||||||
merged := a.merge(b)
|
merged := a.merge(b)
|
||||||
if merged != Unmodified {
|
if merged != Unmodified {
|
||||||
t.Errorf("Expected Unchaged (0) but got %v", merged)
|
t.Errorf("Expected Unchanged (0) but got %v", merged)
|
||||||
}
|
}
|
||||||
a = Modified
|
a = Modified
|
||||||
b = Unmodified
|
b = Unmodified
|
||||||
merged = a.merge(b)
|
merged = a.merge(b)
|
||||||
if merged != Modified {
|
if merged != Modified {
|
||||||
t.Errorf("Expected Unchaged (0) but got %v", merged)
|
t.Errorf("Expected Unchanged (0) but got %v", merged)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ package dive
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/wagoodman/dive/dive/image"
|
"github.com/wagoodman/dive/dive/image"
|
||||||
|
|
@ -41,14 +40,13 @@ func ParseImageSource(r string) ImageSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeriveImageSource(image string) (ImageSource, string) {
|
func DeriveImageSource(image string) (ImageSource, string) {
|
||||||
u, err := url.Parse(image)
|
s := strings.SplitN(image, "://", 2)
|
||||||
if err != nil {
|
if len(s) < 2 {
|
||||||
return SourceUnknown, ""
|
return SourceUnknown, ""
|
||||||
}
|
}
|
||||||
|
scheme, imageSource := s[0], s[1]
|
||||||
|
|
||||||
imageSource := strings.TrimPrefix(image, u.Scheme+"://")
|
switch scheme {
|
||||||
|
|
||||||
switch u.Scheme {
|
|
||||||
case SourceDockerEngine.String():
|
case SourceDockerEngine.String():
|
||||||
return SourceDockerEngine, imageSource
|
return SourceDockerEngine, imageSource
|
||||||
case SourcePodmanEngine.String():
|
case SourcePodmanEngine.String():
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,11 @@ func NewResolverFromArchive() *archiveResolver {
|
||||||
return &archiveResolver{}
|
return &archiveResolver{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Name returns the name of the resolver to display to the user.
|
||||||
|
func (r *archiveResolver) Name() string {
|
||||||
|
return "docker-archive"
|
||||||
|
}
|
||||||
|
|
||||||
func (r *archiveResolver) Fetch(path string) (*image.Image, error) {
|
func (r *archiveResolver) Fetch(path string) (*image.Image, error) {
|
||||||
reader, err := os.Open(path)
|
reader, err := os.Open(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -30,3 +35,7 @@ func (r *archiveResolver) Fetch(path string) (*image.Image, error) {
|
||||||
func (r *archiveResolver) Build(args []string) (*image.Image, error) {
|
func (r *archiveResolver) Build(args []string) (*image.Image, error) {
|
||||||
return nil, fmt.Errorf("build option not supported for docker archive resolver")
|
return nil, fmt.Errorf("build option not supported for docker archive resolver")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *archiveResolver) Extract(id string, l string, p string) error {
|
||||||
|
return fmt.Errorf("not implemented")
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ func buildImageFromCli(buildArgs []string) (string, error) {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
defer os.Remove(iidfile.Name())
|
defer os.Remove(iidfile.Name())
|
||||||
|
defer iidfile.Close()
|
||||||
|
|
||||||
allArgs := append([]string{"--iidfile", iidfile.Name()}, buildArgs...)
|
allArgs := append([]string{"--iidfile", iidfile.Name()}, buildArgs...)
|
||||||
err = runDockerCmd("build", allArgs...)
|
err = runDockerCmd("build", allArgs...)
|
||||||
|
|
|
||||||
|
|
@ -44,3 +44,12 @@ func newConfig(configBytes []byte) config {
|
||||||
|
|
||||||
return imageConfig
|
return imageConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isConfig(configBytes []byte) bool {
|
||||||
|
var imageConfig config
|
||||||
|
err := json.Unmarshal(configBytes, &imageConfig)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return imageConfig.RootFs.Type == "layers"
|
||||||
|
}
|
||||||
|
|
|
||||||
7
dive/image/docker/docker_host_unix.go
Normal file
7
dive/image/docker/docker_host_unix.go
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
//go:build !windows
|
||||||
|
|
||||||
|
package docker
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultDockerHost = "unix:///var/run/docker.sock"
|
||||||
|
)
|
||||||
5
dive/image/docker/docker_host_windows.go
Normal file
5
dive/image/docker/docker_host_windows.go
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
package docker
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultDockerHost = "npipe:////.pipe/docker_engine"
|
||||||
|
)
|
||||||
|
|
@ -7,7 +7,10 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
cliconfig "github.com/docker/cli/cli/config"
|
||||||
"github.com/docker/cli/cli/connhelper"
|
"github.com/docker/cli/cli/connhelper"
|
||||||
|
ddocker "github.com/docker/cli/cli/context/docker"
|
||||||
|
ctxstore "github.com/docker/cli/cli/context/store"
|
||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
|
|
||||||
|
|
@ -20,6 +23,11 @@ func NewResolverFromEngine() *engineResolver {
|
||||||
return &engineResolver{}
|
return &engineResolver{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Name returns the name of the resolver to display to the user.
|
||||||
|
func (r *engineResolver) Name() string {
|
||||||
|
return "docker-engine"
|
||||||
|
}
|
||||||
|
|
||||||
func (r *engineResolver) Fetch(id string) (*image.Image, error) {
|
func (r *engineResolver) Fetch(id string) (*image.Image, error) {
|
||||||
reader, err := r.fetchArchive(id)
|
reader, err := r.fetchArchive(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -42,6 +50,19 @@ func (r *engineResolver) Build(args []string) (*image.Image, error) {
|
||||||
return r.Fetch(id)
|
return r.Fetch(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *engineResolver) Extract(id string, l string, p string) error {
|
||||||
|
reader, err := r.fetchArchive(id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ExtractFromImage(io.NopCloser(reader), l, p); err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("unable to extract from image '%s': %+v", id, err)
|
||||||
|
}
|
||||||
|
|
||||||
func (r *engineResolver) fetchArchive(id string) (io.ReadCloser, error) {
|
func (r *engineResolver) fetchArchive(id string) (io.ReadCloser, error) {
|
||||||
var err error
|
var err error
|
||||||
var dockerClient *client.Client
|
var dockerClient *client.Client
|
||||||
|
|
@ -49,8 +70,12 @@ func (r *engineResolver) fetchArchive(id string) (io.ReadCloser, error) {
|
||||||
// pull the engineResolver if it does not exist
|
// pull the engineResolver if it does not exist
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
host := os.Getenv("DOCKER_HOST")
|
host, err := determineDockerHost()
|
||||||
var clientOpts []client.Opt
|
if err != nil {
|
||||||
|
fmt.Printf("> could not determine docker host: %v\n", err)
|
||||||
|
}
|
||||||
|
clientOpts := []client.Opt{client.FromEnv}
|
||||||
|
clientOpts = append(clientOpts, client.WithHost(host))
|
||||||
|
|
||||||
switch strings.Split(host, ":")[0] {
|
switch strings.Split(host, ":")[0] {
|
||||||
case "ssh":
|
case "ssh":
|
||||||
|
|
@ -66,7 +91,8 @@ func (r *engineResolver) fetchArchive(id string) (io.ReadCloser, error) {
|
||||||
}
|
}
|
||||||
return client.WithHTTPClient(httpClient)(c)
|
return client.WithHTTPClient(httpClient)(c)
|
||||||
})
|
})
|
||||||
clientOpts = append(clientOpts, client.WithHost(helper.Host))
|
|
||||||
|
clientOpts = append(clientOpts, client.WithHost(host))
|
||||||
clientOpts = append(clientOpts, client.WithDialContext(helper.Dialer))
|
clientOpts = append(clientOpts, client.WithDialContext(helper.Dialer))
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
@ -74,8 +100,6 @@ func (r *engineResolver) fetchArchive(id string) (io.ReadCloser, error) {
|
||||||
if os.Getenv("DOCKER_TLS_VERIFY") != "" && os.Getenv("DOCKER_CERT_PATH") == "" {
|
if os.Getenv("DOCKER_TLS_VERIFY") != "" && os.Getenv("DOCKER_CERT_PATH") == "" {
|
||||||
os.Setenv("DOCKER_CERT_PATH", "~/.docker")
|
os.Setenv("DOCKER_CERT_PATH", "~/.docker")
|
||||||
}
|
}
|
||||||
|
|
||||||
clientOpts = append(clientOpts, client.FromEnv)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
clientOpts = append(clientOpts, client.WithAPIVersionNegotiation())
|
clientOpts = append(clientOpts, client.WithAPIVersionNegotiation())
|
||||||
|
|
@ -83,12 +107,17 @@ func (r *engineResolver) fetchArchive(id string) (io.ReadCloser, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
_, _, err = dockerClient.ImageInspectWithRaw(ctx, id)
|
_, err = dockerClient.ImageInspect(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// don't use the API, the CLI has more informative output
|
// check if the error is due to the image not existing locally
|
||||||
fmt.Println("Handler not available locally. Trying to pull '" + id + "'...")
|
if client.IsErrNotFound(err) {
|
||||||
err = runDockerCmd("pull", id)
|
fmt.Println("The image is not available locally. Trying to pull '" + id + "'...")
|
||||||
if err != nil {
|
err = runDockerCmd("pull", id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Some other error occurred, return it
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -100,3 +129,63 @@ func (r *engineResolver) fetchArchive(id string) (io.ReadCloser, error) {
|
||||||
|
|
||||||
return readCloser, nil
|
return readCloser, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// determineDockerHost tries to the determine the docker host that we should connect to
|
||||||
|
// in the following order of decreasing precedence:
|
||||||
|
// - value of "DOCKER_HOST" environment variable
|
||||||
|
// - host retrieved from the current context (specified via DOCKER_CONTEXT)
|
||||||
|
// - "default docker host" for the host operating system, otherwise
|
||||||
|
func determineDockerHost() (string, error) {
|
||||||
|
// If the docker host is explicitly set via the "DOCKER_HOST" environment variable,
|
||||||
|
// then its a no-brainer :shrug:
|
||||||
|
if os.Getenv("DOCKER_HOST") != "" {
|
||||||
|
return os.Getenv("DOCKER_HOST"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
currentContext := os.Getenv("DOCKER_CONTEXT")
|
||||||
|
if currentContext == "" {
|
||||||
|
cf, err := cliconfig.Load(cliconfig.Dir())
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
currentContext = cf.CurrentContext
|
||||||
|
}
|
||||||
|
|
||||||
|
if currentContext == "" {
|
||||||
|
// If a docker context is neither specified via the "DOCKER_CONTEXT" environment variable nor via the
|
||||||
|
// $HOME/.docker/config file, then we fall back to connecting to the "default docker host" meant for
|
||||||
|
// the host operating system.
|
||||||
|
return defaultDockerHost, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
storeConfig := ctxstore.NewConfig(
|
||||||
|
func() interface{} { return &ddocker.EndpointMeta{} },
|
||||||
|
ctxstore.EndpointTypeGetter(ddocker.DockerEndpoint, func() interface{} { return &ddocker.EndpointMeta{} }),
|
||||||
|
)
|
||||||
|
|
||||||
|
st := ctxstore.New(cliconfig.ContextStoreDir(), storeConfig)
|
||||||
|
md, err := st.GetMetadata(currentContext)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
dockerEP, ok := md.Endpoints[ddocker.DockerEndpoint]
|
||||||
|
if !ok {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
dockerEPMeta, ok := dockerEP.(ddocker.EndpointMeta)
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("expected docker.EndpointMeta, got %T", dockerEP)
|
||||||
|
}
|
||||||
|
|
||||||
|
if dockerEPMeta.Host != "" {
|
||||||
|
return dockerEPMeta.Host, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// We might end up here, if the context was created with the `host` set to an empty value (i.e. '').
|
||||||
|
// For example:
|
||||||
|
// ```sh
|
||||||
|
// docker context create foo --docker "host="
|
||||||
|
// ```
|
||||||
|
// In such scenario, we mimic the `docker` cli and try to connect to the "default docker host".
|
||||||
|
return defaultDockerHost, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,11 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/klauspost/compress/zstd"
|
||||||
|
|
||||||
"github.com/wagoodman/dive/dive/filetree"
|
"github.com/wagoodman/dive/dive/filetree"
|
||||||
"github.com/wagoodman/dive/dive/image"
|
"github.com/wagoodman/dive/dive/image"
|
||||||
)
|
)
|
||||||
|
|
@ -93,25 +96,21 @@ func NewImageArchive(tarFile io.ReadCloser) (*ImageArchive, error) {
|
||||||
// never consume more bytes than this buffer contains so we can start again.
|
// never consume more bytes than this buffer contains so we can start again.
|
||||||
|
|
||||||
// 512 bytes ought to be enough (as that's the size of a TAR entry header),
|
// 512 bytes ought to be enough (as that's the size of a TAR entry header),
|
||||||
// but play it safe with 1024 bytes. This should also include very small layers
|
// but play it safe with 1024 bytes. This should also include very small layers.
|
||||||
// (unless they've also been gzipped, but Docker does not appear to do it)
|
|
||||||
buffer := make([]byte, 1024)
|
buffer := make([]byte, 1024)
|
||||||
n, err := io.ReadFull(tarReader, buffer)
|
n, err := io.ReadFull(tarReader, buffer)
|
||||||
if err != nil && err != io.ErrUnexpectedEOF {
|
if err != nil && err != io.ErrUnexpectedEOF {
|
||||||
return img, err
|
return img, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only try reading a TAR if file is "big enough"
|
originalReader := func() io.Reader {
|
||||||
if n == cap(buffer) {
|
return io.MultiReader(bytes.NewReader(buffer[:n]), tarReader)
|
||||||
var unwrappedReader io.Reader
|
}
|
||||||
unwrappedReader, err = gzip.NewReader(io.MultiReader(bytes.NewReader(buffer[:n]), tarReader))
|
|
||||||
if err != nil {
|
|
||||||
// Not a gzipped entry
|
|
||||||
unwrappedReader = io.MultiReader(bytes.NewReader(buffer[:n]), tarReader)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try reading a TAR
|
// Try reading a gzip/estargz compressed layer
|
||||||
layerReader := tar.NewReader(unwrappedReader)
|
gzipReader, err := gzip.NewReader(originalReader())
|
||||||
|
if err == nil {
|
||||||
|
layerReader := tar.NewReader(gzipReader)
|
||||||
tree, err := processLayerTar(name, layerReader)
|
tree, err := processLayerTar(name, layerReader)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
currentLayer++
|
currentLayer++
|
||||||
|
|
@ -121,13 +120,36 @@ func NewImageArchive(tarFile io.ReadCloser) (*ImageArchive, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not a TAR (or smaller than our buffer), might be a JSON file
|
// Try reading a zstd compressed layer
|
||||||
|
zstdReader, err := zstd.NewReader(originalReader())
|
||||||
|
if err == nil {
|
||||||
|
layerReader := tar.NewReader(zstdReader)
|
||||||
|
tree, err := processLayerTar(name, layerReader)
|
||||||
|
if err == nil {
|
||||||
|
currentLayer++
|
||||||
|
// add the layer to the image
|
||||||
|
img.layerMap[tree.Name] = tree
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try reading a plain tar layer
|
||||||
|
layerReader := tar.NewReader(originalReader())
|
||||||
|
tree, err := processLayerTar(name, layerReader)
|
||||||
|
if err == nil {
|
||||||
|
currentLayer++
|
||||||
|
// add the layer to the image
|
||||||
|
img.layerMap[tree.Name] = tree
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not a TAR/GZIP/ZSTD, might be a JSON file
|
||||||
decoder := json.NewDecoder(bytes.NewReader(buffer[:n]))
|
decoder := json.NewDecoder(bytes.NewReader(buffer[:n]))
|
||||||
token, err := decoder.Token()
|
token, err := decoder.Token()
|
||||||
if _, ok := token.(json.Delim); err == nil && ok {
|
if _, ok := token.(json.Delim); err == nil && ok {
|
||||||
// Looks like a JSON object (or array)
|
// Looks like a JSON object (or array)
|
||||||
// XXX: should we add a header.Size check too?
|
// XXX: should we add a header.Size check too?
|
||||||
fileBuffer, err := io.ReadAll(io.MultiReader(bytes.NewReader(buffer[:n]), tarReader))
|
fileBuffer, err := io.ReadAll(originalReader())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return img, err
|
return img, err
|
||||||
}
|
}
|
||||||
|
|
@ -139,11 +161,31 @@ func NewImageArchive(tarFile io.ReadCloser) (*ImageArchive, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
manifestContent, exists := jsonFiles["manifest.json"]
|
manifestContent, exists := jsonFiles["manifest.json"]
|
||||||
if !exists {
|
if exists {
|
||||||
return img, fmt.Errorf("could not find image manifest")
|
img.manifest = newManifest(manifestContent)
|
||||||
}
|
} else {
|
||||||
|
// manifest.json is not part of the OCI spec, docker includes it for compatibility
|
||||||
|
// Provide compatibility by finding the config and using our layerMap
|
||||||
|
var configPath string
|
||||||
|
for path, content := range jsonFiles {
|
||||||
|
if isConfig(content) {
|
||||||
|
configPath = path
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(configPath) == 0 {
|
||||||
|
return img, fmt.Errorf("could not find image manifest")
|
||||||
|
}
|
||||||
|
|
||||||
img.manifest = newManifest(manifestContent)
|
var layerPaths []string
|
||||||
|
for k := range img.layerMap {
|
||||||
|
layerPaths = append(layerPaths, k)
|
||||||
|
}
|
||||||
|
img.manifest = manifest{
|
||||||
|
ConfigPath: configPath,
|
||||||
|
LayerTarPaths: layerPaths,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
configContent, exists := jsonFiles[img.manifest.ConfigPath]
|
configContent, exists := jsonFiles[img.manifest.ConfigPath]
|
||||||
if !exists {
|
if !exists {
|
||||||
|
|
@ -256,3 +298,80 @@ func (img *ImageArchive) ToImage() (*image.Image, error) {
|
||||||
Layers: layers,
|
Layers: layers,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExtractFromImage(tarFile io.ReadCloser, l string, p string) error {
|
||||||
|
tarReader := tar.NewReader(tarFile)
|
||||||
|
|
||||||
|
for {
|
||||||
|
header, err := tarReader.Next()
|
||||||
|
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
name := header.Name
|
||||||
|
|
||||||
|
switch header.Typeflag {
|
||||||
|
case tar.TypeReg:
|
||||||
|
if name == l {
|
||||||
|
err = extractInner(tar.NewReader(tarReader), p)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractInner(reader *tar.Reader, p string) error {
|
||||||
|
target := strings.TrimPrefix(p, "/")
|
||||||
|
|
||||||
|
for {
|
||||||
|
header, err := reader.Next()
|
||||||
|
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
name := header.Name
|
||||||
|
|
||||||
|
switch header.Typeflag {
|
||||||
|
case tar.TypeReg:
|
||||||
|
if strings.HasPrefix(name, target) {
|
||||||
|
err := os.MkdirAll(filepath.Dir(name), 0755)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
out, err := os.Create(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = io.Copy(out, reader)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ func buildImageFromCli(buildArgs []string) (string, error) {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
defer os.Remove(iidfile.Name())
|
defer os.Remove(iidfile.Name())
|
||||||
|
defer iidfile.Close()
|
||||||
|
|
||||||
allArgs := append([]string{"--iidfile", iidfile.Name()}, buildArgs...)
|
allArgs := append([]string{"--iidfile", iidfile.Name()}, buildArgs...)
|
||||||
err = runPodmanCmd("build", allArgs...)
|
err = runPodmanCmd("build", allArgs...)
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,11 @@ func NewResolverFromEngine() *resolver {
|
||||||
return &resolver{}
|
return &resolver{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Name returns the name of the resolver to display to the user.
|
||||||
|
func (r *resolver) Name() string {
|
||||||
|
return "podman"
|
||||||
|
}
|
||||||
|
|
||||||
func (r *resolver) Build(args []string) (*image.Image, error) {
|
func (r *resolver) Build(args []string) (*image.Image, error) {
|
||||||
id, err := buildImageFromCli(args)
|
id, err := buildImageFromCli(args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -36,6 +41,21 @@ func (r *resolver) Fetch(id string) (*image.Image, error) {
|
||||||
return nil, fmt.Errorf("unable to resolve image '%s': %+v", id, err)
|
return nil, fmt.Errorf("unable to resolve image '%s': %+v", id, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *resolver) Extract(id string, l string, p string) error {
|
||||||
|
// todo: add podman fetch attempt via varlink first...
|
||||||
|
|
||||||
|
err, reader := streamPodmanCmd("image", "save", id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := docker.ExtractFromImage(io.NopCloser(reader), l, p); err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("unable to extract from image '%s': %+v", id, err)
|
||||||
|
}
|
||||||
|
|
||||||
func (r *resolver) resolveFromDockerArchive(id string) (*image.Image, error) {
|
func (r *resolver) resolveFromDockerArchive(id string) (*image.Image, error) {
|
||||||
err, reader := streamPodmanCmd("image", "save", id)
|
err, reader := streamPodmanCmd("image", "save", id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,10 @@ func NewResolverFromEngine() *resolver {
|
||||||
return &resolver{}
|
return &resolver{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Name returns the name of the resolver to display to the user.
|
||||||
|
func (r *resolver) Name() string {
|
||||||
|
return "podman"
|
||||||
|
}
|
||||||
func (r *resolver) Build(args []string) (*image.Image, error) {
|
func (r *resolver) Build(args []string) (*image.Image, error) {
|
||||||
return nil, fmt.Errorf("unsupported platform")
|
return nil, fmt.Errorf("unsupported platform")
|
||||||
}
|
}
|
||||||
|
|
@ -22,3 +26,7 @@ func (r *resolver) Build(args []string) (*image.Image, error) {
|
||||||
func (r *resolver) Fetch(id string) (*image.Image, error) {
|
func (r *resolver) Fetch(id string) (*image.Image, error) {
|
||||||
return nil, fmt.Errorf("unsupported platform")
|
return nil, fmt.Errorf("unsupported platform")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *resolver) Extract(id string, l string, p string) error {
|
||||||
|
return fmt.Errorf("unsupported platform")
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
package image
|
package image
|
||||||
|
|
||||||
type Resolver interface {
|
type Resolver interface {
|
||||||
|
Name() string
|
||||||
Fetch(id string) (*Image, error)
|
Fetch(id string) (*Image, error)
|
||||||
Build(options []string) (*Image, error)
|
Build(options []string) (*Image, error)
|
||||||
|
Extract(id string, layer string, path string) error
|
||||||
}
|
}
|
||||||
|
|
|
||||||
84
go.mod
84
go.mod
|
|
@ -1,61 +1,75 @@
|
||||||
module github.com/wagoodman/dive
|
module github.com/wagoodman/dive
|
||||||
|
|
||||||
go 1.19
|
go 1.24
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/awesome-gocui/gocui v1.1.0
|
github.com/awesome-gocui/gocui v1.1.0
|
||||||
github.com/awesome-gocui/keybinding v1.0.1-0.20190805183143-864552bd36b7
|
github.com/awesome-gocui/keybinding v1.0.1-0.20211011072933-86029037a63f
|
||||||
github.com/cespare/xxhash v1.1.0
|
github.com/cespare/xxhash/v2 v2.3.0
|
||||||
github.com/docker/cli v0.0.0-20190906153656-016a3232168d
|
github.com/docker/cli v28.0.1+incompatible
|
||||||
github.com/docker/docker v24.0.7+incompatible
|
github.com/docker/docker v28.0.1+incompatible
|
||||||
github.com/dustin/go-humanize v1.0.0
|
github.com/dustin/go-humanize v1.0.1
|
||||||
github.com/fatih/color v1.7.0
|
github.com/fatih/color v1.18.0
|
||||||
github.com/google/uuid v1.1.1
|
github.com/google/uuid v1.6.0
|
||||||
github.com/logrusorgru/aurora v0.0.0-20190803045625-94edacc10f9b
|
github.com/klauspost/compress v1.18.0
|
||||||
|
github.com/logrusorgru/aurora/v4 v4.0.0
|
||||||
github.com/lunixbochs/vtclean v1.0.0
|
github.com/lunixbochs/vtclean v1.0.0
|
||||||
github.com/mitchellh/go-homedir v1.1.0
|
github.com/mitchellh/go-homedir v1.1.0
|
||||||
github.com/phayes/permbits v0.0.0-20190612203442-39d7c581d2ee
|
github.com/phayes/permbits v0.0.0-20190612203442-39d7c581d2ee
|
||||||
github.com/sergi/go-diff v1.0.0
|
github.com/sergi/go-diff v1.3.1
|
||||||
github.com/sirupsen/logrus v1.4.2
|
github.com/sirupsen/logrus v1.9.3
|
||||||
github.com/spf13/afero v1.2.2
|
github.com/spf13/afero v1.14.0
|
||||||
github.com/spf13/cobra v0.0.5
|
github.com/spf13/cobra v1.9.1
|
||||||
github.com/spf13/viper v1.4.0
|
github.com/spf13/viper v1.20.0
|
||||||
golang.org/x/net v0.17.0
|
golang.org/x/net v0.37.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Microsoft/go-winio v0.4.14 // indirect
|
github.com/Microsoft/go-winio v0.4.14 // indirect
|
||||||
github.com/docker/distribution v2.8.2+incompatible // indirect
|
github.com/containerd/log v0.1.0 // indirect
|
||||||
|
github.com/distribution/reference v0.6.0 // indirect
|
||||||
|
github.com/docker/docker-credential-helpers v0.8.2 // indirect
|
||||||
github.com/docker/go-connections v0.4.0 // indirect
|
github.com/docker/go-connections v0.4.0 // indirect
|
||||||
github.com/docker/go-units v0.4.0 // indirect
|
github.com/docker/go-units v0.4.0 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.4.7 // indirect
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
|
github.com/fsnotify/fsnotify v1.8.0 // indirect
|
||||||
|
github.com/fvbommel/sortorder v1.1.0 // indirect
|
||||||
github.com/gdamore/encoding v1.0.0 // indirect
|
github.com/gdamore/encoding v1.0.0 // indirect
|
||||||
github.com/gdamore/tcell/v2 v2.4.0 // indirect
|
github.com/gdamore/tcell/v2 v2.4.0 // indirect
|
||||||
|
github.com/go-logr/logr v1.4.2 // indirect
|
||||||
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
|
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
|
|
||||||
github.com/lucasb-eyer/go-colorful v1.0.3 // indirect
|
github.com/lucasb-eyer/go-colorful v1.0.3 // indirect
|
||||||
github.com/magiconair/properties v1.8.1 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.2 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.9 // indirect
|
|
||||||
github.com/mattn/go-runewidth v0.0.10 // indirect
|
github.com/mattn/go-runewidth v0.0.10 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.1.2 // indirect
|
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||||
github.com/moby/term v0.5.0 // indirect
|
github.com/moby/term v0.5.0 // indirect
|
||||||
github.com/morikuni/aec v1.0.0 // indirect
|
github.com/morikuni/aec v1.0.0 // indirect
|
||||||
github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
|
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||||
github.com/opencontainers/image-spec v1.0.2 // indirect
|
github.com/opencontainers/image-spec v1.0.2 // indirect
|
||||||
github.com/pelletier/go-toml v1.4.0 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/rivo/uniseg v0.1.0 // indirect
|
github.com/rivo/uniseg v0.1.0 // indirect
|
||||||
github.com/spf13/cast v1.3.0 // indirect
|
github.com/sagikazarmark/locafero v0.7.0 // indirect
|
||||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/cast v1.7.1 // indirect
|
||||||
github.com/stretchr/testify v1.4.0 // indirect
|
github.com/spf13/pflag v1.0.6 // indirect
|
||||||
golang.org/x/sys v0.13.0 // indirect
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
golang.org/x/term v0.13.0 // indirect
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect
|
||||||
golang.org/x/text v0.13.0 // indirect
|
go.opentelemetry.io/otel v1.31.0 // indirect
|
||||||
gopkg.in/yaml.v2 v2.2.8 // indirect
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 // indirect
|
||||||
gotest.tools v2.2.0+incompatible // indirect
|
go.opentelemetry.io/otel/metric v1.31.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/sdk v1.31.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/trace v1.31.0 // indirect
|
||||||
|
go.uber.org/atomic v1.9.0 // indirect
|
||||||
|
go.uber.org/multierr v1.9.0 // indirect
|
||||||
|
golang.org/x/sys v0.31.0 // indirect
|
||||||
|
golang.org/x/term v0.30.0 // indirect
|
||||||
|
golang.org/x/text v0.23.0 // indirect
|
||||||
|
golang.org/x/time v0.11.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
gotest.tools/v3 v3.5.0 // indirect
|
gotest.tools/v3 v3.5.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
|
||||||
321
go.sum
321
go.sum
|
|
@ -1,251 +1,207 @@
|
||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
||||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
|
||||||
github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU=
|
github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU=
|
||||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||||
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
|
|
||||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
|
||||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
|
||||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
|
||||||
github.com/awesome-gocui/gocui v0.5.0/go.mod h1:1QikxFaPhe2frKeKvEwZEIGia3haiOxOUXKinrv17mA=
|
github.com/awesome-gocui/gocui v0.5.0/go.mod h1:1QikxFaPhe2frKeKvEwZEIGia3haiOxOUXKinrv17mA=
|
||||||
github.com/awesome-gocui/gocui v1.1.0 h1:db2j7yFEoHZjpQFeE2xqiatS8bm1lO3THeLwE6MzOII=
|
github.com/awesome-gocui/gocui v1.1.0 h1:db2j7yFEoHZjpQFeE2xqiatS8bm1lO3THeLwE6MzOII=
|
||||||
github.com/awesome-gocui/gocui v1.1.0/go.mod h1:M2BXkrp7PR97CKnPRT7Rk0+rtswChPtksw/vRAESGpg=
|
github.com/awesome-gocui/gocui v1.1.0/go.mod h1:M2BXkrp7PR97CKnPRT7Rk0+rtswChPtksw/vRAESGpg=
|
||||||
github.com/awesome-gocui/keybinding v1.0.1-0.20190805183143-864552bd36b7 h1:DDdWoFOtXWySkgCiGGn80TM/E2FS2T1qJBJJxup9+Vo=
|
github.com/awesome-gocui/keybinding v1.0.1-0.20211011072933-86029037a63f h1:u5xQfLwWC98BFToYDifqEcgK2ht2FFlbvRlzRnMb0cQ=
|
||||||
github.com/awesome-gocui/keybinding v1.0.1-0.20190805183143-864552bd36b7/go.mod h1:z0TyCwIhaT97yU+becTse8Dqh2CvYT0FLw0R0uTk0ag=
|
github.com/awesome-gocui/keybinding v1.0.1-0.20211011072933-86029037a63f/go.mod h1:z0TyCwIhaT97yU+becTse8Dqh2CvYT0FLw0R0uTk0ag=
|
||||||
github.com/awesome-gocui/termbox-go v0.0.0-20190427202837-c0aef3d18bcc/go.mod h1:tOy3o5Nf1bA17mnK4W41gD7PS3u4Cv0P0pqFcoWMy8s=
|
github.com/awesome-gocui/termbox-go v0.0.0-20190427202837-c0aef3d18bcc/go.mod h1:tOy3o5Nf1bA17mnK4W41gD7PS3u4Cv0P0pqFcoWMy8s=
|
||||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
|
||||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
|
||||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
|
||||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
|
||||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||||
github.com/docker/cli v0.0.0-20190906153656-016a3232168d h1:gwX/88xJZfxZV1yjhhuQpWTmEgJis7/XGCVu3iDIZYU=
|
github.com/docker/cli v28.0.1+incompatible h1:g0h5NQNda3/CxIsaZfH4Tyf6vpxFth7PYl3hgCPOKzs=
|
||||||
github.com/docker/cli v0.0.0-20190906153656-016a3232168d/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
github.com/docker/cli v28.0.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||||
github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
|
github.com/docker/docker v28.0.1+incompatible h1:FCHjSRdXhNRFjlHMTv4jUNlIBbTeRjrWfeFuJp7jpo0=
|
||||||
github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
github.com/docker/docker v28.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||||
github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM=
|
github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo=
|
||||||
github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
|
||||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||||
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
|
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
|
||||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||||
|
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||||
|
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||||
|
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
|
||||||
|
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||||
|
github.com/fvbommel/sortorder v1.1.0 h1:fUmoe+HLsBTctBDoaBwpQo5N+nrCp8g/BjKb/6ZQmYw=
|
||||||
|
github.com/fvbommel/sortorder v1.1.0/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
|
||||||
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
|
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
|
||||||
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
|
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
|
||||||
github.com/gdamore/tcell/v2 v2.4.0 h1:W6dxJEmaxYvhICFoTY3WrLLEXsQ11SaFnKGVEXW57KM=
|
github.com/gdamore/tcell/v2 v2.4.0 h1:W6dxJEmaxYvhICFoTY3WrLLEXsQ11SaFnKGVEXW57KM=
|
||||||
github.com/gdamore/tcell/v2 v2.4.0/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU=
|
github.com/gdamore/tcell/v2 v2.4.0/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU=
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
|
||||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
|
||||||
|
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys=
|
||||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I=
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
|
||||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
|
||||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
|
||||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
|
||||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
|
||||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
|
||||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
|
||||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
|
||||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
|
||||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
|
||||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
|
||||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
|
||||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
|
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||||
|
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
|
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
|
||||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
|
||||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/logrusorgru/aurora v0.0.0-20190803045625-94edacc10f9b h1:PMbSa9CgaiQR9NLlUTwKi+7aeLl3GG5JX5ERJxfQ3IE=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/logrusorgru/aurora v0.0.0-20190803045625-94edacc10f9b/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/logrusorgru/aurora/v4 v4.0.0 h1:sRjfPpun/63iADiSvGGjgA1cAYegEWMPCJdUpJYn9JA=
|
||||||
|
github.com/logrusorgru/aurora/v4 v4.0.0/go.mod h1:lP0iIa2nrnT/qoFXcOZSrZQpJ1o6n2CUf/hyHi2Q4ZQ=
|
||||||
github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac=
|
github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac=
|
||||||
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||||
github.com/lunixbochs/vtclean v1.0.0 h1:xu2sLAri4lGiovBDQKxl5mrXyESr3gUr5m5SM5+LVb8=
|
github.com/lunixbochs/vtclean v1.0.0 h1:xu2sLAri4lGiovBDQKxl5mrXyESr3gUr5m5SM5+LVb8=
|
||||||
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
|
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
|
||||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
|
||||||
github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg=
|
|
||||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
|
||||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||||
github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg=
|
github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg=
|
||||||
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
|
||||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||||
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
|
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
|
||||||
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
||||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||||
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
|
|
||||||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
|
||||||
github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
|
github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
|
||||||
github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
||||||
github.com/pelletier/go-toml v1.4.0 h1:u3Z1r+oOXJIkxqw34zVhyPgjBsm6X2wn21NWs/HfSeg=
|
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
||||||
github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=
|
|
||||||
github.com/phayes/permbits v0.0.0-20190612203442-39d7c581d2ee h1:P6U24L02WMfj9ymZTxl7CxS73JC99x3ukk+DBkgQGQs=
|
github.com/phayes/permbits v0.0.0-20190612203442-39d7c581d2ee h1:P6U24L02WMfj9ymZTxl7CxS73JC99x3ukk+DBkgQGQs=
|
||||||
github.com/phayes/permbits v0.0.0-20190612203442-39d7c581d2ee/go.mod h1:3uODdxMgOaPYeWU7RzZLxVtJHZ/x1f/iHkBZuKJDzuY=
|
github.com/phayes/permbits v0.0.0-20190612203442-39d7c581d2ee/go.mod h1:3uODdxMgOaPYeWU7RzZLxVtJHZ/x1f/iHkBZuKJDzuY=
|
||||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
|
||||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
|
||||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
|
||||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
|
||||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
|
||||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
|
||||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
|
||||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
|
||||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
|
||||||
github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY=
|
github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY=
|
||||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||||
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo=
|
||||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k=
|
||||||
|
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
|
||||||
|
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
|
||||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
||||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=
|
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA=
|
||||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo=
|
||||||
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
|
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
|
||||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||||
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
|
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
||||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
||||||
github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
|
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
github.com/spf13/viper v1.20.0 h1:zrxIyR3RQIOsarIrgL8+sAvALXul9jeEPa06Y0Ph6vY=
|
||||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
github.com/spf13/viper v1.20.0/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4=
|
||||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
|
||||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
|
||||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
|
||||||
github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU=
|
|
||||||
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk=
|
||||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8=
|
||||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY=
|
||||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE=
|
||||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0 h1:K0XaT3DwHAcV4nKLzcQvwAgSyisUghWoY20I7huthMk=
|
||||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0/go.mod h1:B5Ki776z/MBnVha1Nzwp5arlzBbE3+1jk+pGmaP5HME=
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 h1:lUsI2TYsQw2r1IASwoROaCnjdj2cvC2+Jbxvk6nHnWU=
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0/go.mod h1:2HpZxxQurfGxJlJDblybejHB6RX6pmExPNe517hREw4=
|
||||||
|
go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE=
|
||||||
|
go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY=
|
||||||
|
go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk=
|
||||||
|
go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0=
|
||||||
|
go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys=
|
||||||
|
go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A=
|
||||||
|
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
|
||||||
|
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
|
||||||
|
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
||||||
|
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
|
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
|
||||||
|
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
|
||||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||||
|
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
|
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
|
||||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
|
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
|
@ -253,22 +209,21 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 h1:T6rh4haD3GVYsgEfWExoCZA2o2FmbNyKpTuAxbEFPTg=
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:wp2WsuBYj6j8wUdo3ToZsdxxixbvQNAHqVJrTgi5E5M=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 h1:QCqS/PdaHTSWGvupk2F/ehwHtGc0/GYkT+3GAcR1CCc=
|
||||||
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
|
||||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E=
|
||||||
|
google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
|
||||||
|
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
|
||||||
|
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
|
||||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
|
||||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY=
|
gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY=
|
||||||
gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
|
gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/dustin/go-humanize"
|
"github.com/dustin/go-humanize"
|
||||||
"github.com/logrusorgru/aurora"
|
"github.com/logrusorgru/aurora/v4"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
"github.com/wagoodman/dive/dive/image"
|
"github.com/wagoodman/dive/dive/image"
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/dustin/go-humanize"
|
"github.com/dustin/go-humanize"
|
||||||
"github.com/logrusorgru/aurora"
|
"github.com/logrusorgru/aurora/v4"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
"github.com/wagoodman/dive/dive/image"
|
"github.com/wagoodman/dive/dive/image"
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,9 @@ package export
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/wagoodman/dive/dive/filetree"
|
||||||
diveImage "github.com/wagoodman/dive/dive/image"
|
diveImage "github.com/wagoodman/dive/dive/image"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -11,6 +14,7 @@ type export struct {
|
||||||
Image image `json:"image"`
|
Image image `json:"image"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewExport exports the analysis to a JSON
|
||||||
func NewExport(analysis *diveImage.AnalysisResult) *export {
|
func NewExport(analysis *diveImage.AnalysisResult) *export {
|
||||||
data := export{
|
data := export{
|
||||||
Layer: make([]layer, len(analysis.Layers)),
|
Layer: make([]layer, len(analysis.Layers)),
|
||||||
|
|
@ -24,12 +28,22 @@ func NewExport(analysis *diveImage.AnalysisResult) *export {
|
||||||
|
|
||||||
// export layers in order
|
// export layers in order
|
||||||
for idx, curLayer := range analysis.Layers {
|
for idx, curLayer := range analysis.Layers {
|
||||||
|
layerFileList := make([]filetree.FileInfo, 0)
|
||||||
|
visitor := func(node *filetree.FileNode) error {
|
||||||
|
layerFileList = append(layerFileList, node.Data.FileInfo)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
err := curLayer.Tree.VisitDepthChildFirst(visitor, nil)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("Unable to propagate layer tree: %+v", err)
|
||||||
|
}
|
||||||
data.Layer[idx] = layer{
|
data.Layer[idx] = layer{
|
||||||
Index: curLayer.Index,
|
Index: curLayer.Index,
|
||||||
ID: curLayer.Id,
|
ID: curLayer.Id,
|
||||||
DigestID: curLayer.Digest,
|
DigestID: curLayer.Digest,
|
||||||
SizeBytes: curLayer.Size,
|
SizeBytes: curLayer.Size,
|
||||||
Command: curLayer.Command,
|
Command: curLayer.Command,
|
||||||
|
FileList: layerFileList,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,9 +1,14 @@
|
||||||
package export
|
package export
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/wagoodman/dive/dive/filetree"
|
||||||
|
)
|
||||||
|
|
||||||
type layer struct {
|
type layer struct {
|
||||||
Index int `json:"index"`
|
Index int `json:"index"`
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
DigestID string `json:"digestId"`
|
DigestID string `json:"digestId"`
|
||||||
SizeBytes uint64 `json:"sizeBytes"`
|
SizeBytes uint64 `json:"sizeBytes"`
|
||||||
Command string `json:"command"`
|
Command string `json:"command"`
|
||||||
|
FileList []filetree.FileInfo `json:"fileList"`
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ func run(enableUi bool, options Options, imageResolver image.Resolver, events ev
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
events.message(utils.TitleFormat("Image Source: ") + options.Source.String() + "://" + options.Image)
|
events.message(utils.TitleFormat("Image Source: ") + options.Source.String() + "://" + options.Image)
|
||||||
events.message(utils.TitleFormat("Fetching image...") + " (this can take a while for large images)")
|
events.message(utils.TitleFormat("Extracting image from "+imageResolver.Name()+"...") + " (this can take a while for large images)")
|
||||||
img, err = imageResolver.Fetch(options.Image)
|
img, err = imageResolver.Fetch(options.Image)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
events.exitWithErrorMessage("cannot fetch image", err)
|
events.exitWithErrorMessage("cannot fetch image", err)
|
||||||
|
|
@ -108,7 +108,7 @@ func run(enableUi bool, options Options, imageResolver image.Resolver, events ev
|
||||||
// enough sleep will prevent this behavior (todo: remove this hack)
|
// enough sleep will prevent this behavior (todo: remove this hack)
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
||||||
err = ui.Run(options.Image, analysis, treeStack)
|
err = ui.Run(options.Image, imageResolver, analysis, treeStack)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
events.exitWithError(err)
|
events.exitWithError(err)
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,14 @@ import (
|
||||||
|
|
||||||
type defaultResolver struct{}
|
type defaultResolver struct{}
|
||||||
|
|
||||||
|
func (r *defaultResolver) Name() string {
|
||||||
|
return "default-resolver"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *defaultResolver) Extract(id string, l string, p string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *defaultResolver) Fetch(id string) (*image.Image, error) {
|
func (r *defaultResolver) Fetch(id string) (*image.Image, error) {
|
||||||
archive, err := docker.TestLoadArchive("../.data/test-docker-image.tar")
|
archive, err := docker.TestLoadArchive("../.data/test-docker-image.tar")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -30,6 +38,14 @@ func (r *defaultResolver) Build(args []string) (*image.Image, error) {
|
||||||
|
|
||||||
type failedBuildResolver struct{}
|
type failedBuildResolver struct{}
|
||||||
|
|
||||||
|
func (r *failedBuildResolver) Name() string {
|
||||||
|
return "failed-build-resolver"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *failedBuildResolver) Extract(id string, l string, p string) error {
|
||||||
|
return fmt.Errorf("some extract failure")
|
||||||
|
}
|
||||||
|
|
||||||
func (r *failedBuildResolver) Fetch(id string) (*image.Image, error) {
|
func (r *failedBuildResolver) Fetch(id string) (*image.Image, error) {
|
||||||
archive, err := docker.TestLoadArchive("../.data/test-docker-image.tar")
|
archive, err := docker.TestLoadArchive("../.data/test-docker-image.tar")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -44,6 +60,14 @@ func (r *failedBuildResolver) Build(args []string) (*image.Image, error) {
|
||||||
|
|
||||||
type failedFetchResolver struct{}
|
type failedFetchResolver struct{}
|
||||||
|
|
||||||
|
func (r *failedFetchResolver) Name() string {
|
||||||
|
return "failed-fetch-resolver"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *failedFetchResolver) Extract(id string, l string, p string) error {
|
||||||
|
return fmt.Errorf("some extract failure")
|
||||||
|
}
|
||||||
|
|
||||||
func (r *failedFetchResolver) Fetch(id string) (*image.Image, error) {
|
func (r *failedFetchResolver) Fetch(id string) (*image.Image, error) {
|
||||||
return nil, fmt.Errorf("some fetch failure")
|
return nil, fmt.Errorf("some fetch failure")
|
||||||
}
|
}
|
||||||
|
|
@ -108,7 +132,7 @@ func TestRun(t *testing.T) {
|
||||||
},
|
},
|
||||||
events: []testEvent{
|
events: []testEvent{
|
||||||
{stdout: "Image Source: docker://dive-example", stderr: "", errorOnExit: false, errMessage: ""},
|
{stdout: "Image Source: docker://dive-example", stderr: "", errorOnExit: false, errMessage: ""},
|
||||||
{stdout: "Fetching image... (this can take a while for large images)", stderr: "", errorOnExit: false, errMessage: ""},
|
{stdout: "Extracting image from default-resolver... (this can take a while for large images)", stderr: "", errorOnExit: false, errMessage: ""},
|
||||||
{stdout: "Analyzing image...", stderr: "", errorOnExit: false, errMessage: ""},
|
{stdout: "Analyzing image...", stderr: "", errorOnExit: false, errMessage: ""},
|
||||||
{stdout: "Building cache...", stderr: "", errorOnExit: false, errMessage: ""},
|
{stdout: "Building cache...", stderr: "", errorOnExit: false, errMessage: ""},
|
||||||
},
|
},
|
||||||
|
|
@ -126,7 +150,7 @@ func TestRun(t *testing.T) {
|
||||||
},
|
},
|
||||||
events: []testEvent{
|
events: []testEvent{
|
||||||
{stdout: "Image Source: docker://dive-example", stderr: "", errorOnExit: false, errMessage: ""},
|
{stdout: "Image Source: docker://dive-example", stderr: "", errorOnExit: false, errMessage: ""},
|
||||||
{stdout: "Fetching image... (this can take a while for large images)", stderr: "", errorOnExit: false, errMessage: ""},
|
{stdout: "Extracting image from default-resolver... (this can take a while for large images)", stderr: "", errorOnExit: false, errMessage: ""},
|
||||||
{stdout: "Analyzing image...", stderr: "", errorOnExit: false, errMessage: ""},
|
{stdout: "Analyzing image...", stderr: "", errorOnExit: false, errMessage: ""},
|
||||||
{stdout: "Building cache...", stderr: "", errorOnExit: false, errMessage: ""},
|
{stdout: "Building cache...", stderr: "", errorOnExit: false, errMessage: ""},
|
||||||
},
|
},
|
||||||
|
|
@ -159,7 +183,7 @@ func TestRun(t *testing.T) {
|
||||||
},
|
},
|
||||||
events: []testEvent{
|
events: []testEvent{
|
||||||
{stdout: "Image Source: docker://dive-example", stderr: "", errorOnExit: false, errMessage: ""},
|
{stdout: "Image Source: docker://dive-example", stderr: "", errorOnExit: false, errMessage: ""},
|
||||||
{stdout: "Fetching image... (this can take a while for large images)", stderr: "", errorOnExit: false, errMessage: ""},
|
{stdout: "Extracting image from failed-fetch-resolver... (this can take a while for large images)", stderr: "", errorOnExit: false, errMessage: ""},
|
||||||
{stdout: "", stderr: "cannot fetch image", errorOnExit: true, errMessage: "some fetch failure"},
|
{stdout: "", stderr: "cannot fetch image", errorOnExit: true, errMessage: "some fetch failure"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -27,13 +27,13 @@ var (
|
||||||
appSingleton *app
|
appSingleton *app
|
||||||
)
|
)
|
||||||
|
|
||||||
func newApp(gui *gocui.Gui, imageName string, analysis *image.AnalysisResult, cache filetree.Comparer) (*app, error) {
|
func newApp(gui *gocui.Gui, imageName string, resolver image.Resolver, analysis *image.AnalysisResult, cache filetree.Comparer) (*app, error) {
|
||||||
var err error
|
var err error
|
||||||
once.Do(func() {
|
once.Do(func() {
|
||||||
var controller *Controller
|
var controller *Controller
|
||||||
var globalHelpKeys []*key.Binding
|
var globalHelpKeys []*key.Binding
|
||||||
|
|
||||||
controller, err = NewCollection(gui, imageName, analysis, cache)
|
controller, err = NewCollection(gui, imageName, resolver, analysis, cache)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -77,12 +77,12 @@ func newApp(gui *gocui.Gui, imageName string, analysis *image.AnalysisResult, ca
|
||||||
Display: "Switch view",
|
Display: "Switch view",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: gocui.KeyArrowRight,
|
ConfigKeys: []string{"keybinding.right"},
|
||||||
OnAction: controller.NextPane,
|
OnAction: controller.NextPane,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: gocui.KeyArrowLeft,
|
ConfigKeys: []string{"keybinding.left"},
|
||||||
OnAction: controller.PrevPane,
|
OnAction: controller.PrevPane,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ConfigKeys: []string{"keybinding.filter-files"},
|
ConfigKeys: []string{"keybinding.filter-files"},
|
||||||
|
|
@ -90,6 +90,10 @@ func newApp(gui *gocui.Gui, imageName string, analysis *image.AnalysisResult, ca
|
||||||
IsSelected: controller.views.Filter.IsVisible,
|
IsSelected: controller.views.Filter.IsVisible,
|
||||||
Display: "Filter",
|
Display: "Filter",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
ConfigKeys: []string{"keybinding.close-filter-files"},
|
||||||
|
OnAction: controller.CloseFilterView,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
globalHelpKeys, err = key.GenerateBindings(gui, "", infos)
|
globalHelpKeys, err = key.GenerateBindings(gui, "", infos)
|
||||||
|
|
@ -134,7 +138,7 @@ func (a *app) quit() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run is the UI entrypoint.
|
// Run is the UI entrypoint.
|
||||||
func Run(imageName string, analysis *image.AnalysisResult, treeStack filetree.Comparer) error {
|
func Run(imageName string, resolver image.Resolver, analysis *image.AnalysisResult, treeStack filetree.Comparer) error {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
g, err := gocui.NewGui(gocui.OutputNormal, true)
|
g, err := gocui.NewGui(gocui.OutputNormal, true)
|
||||||
|
|
@ -143,7 +147,7 @@ func Run(imageName string, analysis *image.AnalysisResult, treeStack filetree.Co
|
||||||
}
|
}
|
||||||
defer g.Close()
|
defer g.Close()
|
||||||
|
|
||||||
_, err = newApp(g, imageName, analysis, treeStack)
|
_, err = newApp(g, imageName, resolver, analysis, treeStack)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,19 +13,23 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Controller struct {
|
type Controller struct {
|
||||||
gui *gocui.Gui
|
gui *gocui.Gui
|
||||||
views *view.Views
|
views *view.Views
|
||||||
|
resolver image.Resolver
|
||||||
|
imageName string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCollection(g *gocui.Gui, imageName string, analysis *image.AnalysisResult, cache filetree.Comparer) (*Controller, error) {
|
func NewCollection(g *gocui.Gui, imageName string, resolver image.Resolver, analysis *image.AnalysisResult, cache filetree.Comparer) (*Controller, error) {
|
||||||
views, err := view.NewViews(g, imageName, analysis, cache)
|
views, err := view.NewViews(g, imageName, analysis, cache)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
controller := &Controller{
|
controller := &Controller{
|
||||||
gui: g,
|
gui: g,
|
||||||
views: views,
|
views: views,
|
||||||
|
resolver: resolver,
|
||||||
|
imageName: imageName,
|
||||||
}
|
}
|
||||||
|
|
||||||
// layer view cursor down event should trigger an update in the file tree
|
// layer view cursor down event should trigger an update in the file tree
|
||||||
|
|
@ -34,6 +38,9 @@ func NewCollection(g *gocui.Gui, imageName string, analysis *image.AnalysisResul
|
||||||
// update the status pane when a filetree option is changed by the user
|
// update the status pane when a filetree option is changed by the user
|
||||||
controller.views.Tree.AddViewOptionChangeListener(controller.onFileTreeViewOptionChange)
|
controller.views.Tree.AddViewOptionChangeListener(controller.onFileTreeViewOptionChange)
|
||||||
|
|
||||||
|
// update the status pane when a filetree option is changed by the user
|
||||||
|
controller.views.Tree.AddViewExtractListener(controller.onFileTreeViewExtract)
|
||||||
|
|
||||||
// update the tree view while the user types into the filter view
|
// update the tree view while the user types into the filter view
|
||||||
controller.views.Filter.AddFilterEditListener(controller.onFilterEdit)
|
controller.views.Filter.AddFilterEditListener(controller.onFilterEdit)
|
||||||
|
|
||||||
|
|
@ -53,6 +60,10 @@ func NewCollection(g *gocui.Gui, imageName string, analysis *image.AnalysisResul
|
||||||
return controller, nil
|
return controller, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Controller) onFileTreeViewExtract(p string) error {
|
||||||
|
return c.resolver.Extract(c.imageName, c.views.LayerDetails.CurrentLayer.Id, p)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Controller) onFileTreeViewOptionChange() error {
|
func (c *Controller) onFileTreeViewOptionChange() error {
|
||||||
err := c.views.Status.Update()
|
err := c.views.Status.Update()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -212,6 +223,15 @@ func (c *Controller) ToggleView() (err error) {
|
||||||
return c.UpdateAndRender()
|
return c.UpdateAndRender()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Controller) CloseFilterView() error {
|
||||||
|
// filter view needs to be visible
|
||||||
|
if c.views.Filter.IsVisible() {
|
||||||
|
// toggle filter view
|
||||||
|
return c.ToggleFilterView()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Controller) ToggleFilterView() error {
|
func (c *Controller) ToggleFilterView() error {
|
||||||
// delete all user input from the tree view
|
// delete all user input from the tree view
|
||||||
err := c.views.Filter.ToggleVisible()
|
err := c.views.Filter.ToggleVisible()
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ func GenerateBindings(gui *gocui.Gui, influence string, infos []BindingInfo) ([]
|
||||||
var err error
|
var err error
|
||||||
var binding *Binding
|
var binding *Binding
|
||||||
|
|
||||||
if info.ConfigKeys != nil && len(info.ConfigKeys) > 0 {
|
if len(info.ConfigKeys) > 0 {
|
||||||
binding, err = NewBindingFromConfig(gui, influence, info.ConfigKeys, info.Display, info.OnAction)
|
binding, err = NewBindingFromConfig(gui, influence, info.ConfigKeys, info.Display, info.OnAction)
|
||||||
} else {
|
} else {
|
||||||
binding, err = NewBinding(gui, influence, info.Key, info.Modifier, info.Display, info.OnAction)
|
binding, err = NewBinding(gui, influence, info.Key, info.Modifier, info.Display, info.OnAction)
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ func (cl *LayerDetailsCompoundLayout) layoutRow(g *gocui.Gui, minX, minY, maxX,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cl *LayerDetailsCompoundLayout) Layout(g *gocui.Gui, minX, minY, maxX, maxY int) error {
|
func (cl *LayerDetailsCompoundLayout) Layout(g *gocui.Gui, minX, minY, maxX, maxY int) error {
|
||||||
logrus.Tracef("LayerDetailsCompountLayout.Layout(minX: %d, minY: %d, maxX: %d, maxY: %d) %s", minX, minY, maxX, maxY, cl.Name())
|
logrus.Tracef("LayerDetailsCompoundLayout.Layout(minX: %d, minY: %d, maxX: %d, maxY: %d) %s", minX, minY, maxX, maxY, cl.Name())
|
||||||
|
|
||||||
layouts := []view.IView{
|
layouts := []view.IView{
|
||||||
cl.layer,
|
cl.layer,
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ type Debug struct {
|
||||||
selectedView Helper
|
selectedView Helper
|
||||||
}
|
}
|
||||||
|
|
||||||
// newDebugView creates a new view object attached the the global [gocui] screen object.
|
// newDebugView creates a new view object attached the global [gocui] screen object.
|
||||||
func newDebugView(gui *gocui.Gui) (controller *Debug) {
|
func newDebugView(gui *gocui.Gui) (controller *Debug) {
|
||||||
controller = new(Debug)
|
controller = new(Debug)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@ import (
|
||||||
|
|
||||||
type ViewOptionChangeListener func() error
|
type ViewOptionChangeListener func() error
|
||||||
|
|
||||||
|
type ViewExtractListener func(string) error
|
||||||
|
|
||||||
// FileTree holds the UI objects and data models for populating the right pane. Specifically the pane that
|
// FileTree holds the UI objects and data models for populating the right pane. Specifically the pane that
|
||||||
// shows selected layer or aggregate file ASCII tree.
|
// shows selected layer or aggregate file ASCII tree.
|
||||||
type FileTree struct {
|
type FileTree struct {
|
||||||
|
|
@ -29,11 +31,12 @@ type FileTree struct {
|
||||||
|
|
||||||
filterRegex *regexp.Regexp
|
filterRegex *regexp.Regexp
|
||||||
listeners []ViewOptionChangeListener
|
listeners []ViewOptionChangeListener
|
||||||
|
extractListeners []ViewExtractListener
|
||||||
helpKeys []*key.Binding
|
helpKeys []*key.Binding
|
||||||
requestedWidthRatio float64
|
requestedWidthRatio float64
|
||||||
}
|
}
|
||||||
|
|
||||||
// newFileTreeView creates a new view object attached the the global [gocui] screen object.
|
// newFileTreeView creates a new view object attached the global [gocui] screen object.
|
||||||
func newFileTreeView(gui *gocui.Gui, tree *filetree.FileTree, refTrees []*filetree.FileTree, cache filetree.Comparer) (controller *FileTree, err error) {
|
func newFileTreeView(gui *gocui.Gui, tree *filetree.FileTree, refTrees []*filetree.FileTree, cache filetree.Comparer) (controller *FileTree, err error) {
|
||||||
controller = new(FileTree)
|
controller = new(FileTree)
|
||||||
controller.listeners = make([]ViewOptionChangeListener, 0)
|
controller.listeners = make([]ViewOptionChangeListener, 0)
|
||||||
|
|
@ -60,6 +63,10 @@ func (v *FileTree) AddViewOptionChangeListener(listener ...ViewOptionChangeListe
|
||||||
v.listeners = append(v.listeners, listener...)
|
v.listeners = append(v.listeners, listener...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *FileTree) AddViewExtractListener(listener ...ViewExtractListener) {
|
||||||
|
v.extractListeners = append(v.extractListeners, listener...)
|
||||||
|
}
|
||||||
|
|
||||||
func (v *FileTree) SetTitle(title string) {
|
func (v *FileTree) SetTitle(title string) {
|
||||||
v.title = title
|
v.title = title
|
||||||
}
|
}
|
||||||
|
|
@ -103,6 +110,11 @@ func (v *FileTree) Setup(view, header *gocui.View) error {
|
||||||
OnAction: v.toggleSortOrder,
|
OnAction: v.toggleSortOrder,
|
||||||
Display: "Toggle sort order",
|
Display: "Toggle sort order",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
ConfigKeys: []string{"keybinding.extract-file"},
|
||||||
|
OnAction: v.extractFile,
|
||||||
|
Display: "Extract File",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
ConfigKeys: []string{"keybinding.toggle-added-files"},
|
ConfigKeys: []string{"keybinding.toggle-added-files"},
|
||||||
OnAction: func() error { return v.toggleShowDiffType(filetree.Added) },
|
OnAction: func() error { return v.toggleShowDiffType(filetree.Added) },
|
||||||
|
|
@ -148,24 +160,24 @@ func (v *FileTree) Setup(view, header *gocui.View) error {
|
||||||
OnAction: v.PageDown,
|
OnAction: v.PageDown,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: gocui.KeyArrowDown,
|
ConfigKeys: []string{"keybinding.down"},
|
||||||
Modifier: gocui.ModNone,
|
Modifier: gocui.ModNone,
|
||||||
OnAction: v.CursorDown,
|
OnAction: v.CursorDown,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: gocui.KeyArrowUp,
|
ConfigKeys: []string{"keybinding.up"},
|
||||||
Modifier: gocui.ModNone,
|
Modifier: gocui.ModNone,
|
||||||
OnAction: v.CursorUp,
|
OnAction: v.CursorUp,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: gocui.KeyArrowLeft,
|
ConfigKeys: []string{"keybinding.left"},
|
||||||
Modifier: gocui.ModNone,
|
Modifier: gocui.ModNone,
|
||||||
OnAction: v.CursorLeft,
|
OnAction: v.CursorLeft,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: gocui.KeyArrowRight,
|
ConfigKeys: []string{"keybinding.right"},
|
||||||
Modifier: gocui.ModNone,
|
Modifier: gocui.ModNone,
|
||||||
OnAction: v.CursorRight,
|
OnAction: v.CursorRight,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -303,9 +315,32 @@ func (v *FileTree) toggleSortOrder() error {
|
||||||
return v.Render()
|
return v.Render()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *FileTree) extractFile() error {
|
||||||
|
node := v.vm.CurrentNode(v.filterRegex)
|
||||||
|
for _, listener := range v.extractListeners {
|
||||||
|
err := listener(node.Path())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (v *FileTree) toggleWrapTree() error {
|
func (v *FileTree) toggleWrapTree() error {
|
||||||
v.view.Wrap = !v.view.Wrap
|
v.view.Wrap = !v.view.Wrap
|
||||||
return nil
|
|
||||||
|
err := v.Update()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = v.Render()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// we need to render the changes to the status pane as well (not just this contoller/view)
|
||||||
|
return v.notifyOnViewOptionChangeListeners()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *FileTree) notifyOnViewOptionChangeListeners() error {
|
func (v *FileTree) notifyOnViewOptionChangeListeners() error {
|
||||||
|
|
@ -335,7 +370,7 @@ func (v *FileTree) toggleAttributes() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// we need to render the changes to the status pane as well (not just this contoller/view)
|
// we need to render the changes to the status pane as well (not just this controller/view)
|
||||||
return v.notifyOnViewOptionChangeListeners()
|
return v.notifyOnViewOptionChangeListeners()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -352,7 +387,7 @@ func (v *FileTree) toggleShowDiffType(diffType filetree.DiffType) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// we need to render the changes to the status pane as well (not just this contoller/view)
|
// we need to render the changes to the status pane as well (not just this controller/view)
|
||||||
return v.notifyOnViewOptionChangeListeners()
|
return v.notifyOnViewOptionChangeListeners()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ type Filter struct {
|
||||||
filterEditListeners []FilterEditListener
|
filterEditListeners []FilterEditListener
|
||||||
}
|
}
|
||||||
|
|
||||||
// newFilterView creates a new view object attached the the global [gocui] screen object.
|
// newFilterView creates a new view object attached the global [gocui] screen object.
|
||||||
func newFilterView(gui *gocui.Gui) (controller *Filter) {
|
func newFilterView(gui *gocui.Gui) (controller *Filter) {
|
||||||
controller = new(Filter)
|
controller = new(Filter)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,14 +44,14 @@ func (v *ImageDetails) Setup(body, header *gocui.View) error {
|
||||||
|
|
||||||
var infos = []key.BindingInfo{
|
var infos = []key.BindingInfo{
|
||||||
{
|
{
|
||||||
Key: gocui.KeyArrowDown,
|
ConfigKeys: []string{"keybinding.down"},
|
||||||
Modifier: gocui.ModNone,
|
Modifier: gocui.ModNone,
|
||||||
OnAction: v.CursorDown,
|
OnAction: v.CursorDown,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: gocui.KeyArrowUp,
|
ConfigKeys: []string{"keybinding.up"},
|
||||||
Modifier: gocui.ModNone,
|
Modifier: gocui.ModNone,
|
||||||
OnAction: v.CursorUp,
|
OnAction: v.CursorUp,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ConfigKeys: []string{"keybinding.page-up"},
|
ConfigKeys: []string{"keybinding.page-up"},
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ type Layer struct {
|
||||||
helpKeys []*key.Binding
|
helpKeys []*key.Binding
|
||||||
}
|
}
|
||||||
|
|
||||||
// newLayerView creates a new view object attached the the global [gocui] screen object.
|
// newLayerView creates a new view object attached the global [gocui] screen object.
|
||||||
func newLayerView(gui *gocui.Gui, layers []*image.Layer) (controller *Layer, err error) {
|
func newLayerView(gui *gocui.Gui, layers []*image.Layer) (controller *Layer, err error) {
|
||||||
controller = new(Layer)
|
controller = new(Layer)
|
||||||
|
|
||||||
|
|
@ -116,14 +116,14 @@ func (v *Layer) Setup(body *gocui.View, header *gocui.View) error {
|
||||||
Display: "Show aggregated changes",
|
Display: "Show aggregated changes",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: gocui.KeyArrowDown,
|
ConfigKeys: []string{"keybinding.down"},
|
||||||
Modifier: gocui.ModNone,
|
Modifier: gocui.ModNone,
|
||||||
OnAction: v.CursorDown,
|
OnAction: v.CursorDown,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: gocui.KeyArrowUp,
|
ConfigKeys: []string{"keybinding.up"},
|
||||||
Modifier: gocui.ModNone,
|
Modifier: gocui.ModNone,
|
||||||
OnAction: v.CursorUp,
|
OnAction: v.CursorUp,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ConfigKeys: []string{"keybinding.page-up"},
|
ConfigKeys: []string{"keybinding.page-up"},
|
||||||
|
|
@ -221,6 +221,14 @@ func (v *Layer) CursorUp() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetOrigin updates the origin of the layer view pane.
|
||||||
|
func (v *Layer) SetOrigin(x, y int) error {
|
||||||
|
if err := v.body.SetOrigin(x, y); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// SetCursor resets the cursor and orients the file tree view based on the given layer index.
|
// SetCursor resets the cursor and orients the file tree view based on the given layer index.
|
||||||
func (v *Layer) SetCursor(layer int) error {
|
func (v *Layer) SetCursor(layer int) error {
|
||||||
v.vm.LayerIndex = layer
|
v.vm.LayerIndex = layer
|
||||||
|
|
@ -340,6 +348,15 @@ func (v *Layer) Render() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Adjust origin, if necessary
|
||||||
|
maxBodyDisplayHeight := int(v.height())
|
||||||
|
if v.vm.LayerIndex > maxBodyDisplayHeight {
|
||||||
|
if err := v.SetOrigin(0, v.vm.LayerIndex-maxBodyDisplayHeight); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/awesome-gocui/gocui"
|
"github.com/awesome-gocui/gocui"
|
||||||
|
"github.com/dustin/go-humanize"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/wagoodman/dive/dive/image"
|
"github.com/wagoodman/dive/dive/image"
|
||||||
|
|
@ -39,14 +40,14 @@ func (v *LayerDetails) Setup(body, header *gocui.View) error {
|
||||||
|
|
||||||
var infos = []key.BindingInfo{
|
var infos = []key.BindingInfo{
|
||||||
{
|
{
|
||||||
Key: gocui.KeyArrowDown,
|
ConfigKeys: []string{"keybinding.down"},
|
||||||
Modifier: gocui.ModNone,
|
Modifier: gocui.ModNone,
|
||||||
OnAction: v.CursorDown,
|
OnAction: v.CursorDown,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: gocui.KeyArrowUp,
|
ConfigKeys: []string{"keybinding.up"},
|
||||||
Modifier: gocui.ModNone,
|
Modifier: gocui.ModNone,
|
||||||
OnAction: v.CursorUp,
|
OnAction: v.CursorUp,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -79,12 +80,14 @@ func (v *LayerDetails) Render() error {
|
||||||
var lines = make([]string, 0)
|
var lines = make([]string, 0)
|
||||||
|
|
||||||
tags := "(none)"
|
tags := "(none)"
|
||||||
if v.CurrentLayer.Names != nil && len(v.CurrentLayer.Names) > 0 {
|
if len(v.CurrentLayer.Names) > 0 {
|
||||||
tags = strings.Join(v.CurrentLayer.Names, ", ")
|
tags = strings.Join(v.CurrentLayer.Names, ", ")
|
||||||
}
|
}
|
||||||
|
|
||||||
lines = append(lines, []string{
|
lines = append(lines, []string{
|
||||||
format.Header("Tags: ") + tags,
|
format.Header("Tags: ") + tags,
|
||||||
format.Header("Id: ") + v.CurrentLayer.Id,
|
format.Header("Id: ") + v.CurrentLayer.Id,
|
||||||
|
format.Header("Size: ") + humanize.Bytes(v.CurrentLayer.Size),
|
||||||
format.Header("Digest: ") + v.CurrentLayer.Digest,
|
format.Header("Digest: ") + v.CurrentLayer.Digest,
|
||||||
format.Header("Command:"),
|
format.Header("Command:"),
|
||||||
v.CurrentLayer.Command,
|
v.CurrentLayer.Command,
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ type Status struct {
|
||||||
helpKeys []*key.Binding
|
helpKeys []*key.Binding
|
||||||
}
|
}
|
||||||
|
|
||||||
// newStatusView creates a new view object attached the the global [gocui] screen object.
|
// newStatusView creates a new view object attached the global [gocui] screen object.
|
||||||
func newStatusView(gui *gocui.Gui) (controller *Status) {
|
func newStatusView(gui *gocui.Gui) (controller *Status) {
|
||||||
controller = new(Status)
|
controller = new(Status)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ type FileTreeViewModel struct {
|
||||||
Buffer bytes.Buffer
|
Buffer bytes.Buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFileTreeViewModel creates a new view object attached the the global [gocui] screen object.
|
// NewFileTreeViewModel creates a new view object attached the global [gocui] screen object.
|
||||||
func NewFileTreeViewModel(tree *filetree.FileTree, refTrees []*filetree.FileTree, cache filetree.Comparer) (treeViewModel *FileTreeViewModel, err error) {
|
func NewFileTreeViewModel(tree *filetree.FileTree, refTrees []*filetree.FileTree, cache filetree.Comparer) (treeViewModel *FileTreeViewModel, err error) {
|
||||||
treeViewModel = new(FileTreeViewModel)
|
treeViewModel = new(FileTreeViewModel)
|
||||||
|
|
||||||
|
|
@ -161,6 +161,11 @@ func (vm *FileTreeViewModel) CursorDown() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CursorLeft moves the cursor up until we reach the Parent Node or top of the tree
|
||||||
|
func (vm *FileTreeViewModel) CurrentNode(filterRegex *regexp.Regexp) *filetree.FileNode {
|
||||||
|
return vm.getAbsPositionNode(filterRegex)
|
||||||
|
}
|
||||||
|
|
||||||
// CursorLeft moves the cursor up until we reach the Parent Node or top of the tree
|
// CursorLeft moves the cursor up until we reach the Parent Node or top of the tree
|
||||||
func (vm *FileTreeViewModel) CursorLeft(filterRegex *regexp.Regexp) error {
|
func (vm *FileTreeViewModel) CursorLeft(filterRegex *regexp.Regexp) error {
|
||||||
var visitor func(*filetree.FileNode) error
|
var visitor func(*filetree.FileNode) error
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ func helperCheckDiff(t *testing.T, expected, actual []byte) {
|
||||||
if !bytes.Equal(expected, actual) {
|
if !bytes.Equal(expected, actual) {
|
||||||
dmp := diffmatchpatch.New()
|
dmp := diffmatchpatch.New()
|
||||||
diffs := dmp.DiffMain(string(expected), string(actual), true)
|
diffs := dmp.DiffMain(string(expected), string(actual), true)
|
||||||
t.Errorf(dmp.DiffPrettyText(diffs))
|
t.Errorf("%s", dmp.DiffPrettyText(diffs))
|
||||||
t.Errorf("%s: bytes mismatch", t.Name())
|
t.Errorf("%s: bytes mismatch", t.Name())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
52
runtime/ui/viewmodel/layer_set_state_test.go
Normal file
52
runtime/ui/viewmodel/layer_set_state_test.go
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
package viewmodel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetCompareIndexes(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
layerIndex int
|
||||||
|
compareMode LayerCompareMode
|
||||||
|
compareStartIndex int
|
||||||
|
expected [4]int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "LayerIndex equals CompareStartIndex",
|
||||||
|
layerIndex: 2,
|
||||||
|
compareMode: CompareSingleLayer,
|
||||||
|
compareStartIndex: 2,
|
||||||
|
expected: [4]int{2, 2, 2, 2},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "CompareMode is CompareSingleLayer",
|
||||||
|
layerIndex: 3,
|
||||||
|
compareMode: CompareSingleLayer,
|
||||||
|
compareStartIndex: 1,
|
||||||
|
expected: [4]int{1, 2, 3, 3},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Default CompareMode",
|
||||||
|
layerIndex: 4,
|
||||||
|
compareMode: CompareAllLayers,
|
||||||
|
compareStartIndex: 1,
|
||||||
|
expected: [4]int{1, 1, 2, 4},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
state := &LayerSetState{
|
||||||
|
LayerIndex: tt.layerIndex,
|
||||||
|
CompareMode: tt.compareMode,
|
||||||
|
CompareStartIndex: tt.compareStartIndex,
|
||||||
|
}
|
||||||
|
bottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop := state.GetCompareIndexes()
|
||||||
|
actual := [4]int{bottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop}
|
||||||
|
if actual != tt.expected {
|
||||||
|
t.Errorf("expected %v, got %v", tt.expected, actual)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -25,7 +25,7 @@ drwxr-xr-x 0:0 0 B │ │ │ └── nested
|
||||||
-rw-r--r-- 0:0 6.4 kB │ │ └── somefile3.txt
|
-rw-r--r-- 0:0 6.4 kB │ │ └── somefile3.txt
|
||||||
-rwxr-xr-x 0:0 6.4 kB │ └── saved.txt
|
-rwxr-xr-x 0:0 6.4 kB │ └── saved.txt
|
||||||
-rw-rw-r-- 0:0 6.4 kB ├── somefile.txt
|
-rw-rw-r-- 0:0 6.4 kB ├── somefile.txt
|
||||||
drwxrwxrwx 0:0 6.4 kB ├── tmp
|
drwxrwxrwt 0:0 6.4 kB ├── tmp
|
||||||
-rw-r--r-- 0:0 6.4 kB │ └── saved.again1.txt
|
-rw-r--r-- 0:0 6.4 kB │ └── saved.again1.txt
|
||||||
drwxr-xr-x 0:0 0 B ├── usr
|
drwxr-xr-x 0:0 0 B ├── usr
|
||||||
drwxr-xr-x 1:1 0 B │ └── sbin
|
drwxr-xr-x 1:1 0 B │ └── sbin
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ drwxr-xr-x 0:0 0 B ├── dev
|
||||||
drwxr-xr-x 0:0 1.0 kB ├─⊕ etc
|
drwxr-xr-x 0:0 1.0 kB ├─⊕ etc
|
||||||
drwxr-xr-x 65534:65534 0 B ├── home
|
drwxr-xr-x 65534:65534 0 B ├── home
|
||||||
drwx------ 0:0 0 B ├── root
|
drwx------ 0:0 0 B ├── root
|
||||||
drwxrwxrwx 0:0 0 B ├── tmp
|
drwxrwxrwt 0:0 0 B ├── tmp
|
||||||
drwxr-xr-x 0:0 0 B ├── usr
|
drwxr-xr-x 0:0 0 B ├── usr
|
||||||
drwxr-xr-x 1:1 0 B │ └── sbin
|
drwxr-xr-x 1:1 0 B │ └── sbin
|
||||||
drwxr-xr-x 0:0 0 B └── var
|
drwxr-xr-x 0:0 0 B └── var
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ drwxr-xr-x 0:0 0 B ├── dev
|
||||||
drwxr-xr-x 0:0 1.0 kB ├─⊕ etc
|
drwxr-xr-x 0:0 1.0 kB ├─⊕ etc
|
||||||
drwxr-xr-x 65534:65534 0 B ├── home
|
drwxr-xr-x 65534:65534 0 B ├── home
|
||||||
drwx------ 0:0 0 B ├── root
|
drwx------ 0:0 0 B ├── root
|
||||||
drwxrwxrwx 0:0 0 B ├── tmp
|
drwxrwxrwt 0:0 0 B ├── tmp
|
||||||
drwxr-xr-x 0:0 0 B ├─⊕ usr
|
drwxr-xr-x 0:0 0 B ├─⊕ usr
|
||||||
drwxr-xr-x 0:0 0 B └─⊕ var
|
drwxr-xr-x 0:0 0 B └─⊕ var
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ drwxr-xr-x 0:0 0 B │ │ └── if-up.d
|
||||||
-rw------- 0:0 243 B │ └── shadow
|
-rw------- 0:0 243 B │ └── shadow
|
||||||
drwxr-xr-x 65534:65534 0 B ├── home
|
drwxr-xr-x 65534:65534 0 B ├── home
|
||||||
drwx------ 0:0 0 B ├── root
|
drwx------ 0:0 0 B ├── root
|
||||||
drwxrwxrwx 0:0 0 B ├── tmp
|
drwxrwxrwt 0:0 0 B ├── tmp
|
||||||
drwxr-xr-x 0:0 0 B ├── usr
|
drwxr-xr-x 0:0 0 B ├── usr
|
||||||
drwxr-xr-x 1:1 0 B │ └── sbin
|
drwxr-xr-x 1:1 0 B │ └── sbin
|
||||||
drwxr-xr-x 0:0 0 B └── var
|
drwxr-xr-x 0:0 0 B └── var
|
||||||
|
|
|
||||||
|
|
@ -406,7 +406,7 @@ drwxr-xr-x 0:0 0 B │ │ └── if-up.d
|
||||||
-rw------- 0:0 243 B │ └── shadow
|
-rw------- 0:0 243 B │ └── shadow
|
||||||
drwxr-xr-x 65534:65534 0 B ├── home
|
drwxr-xr-x 65534:65534 0 B ├── home
|
||||||
drwx------ 0:0 0 B ├── root
|
drwx------ 0:0 0 B ├── root
|
||||||
drwxrwxrwx 0:0 0 B ├── tmp
|
drwxrwxrwt 0:0 0 B ├── tmp
|
||||||
drwxr-xr-x 0:0 0 B ├── usr
|
drwxr-xr-x 0:0 0 B ├── usr
|
||||||
drwxr-xr-x 1:1 0 B │ └── sbin
|
drwxr-xr-x 1:1 0 B │ └── sbin
|
||||||
drwxr-xr-x 0:0 0 B └── var
|
drwxr-xr-x 0:0 0 B └── var
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ drwxr-xr-x 0:0 0 B │ │ └── if-up.d
|
||||||
-rw-r--r-- 0:0 340 B │ ├── passwd
|
-rw-r--r-- 0:0 340 B │ ├── passwd
|
||||||
-rw------- 0:0 243 B │ └── shadow
|
-rw------- 0:0 243 B │ └── shadow
|
||||||
drwxr-xr-x 65534:65534 0 B ├── home
|
drwxr-xr-x 65534:65534 0 B ├── home
|
||||||
drwxrwxrwx 0:0 0 B ├── tmp
|
drwxrwxrwt 0:0 0 B ├── tmp
|
||||||
drwxr-xr-x 0:0 0 B ├── usr
|
drwxr-xr-x 0:0 0 B ├── usr
|
||||||
drwxr-xr-x 1:1 0 B │ └── sbin
|
drwxr-xr-x 1:1 0 B │ └── sbin
|
||||||
drwxr-xr-x 0:0 0 B └── var
|
drwxr-xr-x 0:0 0 B └── var
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ drwxr-xr-x 0:0 0 B │ │ └── if-up.d
|
||||||
drwxr-xr-x 65534:65534 0 B ├── home
|
drwxr-xr-x 65534:65534 0 B ├── home
|
||||||
drwx------ 0:0 0 B ├── root
|
drwx------ 0:0 0 B ├── root
|
||||||
-rw-rw-r-- 0:0 6.4 kB ├── somefile.txt
|
-rw-rw-r-- 0:0 6.4 kB ├── somefile.txt
|
||||||
drwxrwxrwx 0:0 0 B ├── tmp
|
drwxrwxrwt 0:0 0 B ├── tmp
|
||||||
drwxr-xr-x 0:0 0 B ├── usr
|
drwxr-xr-x 0:0 0 B ├── usr
|
||||||
drwxr-xr-x 1:1 0 B │ └── sbin
|
drwxr-xr-x 1:1 0 B │ └── sbin
|
||||||
drwxr-xr-x 0:0 0 B └── var
|
drwxr-xr-x 0:0 0 B └── var
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package utils
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/logrusorgru/aurora"
|
"github.com/logrusorgru/aurora/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TitleFormat(s string) string {
|
func TitleFormat(s string) string {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue