Compare commits

..
781 changed files with 14335 additions and 318658 deletions
-1
View File
@@ -1 +0,0 @@
enforce-v0: true # don't make breaking-change label bump major version before 1.0.
+1 -1
View File
@@ -4,7 +4,7 @@ inputs:
go-version:
description: "Go version to install"
required: true
default: "1.20.x"
default: "1.19.x"
use-go-cache:
description: "Restore go cache"
required: true
+15 -5
View File
@@ -1,17 +1,27 @@
#!/usr/bin/env bash
set -u
if [ "$(git status --porcelain | wc -l)" -ne "0" ]; then
echo " 🔴 there are uncommitted changes, please commit them before running this check"
if ! git diff-index --quiet HEAD --; then
git diff-index HEAD --
git --no-pager diff
echo "there are uncommitted changes, please commit them before running this check"
exit 1
fi
success=true
if ! make generate-json-schema; then
echo "Generating json schema failed"
exit 1
success=false
fi
if [ "$(git status --porcelain | wc -l)" -ne "0" ]; then
echo " 🔴 there are uncommitted changes, please commit them before running this check"
if ! git diff-index --quiet HEAD --; then
git diff-index HEAD --
git --no-pager diff
echo "JSON schema drift detected!"
success=false
fi
if ! $success; then
exit 1
fi
+1 -1
View File
@@ -33,7 +33,7 @@ jobs:
OUTPUT="${OUTPUT//'%'/'%25'}" # URL encode all '%' characters
OUTPUT="${OUTPUT//$'\n'/'%0A'}" # URL encode all '\n' characters
OUTPUT="${OUTPUT//$'\r'/'%0D'}" # URL encode all '\r' characters
echo "result=$OUTPUT" >> $GITHUB_OUTPUT
echo "::set-output name=result::$OUTPUT"
- uses: actions/upload-artifact@v3
with:
+1 -1
View File
@@ -30,7 +30,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
@@ -1,16 +0,0 @@
name: Add to OSS board
on:
issues:
types:
- opened
- reopened
- transferred
- labeled
jobs:
run:
uses: "anchore/workflows/.github/workflows/oss-project-board-add.yaml@main"
secrets:
token: ${{ secrets.OSS_PROJECT_GH_TOKEN }}
+2 -2
View File
@@ -151,12 +151,12 @@ jobs:
with:
status: ${{ job.status }}
fields: repo,workflow,action,eventName
text: "A new Syft release has been published: https://github.com/anchore/syft/releases/tag/${{ github.event.inputs.version }}"
text: "A new Syft release has been published: https://github.com/anchore/syft/releases/tag/${{ github.ref_name }}"
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_TOOLBOX_WEBHOOK_URL }}
if: ${{ success() }}
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v2
with:
name: artifacts
path: dist/**/*
+6 -6
View File
@@ -6,7 +6,7 @@ on:
workflow_dispatch:
env:
GO_VERSION: "1.20.x"
GO_VERSION: "1.19.x"
GO_STABLE_VERSION: true
jobs:
@@ -16,7 +16,7 @@ jobs:
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
- uses: actions/setup-go@v3
with:
go-version: ${{ env.GO_VERSION }}
stable: ${{ env.GO_STABLE_VERSION }}
@@ -28,7 +28,7 @@ jobs:
GORELEASER_LATEST_VERSION=$(go list -m -json github.com/goreleaser/goreleaser@latest 2>/dev/null | jq -r '.Version')
GOSIMPORTS_LATEST_VERSION=$(go list -m -json github.com/rinchsan/gosimports@latest 2>/dev/null | jq -r '.Version')
YAJSV_LATEST_VERSION=$(go list -m -json github.com/neilpa/yajsv@latest 2>/dev/null | jq -r '.Version')
COSIGN_LATEST_VERSION=$(go list -m -json github.com/sigstore/cosign/v2@latest 2>/dev/null | jq -r '.Version')
COSIGN_LATEST_VERSION=$(go list -m -json github.com/sigstore/cosign@latest 2>/dev/null | jq -r '.Version')
GLOW_LATEST_VERSION=$(go list -m -json github.com/charmbracelet/glow@latest 2>/dev/null | jq -r '.Version')
# update version variables in the Makefile
@@ -58,14 +58,14 @@ jobs:
app_id: ${{ secrets.TOKEN_APP_ID }}
private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
- uses: peter-evans/create-pull-request@v5
- uses: peter-evans/create-pull-request@v4
with:
signoff: true
delete-branch: true
branch: auto/latest-bootstrap-tools
labels: dependencies
commit-message: 'chore(deps): update bootstrap tools to latest versions'
title: 'chore(deps): update bootstrap tools to latest versions'
commit-message: 'Update syft bootstrap tools to latest versions.'
title: 'Update syft bootstrap tools to latest versions.'
body: |
- [golangci-lint ${{ steps.latest-versions.outputs.GOLANGCILINT }}](https://github.com/golangci/golangci-lint/releases/tag/${{ steps.latest-versions.outputs.GOLANGCILINT }})
- [bouncer ${{ steps.latest-versions.outputs.BOUNCER }}](https://github.com/wagoodman/go-bouncer/releases/tag/${{ steps.latest-versions.outputs.BOUNCER }})
@@ -1,43 +0,0 @@
name: PR to update CPE dictionary index
on:
schedule:
- cron: "0 1 * * 1" # every monday at 1 AM
workflow_dispatch:
env:
GO_VERSION: "1.20.x"
GO_STABLE_VERSION: true
jobs:
upgrade-cpe-dictionary-index:
runs-on: ubuntu-latest
if: github.repository == 'anchore/syft' # only run for main repo
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version: ${{ env.GO_VERSION }}
stable: ${{ env.GO_STABLE_VERSION }}
- run: |
make generate-cpe-dictionary-index
- uses: tibdex/github-app-token@v1
id: generate-token
with:
app_id: ${{ secrets.TOKEN_APP_ID }}
private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
- uses: peter-evans/create-pull-request@v5
with:
signoff: true
delete-branch: true
branch: auto/latest-cpe-dictionary-index
labels: dependencies
commit-message: "chore(deps): update CPE dictionary index"
title: "chore(deps): update CPE dictionary index"
body: |
Update CPE dictionary index based on the latest available CPE dictionary
token: ${{ steps.generate-token.outputs.token }}
@@ -6,7 +6,7 @@ on:
workflow_dispatch:
env:
GO_VERSION: "1.20.x"
GO_VERSION: "1.19.x"
GO_STABLE_VERSION: true
jobs:
@@ -16,7 +16,7 @@ jobs:
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
- uses: actions/setup-go@v3
with:
go-version: ${{ env.GO_VERSION }}
stable: ${{ env.GO_STABLE_VERSION }}
@@ -29,7 +29,7 @@ jobs:
go mod tidy
# export the version for use with create-pull-request
echo "LATEST_VERSION=$LATEST_VERSION" >> $GITHUB_OUTPUT
echo "::set-output name=LATEST_VERSION::$LATEST_VERSION"
id: latest-version
- uses: tibdex/github-app-token@v1
@@ -38,14 +38,14 @@ jobs:
app_id: ${{ secrets.TOKEN_APP_ID }}
private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
- uses: peter-evans/create-pull-request@v5
- uses: peter-evans/create-pull-request@v4
with:
signoff: true
delete-branch: true
branch: auto/latest-stereoscope
branch: auto/latest
labels: dependencies
commit-message: "chore(deps): update stereoscope to ${{ steps.latest-version.outputs.LATEST_VERSION }}"
title: "chore(deps): update stereoscope to ${{ steps.latest-version.outputs.LATEST_VERSION }}"
commit-message: "Update Stereoscope to ${{ steps.latest-version.outputs.LATEST_VERSION }}"
title: "Update Stereoscope to ${{ steps.latest-version.outputs.LATEST_VERSION }}"
body: |
Update Stereoscope to ${{ steps.latest-version.outputs.LATEST_VERSION }}
token: ${{ steps.generate-token.outputs.token }}
+4 -13
View File
@@ -34,35 +34,26 @@ jobs:
uses: ./.github/actions/bootstrap
- name: Restore Java test-fixture cache
id: unit-java-cache
uses: actions/cache@v3
with:
path: syft/pkg/cataloger/java/test-fixtures/java-builds/packages
key: ${{ runner.os }}-unit-java-cache-${{ hashFiles( 'syft/pkg/cataloger/java/test-fixtures/java-builds/cache.fingerprint' ) }}
key: ${{ runner.os }}-unit-java-cache-${{ hashFiles( 'syft/pkg/cataloger/java/test-fixtures/java-builds/packages.fingerprint' ) }}
- name: Restore RPM test-fixture cache
id: unit-rpm-cache
uses: actions/cache@v3
with:
path: syft/pkg/cataloger/rpm/test-fixtures/rpms
key: ${{ runner.os }}-unit-rpm-cache-${{ hashFiles( 'syft/pkg/cataloger/rpm/test-fixtures/rpms.fingerprint' ) }}
- name: Restore go binary test-fixture cache
id: unit-go-binary-cache
uses: actions/cache@v3
with:
path: syft/pkg/cataloger/golang/test-fixtures/archs/binaries
key: ${{ runner.os }}-unit-go-binaries-cache-${{ hashFiles( 'syft/pkg/cataloger/golang/test-fixtures/archs/binaries.fingerprint' ) }}
- name: Restore binary cataloger test-fixture cache
uses: actions/cache@v3
with:
path: syft/pkg/cataloger/binary/test-fixtures/classifiers/dynamic
key: ${{ runner.os }}-unit-binary-cataloger-cache-${{ hashFiles( 'syft/pkg/cataloger/binary/test-fixtures/cache.fingerprint' ) }}
- name: Restore Kernel test-fixture cache
uses: actions/cache@v3
with:
path: syft/pkg/cataloger/kernel/test-fixtures/cache
key: ${{ runner.os }}-unit-kernel-cache-${{ hashFiles( 'syft/pkg/cataloger/kernel/test-fixtures/cache.fingerprint' ) }}
- name: Run unit tests
run: make unit
-6
View File
@@ -1,8 +1,3 @@
go.work
go.work.sum
/bin
/.bin
/build
CHANGELOG.md
VERSION
/test/results
@@ -20,7 +15,6 @@ VERSION
*.hpi
*.zip
.idea/
*.iml
*.log
.images
.tmp/
+1 -1
View File
@@ -12,6 +12,7 @@ linters:
enable:
- asciicheck
- bodyclose
- depguard
- dogsled
- dupl
- errcheck
@@ -56,7 +57,6 @@ run:
# do not enable...
# - deadcode # The owner seems to have abandoned the linter. Replaced by "unused".
# - depguard # We don't have a configuration for this yet
# - goprintffuncname # does not catch all cases and there are exceptions
# - nakedret # does not catch all cases and should not fail a build
# - gochecknoglobals
+44 -44
View File
@@ -79,23 +79,23 @@ nfpms:
- rpm
- deb
brews:
- tap:
owner: anchore
name: homebrew-syft
token: "{{.Env.GITHUB_BREW_TOKEN}}"
ids:
- darwin-archives
- linux-archives
homepage: *website
description: *description
license: "Apache License 2.0"
#brews:
# - tap:
# owner: anchore
# name: homebrew-syft
# token: "{{.Env.GITHUB_BREW_TOKEN}}"
# ids:
# - darwin-archives
# - linux-archives
# homepage: *website
# description: *description
# license: "Apache License 2.0"
dockers:
- image_templates:
- anchore/syft:debug
# - anchore/syft:debug
- anchore/syft:{{.Tag}}-debug
- ghcr.io/anchore/syft:debug
# - ghcr.io/anchore/syft:debug
- ghcr.io/anchore/syft:{{.Tag}}-debug
goarch: amd64
dockerfile: Dockerfile.debug
@@ -108,9 +108,9 @@ dockers:
- "--build-arg=VCS_URL={{.GitURL}}"
- image_templates:
- anchore/syft:debug-arm64v8
# - anchore/syft:debug-arm64v8
- anchore/syft:{{.Tag}}-debug-arm64v8
- ghcr.io/anchore/syft:debug-arm64v8
# - ghcr.io/anchore/syft:debug-arm64v8
- ghcr.io/anchore/syft:{{.Tag}}-debug-arm64v8
goarch: arm64
dockerfile: Dockerfile.debug
@@ -123,9 +123,9 @@ dockers:
- "--build-arg=VCS_URL={{.GitURL}}"
- image_templates:
- anchore/syft:debug-ppc64le
# - anchore/syft:debug-ppc64le
- anchore/syft:{{.Tag}}-debug-ppc64le
- ghcr.io/anchore/syft:debug-ppc64le
# - ghcr.io/anchore/syft:debug-ppc64le
- ghcr.io/anchore/syft:{{.Tag}}-debug-ppc64le
goarch: ppc64le
dockerfile: Dockerfile.debug
@@ -138,9 +138,9 @@ dockers:
- "--build-arg=VCS_URL={{.GitURL}}"
- image_templates:
- anchore/syft:debug-s390x
# - anchore/syft:debug-s390x
- anchore/syft:{{.Tag}}-debug-s390x
- ghcr.io/anchore/syft:debug-s390x
# - ghcr.io/anchore/syft:debug-s390x
- ghcr.io/anchore/syft:{{.Tag}}-debug-s390x
goarch: s390x
dockerfile: Dockerfile.debug
@@ -153,9 +153,9 @@ dockers:
- "--build-arg=VCS_URL={{.GitURL}}"
- image_templates:
- anchore/syft:latest
# - anchore/syft:latest
- anchore/syft:{{.Tag}}
- ghcr.io/anchore/syft:latest
# - ghcr.io/anchore/syft:latest
- ghcr.io/anchore/syft:{{.Tag}}
goarch: amd64
dockerfile: Dockerfile
@@ -207,18 +207,18 @@ dockers:
- "--build-arg=VCS_URL={{.GitURL}}"
docker_manifests:
- name_template: anchore/syft:latest
image_templates:
- anchore/syft:{{.Tag}}
- anchore/syft:{{.Tag}}-arm64v8
- anchore/syft:{{.Tag}}-ppc64le
- anchore/syft:{{.Tag}}-s390x
# - name_template: anchore/syft:latest
# image_templates:
# - anchore/syft:{{.Tag}}
# - anchore/syft:{{.Tag}}-arm64v8
# - anchore/syft:{{.Tag}}-ppc64le
# - anchore/syft:{{.Tag}}-s390x
- name_template: anchore/syft:debug
- anchore/syft:{{.Tag}}-debug
- anchore/syft:{{.Tag}}-debug-arm64v8
- anchore/syft:{{.Tag}}-debug-ppc64le
- anchore/syft:{{.Tag}}-debug-s390x
# - name_template: anchore/syft:debug
# - anchore/syft:{{.Tag}}-debug
# - anchore/syft:{{.Tag}}-debug-arm64v8
# - anchore/syft:{{.Tag}}-debug-ppc64le
# - anchore/syft:{{.Tag}}-debug-s390x
- name_template: anchore/syft:{{.Tag}}
image_templates:
@@ -227,19 +227,19 @@ docker_manifests:
- anchore/syft:{{.Tag}}-ppc64le
- anchore/syft:{{.Tag}}-s390x
- name_template: ghcr.io/anchore/syft:latest
image_templates:
- ghcr.io/anchore/syft:{{.Tag}}
- ghcr.io/anchore/syft:{{.Tag}}-arm64v8
- ghcr.io/anchore/syft:{{.Tag}}-ppc64le
- ghcr.io/anchore/syft:{{.Tag}}-s390x
# - name_template: ghcr.io/anchore/syft:latest
# image_templates:
# - ghcr.io/anchore/syft:{{.Tag}}
# - ghcr.io/anchore/syft:{{.Tag}}-arm64v8
# - ghcr.io/anchore/syft:{{.Tag}}-ppc64le
# - ghcr.io/anchore/syft:{{.Tag}}-s390x
- name_template: ghcr.io/anchore/syft:debug
image_templates:
- ghcr.io/anchore/syft:{{.Tag}}-debug
- ghcr.io/anchore/syft:{{.Tag}}-debug-arm64v8
- ghcr.io/anchore/syft:{{.Tag}}-debug-ppc64le
- ghcr.io/anchore/syft:{{.Tag}}-debug-s390x
# - name_template: ghcr.io/anchore/syft:debug
# image_templates:
# - ghcr.io/anchore/syft:{{.Tag}}-debug
# - ghcr.io/anchore/syft:{{.Tag}}-debug-arm64v8
# - ghcr.io/anchore/syft:{{.Tag}}-debug-ppc64le
# - ghcr.io/anchore/syft:{{.Tag}}-debug-s390x
- name_template: ghcr.io/anchore/syft:{{.Tag}}
image_templates:
-4
View File
@@ -5,7 +5,3 @@ The following Syft components were contributed by external authors/organizations
## GraalVM Native Image
A cataloger contributed by Oracle Corporation that extracts packages given within GraalVM Native Image SBOMs.
## Swift Package Manager
A cataloger contributed by Axis Communications that catalogs packages resolved by Swift Package Manager.
+5 -52
View File
@@ -7,25 +7,6 @@ In order to test and develop in this repo you will need the following dependenci
- docker
- make
### Docker settings for getting started
Make sure you've updated your docker settings so the default docker socket path is available.
Go to:
docker -> settings -> advanced
Make sure:
```
Allow the default Docker socket to be used
```
is checked.
Also double check that the docker context being used is the default context. If it is not, run:
`docker context use default`
After cloning the following step can help you get setup:
1. run `make bootstrap` to download go mod dependencies, create the `/.tmp` dir, and download helper utilities.
2. run `make` to view the selection of developer commands in the Makefile
@@ -34,30 +15,10 @@ After cloning the following step can help you get setup:
- this command `go run cmd/syft/main.go alpine:latest` will compile and run syft against `alpine:latest`
5. view the README or syft help output for more output options
The main make tasks for common static analysis and testing are `lint`, `format`, `lint-fix`, `unit`, `integration`, and `cli`.
The main make tasks for common static analysis and testing are `lint`, `lint-fix`, `unit`, `integration`, and `cli`.
See `make help` for all the current make tasks.
### Internal Artifactory Settings
**Not always applicable**
Some companies have Artifactory setup internally as a solution for sourcing secure dependencies.
If you're seeing an issue where the unit tests won't run because of the below error then this section might be relevant for your use case.
```
[ERROR] [ERROR] Some problems were encountered while processing the POMs
```
If you're dealing with an issue where the unit tests will not pull/build certain java fixtures check some of these settings:
- a `settings.xml` file should be available to help you communicate with your internal artifactory deployment
- this can be moved to `syft/pkg/cataloger/java/test-fixtures/java-builds/example-jenkins-plugin/` to help build the unit test-fixtures
- you'll also want to modify the `build-example-jenkins-plugin.sh` to use `settings.xml`
For more information on this setup and troubleshooting see [issue 1895](https://github.com/anchore/syft/issues/1895#issuecomment-1610085319)
## Architecture
Syft is used to generate a Software Bill of Materials (SBOM) from different kinds of input.
@@ -158,15 +119,7 @@ sequenceDiagram
Catalogers are the way in which syft is able to identify and construct packages given some amount of source metadata.
For example, Syft can locate and process `package-lock.json` files when performing filesystem scans.
See: [how to specify file globs](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/cataloger/javascript/cataloger.go#L16-L21)
and an implementation of the [package-lock.json parser](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/cataloger/javascript/cataloger.go#L16-L21) for a quick review.
From a high level catalogers have the following properties:
- They are independent from one another. The java cataloger has no idea of the processes, assumptions, or results of the python cataloger, for example.
- They do not know what source is being analyzed. Are we analyzing a local directory? an image? if so, the squashed representation or all layers? The catalogers do not know the answers to these questions. Only that there is an interface to query for file paths and contents from an underlying "source" being scanned.
- Packages created by the cataloger should not be mutated after they are created. There is one exception made for adding CPEs to a package after the cataloging phase, but that will most likely be moved back into the cataloger in the future.
and an implementation of the [package-lock.json parser](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/cataloger/javascript/cataloger.go#L16-L21) fora quick review.
#### Building a new Cataloger
@@ -206,12 +159,12 @@ always feel free to file an issue or reach out to us [on slack](https://anchore.
#### Searching for files
All catalogers are provided an instance of the [`file.Resolver`](https://github.com/anchore/syft/blob/v0.70.0/syft/source/file_resolver.go#L8) to interface with the image and search for files. The implementations for these
All catalogers are provided an instance of the [`source.FileResolver`](https://github.com/anchore/syft/blob/v0.70.0/syft/source/file_resolver.go#L8) to interface with the image and search for files. The implementations for these
abstractions leverage [`stereoscope`](https://github.com/anchore/stereoscope) in order to perform searching. Here is a
rough outline how that works:
1. a stereoscope `file.Index` is searched based on the input given (a path, glob, or MIME type). The index is relatively fast to search, but requires results to be filtered down to the files that exist in the specific layer(s) of interest. This is done automatically by the `filetree.Searcher` abstraction. This abstraction will fallback to searching directly against the raw `filetree.FileTree` if the index does not contain the file(s) of interest. Note: the `filetree.Searcher` is used by the `file.Resolver` abstraction.
2. Once the set of files are returned from the `filetree.Searcher` the results are filtered down further to return the most unique file results. For example, you may have requested for files by a glob that returns multiple results. These results are filtered down to deduplicate by real files, so if a result contains two references to the same file, say one accessed via symlink and one accessed via the real path, then the real path reference is returned and the symlink reference is filtered out. If both were accessed by symlink then the first (by lexical order) is returned. This is done automatically by the `file.Resolver` abstraction.
1. a stereoscope `file.Index` is searched based on the input given (a path, glob, or MIME type). The index is relatively fast to search, but requires results to be filtered down to the files that exist in the specific layer(s) of interest. This is done automatically by the `filetree.Searcher` abstraction. This abstraction will fallback to searching directly against the raw `filetree.FileTree` if the index does not contain the file(s) of interest. Note: the `filetree.Searcher` is used by the `source.FileResolver` abstraction.
2. Once the set of files are returned from the `filetree.Searcher` the results are filtered down further to return the most unique file results. For example, you may have requested for files by a glob that returns multiple results. These results are filtered down to deduplicate by real files, so if a result contains two references to the same file, say one accessed via symlink and one accessed via the real path, then the real path reference is returned and the symlink reference is filtered out. If both were accessed by symlink then the first (by lexical order) is returned. This is done automatically by the `source.FileResolver` abstraction.
3. By the time results reach the `pkg.Cataloger` you are guaranteed to have a set of unique files that exist in the layer(s) of interest (relative to what the resolver supports).
## Testing
+16 -44
View File
@@ -4,21 +4,21 @@ TEMP_DIR := ./.tmp
# Command templates #################################
LINT_CMD := $(TEMP_DIR)/golangci-lint run --tests=false
GOIMPORTS_CMD := $(TEMP_DIR)/gosimports -local github.com/anchore
RELEASE_CMD := $(TEMP_DIR)/goreleaser release --clean
RELEASE_CMD := $(TEMP_DIR)/goreleaser release --rm-dist
SNAPSHOT_CMD := $(RELEASE_CMD) --skip-publish --skip-sign --snapshot
CHRONICLE_CMD = $(TEMP_DIR)/chronicle
GLOW_CMD = $(TEMP_DIR)/glow
# Tool versions #################################
GOLANGCILINT_VERSION := v1.53.3
GOSIMPORTS_VERSION := v0.3.8
GOLANGCILINT_VERSION := v1.51.1
GOSIMPORTS_VERSION := v0.3.5
BOUNCER_VERSION := v0.4.0
CHRONICLE_VERSION := v0.6.0
GORELEASER_VERSION := v1.19.2
GORELEASER_VERSION := v1.15.2
YAJSV_VERSION := v1.4.1
COSIGN_VERSION := v2.1.1
COSIGN_VERSION := v1.13.1
QUILL_VERSION := v0.2.0
GLOW_VERSION := v1.5.1
GLOW_VERSION := v1.5.0
# Formatting variables #################################
BOLD := $(shell tput -T linux bold)
@@ -90,7 +90,7 @@ bootstrap-tools: $(TEMP_DIR)
# the only difference between goimports and gosimports is that gosimports removes extra whitespace between import blocks (see https://github.com/golang/go/issues/20818)
GOBIN="$(realpath $(TEMP_DIR))" go install github.com/rinchsan/gosimports/cmd/gosimports@$(GOSIMPORTS_VERSION)
GOBIN="$(realpath $(TEMP_DIR))" go install github.com/neilpa/yajsv@$(YAJSV_VERSION)
GOBIN="$(realpath $(TEMP_DIR))" go install github.com/sigstore/cosign/v2/cmd/cosign@$(COSIGN_VERSION)
GOBIN="$(realpath $(TEMP_DIR))" go install github.com/sigstore/cosign/cmd/cosign@$(COSIGN_VERSION)
GOBIN="$(realpath $(TEMP_DIR))" go install github.com/charmbracelet/glow@$(GLOW_VERSION)
.PHONY: bootstrap-go
@@ -118,17 +118,13 @@ lint: ## Run gofmt + golangci lint checks
$(eval MALFORMED_FILENAMES := $(shell find . | grep -e ':'))
@bash -c "[[ '$(MALFORMED_FILENAMES)' == '' ]] || (printf '\nfound unsupported filename characters:\n$(MALFORMED_FILENAMES)\n\n' && false)"
.PHONY: format
format: ## Auto-format all source code
$(call title,Running formatters)
.PHONY: lint-fix
lint-fix: ## Auto-format all source code + run golangci lint fixers
$(call title,Running lint fixers)
gofmt -w -s .
$(GOIMPORTS_CMD) -w .
go mod tidy
.PHONY: lint-fix
lint-fix: format ## Auto-format all source code + run golangci lint fixers
$(call title,Running lint fixers)
$(LINT_CMD) --fix
go mod tidy
.PHONY: check-licenses
check-licenses: ## Ensure transitive dependencies are compliant with the current license policy
@@ -193,13 +189,9 @@ fingerprints:
cd test/integration/test-fixtures && \
make cache.fingerprint
# for BINARY test fixtures
cd syft/pkg/cataloger/binary/test-fixtures && \
make cache.fingerprint
# for JAVA BUILD test fixtures
cd syft/pkg/cataloger/java/test-fixtures/java-builds && \
make cache.fingerprint
make packages.fingerprint
# for GO BINARY test fixtures
cd syft/pkg/cataloger/golang/test-fixtures/archs && \
@@ -209,10 +201,6 @@ fingerprints:
cd syft/pkg/cataloger/rpm/test-fixtures && \
make rpms.fingerprint
# for Kernel test fixtures
cd syft/pkg/cataloger/kernel/test-fixtures && \
make cache.fingerprint
# for INSTALL integration test fixtures
cd test/install && \
make cache.fingerprint
@@ -226,7 +214,6 @@ fixtures:
$(call title,Generating test fixtures)
cd syft/pkg/cataloger/java/test-fixtures/java-builds && make
cd syft/pkg/cataloger/rpm/test-fixtures && make
cd syft/pkg/cataloger/binary/test-fixtures && make
.PHONY: show-test-image-cache
show-test-image-cache: ## Show all docker and image tar cache
@@ -260,11 +247,6 @@ install-test-ci-mac: $(SNAPSHOT_DIR)
cd test/install && \
make ci-test-mac
.PHONY: generate-compare-file
generate-compare-file:
$(call title,Generating compare test file)
go run ./cmd/syft $(COMPARE_TEST_IMAGE) -o json > $(COMPARE_DIR)/test-fixtures/acceptance-centos-8.2.2004.json
# note: we cannot clean the snapshot directory since the pipeline builds the snapshot separately
.PHONY: compare-mac
compare-mac: $(TEMP_DIR) $(SNAPSHOT_DIR) ## Run compare tests on build snapshot binaries and packages (Mac)
@@ -298,28 +280,22 @@ compare-test-rpm-package-install: $(TEMP_DIR) $(SNAPSHOT_DIR)
$(TEMP_DIR)
## Code and data generation targets #################################
## Code generation targets #################################
.PHONY: generate-json-schema
generate-json-schema: ## Generate a new json schema
cd syft/internal && go generate . && cd jsonschema && go run .
cd schema/json && go run generate.go
.PHONY: generate-license-list
generate-license-list: ## Generate an updated spdx license list
go generate ./internal/spdxlicense/...
gofmt -s -w ./internal/spdxlicense
.PHONY: generate-cpe-dictionary-index
generate-cpe-dictionary-index: ## Build the CPE index based off of the latest available CPE dictionary
$(call title,Building CPE index)
go generate ./syft/pkg/cataloger/common/cpe/dictionary
## Build-related targets #################################
.PHONY: build
build:
CGO_ENABLED=0 go build -trimpath -ldflags "$(LDFLAGS)" -o $@ ./cmd/syft
build: $(SNAPSHOT_DIR) ## Build release snapshot binaries and packages
$(SNAPSHOT_DIR): ## Build snapshot release binaries and packages
$(call title,Building snapshot artifacts)
@@ -344,7 +320,7 @@ release:
@.github/scripts/trigger-release.sh
.PHONY: ci-release
ci-release: ci-check clean-dist $(CHANGELOG)
ci-release: ci-check clean-dist
$(call title,Publishing release artifacts)
# create a config with the dist dir overridden
@@ -354,12 +330,8 @@ ci-release: ci-check clean-dist $(CHANGELOG)
bash -c "\
$(RELEASE_CMD) \
--config $(TEMP_DIR)/goreleaser.yaml \
--release-notes <(cat $(CHANGELOG)) \
|| (cat /tmp/quill-*.log && false)"
# upload the version file that supports the application version update check (excluding pre-releases)
.github/scripts/update-version-file.sh "$(DIST_DIR)" "$(VERSION)"
.PHONY: ci-check
ci-check:
@.github/scripts/ci-check.sh
+52 -160
View File
@@ -42,18 +42,15 @@ For commercial support options with Syft or Grype, please [contact Anchore](http
- Erlang (rebar3)
- Go (go.mod, Go binaries)
- Haskell (cabal, stack)
- Java (jar, ear, war, par, sar, nar, native-image)
- Java (jar, ear, war, par, sar, native-image)
- JavaScript (npm, yarn)
- Jenkins Plugins (jpi, hpi)
- Linux kernel archives (vmlinz)
- Linux kernel modules (ko)
- Nix (outputs in /nix/store)
- PHP (composer)
- Python (wheel, egg, poetry, requirements.txt)
- Red Hat (rpm)
- Ruby (gem)
- Rust (cargo.lock)
- Swift (cocoapods, swift-package-manager)
- Swift (cocoapods)
## Installation
@@ -78,12 +75,6 @@ The chocolatey distribution of syft is community maintained and not distributed
choco install syft -y
```
### Scoop
```powershell
scoop install syft
```
### Homebrew
```bash
brew install syft
@@ -119,7 +110,9 @@ The above output includes only software that is visible in the container (i.e.,
syft <image> --scope all-layers
```
### Supported sources
## Supported sources
Syft can generate a SBOM from a variety of sources:
@@ -148,72 +141,45 @@ file:path/to/yourproject/file read directly from a path on disk (any
registry:yourrepo/yourimage:tag pull image directly from a registry (no container runtime required)
```
If an image source is not provided and cannot be detected from the given reference it is assumed the image should be pulled from the Docker daemon.
If docker is not present, then the Podman daemon is attempted next, followed by reaching out directly to the image registry last.
This default behavior can be overridden with the `default-image-pull-source` configuration option (See [Configuration](https://github.com/anchore/syft#configuration) for more details).
### Default Cataloger Configuration by scan type
Syft uses different default sets of catalogers depending on what it is scanning: a container image or a directory on disk. The default catalogers for an image scan assumes that package installation steps have already been completed. For example, Syft will identify Python packages that have egg or wheel metadata files under a site-packages directory, since this indicates software actually installed on an image.
However, if you are scanning a directory, Syft doesn't assume that all relevant software is installed, and will use catalogers that can identify declared dependencies that may not yet be installed on the final system: for example, dependencies listed in a Python requirements.txt.
You can override the list of enabled/disabled catalogers by using the "catalogers" keyword in the [Syft configuration file](https://github.com/anchore/syft#configuration).
#### Default Cataloger Configuration by scan type
##### Image Scanning:
- alpmdb
- apkdb
- binary
- dotnet-deps
- rpmdb
- dpkgdb
- go-module-binary
- graalvm-native-image
- java
- javascript-package
- linux-kernel
- nix-store
- php-composer-installed
- apkdb
- portage
- python-package
- rpm-db
- ruby-gemspec
- sbom
- python-package
- php-composer-installed Cataloger
- javascript-package
- java
- go-module-binary
- dotnet-deps
##### Directory Scanning:
- alpmdb
- apkdb
- binary
- cocoapods
- conan
- dartlang-lock
- dotnet-deps
- dpkgdb
- elixir-mix-lock
- erlang-rebar-lock
- go-mod-file
- go-module-binary
- graalvm-native-image
- haskell
- java
- java-gradle-lockfile
- java-pom
- javascript-lock
- linux-kernel
- nix-store
- php-composer-lock
- portage
- rpmdb
- ruby-gemfile
- python-index
- python-package
- rpm-db
- rpm-file
- ruby-gemfile
- php-composer-lock
- javascript-lock
- java
- java-pom
- go-module-binary
- go-mod-file
- rust-cargo-lock
- sbom
- swift-package-manager
- dartlang-lock
- dotnet-deps
- cocoapods
- conan
- hackage
##### Non Default:
#### Non Default:
- cargo-auditable-binary
### Excluding file paths
@@ -247,10 +213,8 @@ Where the `formats` available are:
- `text`: A row-oriented, human-and-machine-friendly output.
- `cyclonedx-xml`: A XML report conforming to the [CycloneDX 1.4 specification](https://cyclonedx.org/specification/overview/).
- `cyclonedx-json`: A JSON report conforming to the [CycloneDX 1.4 specification](https://cyclonedx.org/specification/overview/).
- `spdx-tag-value`: A tag-value formatted report conforming to the [SPDX 2.3 specification](https://spdx.github.io/spdx-spec/v2.3/).
- `spdx-tag-value@2.2`: A tag-value formatted report conforming to the [SPDX 2.2 specification](https://spdx.github.io/spdx-spec/v2.2.2/).
- `spdx-json`: A JSON report conforming to the [SPDX 2.3 JSON Schema](https://github.com/spdx/spdx-spec/blob/v2.3/schemas/spdx-schema.json).
- `spdx-json@2.2`: A JSON report conforming to the [SPDX 2.2 JSON Schema](https://github.com/spdx/spdx-spec/blob/v2.2/schemas/spdx-schema.json).
- `spdx-tag-value`: A tag-value formatted report conforming to the [SPDX 2.2 specification](https://spdx.github.io/spdx-spec/).
- `spdx-json`: A JSON report conforming to the [SPDX 2.2 JSON Schema](https://github.com/spdx/spdx-spec/blob/v2.2/schemas/spdx-schema.json).
- `github`: A JSON report conforming to GitHub's dependency snapshot format.
- `table`: A columnar summary (default).
- `template`: Lets the user specify the output format. See ["Using templates"](#using-templates) below.
@@ -288,8 +252,6 @@ Which would produce output like:
Syft also includes a vast array of utility templating functions from [sprig](http://masterminds.github.io/sprig/) apart from the default Golang [text/template](https://pkg.go.dev/text/template#hdr-Functions) to allow users to customize the output format.
Lastly, Syft has custom templating functions defined in `./syft/format/template/encoder.go` to help parse the passed-in JSON structs.
## Multiple outputs
Syft can also output _multiple_ files in differing formats by appending
@@ -429,7 +391,7 @@ Certificate subject: test.email@testdomain.com
Certificate issuer URL: https://accounts.google.com
```
### Local private key support
#### Local private key support
To generate an SBOM attestation for a container image using a local private key:
```
@@ -472,10 +434,6 @@ file: ""
# same as SYFT_CHECK_FOR_APP_UPDATE env var
check-for-app-update: true
# allows users to specify which image source should be used to generate the sbom
# valid values are: registry, docker, podman
default-image-pull-source: ""
# a list of globs to exclude from scanning. same as --exclude ; for example:
# exclude:
# - "/etc/**"
@@ -489,40 +447,26 @@ platform: ""
# set the list of package catalogers to use when generating the SBOM
# default = empty (cataloger set determined automatically by the source type [image or file/directory])
# catalogers:
# - alpmdb-cataloger
# - apkdb-cataloger
# - binary-cataloger
# - cargo-auditable-binary-cataloger
# - cocoapods-cataloger
# - conan-cataloger
# - dartlang-lock-cataloger
# - dotnet-deps-cataloger
# - dpkgdb-cataloger
# - elixir-mix-lock-cataloger
# - erlang-rebar-lock-cataloger
# - go-mod-file-cataloger
# - go-module-binary-cataloger
# - graalvm-native-image-cataloger
# - haskell-cataloger
# - java-cataloger
# - java-gradle-lockfile-cataloger
# - java-pom-cataloger
# - javascript-lock-cataloger
# - javascript-package-cataloger
# - linux-kernel-cataloger
# - nix-store-cataloger
# - php-composer-installed-cataloger
# - php-composer-lock-cataloger
# - portage-cataloger
# - python-index-cataloger
# - python-package-cataloger
# - rpm-db-cataloger
# - rpm-file-cataloger
# - ruby-gemfile-cataloger
# - ruby-gemspec-cataloger
# - rust-cargo-lock-cataloger
# - sbom-cataloger
# - spm-cataloger
# - ruby-gemfile
# - ruby-gemspec
# - python-index
# - python-package
# - javascript-lock
# - javascript-package
# - php-composer-installed
# - php-composer-lock
# - alpmdb
# - dpkgdb
# - rpmdb
# - java
# - apkdb
# - go-module-binary
# - go-mod-file
# - dartlang-lock
# - rust
# - dotnet-deps
# rust-audit-binary scans Rust binaries built with https://github.com/Shnatsel/rust-audit
# - rust-audit-binary
catalogers:
# cataloging packages is exposed through the packages and power-user subcommands
@@ -548,42 +492,6 @@ package:
# same as -s ; SYFT_PACKAGE_CATALOGER_SCOPE env var
scope: "squashed"
golang:
# search for go package licences in the GOPATH of the system running Syft, note that this is outside the
# container filesystem and potentially outside the root of a local directory scan
# SYFT_GOLANG_SEARCH_LOCAL_MOD_CACHE_LICENSES env var
search-local-mod-cache-licenses: false
# specify an explicit go mod cache directory, if unset this defaults to $GOPATH/pkg/mod or $HOME/go/pkg/mod
# SYFT_GOLANG_LOCAL_MOD_CACHE_DIR env var
local-mod-cache-dir: ""
# search for go package licences by retrieving the package from a network proxy
# SYFT_GOLANG_SEARCH_REMOTE_LICENSES env var
search-remote-licenses: false
# remote proxy to use when retrieving go packages from the network,
# if unset this defaults to $GOPROXY followed by https://proxy.golang.org
# SYFT_GOLANG_PROXY env var
proxy: ""
# specifies packages which should not be fetched by proxy
# if unset this defaults to $GONOPROXY
# SYFT_GOLANG_NOPROXY env var
no-proxy: ""
linux-kernel:
# whether to catalog linux kernel modules found within lib/modules/** directories
# SYFT_LINUX_KERNEL_CATALOG_MODULES env var
catalog-modules: true
python:
# when running across entries in requirements.txt that do not specify a specific version
# (e.g. "sqlalchemy >= 1.0.0, <= 2.0.0, != 3.0.0, <= 3.0.0"), attempt to guess what the version could
# be based on the version requirements specified (e.g. "1.0.0"). When enabled the lowest expressible version
# when given an arbitrary constraint will be used (even if that version may not be available/published).
guess-unpinned-requirements: false
# cataloging file contents is exposed through the power-user subcommand
file-contents:
cataloger:
@@ -614,7 +522,7 @@ file-metadata:
# SYFT_FILE_METADATA_CATALOGER_SCOPE env var
scope: "squashed"
# the file digest algorithms to use when cataloging files (options: "md5", "sha1", "sha224", "sha256", "sha384", "sha512")
# the file digest algorithms to use when cataloging files (options: "sha256", "md5", "sha1")
# SYFT_FILE_METADATA_DIGESTS env var
digests: ["sha256"]
@@ -652,27 +560,11 @@ secrets:
# SYFT_SECRETS_EXCLUDE_PATTERN_NAMES env var
exclude-pattern-names: []
# options that apply to all scan sources
source:
# alias name for the source
# SYFT_SOURCE_NAME env var; --source-name flag
name: ""
# alias version for the source
# SYFT_SOURCE_VERSION env var; --source-version flag
version: ""
# options affecting the file source type
file:
# the file digest algorithms to use on the scanned file (options: "md5", "sha1", "sha224", "sha256", "sha384", "sha512")
digests: ["sha256"]
# options when pulling directly from a registry via the "registry:" scheme
registry:
# skip TLS verification when communicating with the registry
# SYFT_REGISTRY_INSECURE_SKIP_TLS_VERIFY env var
insecure-skip-tls-verify: false
# use http instead of https when connecting to the registry
# SYFT_REGISTRY_INSECURE_USE_HTTP env var
insecure-use-http: false
-1
View File
@@ -43,7 +43,6 @@ func Attest(v *viper.Viper, app *config.Application, ro *options.RootOptions, po
RunE: func(cmd *cobra.Command, args []string) error {
if app.CheckForAppUpdate {
checkForApplicationUpdate()
// TODO: this is broke, the bus isn't available yet
}
return attest.Run(cmd.Context(), app, args)
+128 -160
View File
@@ -5,22 +5,18 @@ import (
"fmt"
"os"
"os/exec"
"strings"
"github.com/wagoodman/go-partybus"
"github.com/wagoodman/go-progress"
"golang.org/x/exp/slices"
"github.com/anchore/stereoscope"
"github.com/anchore/stereoscope/pkg/image"
"github.com/anchore/syft/cmd/syft/cli/eventloop"
"github.com/anchore/syft/cmd/syft/cli/options"
"github.com/anchore/syft/cmd/syft/cli/packages"
"github.com/anchore/syft/cmd/syft/internal/ui"
"github.com/anchore/syft/internal/bus"
"github.com/anchore/syft/internal/config"
"github.com/anchore/syft/internal/file"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/ui"
"github.com/anchore/syft/syft"
"github.com/anchore/syft/syft/event"
"github.com/anchore/syft/syft/event/monitor"
@@ -30,20 +26,33 @@ import (
"github.com/anchore/syft/syft/source"
)
func Run(_ context.Context, app *config.Application, args []string) error {
func Run(ctx context.Context, app *config.Application, args []string) error {
err := ValidateOutputOptions(app)
if err != nil {
return err
}
// note: must be a container image
userInput := args[0]
_, err = exec.LookPath("cosign")
writer, err := options.MakeWriter(app.Outputs, app.File, app.OutputTemplatePath)
if err != nil {
// when cosign is not installed the error will be rendered like so:
// 2023/06/30 08:31:52 error during command execution: 'syft attest' requires cosign to be installed: exec: "cosign": executable file not found in $PATH
return fmt.Errorf("'syft attest' requires cosign to be installed: %w", err)
return fmt.Errorf("unable to write to report destination: %w", err)
}
defer func() {
if err := writer.Close(); err != nil {
fmt.Printf("unable to close report destination: %+v", err)
}
}()
// could be an image or a directory, with or without a scheme
// TODO: validate that source is image
userInput := args[0]
si, err := source.ParseInputWithName(userInput, app.Platform, true, app.Name)
if err != nil {
return fmt.Errorf("could not generate source input for packages command: %w", err)
}
if si.Scheme != source.ImageScheme {
return fmt.Errorf("attestations are only supported for oci images at this time")
}
eventBus := partybus.NewBus()
@@ -52,7 +61,7 @@ func Run(_ context.Context, app *config.Application, args []string) error {
subscription := eventBus.Subscribe()
return eventloop.EventLoop(
execWorker(app, userInput),
execWorker(app, *si, writer),
eventloop.SetupSignals(),
subscription,
stereoscope.Cleanup,
@@ -60,54 +69,13 @@ func Run(_ context.Context, app *config.Application, args []string) error {
)
}
func buildSBOM(app *config.Application, userInput string, errs chan error) (*sbom.SBOM, error) {
cfg := source.DetectConfig{
DefaultImageSource: app.DefaultImagePullSource,
}
detection, err := source.Detect(userInput, cfg)
if err != nil {
return nil, fmt.Errorf("could not deteremine source: %w", err)
}
if detection.IsContainerImage() {
return nil, fmt.Errorf("attestations are only supported for oci images at this time")
}
var platform *image.Platform
if app.Platform != "" {
platform, err = image.NewPlatform(app.Platform)
if err != nil {
return nil, fmt.Errorf("invalid platform: %w", err)
}
}
hashers, err := file.Hashers(app.Source.File.Digests...)
if err != nil {
return nil, fmt.Errorf("invalid hash: %w", err)
}
src, err := detection.NewSource(
source.DetectionSourceConfig{
Alias: source.Alias{
Name: app.Source.Name,
Version: app.Source.Version,
},
RegistryOptions: app.Registry.ToOptions(),
Platform: platform,
Exclude: source.ExcludeConfig{
Paths: app.Exclusions,
},
DigestAlgorithms: hashers,
BasePath: app.BasePath,
},
)
if src != nil {
defer src.Close()
func buildSBOM(app *config.Application, si source.Input, writer sbom.Writer, errs chan error) ([]byte, error) {
src, cleanup, err := source.New(si, app.Registry.ToOptions(), app.Exclusions)
if cleanup != nil {
defer cleanup()
}
if err != nil {
return nil, fmt.Errorf("failed to construct source from user input %q: %w", userInput, err)
return nil, fmt.Errorf("failed to construct source from user input %q: %w", si.UserInput, err)
}
s, err := packages.GenerateSBOM(src, errs, app)
@@ -116,123 +84,92 @@ func buildSBOM(app *config.Application, userInput string, errs chan error) (*sbo
}
if s == nil {
return nil, fmt.Errorf("no SBOM produced for %q", userInput)
return nil, fmt.Errorf("no SBOM produced for %q", si.UserInput)
}
return s, nil
// note: only works for single format no multi writer support
sBytes, err := writer.Bytes(*s)
if err != nil {
return nil, fmt.Errorf("unable to build SBOM bytes: %w", err)
}
return sBytes, nil
}
//nolint:funlen
func execWorker(app *config.Application, userInput string) <-chan error {
func execWorker(app *config.Application, si source.Input, writer sbom.Writer) <-chan error {
errs := make(chan error)
go func() {
defer close(errs)
defer bus.Exit()
s, err := buildSBOM(app, userInput, errs)
sBytes, err := buildSBOM(app, si, writer, errs)
if err != nil {
errs <- fmt.Errorf("unable to build SBOM: %w", err)
return
}
// note: ValidateOutputOptions ensures that there is no more than one output type
o := app.Outputs[0]
// TODO: add multi writer support
for _, o := range app.Outputs {
f, err := os.CreateTemp("", o)
if err != nil {
errs <- fmt.Errorf("unable to create temp file: %w", err)
return
}
f, err := os.CreateTemp("", o)
if err != nil {
errs <- fmt.Errorf("unable to create temp file: %w", err)
return
}
defer os.Remove(f.Name())
defer f.Close()
defer os.Remove(f.Name())
writer, err := options.MakeSBOMWriter(app.Outputs, f.Name(), app.OutputTemplatePath)
if err != nil {
errs <- fmt.Errorf("unable to create SBOM writer: %w", err)
return
if _, err := f.Write(sBytes); err != nil {
errs <- fmt.Errorf("unable to write SBOM to temp file: %w", err)
return
}
// TODO: what other validation here besides binary name?
cmd := "cosign"
if !commandExists(cmd) {
errs <- fmt.Errorf("unable to find cosign in PATH; make sure you have it installed")
return
}
args := []string{"attest", si.UserInput, "--predicate", f.Name()}
if app.Attest.Key != "" {
args = append(args, "--key", app.Attest.Key)
}
execCmd := exec.Command(cmd, args...)
execCmd.Env = os.Environ()
if app.Attest.Key != "" {
execCmd.Env = append(execCmd.Env, fmt.Sprintf("COSIGN_PASSWORD=%s", app.Attest.Password))
} else {
// no key provided, use cosign's keyless mode
execCmd.Env = append(execCmd.Env, "COSIGN_EXPERIMENTAL=1")
}
// bus adapter for ui to hook into stdout via an os pipe
r, w, err := os.Pipe()
if err != nil {
errs <- fmt.Errorf("unable to create os pipe: %w", err)
return
}
defer w.Close()
b := &busWriter{r: r, w: w, mon: &progress.Manual{N: -1}}
execCmd.Stdout = b
execCmd.Stderr = b
defer b.mon.SetCompleted()
// attest the SBOM
err = execCmd.Run()
if err != nil {
b.mon.Err = err
errs <- fmt.Errorf("unable to attest SBOM: %w", err)
return
}
}
if err := writer.Write(*s); err != nil {
errs <- fmt.Errorf("unable to write SBOM to temp file: %w", err)
return
}
// TODO: what other validation here besides binary name?
cmd := "cosign"
if !commandExists(cmd) {
errs <- fmt.Errorf("unable to find cosign in PATH; make sure you have it installed")
return
}
// Select Cosign predicate type based on defined output type
// As orientation, check: https://github.com/sigstore/cosign/blob/main/pkg/cosign/attestation/attestation.go
var predicateType string
switch strings.ToLower(o) {
case "cyclonedx-json":
predicateType = "cyclonedx"
case "spdx-tag-value", "spdx-tv":
predicateType = "spdx"
case "spdx-json", "json":
predicateType = "spdxjson"
default:
predicateType = "custom"
}
args := []string{"attest", userInput, "--predicate", f.Name(), "--type", predicateType}
if app.Attest.Key != "" {
args = append(args, "--key", app.Attest.Key)
}
execCmd := exec.Command(cmd, args...)
execCmd.Env = os.Environ()
if app.Attest.Key != "" {
execCmd.Env = append(execCmd.Env, fmt.Sprintf("COSIGN_PASSWORD=%s", app.Attest.Password))
} else {
// no key provided, use cosign's keyless mode
execCmd.Env = append(execCmd.Env, "COSIGN_EXPERIMENTAL=1")
}
log.WithFields("cmd", strings.Join(execCmd.Args, " ")).Trace("creating attestation")
// bus adapter for ui to hook into stdout via an os pipe
r, w, err := os.Pipe()
if err != nil {
errs <- fmt.Errorf("unable to create os pipe: %w", err)
return
}
defer w.Close()
mon := progress.NewManual(-1)
bus.Publish(
partybus.Event{
Type: event.AttestationStarted,
Source: monitor.GenericTask{
Title: monitor.Title{
Default: "Create attestation",
WhileRunning: "Creating attestation",
OnSuccess: "Created attestation",
},
Context: "cosign",
},
Value: &monitor.ShellProgress{
Reader: r,
Progressable: mon,
},
},
)
execCmd.Stdout = w
execCmd.Stderr = w
// attest the SBOM
err = execCmd.Run()
if err != nil {
mon.SetError(err)
errs <- fmt.Errorf("unable to attest SBOM: %w", err)
return
}
mon.SetCompleted()
bus.Publish(partybus.Event{
Type: event.Exit,
Value: func() error { return nil },
})
}()
return errs
}
@@ -255,6 +192,37 @@ func ValidateOutputOptions(app *config.Application) error {
return nil
}
type busWriter struct {
w *os.File
r *os.File
hasWritten bool
mon *progress.Manual
}
func (b *busWriter) Write(p []byte) (n int, err error) {
if !b.hasWritten {
b.hasWritten = true
bus.Publish(
partybus.Event{
Type: event.AttestationStarted,
Source: monitor.GenericTask{
Title: monitor.Title{
Default: "Create attestation",
WhileRunning: "Creating attestation",
OnSuccess: "Created attestation",
},
Context: "cosign",
},
Value: &monitor.ShellProgress{
Reader: b.r,
Manual: b.mon,
},
},
)
}
return b.w.Write(p)
}
func commandExists(cmd string) bool {
_, err := exec.LookPath(cmd)
return err == nil
+1 -2
View File
@@ -55,7 +55,6 @@ func New() (*cobra.Command, error) {
// rootCmd is currently an alias for the packages command
rootCmd := &cobra.Command{
Use: fmt.Sprintf("%s [SOURCE]", internal.ApplicationName),
Short: packagesCmd.Short,
Long: packagesCmd.Long,
Args: packagesCmd.Args,
@@ -125,7 +124,7 @@ func checkForApplicationUpdate() {
log.Infof("new version of %s is available: %s (current version is %s)", internal.ApplicationName, newVersion, version.FromBuild().Version)
bus.Publish(partybus.Event{
Type: event.CLIAppUpdateAvailable,
Type: event.AppUpdateAvailable,
Value: newVersion,
})
} else {
-1
View File
@@ -43,7 +43,6 @@ func Convert(v *viper.Viper, app *config.Application, ro *options.RootOptions, p
RunE: func(cmd *cobra.Command, args []string) error {
if app.CheckForAppUpdate {
checkForApplicationUpdate()
// TODO: this is broke, the bus isn't available yet
}
return convert.Run(cmd.Context(), app, args)
},
+13 -46
View File
@@ -6,29 +6,26 @@ import (
"io"
"os"
"github.com/wagoodman/go-partybus"
"github.com/anchore/stereoscope"
"github.com/anchore/syft/cmd/syft/cli/eventloop"
"github.com/anchore/syft/cmd/syft/cli/options"
"github.com/anchore/syft/cmd/syft/internal/ui"
"github.com/anchore/syft/internal/bus"
"github.com/anchore/syft/internal/config"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft"
"github.com/anchore/syft/syft/formats"
"github.com/anchore/syft/syft/sbom"
)
func Run(_ context.Context, app *config.Application, args []string) error {
log.Warn("convert is an experimental feature, run `syft convert -h` for help")
writer, err := options.MakeSBOMWriter(app.Outputs, app.File, app.OutputTemplatePath)
writer, err := options.MakeWriter(app.Outputs, app.File, app.OutputTemplatePath)
if err != nil {
return err
}
// could be an image or a directory, with or without a scheme
defer func() {
if err := writer.Close(); err != nil {
log.Warnf("unable to write to report destination: %w", err)
}
}()
// this can only be a SBOM file
userInput := args[0]
var reader io.ReadCloser
@@ -46,40 +43,10 @@ func Run(_ context.Context, app *config.Application, args []string) error {
reader = f
}
eventBus := partybus.NewBus()
stereoscope.SetBus(eventBus)
syft.SetBus(eventBus)
subscription := eventBus.Subscribe()
sbom, _, err := formats.Decode(reader)
if err != nil {
return fmt.Errorf("failed to decode SBOM: %w", err)
}
return eventloop.EventLoop(
execWorker(reader, writer),
eventloop.SetupSignals(),
subscription,
stereoscope.Cleanup,
ui.Select(options.IsVerbose(app), app.Quiet)...,
)
}
func execWorker(reader io.Reader, writer sbom.Writer) <-chan error {
errs := make(chan error)
go func() {
defer close(errs)
defer bus.Exit()
s, _, err := formats.Decode(reader)
if err != nil {
errs <- fmt.Errorf("failed to decode SBOM: %w", err)
return
}
if s == nil {
errs <- fmt.Errorf("no SBOM produced")
return
}
if err := writer.Write(*s); err != nil {
errs <- fmt.Errorf("failed to write SBOM: %w", err)
}
}()
return errs
return writer.Write(*sbom)
}
+7 -7
View File
@@ -8,20 +8,20 @@ import (
"github.com/hashicorp/go-multierror"
"github.com/wagoodman/go-partybus"
"github.com/anchore/clio"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/ui"
)
// EventLoop listens to worker errors (from execution path), worker events (from a partybus subscription), and
// eventLoop listens to worker errors (from execution path), worker events (from a partybus subscription), and
// signal interrupts. Is responsible for handling each event relative to a given UI an to coordinate eventing until
// an eventual graceful exit.
func EventLoop(workerErrs <-chan error, signals <-chan os.Signal, subscription *partybus.Subscription, cleanupFn func(), uxs ...clio.UI) error {
func EventLoop(workerErrs <-chan error, signals <-chan os.Signal, subscription *partybus.Subscription, cleanupFn func(), uxs ...ui.UI) error {
defer cleanupFn()
events := subscription.Events()
var err error
var ux clio.UI
var ux ui.UI
if ux, err = setupUI(subscription, uxs...); err != nil {
if ux, err = setupUI(subscription.Unsubscribe, uxs...); err != nil {
return err
}
@@ -85,9 +85,9 @@ func EventLoop(workerErrs <-chan error, signals <-chan os.Signal, subscription *
// during teardown. With the given UIs, the first UI which the ui.Setup() function does not return an error
// will be utilized in execution. Providing a set of UIs allows for the caller to provide graceful fallbacks
// when there are environmental problem (e.g. unable to setup a TUI with the current TTY).
func setupUI(subscription *partybus.Subscription, uis ...clio.UI) (clio.UI, error) {
func setupUI(unsubscribe func() error, uis ...ui.UI) (ui.UI, error) {
for _, ux := range uis {
if err := ux.Setup(subscription); err != nil {
if err := ux.Setup(unsubscribe); err != nil {
log.Warnf("unable to setup given UI, falling back to alternative UI: %+v", err)
continue
}
+13 -16
View File
@@ -11,37 +11,34 @@ import (
"github.com/stretchr/testify/mock"
"github.com/wagoodman/go-partybus"
"github.com/anchore/clio"
"github.com/anchore/syft/internal/ui"
"github.com/anchore/syft/syft/event"
)
var _ clio.UI = (*uiMock)(nil)
var _ ui.UI = (*uiMock)(nil)
type uiMock struct {
t *testing.T
finalEvent partybus.Event
subscription partybus.Unsubscribable
t *testing.T
finalEvent partybus.Event
unsubscribe func() error
mock.Mock
}
func (u *uiMock) Setup(unsubscribe partybus.Unsubscribable) error {
u.t.Helper()
func (u *uiMock) Setup(unsubscribe func() error) error {
u.t.Logf("UI Setup called")
u.subscription = unsubscribe
return u.Called(unsubscribe.Unsubscribe).Error(0)
u.unsubscribe = unsubscribe
return u.Called(unsubscribe).Error(0)
}
func (u *uiMock) Handle(event partybus.Event) error {
u.t.Helper()
u.t.Logf("UI Handle called: %+v", event.Type)
if event == u.finalEvent {
assert.NoError(u.t, u.subscription.Unsubscribe())
assert.NoError(u.t, u.unsubscribe())
}
return u.Called(event).Error(0)
}
func (u *uiMock) Teardown(_ bool) error {
u.t.Helper()
u.t.Logf("UI Teardown called")
return u.Called().Error(0)
}
@@ -54,7 +51,7 @@ func Test_EventLoop_gracefulExit(t *testing.T) {
t.Cleanup(testBus.Close)
finalEvent := partybus.Event{
Type: event.CLIExit,
Type: event.Exit,
}
worker := func() <-chan error {
@@ -186,7 +183,7 @@ func Test_EventLoop_unsubscribeError(t *testing.T) {
t.Cleanup(testBus.Close)
finalEvent := partybus.Event{
Type: event.CLIExit,
Type: event.Exit,
}
worker := func() <-chan error {
@@ -255,7 +252,7 @@ func Test_EventLoop_handlerError(t *testing.T) {
t.Cleanup(testBus.Close)
finalEvent := partybus.Event{
Type: event.CLIExit,
Type: event.Exit,
Error: fmt.Errorf("an exit error occured"),
}
@@ -380,7 +377,7 @@ func Test_EventLoop_uiTeardownError(t *testing.T) {
t.Cleanup(testBus.Close)
finalEvent := partybus.Event{
Type: event.CLIExit,
Type: event.Exit,
}
worker := func() <-chan error {
+40 -21
View File
@@ -1,19 +1,18 @@
package eventloop
import (
"crypto"
"fmt"
"github.com/anchore/syft/internal/config"
"github.com/anchore/syft/internal/file"
"github.com/anchore/syft/syft"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file/cataloger/filecontent"
"github.com/anchore/syft/syft/file/cataloger/filedigest"
"github.com/anchore/syft/syft/file/cataloger/filemetadata"
"github.com/anchore/syft/syft/file/cataloger/secrets"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/sbom"
"github.com/anchore/syft/syft/source"
)
type Task func(*sbom.Artifacts, source.Source) ([]artifact.Relationship, error)
type Task func(*sbom.Artifacts, *source.Source) ([]artifact.Relationship, error)
func Tasks(app *config.Application) ([]Task, error) {
var tasks []Task
@@ -45,13 +44,16 @@ func generateCatalogPackagesTask(app *config.Application) (Task, error) {
return nil, nil
}
task := func(results *sbom.Artifacts, src source.Source) ([]artifact.Relationship, error) {
task := func(results *sbom.Artifacts, src *source.Source) ([]artifact.Relationship, error) {
packageCatalog, relationships, theDistro, err := syft.CatalogPackages(src, app.ToCatalogerConfig())
if err != nil {
return nil, err
}
results.Packages = packageCatalog
results.PackageCatalog = packageCatalog
results.LinuxDistribution = theDistro
return relationships, err
return relationships, nil
}
return task, nil
@@ -62,9 +64,9 @@ func generateCatalogFileMetadataTask(app *config.Application) (Task, error) {
return nil, nil
}
metadataCataloger := filemetadata.NewCataloger()
metadataCataloger := file.NewMetadataCataloger()
task := func(results *sbom.Artifacts, src source.Source) ([]artifact.Relationship, error) {
task := func(results *sbom.Artifacts, src *source.Source) ([]artifact.Relationship, error) {
resolver, err := src.FileResolver(app.FileMetadata.Cataloger.ScopeOpt)
if err != nil {
return nil, err
@@ -86,14 +88,31 @@ func generateCatalogFileDigestsTask(app *config.Application) (Task, error) {
return nil, nil
}
hashes, err := file.Hashers(app.FileMetadata.Digests...)
supportedHashAlgorithms := make(map[string]crypto.Hash)
for _, h := range []crypto.Hash{
crypto.MD5,
crypto.SHA1,
crypto.SHA256,
} {
supportedHashAlgorithms[file.DigestAlgorithmName(h)] = h
}
var hashes []crypto.Hash
for _, hashStr := range app.FileMetadata.Digests {
name := file.CleanDigestAlgorithmName(hashStr)
hashObj, ok := supportedHashAlgorithms[name]
if !ok {
return nil, fmt.Errorf("unsupported hash algorithm: %s", hashStr)
}
hashes = append(hashes, hashObj)
}
digestsCataloger, err := file.NewDigestsCataloger(hashes)
if err != nil {
return nil, err
}
digestsCataloger := filedigest.NewCataloger(hashes)
task := func(results *sbom.Artifacts, src source.Source) ([]artifact.Relationship, error) {
task := func(results *sbom.Artifacts, src *source.Source) ([]artifact.Relationship, error) {
resolver, err := src.FileResolver(app.FileMetadata.Cataloger.ScopeOpt)
if err != nil {
return nil, err
@@ -115,17 +134,17 @@ func generateCatalogSecretsTask(app *config.Application) (Task, error) {
return nil, nil
}
patterns, err := secrets.GenerateSearchPatterns(secrets.DefaultSecretsPatterns, app.Secrets.AdditionalPatterns, app.Secrets.ExcludePatternNames)
patterns, err := file.GenerateSearchPatterns(file.DefaultSecretsPatterns, app.Secrets.AdditionalPatterns, app.Secrets.ExcludePatternNames)
if err != nil {
return nil, err
}
secretsCataloger, err := secrets.NewCataloger(patterns, app.Secrets.RevealValues, app.Secrets.SkipFilesAboveSize) //nolint:staticcheck
secretsCataloger, err := file.NewSecretsCataloger(patterns, app.Secrets.RevealValues, app.Secrets.SkipFilesAboveSize)
if err != nil {
return nil, err
}
task := func(results *sbom.Artifacts, src source.Source) ([]artifact.Relationship, error) {
task := func(results *sbom.Artifacts, src *source.Source) ([]artifact.Relationship, error) {
resolver, err := src.FileResolver(app.Secrets.Cataloger.ScopeOpt)
if err != nil {
return nil, err
@@ -147,12 +166,12 @@ func generateCatalogContentsTask(app *config.Application) (Task, error) {
return nil, nil
}
contentsCataloger, err := filecontent.NewCataloger(app.FileContents.Globs, app.FileContents.SkipFilesAboveSize) //nolint:staticcheck
contentsCataloger, err := file.NewContentsCataloger(app.FileContents.Globs, app.FileContents.SkipFilesAboveSize)
if err != nil {
return nil, err
}
task := func(results *sbom.Artifacts, src source.Source) ([]artifact.Relationship, error) {
task := func(results *sbom.Artifacts, src *source.Source) ([]artifact.Relationship, error) {
resolver, err := src.FileResolver(app.FileContents.Cataloger.ScopeOpt)
if err != nil {
return nil, err
@@ -169,7 +188,7 @@ func generateCatalogContentsTask(app *config.Application) (Task, error) {
return task, nil
}
func RunTask(t Task, a *sbom.Artifacts, src source.Source, c chan<- artifact.Relationship, errs chan<- error) {
func RunTask(t Task, a *sbom.Artifacts, src *source.Source, c chan<- artifact.Relationship, errs chan<- error) {
defer close(c)
relationships, err := t(a, src)
-1
View File
@@ -17,7 +17,6 @@ func (o AttestOptions) AddFlags(cmd *cobra.Command, v *viper.Viper) error {
return bindAttestConfigOptions(cmd.Flags(), v)
}
//nolint:revive
func bindAttestConfigOptions(flags *pflag.FlagSet, v *viper.Viper) error {
if err := v.BindPFlag("attest.key", flags.Lookup("key")); err != nil {
return err
-1
View File
@@ -31,7 +31,6 @@ func (o *FulcioOptions) AddFlags(cmd *cobra.Command, v *viper.Viper) error {
return bindFulcioConfigOptions(cmd.Flags(), v)
}
//nolint:revive
func bindFulcioConfigOptions(flags *pflag.FlagSet, v *viper.Viper) error {
if err := v.BindPFlag("attest.fulcio-url", flags.Lookup("fulcio-url")); err != nil {
return err
-1
View File
@@ -31,7 +31,6 @@ func (o *OIDCOptions) AddFlags(cmd *cobra.Command, v *viper.Viper) error {
return bindOIDCConfigOptions(cmd.Flags(), v)
}
//nolint:revive
func bindOIDCConfigOptions(flags *pflag.FlagSet, v *viper.Viper) error {
if err := v.BindPFlag("attest.oidc-issuer", flags.Lookup("oidc-issuer")); err != nil {
return err
+2 -27
View File
@@ -21,9 +21,7 @@ type PackagesOptions struct {
Platform string
Exclude []string
Catalogers []string
SourceName string
SourceVersion string
BasePath string
Name string
}
var _ Interface = (*PackagesOptions)(nil)
@@ -50,23 +48,12 @@ func (o *PackagesOptions) AddFlags(cmd *cobra.Command, v *viper.Viper) error {
cmd.Flags().StringArrayVarP(&o.Catalogers, "catalogers", "", nil,
"enable one or more package catalogers")
cmd.Flags().StringVarP(&o.SourceName, "name", "", "",
cmd.Flags().StringVarP(&o.Name, "name", "", "",
"set the name of the target being analyzed")
cmd.Flags().Lookup("name").Deprecated = "use: source-name"
cmd.Flags().StringVarP(&o.SourceName, "source-name", "", "",
"set the name of the target being analyzed")
cmd.Flags().StringVarP(&o.SourceVersion, "source-version", "", "",
"set the name of the target being analyzed")
cmd.Flags().StringVarP(&o.BasePath, "base-path", "", "",
"base directory for scanning, no links will be followed above this directory, and all paths will be reported relative to this directory")
return bindPackageConfigOptions(cmd.Flags(), v)
}
//nolint:revive
func bindPackageConfigOptions(flags *pflag.FlagSet, v *viper.Viper) error {
// Formatting & Input options //////////////////////////////////////////////
@@ -90,14 +77,6 @@ func bindPackageConfigOptions(flags *pflag.FlagSet, v *viper.Viper) error {
return err
}
if err := v.BindPFlag("source.name", flags.Lookup("source-name")); err != nil {
return err
}
if err := v.BindPFlag("source.version", flags.Lookup("source-version")); err != nil {
return err
}
if err := v.BindPFlag("output", flags.Lookup("output")); err != nil {
return err
}
@@ -110,9 +89,5 @@ func bindPackageConfigOptions(flags *pflag.FlagSet, v *viper.Viper) error {
return err
}
if err := v.BindPFlag("base-path", flags.Lookup("base-path")); err != nil {
return err
}
return nil
}
-1
View File
@@ -22,7 +22,6 @@ func (o *RekorOptions) AddFlags(cmd *cobra.Command, v *viper.Viper) error {
return bindRekorConfigOptions(cmd.Flags(), v)
}
//nolint:revive
func bindRekorConfigOptions(flags *pflag.FlagSet, v *viper.Viper) error {
// TODO: config re-design
if err := v.BindPFlag("attest.rekor-url", flags.Lookup("rekor-url")); err != nil {
-1
View File
@@ -22,7 +22,6 @@ func (o *RootOptions) AddFlags(cmd *cobra.Command, v *viper.Viper) error {
return bindRootConfigOptions(cmd.PersistentFlags(), v)
}
//nolint:revive
func bindRootConfigOptions(flags *pflag.FlagSet, v *viper.Viper) error {
if err := v.BindPFlag("config", flags.Lookup("config")); err != nil {
return err
+1 -1
View File
@@ -11,7 +11,7 @@ type VersionOptions struct {
var _ Interface = (*VersionOptions)(nil)
func (o *VersionOptions) AddFlags(cmd *cobra.Command, _ *viper.Viper) error {
func (o *VersionOptions) AddFlags(cmd *cobra.Command, v *viper.Viper) error {
cmd.Flags().StringVarP(&o.Output, "output", "o", "text", "format to show version information (available=[text, json])")
return nil
}
+7 -144
View File
@@ -1,40 +1,26 @@
package options
import (
"bytes"
"fmt"
"io"
"os"
"path"
"strings"
"github.com/hashicorp/go-multierror"
"github.com/mitchellh/go-homedir"
"github.com/anchore/syft/internal/bus"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/formats"
"github.com/anchore/syft/syft/formats/table"
"github.com/anchore/syft/syft/formats/template"
"github.com/anchore/syft/syft/sbom"
)
var _ sbom.Writer = (*sbomMultiWriter)(nil)
var _ interface {
io.Closer
sbom.Writer
} = (*sbomStreamWriter)(nil)
// MakeSBOMWriter creates a sbom.Writer for output or returns an error. this will either return a valid writer
// makeWriter creates a sbom.Writer for output or returns an error. this will either return a valid writer
// or an error but neither both and if there is no error, sbom.Writer.Close() should be called
func MakeSBOMWriter(outputs []string, defaultFile, templateFilePath string) (sbom.Writer, error) {
outputOptions, err := parseSBOMOutputFlags(outputs, defaultFile, templateFilePath)
func MakeWriter(outputs []string, defaultFile, templateFilePath string) (sbom.Writer, error) {
outputOptions, err := parseOutputs(outputs, defaultFile, templateFilePath)
if err != nil {
return nil, err
}
writer, err := newSBOMMultiWriter(outputOptions...)
writer, err := sbom.NewWriter(outputOptions...)
if err != nil {
return nil, err
}
@@ -42,18 +28,8 @@ func MakeSBOMWriter(outputs []string, defaultFile, templateFilePath string) (sbo
return writer, nil
}
// MakeSBOMWriterForFormat creates a sbom.Writer for for the given format or returns an error.
func MakeSBOMWriterForFormat(format sbom.Format, path string) (sbom.Writer, error) {
writer, err := newSBOMMultiWriter(newSBOMWriterDescription(format, path))
if err != nil {
return nil, err
}
return writer, nil
}
// parseSBOMOutputFlags utility to parse command-line option strings and retain the existing behavior of default format and file
func parseSBOMOutputFlags(outputs []string, defaultFile, templateFilePath string) (out []sbomWriterDescription, errs error) {
// parseOptions utility to parse command-line option strings and retain the existing behavior of default format and file
func parseOutputs(outputs []string, defaultFile, templateFilePath string) (out []sbom.WriterOption, errs error) {
// always should have one option -- we generally get the default of "table", but just make sure
if len(outputs) == 0 {
outputs = append(outputs, table.ID.String())
@@ -87,120 +63,7 @@ func parseSBOMOutputFlags(outputs []string, defaultFile, templateFilePath string
format = tmpl
}
out = append(out, newSBOMWriterDescription(format, file))
out = append(out, sbom.NewWriterOption(format, file))
}
return out, errs
}
// sbomWriterDescription Format and path strings used to create sbom.Writer
type sbomWriterDescription struct {
Format sbom.Format
Path string
}
func newSBOMWriterDescription(f sbom.Format, p string) sbomWriterDescription {
expandedPath, err := homedir.Expand(p)
if err != nil {
log.Warnf("could not expand given writer output path=%q: %w", p, err)
// ignore errors
expandedPath = p
}
return sbomWriterDescription{
Format: f,
Path: expandedPath,
}
}
// sbomMultiWriter holds a list of child sbom.Writers to apply all Write and Close operations to
type sbomMultiWriter struct {
writers []sbom.Writer
}
// newSBOMMultiWriter create all report writers from input options; if a file is not specified the given defaultWriter is used
func newSBOMMultiWriter(options ...sbomWriterDescription) (_ *sbomMultiWriter, err error) {
if len(options) == 0 {
return nil, fmt.Errorf("no output options provided")
}
out := &sbomMultiWriter{}
for _, option := range options {
switch len(option.Path) {
case 0:
out.writers = append(out.writers, &sbomPublisher{
format: option.Format,
})
default:
// create any missing subdirectories
dir := path.Dir(option.Path)
if dir != "" {
s, err := os.Stat(dir)
if err != nil {
err = os.MkdirAll(dir, 0755) // maybe should be os.ModePerm ?
if err != nil {
return nil, err
}
} else if !s.IsDir() {
return nil, fmt.Errorf("output path does not contain a valid directory: %s", option.Path)
}
}
fileOut, err := os.OpenFile(option.Path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
if err != nil {
return nil, fmt.Errorf("unable to create report file: %w", err)
}
out.writers = append(out.writers, &sbomStreamWriter{
format: option.Format,
out: fileOut,
})
}
}
return out, nil
}
// Write writes the SBOM to all writers
func (m *sbomMultiWriter) Write(s sbom.SBOM) (errs error) {
for _, w := range m.writers {
err := w.Write(s)
if err != nil {
errs = multierror.Append(errs, fmt.Errorf("unable to write SBOM: %w", err))
}
}
return errs
}
// sbomStreamWriter implements sbom.Writer for a given format and io.Writer, also providing a close function for cleanup
type sbomStreamWriter struct {
format sbom.Format
out io.Writer
}
// Write the provided SBOM to the data stream
func (w *sbomStreamWriter) Write(s sbom.SBOM) error {
defer w.Close()
return w.format.Encode(w.out, s)
}
// Close any resources, such as open files
func (w *sbomStreamWriter) Close() error {
if closer, ok := w.out.(io.Closer); ok {
return closer.Close()
}
return nil
}
// sbomPublisher implements sbom.Writer that publishes results to the event bus
type sbomPublisher struct {
format sbom.Format
}
// Write the provided SBOM to the data stream
func (w *sbomPublisher) Write(s sbom.SBOM) error {
buf := &bytes.Buffer{}
if err := w.format.Encode(buf, s); err != nil {
return fmt.Errorf("unable to encode SBOM: %w", err)
}
bus.Report(buf.String())
return nil
}
+2 -201
View File
@@ -1,18 +1,12 @@
package options
import (
"io"
"path/filepath"
"strings"
"testing"
"github.com/docker/docker/pkg/homedir"
"github.com/stretchr/testify/assert"
"github.com/anchore/syft/syft/sbom"
)
func Test_MakeSBOMWriter(t *testing.T) {
func TestIsSupportedFormat(t *testing.T) {
tests := []struct {
outputs []string
wantErr assert.ErrorAssertionFunc
@@ -34,200 +28,7 @@ func Test_MakeSBOMWriter(t *testing.T) {
}
for _, tt := range tests {
_, err := MakeSBOMWriter(tt.outputs, "", "")
_, err := MakeWriter(tt.outputs, "", "")
tt.wantErr(t, err)
}
}
func dummyEncoder(io.Writer, sbom.SBOM) error {
return nil
}
func dummyFormat(name string) sbom.Format {
return sbom.NewFormat(sbom.AnyVersion, dummyEncoder, nil, nil, sbom.FormatID(name))
}
func Test_newSBOMMultiWriter(t *testing.T) {
type writerConfig struct {
format string
file string
}
tmp := t.TempDir()
testName := func(options []sbomWriterDescription, err bool) string {
var out []string
for _, opt := range options {
out = append(out, string(opt.Format.ID())+"="+opt.Path)
}
errs := ""
if err {
errs = "(err)"
}
return strings.Join(out, ", ") + errs
}
tests := []struct {
outputs []sbomWriterDescription
err bool
expected []writerConfig
}{
{
outputs: []sbomWriterDescription{},
err: true,
},
{
outputs: []sbomWriterDescription{
{
Format: dummyFormat("table"),
Path: "",
},
},
expected: []writerConfig{
{
format: "table",
},
},
},
{
outputs: []sbomWriterDescription{
{
Format: dummyFormat("json"),
},
},
expected: []writerConfig{
{
format: "json",
},
},
},
{
outputs: []sbomWriterDescription{
{
Format: dummyFormat("json"),
Path: "test-2.json",
},
},
expected: []writerConfig{
{
format: "json",
file: "test-2.json",
},
},
},
{
outputs: []sbomWriterDescription{
{
Format: dummyFormat("json"),
Path: "test-3/1.json",
},
{
Format: dummyFormat("spdx-json"),
Path: "test-3/2.json",
},
},
expected: []writerConfig{
{
format: "json",
file: "test-3/1.json",
},
{
format: "spdx-json",
file: "test-3/2.json",
},
},
},
{
outputs: []sbomWriterDescription{
{
Format: dummyFormat("text"),
},
{
Format: dummyFormat("spdx-json"),
Path: "test-4.json",
},
},
expected: []writerConfig{
{
format: "text",
},
{
format: "spdx-json",
file: "test-4.json",
},
},
},
}
for _, test := range tests {
t.Run(testName(test.outputs, test.err), func(t *testing.T) {
outputs := test.outputs
for i := range outputs {
if outputs[i].Path != "" {
outputs[i].Path = tmp + outputs[i].Path
}
}
mw, err := newSBOMMultiWriter(outputs...)
if test.err {
assert.Error(t, err)
return
} else {
assert.NoError(t, err)
}
assert.Len(t, mw.writers, len(test.expected))
for i, e := range test.expected {
switch w := mw.writers[i].(type) {
case *sbomStreamWriter:
assert.Equal(t, string(w.format.ID()), e.format)
if e.file != "" {
assert.NotNil(t, w.out)
} else {
assert.NotNil(t, w.out)
}
if e.file != "" {
assert.FileExists(t, tmp+e.file)
}
case *sbomPublisher:
assert.Equal(t, string(w.format.ID()), e.format)
default:
t.Fatalf("unknown writer type: %T", w)
}
}
})
}
}
func Test_newSBOMWriterDescription(t *testing.T) {
tests := []struct {
name string
path string
expected string
}{
{
name: "expand home dir",
path: "~/place.txt",
expected: filepath.Join(homedir.Get(), "place.txt"),
},
{
name: "passthrough other paths",
path: "/other/place.txt",
expected: "/other/place.txt",
},
{
name: "no path",
path: "",
expected: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
o := newSBOMWriterDescription(dummyFormat("table"), tt.path)
assert.Equal(t, tt.expected, o.Path)
})
}
}
+2 -5
View File
@@ -18,10 +18,8 @@ const (
{{.appName}} {{.command}} alpine:latest -o json show all possible cataloging details
{{.appName}} {{.command}} alpine:latest -o cyclonedx show a CycloneDX formatted SBOM
{{.appName}} {{.command}} alpine:latest -o cyclonedx-json show a CycloneDX JSON formatted SBOM
{{.appName}} {{.command}} alpine:latest -o spdx show a SPDX 2.3 Tag-Value formatted SBOM
{{.appName}} {{.command}} alpine:latest -o spdx@2.2 show a SPDX 2.2 Tag-Value formatted SBOM
{{.appName}} {{.command}} alpine:latest -o spdx-json show a SPDX 2.3 JSON formatted SBOM
{{.appName}} {{.command}} alpine:latest -o spdx-json@2.2 show a SPDX 2.2 JSON formatted SBOM
{{.appName}} {{.command}} alpine:latest -o spdx show a SPDX 2.2 Tag-Value formatted SBOM
{{.appName}} {{.command}} alpine:latest -o spdx-json show a SPDX 2.2 JSON formatted SBOM
{{.appName}} {{.command}} alpine:latest -vv show verbose debug information
{{.appName}} {{.command}} alpine:latest -o template -t my_format.tmpl show a SBOM formatted according to given template file
@@ -70,7 +68,6 @@ func Packages(v *viper.Viper, app *config.Application, ro *options.RootOptions,
SilenceErrors: true,
RunE: func(cmd *cobra.Command, args []string) error {
if app.CheckForAppUpdate {
// TODO: this is broke, the bus isn't available yet
checkForApplicationUpdate()
}
return packages.Run(cmd.Context(), app, args)
+29 -70
View File
@@ -7,36 +7,45 @@ import (
"github.com/wagoodman/go-partybus"
"github.com/anchore/stereoscope"
"github.com/anchore/stereoscope/pkg/image"
"github.com/anchore/syft/cmd/syft/cli/eventloop"
"github.com/anchore/syft/cmd/syft/cli/options"
"github.com/anchore/syft/cmd/syft/internal/ui"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/internal/bus"
"github.com/anchore/syft/internal/config"
"github.com/anchore/syft/internal/file"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/ui"
"github.com/anchore/syft/internal/version"
"github.com/anchore/syft/syft"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/event"
"github.com/anchore/syft/syft/formats/template"
"github.com/anchore/syft/syft/sbom"
"github.com/anchore/syft/syft/source"
)
func Run(_ context.Context, app *config.Application, args []string) error {
func Run(ctx context.Context, app *config.Application, args []string) error {
err := ValidateOutputOptions(app)
if err != nil {
return err
}
writer, err := options.MakeSBOMWriter(app.Outputs, app.File, app.OutputTemplatePath)
writer, err := options.MakeWriter(app.Outputs, app.File, app.OutputTemplatePath)
if err != nil {
return err
}
defer func() {
if err := writer.Close(); err != nil {
log.Warnf("unable to write to report destination: %w", err)
}
}()
// could be an image or a directory, with or without a scheme
userInput := args[0]
si, err := source.ParseInputWithName(userInput, app.Platform, true, app.Name)
if err != nil {
return fmt.Errorf("could not generate source input for packages command: %w", err)
}
eventBus := partybus.NewBus()
stereoscope.SetBus(eventBus)
@@ -44,7 +53,7 @@ func Run(_ context.Context, app *config.Application, args []string) error {
subscription := eventBus.Subscribe()
return eventloop.EventLoop(
execWorker(app, userInput, writer),
execWorker(app, *si, writer),
eventloop.SetupSignals(),
subscription,
stereoscope.Cleanup,
@@ -52,69 +61,20 @@ func Run(_ context.Context, app *config.Application, args []string) error {
)
}
// nolint:funlen
func execWorker(app *config.Application, userInput string, writer sbom.Writer) <-chan error {
func execWorker(app *config.Application, si source.Input, writer sbom.Writer) <-chan error {
errs := make(chan error)
go func() {
defer close(errs)
defer bus.Exit()
detection, err := source.Detect(
userInput,
source.DetectConfig{
DefaultImageSource: app.DefaultImagePullSource,
},
)
src, cleanup, err := source.New(si, app.Registry.ToOptions(), app.Exclusions)
if cleanup != nil {
defer cleanup()
}
if err != nil {
errs <- fmt.Errorf("could not deteremine source: %w", err)
errs <- fmt.Errorf("failed to construct source from user input %q: %w", si.UserInput, err)
return
}
var platform *image.Platform
if app.Platform != "" {
platform, err = image.NewPlatform(app.Platform)
if err != nil {
errs <- fmt.Errorf("invalid platform: %w", err)
return
}
}
hashers, err := file.Hashers(app.Source.File.Digests...)
if err != nil {
errs <- fmt.Errorf("invalid hash: %w", err)
return
}
src, err := detection.NewSource(
source.DetectionSourceConfig{
Alias: source.Alias{
Name: app.Source.Name,
Version: app.Source.Version,
},
RegistryOptions: app.Registry.ToOptions(),
Platform: platform,
Exclude: source.ExcludeConfig{
Paths: app.Exclusions,
},
DigestAlgorithms: hashers,
BasePath: app.BasePath,
},
)
if err != nil {
errs <- fmt.Errorf("failed to construct source from user input %q: %w", userInput, err)
return
}
defer func() {
if src != nil {
if err := src.Close(); err != nil {
log.Tracef("unable to close source: %+v", err)
}
}
}()
s, err := GenerateSBOM(src, errs, app)
if err != nil {
errs <- err
@@ -122,26 +82,25 @@ func execWorker(app *config.Application, userInput string, writer sbom.Writer) <
}
if s == nil {
errs <- fmt.Errorf("no SBOM produced for %q", userInput)
return
errs <- fmt.Errorf("no SBOM produced for %q", si.UserInput)
}
if err := writer.Write(*s); err != nil {
errs <- fmt.Errorf("failed to write SBOM: %w", err)
return
}
bus.Publish(partybus.Event{
Type: event.Exit,
Value: func() error { return writer.Write(*s) },
})
}()
return errs
}
func GenerateSBOM(src source.Source, errs chan error, app *config.Application) (*sbom.SBOM, error) {
func GenerateSBOM(src *source.Source, errs chan error, app *config.Application) (*sbom.SBOM, error) {
tasks, err := eventloop.Tasks(app)
if err != nil {
return nil, err
}
s := sbom.SBOM{
Source: src.Describe(),
Source: src.Metadata,
Descriptor: sbom.Descriptor{
Name: internal.ApplicationName,
Version: version.FromBuild().Version,
@@ -154,7 +113,7 @@ func GenerateSBOM(src source.Source, errs chan error, app *config.Application) (
return &s, nil
}
func buildRelationships(s *sbom.SBOM, src source.Source, tasks []eventloop.Task, errs chan error) {
func buildRelationships(s *sbom.SBOM, src *source.Source, tasks []eventloop.Task, errs chan error) {
var relationships []<-chan artifact.Relationship
for _, task := range tasks {
c := make(chan artifact.Relationship)
-1
View File
@@ -41,7 +41,6 @@ func PowerUser(v *viper.Viper, app *config.Application, ro *options.RootOptions)
RunE: func(cmd *cobra.Command, args []string) error {
if app.CheckForAppUpdate {
checkForApplicationUpdate()
// TODO: this is broke, the bus isn't available yet
}
return poweruser.Run(cmd.Context(), app, args)
},
+27 -52
View File
@@ -9,17 +9,18 @@ import (
"github.com/wagoodman/go-partybus"
"github.com/anchore/stereoscope"
"github.com/anchore/stereoscope/pkg/image"
"github.com/anchore/syft/cmd/syft/cli/eventloop"
"github.com/anchore/syft/cmd/syft/cli/options"
"github.com/anchore/syft/cmd/syft/cli/packages"
"github.com/anchore/syft/cmd/syft/internal/ui"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/internal/bus"
"github.com/anchore/syft/internal/config"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/ui"
"github.com/anchore/syft/internal/version"
"github.com/anchore/syft/syft"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/event"
"github.com/anchore/syft/syft/formats/syftjson"
"github.com/anchore/syft/syft/sbom"
"github.com/anchore/syft/syft/source"
@@ -27,17 +28,29 @@ import (
func Run(_ context.Context, app *config.Application, args []string) error {
f := syftjson.Format()
writer, err := options.MakeSBOMWriterForFormat(f, app.File)
writer, err := sbom.NewWriter(sbom.WriterOption{
Format: f,
Path: app.File,
})
if err != nil {
return err
}
defer func() {
if err := writer.Close(); err != nil {
log.Warnf("unable to write to report destination: %+v", err)
}
// inform user at end of run that command will be removed
deprecated := color.Style{color.Red, color.OpBold}.Sprint("DEPRECATED: This command will be removed in v1.0.0")
fmt.Fprintln(os.Stderr, deprecated)
}()
userInput := args[0]
si, err := source.ParseInputWithName(userInput, app.Platform, true, app.Name)
if err != nil {
return fmt.Errorf("could not generate source input for packages command: %w", err)
}
eventBus := partybus.NewBus()
stereoscope.SetBus(eventBus)
@@ -45,7 +58,7 @@ func Run(_ context.Context, app *config.Application, args []string) error {
subscription := eventBus.Subscribe()
return eventloop.EventLoop(
execWorker(app, userInput, writer),
execWorker(app, *si, writer),
eventloop.SetupSignals(),
subscription,
stereoscope.Cleanup,
@@ -53,12 +66,10 @@ func Run(_ context.Context, app *config.Application, args []string) error {
)
}
//nolint:funlen
func execWorker(app *config.Application, userInput string, writer sbom.Writer) <-chan error {
func execWorker(app *config.Application, si source.Input, writer sbom.Writer) <-chan error {
errs := make(chan error)
go func() {
defer close(errs)
defer bus.Exit()
app.Secrets.Cataloger.Enabled = true
app.FileMetadata.Cataloger.Enabled = true
@@ -70,53 +81,17 @@ func execWorker(app *config.Application, userInput string, writer sbom.Writer) <
return
}
detection, err := source.Detect(
userInput,
source.DetectConfig{
DefaultImageSource: app.DefaultImagePullSource,
},
)
src, cleanup, err := source.New(si, app.Registry.ToOptions(), app.Exclusions)
if err != nil {
errs <- fmt.Errorf("could not deteremine source: %w", err)
errs <- err
return
}
var platform *image.Platform
if app.Platform != "" {
platform, err = image.NewPlatform(app.Platform)
if err != nil {
errs <- fmt.Errorf("invalid platform: %w", err)
return
}
}
src, err := detection.NewSource(
source.DetectionSourceConfig{
Alias: source.Alias{
Name: app.Source.Name,
Version: app.Source.Version,
},
RegistryOptions: app.Registry.ToOptions(),
Platform: platform,
Exclude: source.ExcludeConfig{
Paths: app.Exclusions,
},
DigestAlgorithms: nil,
BasePath: app.BasePath,
},
)
if src != nil {
defer src.Close()
}
if err != nil {
errs <- fmt.Errorf("failed to construct source from user input %q: %w", userInput, err)
return
if cleanup != nil {
defer cleanup()
}
s := sbom.SBOM{
Source: src.Describe(),
Source: src.Metadata,
Descriptor: sbom.Descriptor{
Name: internal.ApplicationName,
Version: version.FromBuild().Version,
@@ -134,10 +109,10 @@ func execWorker(app *config.Application, userInput string, writer sbom.Writer) <
s.Relationships = append(s.Relationships, packages.MergeRelationships(relationships...)...)
if err := writer.Write(s); err != nil {
errs <- fmt.Errorf("failed to write sbom: %w", err)
return
}
bus.Publish(partybus.Event{
Type: event.Exit,
Value: func() error { return writer.Write(s) },
})
}()
return errs
@@ -1,19 +0,0 @@
[TestHandler_handleAttestationStarted/attesting_in_progress/task_line - 1]
⠋ Creating a thing running a thing
---
[TestHandler_handleAttestationStarted/attesting_in_progress/log - 1]
░░ contents
░░ of
░░ stuff!
---
[TestHandler_handleAttestationStarted/attesting_complete/task_line - 1]
✔ Created a thing running a thing
---
[TestHandler_handleAttestationStarted/attesting_complete/log - 1]
---
@@ -1,16 +0,0 @@
[TestHandler_handleCatalogerTaskStarted/cataloging_task_in_progress - 1]
some task title [some value]
---
[TestHandler_handleCatalogerTaskStarted/cataloging_sub_task_in_progress - 1]
└── some task title [some value]
---
[TestHandler_handleCatalogerTaskStarted/cataloging_sub_task_complete - 1]
✔ └── some task done [some value]
---
[TestHandler_handleCatalogerTaskStarted/cataloging_sub_task_complete_with_removal - 1]
---
@@ -1,8 +0,0 @@
[TestHandler_handleFetchImage/fetch_image_in_progress - 1]
⠋ Loading image ━━━━━━━━━━━━━━━━━━━━ [current] the-image
---
[TestHandler_handleFetchImage/fetch_image_complete - 1]
✔ Loaded image the-image
---
@@ -1,8 +0,0 @@
[TestHandler_handleFileDigestsCatalogerStarted/cataloging_in_progress - 1]
⠋ Cataloging file digests ━━━━━━━━━━━━━━━━━━━━ [current]
---
[TestHandler_handleFileDigestsCatalogerStarted/cataloging_complete - 1]
✔ Cataloged file digests
---
@@ -1,8 +0,0 @@
[TestHandler_handleFileIndexingStarted/cataloging_in_progress - 1]
⠋ Indexing file system ━━━━━━━━━━━━━━━━━━━━ [current] /some/path
---
[TestHandler_handleFileIndexingStarted/cataloging_complete - 1]
✔ Indexed file system /some/path
---
@@ -1,8 +0,0 @@
[TestHandler_handleFileMetadataCatalogerStarted/cataloging_in_progress - 1]
⠋ Cataloging file metadata ━━━━━━━━━━━━━━━━━━━━ [current]
---
[TestHandler_handleFileMetadataCatalogerStarted/cataloging_complete - 1]
✔ Cataloged file metadata
---
@@ -1,16 +0,0 @@
[TestHandler_handlePackageCatalogerStarted/cataloging_in_progress - 1]
⠋ Cataloging packages [50 packages]
---
[TestHandler_handlePackageCatalogerStarted/cataloging_only_files_complete - 1]
⠋ Cataloging packages [50 packages]
---
[TestHandler_handlePackageCatalogerStarted/cataloging_only_packages_complete - 1]
⠋ Cataloging packages [100 packages]
---
[TestHandler_handlePackageCatalogerStarted/cataloging_complete - 1]
✔ Cataloged packages [100 packages]
---
@@ -1,12 +0,0 @@
[Test_dockerPullStatusFormatter_Render/pulling - 1]
3 Layers▕▅▃ ▏[12 B / 30 B]
---
[Test_dockerPullStatusFormatter_Render/download_complete - 1]
3 Layers▕█▃ ▏[30 B] Extracting...
---
[Test_dockerPullStatusFormatter_Render/complete - 1]
3 Layers▕███▏[30 B] Extracting...
---
@@ -1,8 +0,0 @@
[TestHandler_handleReadImage/read_image_in_progress - 1]
⠋ Parsing image ━━━━━━━━━━━━━━━━━━━━ id
---
[TestHandler_handleReadImage/read_image_complete - 1]
✔ Parsed image id
---
@@ -1,8 +0,0 @@
[TestHandler_handleSecretsCatalogerStarted/cataloging_in_progress - 1]
⠋ Cataloging secrets ━━━━━━━━━━━━━━━━━━━━ [64 secrets]
---
[TestHandler_handleSecretsCatalogerStarted/cataloging_complete - 1]
✔ Cataloged secrets [64 secrets]
---
-255
View File
@@ -1,255 +0,0 @@
package ui
import (
"bufio"
"fmt"
"io"
"strings"
"sync"
"time"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/google/uuid"
"github.com/wagoodman/go-partybus"
"github.com/wagoodman/go-progress"
"github.com/zyedidia/generic/queue"
"github.com/anchore/bubbly/bubbles/taskprogress"
"github.com/anchore/syft/internal/log"
syftEventParsers "github.com/anchore/syft/syft/event/parsers"
)
var (
_ tea.Model = (*attestLogFrame)(nil)
)
type attestLogFrame struct {
reader *backgroundLineReader
prog progress.Progressable
lines []string
completed bool
failed bool
windowSize tea.WindowSizeMsg
id uint32
sequence int
updateDuration time.Duration
borderStype lipgloss.Style
}
// attestLogFrameTickMsg indicates that the timer has ticked and we should render a frame.
type attestLogFrameTickMsg struct {
Time time.Time
Sequence int
ID uint32
}
type backgroundLineReader struct {
limit int
lines *queue.Queue[string]
lock *sync.RWMutex
// This is added specifically for tests to assert when the background reader is done.
// The main UI uses the global ui wait group from the handler to otherwise block
// Shared concerns among multiple model made it difficult to test using the global wait group
// so this is added to allow tests to assert when the background reader is done.
running *sync.WaitGroup
}
func (m *Handler) handleAttestationStarted(e partybus.Event) []tea.Model {
reader, prog, taskInfo, err := syftEventParsers.ParseAttestationStartedEvent(e)
if err != nil {
log.WithFields("error", err).Warn("unable to parse event")
return nil
}
stage := progress.Stage{}
tsk := m.newTaskProgress(
taskprogress.Title{
Default: taskInfo.Title.Default,
Running: taskInfo.Title.WhileRunning,
Success: taskInfo.Title.OnSuccess,
},
taskprogress.WithStagedProgressable(
struct {
progress.Progressable
progress.Stager
}{
Progressable: prog,
Stager: &stage,
},
),
)
tsk.HideStageOnSuccess = false
if taskInfo.Context != "" {
tsk.Context = []string{taskInfo.Context}
}
borderStyle := tsk.HintStyle
return []tea.Model{
tsk,
newLogFrame(newBackgroundLineReader(m.Running, reader, &stage), prog, borderStyle),
}
}
func newLogFrame(reader *backgroundLineReader, prog progress.Progressable, borderStyle lipgloss.Style) attestLogFrame {
return attestLogFrame{
reader: reader,
prog: prog,
id: uuid.Must(uuid.NewUUID()).ID(),
updateDuration: 250 * time.Millisecond,
borderStype: borderStyle,
}
}
func newBackgroundLineReader(wg *sync.WaitGroup, reader io.Reader, stage *progress.Stage) *backgroundLineReader {
r := &backgroundLineReader{
limit: 7,
lock: &sync.RWMutex{},
lines: queue.New[string](),
running: &sync.WaitGroup{},
}
// tracks the background reader for the global handler wait group
wg.Add(1)
// tracks the background reader for the local wait group (used in tests to decouple from the global handler wait group)
r.running.Add(1)
go func() {
r.read(reader, stage)
wg.Done()
r.running.Done()
}()
return r
}
func (l *backgroundLineReader) read(reader io.Reader, stage *progress.Stage) {
s := bufio.NewScanner(reader)
for s.Scan() {
l.lock.Lock()
text := s.Text()
l.lines.Enqueue(text)
if strings.Contains(text, "tlog entry created with index") {
fields := strings.SplitN(text, ":", 2)
present := text
if len(fields) == 2 {
present = fmt.Sprintf("transparency log index: %s", fields[1])
}
stage.Current = present
} else if strings.Contains(text, "WARNING: skipping transparency log upload") {
stage.Current = "transparency log upload skipped"
}
// only show the last X lines of the shell output
for l.lines.Len() > l.limit {
l.lines.Dequeue()
}
l.lock.Unlock()
}
}
func (l backgroundLineReader) Lines() []string {
l.lock.RLock()
defer l.lock.RUnlock()
var lines []string
l.lines.Each(func(line string) {
lines = append(lines, line)
})
return lines
}
func (l attestLogFrame) Init() tea.Cmd {
// this is the periodic update of state information
return func() tea.Msg {
return attestLogFrameTickMsg{
// The time at which the tick occurred.
Time: time.Now(),
// The ID of the log frame that this message belongs to. This can be
// helpful when routing messages, however bear in mind that log frames
// will ignore messages that don't contain ID by default.
ID: l.id,
Sequence: l.sequence,
}
}
}
func (l attestLogFrame) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.WindowSizeMsg:
l.windowSize = msg
return l, nil
case attestLogFrameTickMsg:
l.lines = l.reader.Lines()
l.completed = progress.IsCompleted(l.prog)
err := l.prog.Error()
l.failed = err != nil && !progress.IsErrCompleted(err)
tickCmd := l.handleTick(msg)
return l, tickCmd
}
return l, nil
}
func (l attestLogFrame) View() string {
if l.completed && !l.failed {
return ""
}
sb := strings.Builder{}
for _, line := range l.lines {
sb.WriteString(fmt.Sprintf(" %s %s\n", l.borderStype.Render("░░"), line))
}
return sb.String()
}
func (l attestLogFrame) queueNextTick() tea.Cmd {
return tea.Tick(l.updateDuration, func(t time.Time) tea.Msg {
return attestLogFrameTickMsg{
Time: t,
ID: l.id,
Sequence: l.sequence,
}
})
}
func (l *attestLogFrame) handleTick(msg attestLogFrameTickMsg) tea.Cmd {
// If an ID is set, and the ID doesn't belong to this log frame, reject the message.
if msg.ID > 0 && msg.ID != l.id {
return nil
}
// If a sequence is set, and it's not the one we expect, reject the message.
// This prevents the log frame from receiving too many messages and
// thus updating too frequently.
if msg.Sequence > 0 && msg.Sequence != l.sequence {
return nil
}
l.sequence++
// note: even if the log is completed we should still respond to stage changes and window size events
return l.queueNextTick()
}
-133
View File
@@ -1,133 +0,0 @@
package ui
import (
"strings"
"testing"
"time"
tea "github.com/charmbracelet/bubbletea"
"github.com/gkampitakis/go-snaps/snaps"
"github.com/stretchr/testify/require"
"github.com/wagoodman/go-partybus"
"github.com/wagoodman/go-progress"
"github.com/anchore/bubbly/bubbles/taskprogress"
syftEvent "github.com/anchore/syft/syft/event"
"github.com/anchore/syft/syft/event/monitor"
)
func TestHandler_handleAttestationStarted(t *testing.T) {
tests := []struct {
name string
eventFn func(*testing.T) partybus.Event
iterations int
}{
{
name: "attesting in progress",
// note: this model depends on a background reader. Multiple iterations ensures that the
// reader has time to at least start and process the test fixture before the runModel
// test harness completes (which is a fake event loop anyway).
iterations: 1,
eventFn: func(t *testing.T) partybus.Event {
reader := strings.NewReader("contents\nof\nstuff!")
src := monitor.GenericTask{
Title: monitor.Title{
Default: "Create a thing",
WhileRunning: "Creating a thing",
OnSuccess: "Created a thing",
},
Context: "running a thing",
}
mon := progress.NewManual(-1)
mon.Set(50)
value := &monitor.ShellProgress{
Reader: reader,
Progressable: mon,
}
return partybus.Event{
Type: syftEvent.AttestationStarted,
Source: src,
Value: value,
}
},
},
{
name: "attesting complete",
// note: this model depends on a background reader. Multiple iterations ensures that the
// reader has time to at least start and process the test fixture before the runModel
// test harness completes (which is a fake event loop anyway).
iterations: 1,
eventFn: func(t *testing.T) partybus.Event {
reader := strings.NewReader("contents\nof\nstuff!")
src := monitor.GenericTask{
Title: monitor.Title{
Default: "Create a thing",
WhileRunning: "Creating a thing",
OnSuccess: "Created a thing",
},
Context: "running a thing",
}
mon := progress.NewManual(-1)
mon.Set(50)
mon.SetCompleted()
value := &monitor.ShellProgress{
Reader: reader,
Progressable: mon,
}
return partybus.Event{
Type: syftEvent.AttestationStarted,
Source: src,
Value: value,
}
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
event := tt.eventFn(t)
handler := New(DefaultHandlerConfig())
handler.WindowSize = tea.WindowSizeMsg{
Width: 100,
Height: 80,
}
models := handler.Handle(event)
require.Len(t, models, 2)
t.Run("task line", func(t *testing.T) {
tsk, ok := models[0].(taskprogress.Model)
require.True(t, ok)
got := runModel(t, tsk, tt.iterations, taskprogress.TickMsg{
Time: time.Now(),
Sequence: tsk.Sequence(),
ID: tsk.ID(),
})
t.Log(got)
snaps.MatchSnapshot(t, got)
})
t.Run("log", func(t *testing.T) {
log, ok := models[1].(attestLogFrame)
require.True(t, ok)
got := runModel(t, log, tt.iterations, attestLogFrameTickMsg{
Time: time.Now(),
Sequence: log.sequence,
ID: log.id,
}, log.reader.running)
t.Log(got)
snaps.MatchSnapshot(t, got)
})
})
}
}
-72
View File
@@ -1,72 +0,0 @@
package ui
import (
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/wagoodman/go-partybus"
"github.com/wagoodman/go-progress"
"github.com/anchore/bubbly/bubbles/taskprogress"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/event/monitor"
syftEventParsers "github.com/anchore/syft/syft/event/parsers"
)
var _ progress.Stager = (*catalogerTaskStageAdapter)(nil)
type catalogerTaskStageAdapter struct {
mon *monitor.CatalogerTask
}
func newCatalogerTaskStageAdapter(mon *monitor.CatalogerTask) *catalogerTaskStageAdapter {
return &catalogerTaskStageAdapter{
mon: mon,
}
}
func (c catalogerTaskStageAdapter) Stage() string {
return c.mon.GetValue()
}
func (m *Handler) handleCatalogerTaskStarted(e partybus.Event) []tea.Model {
mon, err := syftEventParsers.ParseCatalogerTaskStarted(e)
if err != nil {
log.WithFields("error", err).Warn("unable to parse event")
return nil
}
var prefix string
if mon.SubStatus {
// TODO: support list of sub-statuses, not just a single leaf
prefix = "└── "
}
tsk := m.newTaskProgress(
taskprogress.Title{
// TODO: prefix should not be part of the title, but instead a separate field that is aware of the tree structure
Default: prefix + mon.Title,
Running: prefix + mon.Title,
Success: prefix + mon.TitleOnCompletion,
},
taskprogress.WithStagedProgressable(
struct {
progress.Stager
progress.Progressable
}{
Progressable: mon.GetMonitor(),
Stager: newCatalogerTaskStageAdapter(mon),
},
),
)
// TODO: this isn't ideal since the model stays around after it is no longer needed, but it works for now
tsk.HideOnSuccess = mon.RemoveOnCompletion
tsk.HideStageOnSuccess = false
tsk.HideProgressOnSuccess = false
tsk.TitleStyle = lipgloss.NewStyle()
// TODO: this is a hack to get the spinner to not show up, but ideally the component would support making the spinner optional
tsk.Spinner.Spinner.Frames = []string{" "}
return []tea.Model{tsk}
}
@@ -1,123 +0,0 @@
package ui
import (
"testing"
"time"
tea "github.com/charmbracelet/bubbletea"
"github.com/gkampitakis/go-snaps/snaps"
"github.com/stretchr/testify/require"
"github.com/wagoodman/go-partybus"
"github.com/anchore/bubbly/bubbles/taskprogress"
syftEvent "github.com/anchore/syft/syft/event"
"github.com/anchore/syft/syft/event/monitor"
)
func TestHandler_handleCatalogerTaskStarted(t *testing.T) {
tests := []struct {
name string
eventFn func(*testing.T) partybus.Event
iterations int
}{
{
name: "cataloging task in progress",
eventFn: func(t *testing.T) partybus.Event {
src := &monitor.CatalogerTask{
SubStatus: false,
RemoveOnCompletion: false,
Title: "some task title",
TitleOnCompletion: "some task done",
}
src.SetValue("some value")
return partybus.Event{
Type: syftEvent.CatalogerTaskStarted,
Source: src,
}
},
},
{
name: "cataloging sub task in progress",
eventFn: func(t *testing.T) partybus.Event {
src := &monitor.CatalogerTask{
SubStatus: true,
RemoveOnCompletion: false,
Title: "some task title",
TitleOnCompletion: "some task done",
}
src.SetValue("some value")
return partybus.Event{
Type: syftEvent.CatalogerTaskStarted,
Source: src,
}
},
},
{
name: "cataloging sub task complete",
eventFn: func(t *testing.T) partybus.Event {
src := &monitor.CatalogerTask{
SubStatus: true,
RemoveOnCompletion: false,
Title: "some task title",
TitleOnCompletion: "some task done",
}
src.SetValue("some value")
src.SetCompleted()
return partybus.Event{
Type: syftEvent.CatalogerTaskStarted,
Source: src,
}
},
},
{
name: "cataloging sub task complete with removal",
eventFn: func(t *testing.T) partybus.Event {
src := &monitor.CatalogerTask{
SubStatus: true,
RemoveOnCompletion: true,
Title: "some task title",
TitleOnCompletion: "some task done",
}
src.SetValue("some value")
src.SetCompleted()
return partybus.Event{
Type: syftEvent.CatalogerTaskStarted,
Source: src,
}
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
event := tt.eventFn(t)
handler := New(DefaultHandlerConfig())
handler.WindowSize = tea.WindowSizeMsg{
Width: 100,
Height: 80,
}
models := handler.Handle(event)
require.Len(t, models, 1)
model := models[0]
tsk, ok := model.(taskprogress.Model)
require.True(t, ok)
got := runModel(t, tsk, tt.iterations, taskprogress.TickMsg{
Time: time.Now(),
Sequence: tsk.Sequence(),
ID: tsk.ID(),
})
t.Log(got)
snaps.MatchSnapshot(t, got)
})
}
}
-32
View File
@@ -1,32 +0,0 @@
package ui
import (
tea "github.com/charmbracelet/bubbletea"
"github.com/wagoodman/go-partybus"
"github.com/anchore/bubbly/bubbles/taskprogress"
stereoEventParsers "github.com/anchore/stereoscope/pkg/event/parsers"
"github.com/anchore/syft/internal/log"
)
func (m *Handler) handleFetchImage(e partybus.Event) []tea.Model {
imgName, prog, err := stereoEventParsers.ParseFetchImage(e)
if err != nil {
log.WithFields("error", err).Warn("unable to parse event")
return nil
}
tsk := m.newTaskProgress(
taskprogress.Title{
Default: "Load image",
Running: "Loading image",
Success: "Loaded image",
},
taskprogress.WithStagedProgressable(prog),
)
if imgName != "" {
tsk.Context = []string{imgName}
}
return []tea.Model{tsk}
}
@@ -1,99 +0,0 @@
package ui
import (
"testing"
"time"
tea "github.com/charmbracelet/bubbletea"
"github.com/gkampitakis/go-snaps/snaps"
"github.com/stretchr/testify/require"
"github.com/wagoodman/go-partybus"
"github.com/wagoodman/go-progress"
"github.com/anchore/bubbly/bubbles/taskprogress"
stereoscopeEvent "github.com/anchore/stereoscope/pkg/event"
)
func TestHandler_handleFetchImage(t *testing.T) {
tests := []struct {
name string
eventFn func(*testing.T) partybus.Event
iterations int
}{
{
name: "fetch image in progress",
eventFn: func(t *testing.T) partybus.Event {
prog := &progress.Manual{}
prog.SetTotal(100)
prog.Set(50)
mon := struct {
progress.Progressable
progress.Stager
}{
Progressable: prog,
Stager: &progress.Stage{
Current: "current",
},
}
return partybus.Event{
Type: stereoscopeEvent.FetchImage,
Source: "the-image",
Value: mon,
}
},
},
{
name: "fetch image complete",
eventFn: func(t *testing.T) partybus.Event {
prog := &progress.Manual{}
prog.SetTotal(100)
prog.Set(100)
prog.SetCompleted()
mon := struct {
progress.Progressable
progress.Stager
}{
Progressable: prog,
Stager: &progress.Stage{
Current: "current",
},
}
return partybus.Event{
Type: stereoscopeEvent.FetchImage,
Source: "the-image",
Value: mon,
}
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
event := tt.eventFn(t)
handler := New(DefaultHandlerConfig())
handler.WindowSize = tea.WindowSizeMsg{
Width: 100,
Height: 80,
}
models := handler.Handle(event)
require.Len(t, models, 1)
model := models[0]
tsk, ok := model.(taskprogress.Model)
require.True(t, ok)
got := runModel(t, tsk, tt.iterations, taskprogress.TickMsg{
Time: time.Now(),
Sequence: tsk.Sequence(),
ID: tsk.ID(),
})
t.Log(got)
snaps.MatchSnapshot(t, got)
})
}
}
@@ -1,28 +0,0 @@
package ui
import (
tea "github.com/charmbracelet/bubbletea"
"github.com/wagoodman/go-partybus"
"github.com/anchore/bubbly/bubbles/taskprogress"
"github.com/anchore/syft/internal/log"
syftEventParsers "github.com/anchore/syft/syft/event/parsers"
)
func (m *Handler) handleFileDigestsCatalogerStarted(e partybus.Event) []tea.Model {
prog, err := syftEventParsers.ParseFileDigestsCatalogingStarted(e)
if err != nil {
log.WithFields("error", err).Warn("unable to parse event")
return nil
}
tsk := m.newTaskProgress(
taskprogress.Title{
Default: "Catalog file digests",
Running: "Cataloging file digests",
Success: "Cataloged file digests",
}, taskprogress.WithStagedProgressable(prog),
)
return []tea.Model{tsk}
}
@@ -1,97 +0,0 @@
package ui
import (
"testing"
"time"
tea "github.com/charmbracelet/bubbletea"
"github.com/gkampitakis/go-snaps/snaps"
"github.com/stretchr/testify/require"
"github.com/wagoodman/go-partybus"
"github.com/wagoodman/go-progress"
"github.com/anchore/bubbly/bubbles/taskprogress"
syftEvent "github.com/anchore/syft/syft/event"
)
func TestHandler_handleFileDigestsCatalogerStarted(t *testing.T) {
tests := []struct {
name string
eventFn func(*testing.T) partybus.Event
iterations int
}{
{
name: "cataloging in progress",
eventFn: func(t *testing.T) partybus.Event {
prog := &progress.Manual{}
prog.SetTotal(100)
prog.Set(50)
mon := struct {
progress.Progressable
progress.Stager
}{
Progressable: prog,
Stager: &progress.Stage{
Current: "current",
},
}
return partybus.Event{
Type: syftEvent.FileDigestsCatalogerStarted,
Value: mon,
}
},
},
{
name: "cataloging complete",
eventFn: func(t *testing.T) partybus.Event {
prog := &progress.Manual{}
prog.SetTotal(100)
prog.Set(100)
prog.SetCompleted()
mon := struct {
progress.Progressable
progress.Stager
}{
Progressable: prog,
Stager: &progress.Stage{
Current: "current",
},
}
return partybus.Event{
Type: syftEvent.FileDigestsCatalogerStarted,
Value: mon,
}
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
event := tt.eventFn(t)
handler := New(DefaultHandlerConfig())
handler.WindowSize = tea.WindowSizeMsg{
Width: 100,
Height: 80,
}
models := handler.Handle(event)
require.Len(t, models, 1)
model := models[0]
tsk, ok := model.(taskprogress.Model)
require.True(t, ok)
got := runModel(t, tsk, tt.iterations, taskprogress.TickMsg{
Time: time.Now(),
Sequence: tsk.Sequence(),
ID: tsk.ID(),
})
t.Log(got)
snaps.MatchSnapshot(t, got)
})
}
}
-31
View File
@@ -1,31 +0,0 @@
package ui
import (
tea "github.com/charmbracelet/bubbletea"
"github.com/wagoodman/go-partybus"
"github.com/anchore/bubbly/bubbles/taskprogress"
"github.com/anchore/syft/internal/log"
syftEventParsers "github.com/anchore/syft/syft/event/parsers"
)
func (m *Handler) handleFileIndexingStarted(e partybus.Event) []tea.Model {
path, prog, err := syftEventParsers.ParseFileIndexingStarted(e)
if err != nil {
log.WithFields("error", err).Warn("unable to parse event")
return nil
}
tsk := m.newTaskProgress(
taskprogress.Title{
Default: "Index files system",
Running: "Indexing file system",
Success: "Indexed file system",
},
taskprogress.WithStagedProgressable(prog),
)
tsk.Context = []string{path}
return []tea.Model{tsk}
}
@@ -1,99 +0,0 @@
package ui
import (
"testing"
"time"
tea "github.com/charmbracelet/bubbletea"
"github.com/gkampitakis/go-snaps/snaps"
"github.com/stretchr/testify/require"
"github.com/wagoodman/go-partybus"
"github.com/wagoodman/go-progress"
"github.com/anchore/bubbly/bubbles/taskprogress"
syftEvent "github.com/anchore/syft/syft/event"
)
func TestHandler_handleFileIndexingStarted(t *testing.T) {
tests := []struct {
name string
eventFn func(*testing.T) partybus.Event
iterations int
}{
{
name: "cataloging in progress",
eventFn: func(t *testing.T) partybus.Event {
prog := &progress.Manual{}
prog.SetTotal(100)
prog.Set(50)
mon := struct {
progress.Progressable
progress.Stager
}{
Progressable: prog,
Stager: &progress.Stage{
Current: "current",
},
}
return partybus.Event{
Type: syftEvent.FileIndexingStarted,
Source: "/some/path",
Value: mon,
}
},
},
{
name: "cataloging complete",
eventFn: func(t *testing.T) partybus.Event {
prog := &progress.Manual{}
prog.SetTotal(100)
prog.Set(100)
prog.SetCompleted()
mon := struct {
progress.Progressable
progress.Stager
}{
Progressable: prog,
Stager: &progress.Stage{
Current: "current",
},
}
return partybus.Event{
Type: syftEvent.FileIndexingStarted,
Source: "/some/path",
Value: mon,
}
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
event := tt.eventFn(t)
handler := New(DefaultHandlerConfig())
handler.WindowSize = tea.WindowSizeMsg{
Width: 100,
Height: 80,
}
models := handler.Handle(event)
require.Len(t, models, 1)
model := models[0]
tsk, ok := model.(taskprogress.Model)
require.True(t, ok)
got := runModel(t, tsk, tt.iterations, taskprogress.TickMsg{
Time: time.Now(),
Sequence: tsk.Sequence(),
ID: tsk.ID(),
})
t.Log(got)
snaps.MatchSnapshot(t, got)
})
}
}
@@ -1,29 +0,0 @@
package ui
import (
tea "github.com/charmbracelet/bubbletea"
"github.com/wagoodman/go-partybus"
"github.com/anchore/bubbly/bubbles/taskprogress"
"github.com/anchore/syft/internal/log"
syftEventParsers "github.com/anchore/syft/syft/event/parsers"
)
func (m *Handler) handleFileMetadataCatalogerStarted(e partybus.Event) []tea.Model {
prog, err := syftEventParsers.ParseFileMetadataCatalogingStarted(e)
if err != nil {
log.WithFields("error", err).Warn("unable to parse event")
return nil
}
tsk := m.newTaskProgress(
taskprogress.Title{
Default: "Catalog file metadata",
Running: "Cataloging file metadata",
Success: "Cataloged file metadata",
},
taskprogress.WithStagedProgressable(prog),
)
return []tea.Model{tsk}
}
@@ -1,97 +0,0 @@
package ui
import (
"testing"
"time"
tea "github.com/charmbracelet/bubbletea"
"github.com/gkampitakis/go-snaps/snaps"
"github.com/stretchr/testify/require"
"github.com/wagoodman/go-partybus"
"github.com/wagoodman/go-progress"
"github.com/anchore/bubbly/bubbles/taskprogress"
syftEvent "github.com/anchore/syft/syft/event"
)
func TestHandler_handleFileMetadataCatalogerStarted(t *testing.T) {
tests := []struct {
name string
eventFn func(*testing.T) partybus.Event
iterations int
}{
{
name: "cataloging in progress",
eventFn: func(t *testing.T) partybus.Event {
prog := &progress.Manual{}
prog.SetTotal(100)
prog.Set(50)
mon := struct {
progress.Progressable
progress.Stager
}{
Progressable: prog,
Stager: &progress.Stage{
Current: "current",
},
}
return partybus.Event{
Type: syftEvent.FileMetadataCatalogerStarted,
Value: mon,
}
},
},
{
name: "cataloging complete",
eventFn: func(t *testing.T) partybus.Event {
prog := &progress.Manual{}
prog.SetTotal(100)
prog.Set(100)
prog.SetCompleted()
mon := struct {
progress.Progressable
progress.Stager
}{
Progressable: prog,
Stager: &progress.Stage{
Current: "current",
},
}
return partybus.Event{
Type: syftEvent.FileMetadataCatalogerStarted,
Value: mon,
}
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
event := tt.eventFn(t)
handler := New(DefaultHandlerConfig())
handler.WindowSize = tea.WindowSizeMsg{
Width: 100,
Height: 80,
}
models := handler.Handle(event)
require.Len(t, models, 1)
model := models[0]
tsk, ok := model.(taskprogress.Model)
require.True(t, ok)
got := runModel(t, tsk, tt.iterations, taskprogress.TickMsg{
Time: time.Now(),
Sequence: tsk.Sequence(),
ID: tsk.ID(),
})
t.Log(got)
snaps.MatchSnapshot(t, got)
})
}
}
@@ -1,87 +0,0 @@
package ui
import (
"fmt"
tea "github.com/charmbracelet/bubbletea"
"github.com/wagoodman/go-partybus"
"github.com/wagoodman/go-progress"
"github.com/anchore/bubbly/bubbles/taskprogress"
"github.com/anchore/syft/internal/log"
syftEventParsers "github.com/anchore/syft/syft/event/parsers"
"github.com/anchore/syft/syft/pkg/cataloger"
)
var _ progress.StagedProgressable = (*packageCatalogerProgressAdapter)(nil)
type packageCatalogerProgressAdapter struct {
monitor *cataloger.Monitor
monitors []progress.Monitorable
}
func newPackageCatalogerProgressAdapter(monitor *cataloger.Monitor) packageCatalogerProgressAdapter {
return packageCatalogerProgressAdapter{
monitor: monitor,
monitors: []progress.Monitorable{
monitor.FilesProcessed,
monitor.PackagesDiscovered,
},
}
}
func (p packageCatalogerProgressAdapter) Stage() string {
return fmt.Sprintf("%d packages", p.monitor.PackagesDiscovered.Current())
}
func (p packageCatalogerProgressAdapter) Current() int64 {
return p.monitor.PackagesDiscovered.Current()
}
func (p packageCatalogerProgressAdapter) Error() error {
completedMonitors := 0
for _, monitor := range p.monitors {
err := monitor.Error()
if err == nil {
continue
}
if progress.IsErrCompleted(err) {
completedMonitors++
continue
}
// something went wrong
return err
}
if completedMonitors == len(p.monitors) && len(p.monitors) > 0 {
return p.monitors[0].Error()
}
return nil
}
func (p packageCatalogerProgressAdapter) Size() int64 {
// this is an inherently unknown value (indeterminate total number of packages to discover)
return -1
}
func (m *Handler) handlePackageCatalogerStarted(e partybus.Event) []tea.Model {
monitor, err := syftEventParsers.ParsePackageCatalogerStarted(e)
if err != nil {
log.WithFields("error", err).Warn("unable to parse event")
return nil
}
tsk := m.newTaskProgress(
taskprogress.Title{
Default: "Catalog packages",
Running: "Cataloging packages",
Success: "Cataloged packages",
},
taskprogress.WithStagedProgressable(
newPackageCatalogerProgressAdapter(monitor),
),
)
tsk.HideStageOnSuccess = false
return []tea.Model{tsk}
}
@@ -1,133 +0,0 @@
package ui
import (
"testing"
"time"
tea "github.com/charmbracelet/bubbletea"
"github.com/gkampitakis/go-snaps/snaps"
"github.com/stretchr/testify/require"
"github.com/wagoodman/go-partybus"
"github.com/wagoodman/go-progress"
"github.com/anchore/bubbly/bubbles/taskprogress"
syftEvent "github.com/anchore/syft/syft/event"
"github.com/anchore/syft/syft/pkg/cataloger"
)
func TestHandler_handlePackageCatalogerStarted(t *testing.T) {
tests := []struct {
name string
eventFn func(*testing.T) partybus.Event
iterations int
}{
{
name: "cataloging in progress",
eventFn: func(t *testing.T) partybus.Event {
prog := &progress.Manual{}
prog.SetTotal(100)
prog.Set(50)
mon := cataloger.Monitor{
FilesProcessed: progress.NewManual(-1),
PackagesDiscovered: prog,
}
return partybus.Event{
Type: syftEvent.PackageCatalogerStarted,
Value: mon,
}
},
},
{
name: "cataloging only files complete",
eventFn: func(t *testing.T) partybus.Event {
prog := &progress.Manual{}
prog.SetTotal(100)
prog.Set(50)
files := progress.NewManual(-1)
files.SetCompleted()
mon := cataloger.Monitor{
FilesProcessed: files,
PackagesDiscovered: prog,
}
return partybus.Event{
Type: syftEvent.PackageCatalogerStarted,
Value: mon,
}
},
},
{
name: "cataloging only packages complete",
eventFn: func(t *testing.T) partybus.Event {
prog := &progress.Manual{}
prog.SetTotal(100)
prog.Set(100)
prog.SetCompleted()
files := progress.NewManual(-1)
mon := cataloger.Monitor{
FilesProcessed: files,
PackagesDiscovered: prog,
}
return partybus.Event{
Type: syftEvent.PackageCatalogerStarted,
Value: mon,
}
},
},
{
name: "cataloging complete",
eventFn: func(t *testing.T) partybus.Event {
prog := &progress.Manual{}
prog.SetTotal(100)
prog.Set(100)
prog.SetCompleted()
files := progress.NewManual(-1)
files.SetCompleted()
mon := cataloger.Monitor{
FilesProcessed: files,
PackagesDiscovered: prog,
}
return partybus.Event{
Type: syftEvent.PackageCatalogerStarted,
Value: mon,
}
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
event := tt.eventFn(t)
handler := New(DefaultHandlerConfig())
handler.WindowSize = tea.WindowSizeMsg{
Width: 100,
Height: 80,
}
models := handler.Handle(event)
require.Len(t, models, 1)
model := models[0]
tsk, ok := model.(taskprogress.Model)
require.True(t, ok)
got := runModel(t, tsk, tt.iterations, taskprogress.TickMsg{
Time: time.Now(),
Sequence: tsk.Sequence(),
ID: tsk.ID(),
})
t.Log(got)
snaps.MatchSnapshot(t, got)
})
}
}
-201
View File
@@ -1,201 +0,0 @@
package ui
import (
"fmt"
"strings"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/dustin/go-humanize"
"github.com/wagoodman/go-partybus"
"github.com/wagoodman/go-progress"
"github.com/anchore/bubbly/bubbles/taskprogress"
stereoscopeParsers "github.com/anchore/stereoscope/pkg/event/parsers"
"github.com/anchore/stereoscope/pkg/image/docker"
"github.com/anchore/syft/internal/log"
)
var _ interface {
progress.Stager
progress.Progressable
} = (*dockerPullProgressAdapter)(nil)
type dockerPullStatus interface {
Complete() bool
Layers() []docker.LayerID
Current(docker.LayerID) docker.LayerState
}
type dockerPullProgressAdapter struct {
status dockerPullStatus
formatter dockerPullStatusFormatter
}
type dockerPullStatusFormatter struct {
auxInfoStyle lipgloss.Style
dockerPullCompletedStyle lipgloss.Style
dockerPullDownloadStyle lipgloss.Style
dockerPullExtractStyle lipgloss.Style
dockerPullStageChars []string
layerCaps []string
}
func (m *Handler) handlePullDockerImage(e partybus.Event) []tea.Model {
_, pullStatus, err := stereoscopeParsers.ParsePullDockerImage(e)
if err != nil {
log.WithFields("error", err).Warn("unable to parse event")
return nil
}
tsk := m.newTaskProgress(
taskprogress.Title{
Default: "Pull image",
Running: "Pulling image",
Success: "Pulled image",
},
taskprogress.WithStagedProgressable(
newDockerPullProgressAdapter(pullStatus),
),
)
tsk.HintStyle = lipgloss.NewStyle()
tsk.HintEndCaps = nil
return []tea.Model{tsk}
}
func newDockerPullProgressAdapter(status dockerPullStatus) *dockerPullProgressAdapter {
return &dockerPullProgressAdapter{
status: status,
formatter: newDockerPullStatusFormatter(),
}
}
func newDockerPullStatusFormatter() dockerPullStatusFormatter {
return dockerPullStatusFormatter{
auxInfoStyle: lipgloss.NewStyle().Foreground(lipgloss.Color("#777777")),
dockerPullCompletedStyle: lipgloss.NewStyle().Foreground(lipgloss.Color("#fcba03")),
dockerPullDownloadStyle: lipgloss.NewStyle().Foreground(lipgloss.Color("#777777")),
dockerPullExtractStyle: lipgloss.NewStyle().Foreground(lipgloss.Color("#ffffff")),
dockerPullStageChars: strings.Split("▁▃▄▅▆▇█", ""),
layerCaps: strings.Split("▕▏", ""),
}
}
func (d dockerPullProgressAdapter) Size() int64 {
return -1
}
func (d dockerPullProgressAdapter) Current() int64 {
return 1
}
func (d dockerPullProgressAdapter) Error() error {
if d.status.Complete() {
return progress.ErrCompleted
}
// TODO: return intermediate error indications
return nil
}
func (d dockerPullProgressAdapter) Stage() string {
return d.formatter.Render(d.status)
}
// Render crafts the given docker image pull status summarized into a single line.
func (f dockerPullStatusFormatter) Render(pullStatus dockerPullStatus) string {
var size, current uint64
layers := pullStatus.Layers()
status := make(map[docker.LayerID]docker.LayerState)
completed := make([]string, len(layers))
// fetch the current state
for idx, layer := range layers {
completed[idx] = " "
status[layer] = pullStatus.Current(layer)
}
numCompleted := 0
for idx, layer := range layers {
prog := status[layer].PhaseProgress
curN := prog.Current()
curSize := prog.Size()
if progress.IsCompleted(prog) {
input := f.dockerPullStageChars[len(f.dockerPullStageChars)-1]
completed[idx] = f.formatDockerPullPhase(status[layer].Phase, input)
} else if curN != 0 {
var ratio float64
switch {
case curN == 0 || curSize < 0:
ratio = 0
case curN >= curSize:
ratio = 1
default:
ratio = float64(curN) / float64(curSize)
}
i := int(ratio * float64(len(f.dockerPullStageChars)-1))
input := f.dockerPullStageChars[i]
completed[idx] = f.formatDockerPullPhase(status[layer].Phase, input)
}
if progress.IsErrCompleted(status[layer].DownloadProgress.Error()) {
numCompleted++
}
}
for _, layer := range layers {
prog := status[layer].DownloadProgress
size += uint64(prog.Size())
current += uint64(prog.Current())
}
var progStr, auxInfo string
if len(layers) > 0 {
render := strings.Join(completed, "")
prefix := f.dockerPullCompletedStyle.Render(fmt.Sprintf("%d Layers", len(layers)))
auxInfo = f.auxInfoStyle.Render(fmt.Sprintf("[%s / %s]", humanize.Bytes(current), humanize.Bytes(size)))
if len(layers) == numCompleted {
auxInfo = f.auxInfoStyle.Render(fmt.Sprintf("[%s] Extracting...", humanize.Bytes(size)))
}
progStr = fmt.Sprintf("%s%s%s%s", prefix, f.layerCap(false), render, f.layerCap(true))
}
return progStr + auxInfo
}
// formatDockerPullPhase returns a single character that represents the status of a layer pull.
func (f dockerPullStatusFormatter) formatDockerPullPhase(phase docker.PullPhase, inputStr string) string {
switch phase {
case docker.WaitingPhase:
// ignore any progress related to waiting
return " "
case docker.PullingFsPhase, docker.DownloadingPhase:
return f.dockerPullDownloadStyle.Render(inputStr)
case docker.DownloadCompletePhase:
return f.dockerPullDownloadStyle.Render(f.dockerPullStageChars[len(f.dockerPullStageChars)-1])
case docker.ExtractingPhase:
return f.dockerPullExtractStyle.Render(inputStr)
case docker.VerifyingChecksumPhase, docker.PullCompletePhase:
return f.dockerPullCompletedStyle.Render(inputStr)
case docker.AlreadyExistsPhase:
return f.dockerPullCompletedStyle.Render(f.dockerPullStageChars[len(f.dockerPullStageChars)-1])
default:
return inputStr
}
}
func (f dockerPullStatusFormatter) layerCap(end bool) string {
l := len(f.layerCaps)
if l == 0 {
return ""
}
if end {
return f.layerCaps[l-1]
}
return f.layerCaps[0]
}
@@ -1,163 +0,0 @@
package ui
import (
"testing"
"github.com/gkampitakis/go-snaps/snaps"
"github.com/wagoodman/go-progress"
"github.com/anchore/stereoscope/pkg/image/docker"
)
var _ dockerPullStatus = (*mockDockerPullStatus)(nil)
type mockDockerPullStatus struct {
complete bool
layers []docker.LayerID
current map[docker.LayerID]docker.LayerState
}
func (m mockDockerPullStatus) Complete() bool {
return m.complete
}
func (m mockDockerPullStatus) Layers() []docker.LayerID {
return m.layers
}
func (m mockDockerPullStatus) Current(id docker.LayerID) docker.LayerState {
return m.current[id]
}
func Test_dockerPullStatusFormatter_Render(t *testing.T) {
tests := []struct {
name string
status dockerPullStatus
}{
{
name: "pulling",
status: func() dockerPullStatus {
complete := progress.NewManual(10)
complete.Set(10)
complete.SetCompleted()
quarter := progress.NewManual(10)
quarter.Set(2)
half := progress.NewManual(10)
half.Set(6)
empty := progress.NewManual(10)
return mockDockerPullStatus{
complete: false,
layers: []docker.LayerID{
"sha256:1",
"sha256:2",
"sha256:3",
},
current: map[docker.LayerID]docker.LayerState{
"sha256:1": {
Phase: docker.ExtractingPhase,
PhaseProgress: half,
DownloadProgress: complete,
},
"sha256:2": {
Phase: docker.DownloadingPhase,
PhaseProgress: quarter,
DownloadProgress: quarter,
},
"sha256:3": {
Phase: docker.WaitingPhase,
PhaseProgress: empty,
DownloadProgress: empty,
},
},
}
}(),
},
{
name: "download complete",
status: func() dockerPullStatus {
complete := progress.NewManual(10)
complete.Set(10)
complete.SetCompleted()
quarter := progress.NewManual(10)
quarter.Set(2)
half := progress.NewManual(10)
half.Set(6)
empty := progress.NewManual(10)
return mockDockerPullStatus{
complete: false,
layers: []docker.LayerID{
"sha256:1",
"sha256:2",
"sha256:3",
},
current: map[docker.LayerID]docker.LayerState{
"sha256:1": {
Phase: docker.ExtractingPhase,
PhaseProgress: complete,
DownloadProgress: complete,
},
"sha256:2": {
Phase: docker.ExtractingPhase,
PhaseProgress: quarter,
DownloadProgress: complete,
},
"sha256:3": {
Phase: docker.ExtractingPhase,
PhaseProgress: empty,
DownloadProgress: complete,
},
},
}
}(),
},
{
name: "complete",
status: func() dockerPullStatus {
complete := progress.NewManual(10)
complete.Set(10)
complete.SetCompleted()
return mockDockerPullStatus{
complete: true,
layers: []docker.LayerID{
"sha256:1",
"sha256:2",
"sha256:3",
},
current: map[docker.LayerID]docker.LayerState{
"sha256:1": {
Phase: docker.PullCompletePhase,
PhaseProgress: complete,
DownloadProgress: complete,
},
"sha256:2": {
Phase: docker.PullCompletePhase,
PhaseProgress: complete,
DownloadProgress: complete,
},
"sha256:3": {
Phase: docker.PullCompletePhase,
PhaseProgress: complete,
DownloadProgress: complete,
},
},
}
}(),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
f := newDockerPullStatusFormatter()
snaps.MatchSnapshot(t, f.Render(tt.status))
})
}
}
-33
View File
@@ -1,33 +0,0 @@
package ui
import (
tea "github.com/charmbracelet/bubbletea"
"github.com/wagoodman/go-partybus"
"github.com/anchore/bubbly/bubbles/taskprogress"
stereoEventParsers "github.com/anchore/stereoscope/pkg/event/parsers"
"github.com/anchore/syft/internal/log"
)
func (m *Handler) handleReadImage(e partybus.Event) []tea.Model {
imgMetadata, prog, err := stereoEventParsers.ParseReadImage(e)
if err != nil {
log.WithFields("error", err).Warn("unable to parse event")
return nil
}
tsk := m.newTaskProgress(
taskprogress.Title{
Default: "Parse image",
Running: "Parsing image",
Success: "Parsed image",
},
taskprogress.WithProgress(prog),
)
if imgMetadata != nil {
tsk.Context = []string{imgMetadata.ID}
}
return []tea.Model{tsk}
}
-117
View File
@@ -1,117 +0,0 @@
package ui
import (
"testing"
"time"
tea "github.com/charmbracelet/bubbletea"
"github.com/gkampitakis/go-snaps/snaps"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/stretchr/testify/require"
"github.com/wagoodman/go-partybus"
"github.com/wagoodman/go-progress"
"github.com/anchore/bubbly/bubbles/taskprogress"
stereoscopeEvent "github.com/anchore/stereoscope/pkg/event"
"github.com/anchore/stereoscope/pkg/image"
)
func TestHandler_handleReadImage(t *testing.T) {
tests := []struct {
name string
eventFn func(*testing.T) partybus.Event
iterations int
}{
{
name: "read image in progress",
eventFn: func(t *testing.T) partybus.Event {
prog := &progress.Manual{}
prog.SetTotal(100)
prog.Set(50)
src := image.Metadata{
ID: "id",
Size: 42,
Config: v1.ConfigFile{
Architecture: "arch",
Author: "auth",
Container: "cont",
OS: "os",
OSVersion: "os-ver",
Variant: "vari",
},
MediaType: "media",
ManifestDigest: "digest",
Architecture: "arch",
Variant: "var",
OS: "os",
}
return partybus.Event{
Type: stereoscopeEvent.ReadImage,
Source: src,
Value: prog,
}
},
},
{
name: "read image complete",
eventFn: func(t *testing.T) partybus.Event {
prog := &progress.Manual{}
prog.SetTotal(100)
prog.Set(100)
prog.SetCompleted()
src := image.Metadata{
ID: "id",
Size: 42,
Config: v1.ConfigFile{
Architecture: "arch",
Author: "auth",
Container: "cont",
OS: "os",
OSVersion: "os-ver",
Variant: "vari",
},
MediaType: "media",
ManifestDigest: "digest",
Architecture: "arch",
Variant: "var",
OS: "os",
}
return partybus.Event{
Type: stereoscopeEvent.ReadImage,
Source: src,
Value: prog,
}
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
event := tt.eventFn(t)
handler := New(DefaultHandlerConfig())
handler.WindowSize = tea.WindowSizeMsg{
Width: 100,
Height: 80,
}
models := handler.Handle(event)
require.Len(t, models, 1)
model := models[0]
tsk, ok := model.(taskprogress.Model)
require.True(t, ok)
got := runModel(t, tsk, tt.iterations, taskprogress.TickMsg{
Time: time.Now(),
Sequence: tsk.Sequence(),
ID: tsk.ID(),
})
t.Log(got)
snaps.MatchSnapshot(t, got)
})
}
}
@@ -1,57 +0,0 @@
package ui
import (
"fmt"
tea "github.com/charmbracelet/bubbletea"
"github.com/wagoodman/go-partybus"
"github.com/wagoodman/go-progress"
"github.com/anchore/bubbly/bubbles/taskprogress"
"github.com/anchore/syft/internal/log"
syftEventParsers "github.com/anchore/syft/syft/event/parsers"
"github.com/anchore/syft/syft/file/cataloger/secrets"
)
var _ progress.StagedProgressable = (*secretsCatalogerProgressAdapter)(nil)
// Deprecated: will be removed in syft 1.0
type secretsCatalogerProgressAdapter struct {
*secrets.Monitor
}
// Deprecated: will be removed in syft 1.0
func newSecretsCatalogerProgressAdapter(monitor *secrets.Monitor) secretsCatalogerProgressAdapter {
return secretsCatalogerProgressAdapter{
Monitor: monitor,
}
}
func (s secretsCatalogerProgressAdapter) Stage() string {
return fmt.Sprintf("%d secrets", s.Monitor.SecretsDiscovered.Current())
}
// Deprecated: will be removed in syft 1.0
func (m *Handler) handleSecretsCatalogerStarted(e partybus.Event) []tea.Model {
mon, err := syftEventParsers.ParseSecretsCatalogingStarted(e)
if err != nil {
log.WithFields("error", err).Warn("unable to parse event")
return nil
}
tsk := m.newTaskProgress(
taskprogress.Title{
Default: "Catalog secrets",
Running: "Cataloging secrets",
Success: "Cataloged secrets",
},
taskprogress.WithStagedProgressable(
newSecretsCatalogerProgressAdapter(mon),
),
)
tsk.HideStageOnSuccess = false
return []tea.Model{tsk}
}
@@ -1,96 +0,0 @@
package ui
import (
"testing"
"time"
tea "github.com/charmbracelet/bubbletea"
"github.com/gkampitakis/go-snaps/snaps"
"github.com/stretchr/testify/require"
"github.com/wagoodman/go-partybus"
"github.com/wagoodman/go-progress"
"github.com/anchore/bubbly/bubbles/taskprogress"
syftEvent "github.com/anchore/syft/syft/event"
"github.com/anchore/syft/syft/file/cataloger/secrets"
)
func TestHandler_handleSecretsCatalogerStarted(t *testing.T) {
tests := []struct {
name string
eventFn func(*testing.T) partybus.Event
iterations int
}{
{
name: "cataloging in progress",
eventFn: func(t *testing.T) partybus.Event {
stage := &progress.Stage{
Current: "current",
}
secretsDiscovered := progress.NewManual(-1)
secretsDiscovered.Set(64)
prog := progress.NewManual(72)
prog.Set(50)
return partybus.Event{
Type: syftEvent.SecretsCatalogerStarted,
Source: secretsDiscovered,
Value: secrets.Monitor{
Stager: progress.Stager(stage),
SecretsDiscovered: secretsDiscovered,
Progressable: prog,
},
}
},
},
{
name: "cataloging complete",
eventFn: func(t *testing.T) partybus.Event {
stage := &progress.Stage{
Current: "current",
}
secretsDiscovered := progress.NewManual(-1)
secretsDiscovered.Set(64)
prog := progress.NewManual(72)
prog.Set(72)
prog.SetCompleted()
return partybus.Event{
Type: syftEvent.SecretsCatalogerStarted,
Source: secretsDiscovered,
Value: secrets.Monitor{
Stager: progress.Stager(stage),
SecretsDiscovered: secretsDiscovered,
Progressable: prog,
},
}
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
event := tt.eventFn(t)
handler := New(DefaultHandlerConfig())
handler.WindowSize = tea.WindowSizeMsg{
Width: 100,
Height: 80,
}
models := handler.Handle(event)
require.Len(t, models, 1)
model := models[0]
tsk, ok := model.(taskprogress.Model)
require.True(t, ok)
got := runModel(t, tsk, tt.iterations, taskprogress.TickMsg{
Time: time.Now(),
Sequence: tsk.Sequence(),
ID: tsk.ID(),
})
t.Log(got)
snaps.MatchSnapshot(t, got)
})
}
}
-76
View File
@@ -1,76 +0,0 @@
package ui
import (
"sync"
tea "github.com/charmbracelet/bubbletea"
"github.com/wagoodman/go-partybus"
"github.com/anchore/bubbly"
"github.com/anchore/bubbly/bubbles/taskprogress"
stereoscopeEvent "github.com/anchore/stereoscope/pkg/event"
syftEvent "github.com/anchore/syft/syft/event"
)
var _ interface {
bubbly.EventHandler
bubbly.MessageListener
bubbly.HandleWaiter
} = (*Handler)(nil)
type HandlerConfig struct {
TitleWidth int
AdjustDefaultTask func(taskprogress.Model) taskprogress.Model
}
type Handler struct {
WindowSize tea.WindowSizeMsg
Running *sync.WaitGroup
Config HandlerConfig
bubbly.EventHandler
}
func DefaultHandlerConfig() HandlerConfig {
return HandlerConfig{
TitleWidth: 30,
}
}
func New(cfg HandlerConfig) *Handler {
d := bubbly.NewEventDispatcher()
h := &Handler{
EventHandler: d,
Running: &sync.WaitGroup{},
Config: cfg,
}
// register all supported event types with the respective handler functions
d.AddHandlers(map[partybus.EventType]bubbly.EventHandlerFn{
stereoscopeEvent.PullDockerImage: h.handlePullDockerImage,
stereoscopeEvent.ReadImage: h.handleReadImage,
stereoscopeEvent.FetchImage: h.handleFetchImage,
syftEvent.PackageCatalogerStarted: h.handlePackageCatalogerStarted,
syftEvent.FileDigestsCatalogerStarted: h.handleFileDigestsCatalogerStarted,
syftEvent.FileMetadataCatalogerStarted: h.handleFileMetadataCatalogerStarted,
syftEvent.FileIndexingStarted: h.handleFileIndexingStarted,
syftEvent.AttestationStarted: h.handleAttestationStarted,
syftEvent.CatalogerTaskStarted: h.handleCatalogerTaskStarted,
// deprecated
syftEvent.SecretsCatalogerStarted: h.handleSecretsCatalogerStarted,
})
return h
}
func (m *Handler) OnMessage(msg tea.Msg) {
if msg, ok := msg.(tea.WindowSizeMsg); ok {
m.WindowSize = msg
}
}
func (m *Handler) Wait() {
m.Running.Wait()
}
-19
View File
@@ -1,19 +0,0 @@
package ui
import "github.com/anchore/bubbly/bubbles/taskprogress"
func (m Handler) newTaskProgress(title taskprogress.Title, opts ...taskprogress.Option) taskprogress.Model {
tsk := taskprogress.New(m.Running, opts...)
tsk.HideProgressOnSuccess = true
tsk.HideStageOnSuccess = true
tsk.WindowSize = m.WindowSize
tsk.TitleWidth = m.Config.TitleWidth
tsk.TitleOptions = title
if m.Config.AdjustDefaultTask != nil {
tsk = m.Config.AdjustDefaultTask(tsk)
}
return tsk
}
-70
View File
@@ -1,70 +0,0 @@
package ui
import (
"reflect"
"sync"
"testing"
"unsafe"
tea "github.com/charmbracelet/bubbletea"
)
func runModel(t testing.TB, m tea.Model, iterations int, message tea.Msg, h ...*sync.WaitGroup) string {
t.Helper()
if iterations == 0 {
iterations = 1
}
m.Init()
var cmd tea.Cmd = func() tea.Msg {
return message
}
for _, each := range h {
if each != nil {
each.Wait()
}
}
for i := 0; cmd != nil && i < iterations; i++ {
msgs := flatten(cmd())
var nextCmds []tea.Cmd
var next tea.Cmd
for _, msg := range msgs {
t.Logf("Message: %+v %+v\n", reflect.TypeOf(msg), msg)
m, next = m.Update(msg)
nextCmds = append(nextCmds, next)
}
cmd = tea.Batch(nextCmds...)
}
return m.View()
}
func flatten(p tea.Msg) (msgs []tea.Msg) {
if reflect.TypeOf(p).Name() == "batchMsg" {
partials := extractBatchMessages(p)
for _, m := range partials {
msgs = append(msgs, flatten(m)...)
}
} else {
msgs = []tea.Msg{p}
}
return msgs
}
func extractBatchMessages(m tea.Msg) (ret []tea.Msg) {
sliceMsgType := reflect.SliceOf(reflect.TypeOf(tea.Cmd(nil)))
value := reflect.ValueOf(m) // note: this is technically unaddressable
// make our own instance that is addressable
valueCopy := reflect.New(value.Type()).Elem()
valueCopy.Set(value)
cmds := reflect.NewAt(sliceMsgType, unsafe.Pointer(valueCopy.UnsafeAddr())).Elem()
for i := 0; i < cmds.Len(); i++ {
item := cmds.Index(i)
r := item.Call(nil)
ret = append(ret, r[0].Interface().(tea.Msg))
}
return ret
}
+1 -1
View File
@@ -15,7 +15,7 @@ import (
"github.com/anchore/syft/internal/version"
)
func Version(v *viper.Viper, _ *config.Application) *cobra.Command {
func Version(v *viper.Viper, app *config.Application) *cobra.Command {
o := &options.VersionOptions{}
cmd := &cobra.Command{
Use: "version",
@@ -1,46 +0,0 @@
[Test_postUIEventWriter_write/no_events/stdout - 1]
---
[Test_postUIEventWriter_write/no_events/stderr - 1]
---
[Test_postUIEventWriter_write/all_events/stdout - 1]
<my --
-
-
report 1!!>
<report 2>
---
[Test_postUIEventWriter_write/all_events/stderr - 1]
<my notification 1!!
...still notifying>
<notification 2>
<notification 3>
<my app can be updated!!
...to this version>
---
[Test_postUIEventWriter_write/quiet_only_shows_report/stdout - 1]
<report 1>
---
[Test_postUIEventWriter_write/quiet_only_shows_report/stderr - 1]
---
-44
View File
@@ -1,44 +0,0 @@
package ui
import (
"os"
"github.com/wagoodman/go-partybus"
"github.com/anchore/clio"
"github.com/anchore/syft/syft/event"
)
var _ clio.UI = (*NoUI)(nil)
type NoUI struct {
finalizeEvents []partybus.Event
subscription partybus.Unsubscribable
quiet bool
}
func None(quiet bool) *NoUI {
return &NoUI{
quiet: quiet,
}
}
func (n *NoUI) Setup(subscription partybus.Unsubscribable) error {
n.subscription = subscription
return nil
}
func (n *NoUI) Handle(e partybus.Event) error {
switch e.Type {
case event.CLIReport, event.CLINotification:
// keep these for when the UI is terminated to show to the screen (or perform other events)
n.finalizeEvents = append(n.finalizeEvents, e)
case event.CLIExit:
return n.subscription.Unsubscribe()
}
return nil
}
func (n NoUI) Teardown(_ bool) error {
return newPostUIEventWriter(os.Stdout, os.Stderr).write(n.quiet, n.finalizeEvents...)
}
@@ -1,133 +0,0 @@
package ui
import (
"fmt"
"io"
"strings"
"github.com/charmbracelet/lipgloss"
"github.com/hashicorp/go-multierror"
"github.com/wagoodman/go-partybus"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/event"
"github.com/anchore/syft/syft/event/parsers"
)
type postUIEventWriter struct {
handles []postUIHandle
}
type postUIHandle struct {
respectQuiet bool
event partybus.EventType
writer io.Writer
dispatch eventWriter
}
type eventWriter func(io.Writer, ...partybus.Event) error
func newPostUIEventWriter(stdout, stderr io.Writer) *postUIEventWriter {
return &postUIEventWriter{
handles: []postUIHandle{
{
event: event.CLIReport,
respectQuiet: false,
writer: stdout,
dispatch: writeReports,
},
{
event: event.CLINotification,
respectQuiet: true,
writer: stderr,
dispatch: writeNotifications,
},
{
event: event.CLIAppUpdateAvailable,
respectQuiet: true,
writer: stderr,
dispatch: writeAppUpdate,
},
},
}
}
func (w postUIEventWriter) write(quiet bool, events ...partybus.Event) error {
var errs error
for _, h := range w.handles {
if quiet && h.respectQuiet {
continue
}
for _, e := range events {
if e.Type != h.event {
continue
}
if err := h.dispatch(h.writer, e); err != nil {
errs = multierror.Append(errs, err)
}
}
}
return errs
}
func writeReports(writer io.Writer, events ...partybus.Event) error {
var reports []string
for _, e := range events {
_, report, err := parsers.ParseCLIReport(e)
if err != nil {
log.WithFields("error", err).Warn("failed to gather final report")
continue
}
// remove all whitespace padding from the end of the report
reports = append(reports, strings.TrimRight(report, "\n ")+"\n")
}
// prevent the double new-line at the end of the report
report := strings.Join(reports, "\n")
if _, err := fmt.Fprint(writer, report); err != nil {
return fmt.Errorf("failed to write final report to stdout: %w", err)
}
return nil
}
func writeNotifications(writer io.Writer, events ...partybus.Event) error {
// 13 = high intensity magenta (ANSI 16 bit code)
style := lipgloss.NewStyle().Foreground(lipgloss.Color("13"))
for _, e := range events {
_, notification, err := parsers.ParseCLINotification(e)
if err != nil {
log.WithFields("error", err).Warn("failed to parse notification")
continue
}
if _, err := fmt.Fprintln(writer, style.Render(notification)); err != nil {
// don't let this be fatal
log.WithFields("error", err).Warn("failed to write final notifications")
}
}
return nil
}
func writeAppUpdate(writer io.Writer, events ...partybus.Event) error {
// 13 = high intensity magenta (ANSI 16 bit code) + italics
style := lipgloss.NewStyle().Foreground(lipgloss.Color("13")).Italic(true)
for _, e := range events {
notice, err := parsers.ParseCLIAppUpdateAvailable(e)
if err != nil {
log.WithFields("error", err).Warn("failed to parse app update notification")
continue
}
if _, err := fmt.Fprintln(writer, style.Render(notice)); err != nil {
// don't let this be fatal
log.WithFields("error", err).Warn("failed to write app update notification")
}
}
return nil
}
@@ -1,95 +0,0 @@
package ui
import (
"bytes"
"testing"
"github.com/gkampitakis/go-snaps/snaps"
"github.com/stretchr/testify/require"
"github.com/wagoodman/go-partybus"
"github.com/anchore/syft/syft/event"
)
func Test_postUIEventWriter_write(t *testing.T) {
tests := []struct {
name string
quiet bool
events []partybus.Event
wantErr require.ErrorAssertionFunc
}{
{
name: "no events",
},
{
name: "all events",
events: []partybus.Event{
{
Type: event.CLINotification,
Value: "\n\n<my notification 1!!\n...still notifying>\n\n",
},
{
Type: event.CLINotification,
Value: "<notification 2>",
},
{
Type: event.CLIAppUpdateAvailable,
Value: "\n\n<my app can be updated!!\n...to this version>\n\n",
},
{
Type: event.CLINotification,
Value: "<notification 3>",
},
{
Type: event.CLIReport,
Value: "\n\n<my --\n-\n-\nreport 1!!>\n\n",
},
{
Type: event.CLIReport,
Value: "<report 2>",
},
},
},
{
name: "quiet only shows report",
quiet: true,
events: []partybus.Event{
{
Type: event.CLINotification,
Value: "<notification 1>",
},
{
Type: event.CLIAppUpdateAvailable,
Value: "<app update>",
},
{
Type: event.CLIReport,
Value: "<report 1>",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.wantErr == nil {
tt.wantErr = require.NoError
}
stdout := &bytes.Buffer{}
stderr := &bytes.Buffer{}
w := newPostUIEventWriter(stdout, stderr)
tt.wantErr(t, w.write(tt.quiet, tt.events...))
t.Run("stdout", func(t *testing.T) {
snaps.MatchSnapshot(t, stdout.String())
})
t.Run("stderr", func(t *testing.T) {
snaps.MatchSnapshot(t, stderr.String())
})
})
}
}
-166
View File
@@ -1,166 +0,0 @@
package ui
import (
"os"
"sync"
tea "github.com/charmbracelet/bubbletea"
"github.com/wagoodman/go-partybus"
"github.com/anchore/bubbly/bubbles/frame"
"github.com/anchore/clio"
"github.com/anchore/go-logger"
handler "github.com/anchore/syft/cmd/syft/cli/ui"
"github.com/anchore/syft/internal/bus"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/event"
)
var _ interface {
tea.Model
partybus.Responder
clio.UI
} = (*UI)(nil)
type UI struct {
program *tea.Program
running *sync.WaitGroup
quiet bool
subscription partybus.Unsubscribable
finalizeEvents []partybus.Event
handler *handler.Handler
frame tea.Model
}
func New(h *handler.Handler, _, quiet bool) *UI {
return &UI{
handler: h,
frame: frame.New(),
running: &sync.WaitGroup{},
quiet: quiet,
}
}
func (m *UI) Setup(subscription partybus.Unsubscribable) error {
// we still want to collect log messages, however, we also the logger shouldn't write to the screen directly
if logWrapper, ok := log.Get().(logger.Controller); ok {
logWrapper.SetOutput(m.frame.(*frame.Frame).Footer())
}
m.subscription = subscription
m.program = tea.NewProgram(m, tea.WithOutput(os.Stderr), tea.WithInput(os.Stdin))
m.running.Add(1)
go func() {
defer m.running.Done()
if _, err := m.program.Run(); err != nil {
log.Errorf("unable to start UI: %+v", err)
m.exit()
}
}()
return nil
}
func (m *UI) exit() {
// stop the event loop
bus.Exit()
}
func (m *UI) Handle(e partybus.Event) error {
if m.program != nil {
m.program.Send(e)
if e.Type == event.CLIExit {
return m.subscription.Unsubscribe()
}
}
return nil
}
func (m *UI) Teardown(force bool) error {
if !force {
m.handler.Running.Wait()
m.program.Quit()
// typically in all cases we would want to wait for the UI to finish. However there are still error cases
// that are not accounted for, resulting in hangs. For now, we'll just wait for the UI to finish in the
// happy path only. There will always be an indication of the problem to the user via reporting the error
// string from the worker (outside of the UI after teardown).
m.running.Wait()
} else {
m.program.Kill()
}
// TODO: allow for writing out the full log output to the screen (only a partial log is shown currently)
// this needs coordination to know what the last frame event is to change the state accordingly (which isn't possible now)
return newPostUIEventWriter(os.Stdout, os.Stderr).write(m.quiet, m.finalizeEvents...)
}
// bubbletea.Model functions
func (m UI) Init() tea.Cmd {
return m.frame.Init()
}
func (m UI) RespondsTo() []partybus.EventType {
return append([]partybus.EventType{
event.CLIReport,
event.CLINotification,
event.CLIExit,
event.CLIAppUpdateAvailable,
}, m.handler.RespondsTo()...)
}
func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
// note: we need a pointer receiver such that the same instance of UI used in Teardown is referenced (to keep finalize events)
var cmds []tea.Cmd
// allow for non-partybus UI updates (such as window size events). Note: these must not affect existing models,
// that is the responsibility of the frame object on this UI object. The handler is a factory of models
// which the frame is responsible for the lifecycle of. This update allows for injecting the initial state
// of the world when creating those models.
m.handler.OnMessage(msg)
switch msg := msg.(type) {
case tea.KeyMsg:
switch msg.String() {
case "esc", "ctrl+c":
m.exit()
return m, tea.Quit
}
case partybus.Event:
log.WithFields("component", "ui").Tracef("event: %q", msg.Type)
switch msg.Type {
case event.CLIReport, event.CLINotification, event.CLIExit, event.CLIAppUpdateAvailable:
// keep these for when the UI is terminated to show to the screen (or perform other events)
m.finalizeEvents = append(m.finalizeEvents, msg)
// why not return tea.Quit here for exit events? because there may be UI components that still need the update-render loop.
// for this reason we'll let the syft event loop call Teardown() which will explicitly wait for these components
return m, nil
}
for _, newModel := range m.handler.Handle(msg) {
if newModel == nil {
continue
}
cmds = append(cmds, newModel.Init())
m.frame.(*frame.Frame).AppendModel(newModel)
}
// intentionally fallthrough to update the frame model
}
frameModel, cmd := m.frame.Update(msg)
m.frame = frameModel
cmds = append(cmds, cmd)
return m, tea.Batch(cmds...)
}
func (m UI) View() string {
return m.frame.View()
}
-2
View File
@@ -3,8 +3,6 @@ package main
import (
"log"
_ "modernc.org/sqlite"
"github.com/anchore/syft/cmd/syft/cli"
)
+57 -110
View File
@@ -16,7 +16,7 @@ require (
github.com/go-test/deep v1.1.0
github.com/google/go-cmp v0.5.9
github.com/google/uuid v1.3.0
github.com/gookit/color v1.5.4
github.com/gookit/color v1.5.2
github.com/hashicorp/go-multierror v1.1.1
github.com/jinzhu/copier v0.3.5
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
@@ -30,173 +30,120 @@ require (
// pinned to pull in 386 arch fix: https://github.com/scylladb/go-set/commit/cc7b2070d91ebf40d233207b633e28f5bd8f03a5
github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e
github.com/sergi/go-diff v1.3.1
github.com/sirupsen/logrus v1.9.3
github.com/spdx/tools-golang v0.5.3
github.com/spf13/afero v1.9.5
github.com/spf13/cobra v1.7.0
github.com/sirupsen/logrus v1.9.0
github.com/spdx/tools-golang v0.5.0-rc1
github.com/spf13/afero v1.9.3
github.com/spf13/cobra v1.6.1
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.16.0
github.com/stretchr/testify v1.8.4
github.com/vifraa/gopom v0.2.2
github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651
github.com/wagoodman/go-progress v0.0.0-20230301185719-21920a456ad5
github.com/spf13/viper v1.15.0
github.com/stretchr/testify v1.8.1
github.com/vifraa/gopom v0.2.1
github.com/wagoodman/go-partybus v0.0.0-20210627031916-db1f5573bbc5
github.com/wagoodman/go-progress v0.0.0-20200731105512-1020f39e6240
github.com/wagoodman/jotframe v0.0.0-20211129225309-56b0d0a4aebb
github.com/xeipuuv/gojsonschema v1.2.0
golang.org/x/mod v0.12.0
golang.org/x/net v0.12.0
golang.org/x/term v0.10.0
golang.org/x/mod v0.8.0
golang.org/x/net v0.7.0
golang.org/x/term v0.5.0
gopkg.in/yaml.v2 v2.4.0
)
require (
github.com/CycloneDX/cyclonedx-go v0.7.1
github.com/Masterminds/semver v1.5.0
github.com/CycloneDX/cyclonedx-go v0.7.1-0.20221222100750-41a1ac565cce
github.com/Masterminds/sprig/v3 v3.2.3
github.com/anchore/bubbly v0.0.0-20230712165553-812110ab0a10
github.com/anchore/clio v0.0.0-20230602170917-e747e60c4aa0
github.com/anchore/go-logger v0.0.0-20230531193951-db5ae83e7dbe
github.com/anchore/stereoscope v0.0.0-20230724160817-d515761c6ca2
github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46
github.com/charmbracelet/bubbletea v0.24.2
github.com/charmbracelet/lipgloss v0.7.1
github.com/dave/jennifer v1.6.1
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da
github.com/docker/distribution v2.8.2+incompatible
github.com/docker/docker v24.0.5+incompatible
github.com/github/go-spdx/v2 v2.1.2
github.com/gkampitakis/go-snaps v0.4.7
github.com/go-git/go-billy/v5 v5.4.1
github.com/go-git/go-git/v5 v5.8.1
github.com/google/go-containerregistry v0.15.2
github.com/google/licensecheck v0.3.1
github.com/anchore/go-logger v0.0.0-20220728155337-03b66a5207d8
github.com/anchore/stereoscope v0.0.0-20230222185948-fab1c9638abc
github.com/docker/docker v23.0.1+incompatible
github.com/google/go-containerregistry v0.13.0
github.com/invopop/jsonschema v0.7.0
github.com/knqyf263/go-rpmdb v0.0.0-20230301153543-ba94b245509b
github.com/knqyf263/go-rpmdb v0.0.0-20221030135625-4082a22221ce
github.com/opencontainers/go-digest v1.0.0
github.com/saferwall/pe v1.4.4
github.com/sassoftware/go-rpmutils v0.2.0
github.com/vbatts/go-mtree v0.5.3
github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1
github.com/vbatts/go-mtree v0.5.2
golang.org/x/exp v0.0.0-20230202163644-54bba9f4231b
gopkg.in/yaml.v3 v3.0.1
modernc.org/sqlite v1.24.0
)
require (
dario.cat/mergo v1.0.0 // indirect
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/DataDog/zstd v1.4.5 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.2.0 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95 // indirect
github.com/acomagu/bufpipe v1.0.4 // indirect
github.com/anchore/fangs v0.0.0-20230531202914-48a718c6b4ba // indirect
github.com/Microsoft/go-winio v0.6.0 // indirect
github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 // indirect
github.com/aquasecurity/go-version v0.0.0-20210121072130-637058cfe492 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/becheran/wildmatch-go v1.0.0 // indirect
github.com/charmbracelet/bubbles v0.16.1 // indirect
github.com/charmbracelet/harmonica v0.2.0 // indirect
github.com/cloudflare/circl v1.3.3 // indirect
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
github.com/containerd/containerd v1.7.0 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect
github.com/containerd/containerd v1.6.18 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.12.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/docker/cli v23.0.5+incompatible // indirect
github.com/docker/cli v20.10.20+incompatible // indirect
github.com/docker/distribution v2.8.1+incompatible // indirect
github.com/docker/docker-credential-helpers v0.7.0 // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
github.com/edsrzf/mmap-go v1.1.0 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/felixge/fgprof v0.9.3 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.0 // indirect
github.com/gkampitakis/ciinfo v0.2.4 // indirect
github.com/gkampitakis/go-diff v1.3.2 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-restruct/restruct v1.2.0-alpha // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/huandu/xstrings v1.3.3 // indirect
github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0 // indirect
github.com/imdario/mergo v0.3.15 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/inconshreveable/mousetrap v1.0.1 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/klauspost/compress v1.16.5 // indirect
github.com/klauspost/compress v1.15.11 // indirect
github.com/klauspost/pgzip v1.2.5 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.18 // indirect
github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/reflow v0.3.0 // indirect
github.com/muesli/termenv v0.15.1 // indirect
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect
github.com/nwaples/rardecode v1.1.0 // indirect
github.com/opencontainers/image-spec v1.1.0-rc3 // indirect
github.com/pborman/indent v1.2.1 // indirect
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
github.com/opencontainers/image-spec v1.1.0-rc2 // indirect
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
github.com/pierrec/lz4/v4 v4.1.15 // indirect
github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/pkg/profile v1.7.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/rogpeppe/go-internal v1.9.0 // indirect
github.com/rogpeppe/go-internal v1.8.0 // indirect
github.com/shopspring/decimal v1.2.0 // indirect
github.com/skeema/knownhosts v1.2.0 // indirect
github.com/spf13/cast v1.5.1 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/stretchr/objx v0.5.0 // indirect
github.com/subosito/gotenv v1.4.2 // indirect
github.com/sylabs/sif/v2 v2.11.5 // indirect
github.com/sylabs/sif/v2 v2.8.1 // indirect
github.com/sylabs/squashfs v0.6.1 // indirect
github.com/therootcompany/xz v1.0.1 // indirect
github.com/tidwall/gjson v1.14.4 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
github.com/tidwall/sjson v1.2.5 // indirect
github.com/ulikunitz/xz v0.5.10 // indirect
github.com/vbatts/tar-split v0.11.3 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/vbatts/tar-split v0.11.2 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/sys v0.10.0 // indirect
golang.org/x/text v0.11.0 // indirect
golang.org/x/tools v0.8.0 // indirect
golang.org/x/sys v0.5.0 // indirect
golang.org/x/text v0.7.0 // indirect
golang.org/x/tools v0.2.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
google.golang.org/grpc v1.55.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect
google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef // indirect
google.golang.org/grpc v1.52.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
lukechampine.com/uint128 v1.2.0 // indirect
modernc.org/cc/v3 v3.40.0 // indirect
modernc.org/ccgo/v3 v3.16.13 // indirect
modernc.org/libc v1.22.5 // indirect
modernc.org/mathutil v1.5.0 // indirect
modernc.org/memory v1.5.0 // indirect
modernc.org/opt v0.1.3 // indirect
modernc.org/strutil v1.1.3 // indirect
modernc.org/token v1.0.1 // indirect
lukechampine.com/uint128 v1.1.1 // indirect
modernc.org/cc/v3 v3.36.0 // indirect
modernc.org/ccgo/v3 v3.16.6 // indirect
modernc.org/libc v1.16.7 // indirect
modernc.org/mathutil v1.4.1 // indirect
modernc.org/memory v1.1.1 // indirect
modernc.org/opt v0.1.1 // indirect
modernc.org/sqlite v1.17.3 // indirect
modernc.org/strutil v1.1.1 // indirect
modernc.org/token v1.0.0 // indirect
)
require (
@@ -204,7 +151,7 @@ require (
// go: warning: github.com/andybalholm/brotli@v1.0.1: retracted by module author: occasional panics and data corruption
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/pkg/errors v0.9.1 // indirect
golang.org/x/crypto v0.11.0 // indirect
golang.org/x/crypto v0.3.0 // indirect
)
retract (
+134 -261
View File
@@ -48,54 +48,38 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
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/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/CycloneDX/cyclonedx-go v0.7.1 h1:5w1SxjGm9MTMNTuRbEPyw21ObdbaagTWF/KfF0qHTRE=
github.com/CycloneDX/cyclonedx-go v0.7.1/go.mod h1:N/nrdWQI2SIjaACyyDs/u7+ddCkyl/zkNs8xFsHF2Ps=
github.com/CycloneDX/cyclonedx-go v0.7.1-0.20221222100750-41a1ac565cce h1:o5r3msApzvtE5LhcMkxWaKernD/PK0HpMccu7ywBj5Q=
github.com/CycloneDX/cyclonedx-go v0.7.1-0.20221222100750-41a1ac565cce/go.mod h1:XURd0m8zvnLE5aIRqg6JOVRl7qZ/pWBtuFa9EHjQwFc=
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ=
github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g=
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg=
github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95 h1:KLq8BE0KwCL+mmXnjLWEAOYO+2l2AE4YMmqG1ZpZHBs=
github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
github.com/acobaugh/osrelease v0.1.0 h1:Yb59HQDGGNhCj4suHaFQQfBps5wyoKLSSX/J/+UifRE=
github.com/acobaugh/osrelease v0.1.0/go.mod h1:4bFEs0MtgHNHBrmHCt67gNisnabCRAlzdVasCEGHTWY=
github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ=
github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls=
github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/anchore/bubbly v0.0.0-20230712165553-812110ab0a10 h1:Wrqt9fd8ygEMyFtxncZU7RgW2qBu5CL1x876xIyUlPU=
github.com/anchore/bubbly v0.0.0-20230712165553-812110ab0a10/go.mod h1:Ger02eh5NpPm2IqkPAy396HU1KlK3BhOeCljDYXySSk=
github.com/anchore/clio v0.0.0-20230602170917-e747e60c4aa0 h1:g0UqRW60JDrf5fb40RUyIwwcfQ3nAJqGj4aUCVTwFE4=
github.com/anchore/clio v0.0.0-20230602170917-e747e60c4aa0/go.mod h1:0IQVIROfgRX4WZFMfgsbNZmMgLKqW/KgByyJDYvWiDE=
github.com/anchore/fangs v0.0.0-20230531202914-48a718c6b4ba h1:tJ186HK8e0Lf+hhNWX4fJrq14yj3mw8JQkkLhA0nFhE=
github.com/anchore/fangs v0.0.0-20230531202914-48a718c6b4ba/go.mod h1:E3zNHEz7mizIFGJhuX+Ga7AbCmEN5TfzVDxmOfj7XZw=
github.com/anchore/go-logger v0.0.0-20230531193951-db5ae83e7dbe h1:Df867YMmymdMG6z5IW8pR0/2CRpLIjYnaTXLp6j+s0k=
github.com/anchore/go-logger v0.0.0-20230531193951-db5ae83e7dbe/go.mod h1:ubLFmlsv8/DFUQrZwY5syT5/8Er3ugSr4rDFwHsE3hg=
github.com/anchore/go-logger v0.0.0-20220728155337-03b66a5207d8 h1:imgMA0gN0TZx7PSa/pdWqXadBvrz8WsN6zySzCe4XX0=
github.com/anchore/go-logger v0.0.0-20220728155337-03b66a5207d8/go.mod h1:+gPap4jha079qzRTUaehv+UZ6sSdaNwkH0D3b6zhTuk=
github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb h1:iDMnx6LIjtjZ46C0akqveX83WFzhpTD3eqOthawb5vU=
github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb/go.mod h1:DmTY2Mfcv38hsHbG78xMiTDdxFtkHpgYNVDPsF2TgHk=
github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 h1:aM1rlcoLz8y5B2r4tTLMiVTrMtpfY0O8EScKJxaSaEc=
@@ -106,26 +90,18 @@ github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZV
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E=
github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501 h1:AV7qjwMcM4r8wFhJq3jLRztew3ywIyPTRapl2T1s9o8=
github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501/go.mod h1:Blo6OgJNiYF41ufcgHKkbCKF2MDOMlrqhXv/ij6ocR4=
github.com/anchore/stereoscope v0.0.0-20230724160817-d515761c6ca2 h1:qIxlZFFZIl5YNHwxsVQ6UJG8QtkR3W0W9ozaI9JQiPk=
github.com/anchore/stereoscope v0.0.0-20230724160817-d515761c6ca2/go.mod h1:0LsgHgXO4QFnk2hsYwtqd3fR18PIZXlFLIl2qb9tu3g=
github.com/anchore/stereoscope v0.0.0-20230222185948-fab1c9638abc h1:b+2KauWByrCPLNnzRHjLoUxo85tpszFtU7S1I5pAKK0=
github.com/anchore/stereoscope v0.0.0-20230222185948-fab1c9638abc/go.mod h1:Y+jiUa5PmQh9jUvzmvus4EvLnEG87cDTOYgV3nw3wDg=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46 h1:vmXNl+HDfqqXgr0uY1UgK1GAhps8nbAAtqHNBcgyf+4=
github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46/go.mod h1:olhPNdiiAAMiSujemd1O/sc6GcyePr23f/6uGKtthNg=
github.com/aquasecurity/go-version v0.0.0-20210121072130-637058cfe492 h1:rcEG5HI490FF0a7zuvxOxen52ddygCfNVjP0XOCMl+M=
github.com/aquasecurity/go-version v0.0.0-20210121072130-637058cfe492/go.mod h1:9Beu8XsUNNfzml7WBf3QmyPToP1wm1Gj/Vc5UJKqTzU=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/becheran/wildmatch-go v1.0.0 h1:mE3dGGkTmpKtT4Z+88t8RStG40yN9T+kFEGj2PZFSzA=
github.com/becheran/wildmatch-go v1.0.0/go.mod h1:gbMvj0NtVdJ15Mg/mH9uxk2R1QCistMyU7d9KFzroX4=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
@@ -135,28 +111,17 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB
github.com/bmatcuk/doublestar/v4 v4.6.0 h1:HTuxyug8GyFbRkrffIpzNCSK4luc0TY3wzXvzIZhEXc=
github.com/bmatcuk/doublestar/v4 v4.6.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M=
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/charmbracelet/bubbles v0.16.1 h1:6uzpAAaT9ZqKssntbvZMlksWHruQLNxg49H5WdeuYSY=
github.com/charmbracelet/bubbles v0.16.1/go.mod h1:2QCp9LFlEsBQMvIYERr7Ww2H2bA7xen1idUDIzm/+Xc=
github.com/charmbracelet/bubbletea v0.24.2 h1:uaQIKx9Ai6Gdh5zpTbGiWpytMU+CfsPp06RaW2cx/SY=
github.com/charmbracelet/bubbletea v0.24.2/go.mod h1:XdrNrV4J8GiyshTtx3DNuYkR1FDaJmO3l2nejekbsgg=
github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ=
github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao=
github.com/charmbracelet/lipgloss v0.7.1 h1:17WMwi7N1b1rVWOjMT+rCh7sQkvDU75B2hbZpc5Kc1E=
github.com/charmbracelet/lipgloss v0.7.1/go.mod h1:yG0k3giv8Qj8edTCbbg6AlQ5e8KNWpFujkNawKNhE2c=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs=
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
@@ -167,32 +132,26 @@ github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWH
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY=
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
github.com/containerd/containerd v1.7.0 h1:G/ZQr3gMZs6ZT0qPUZ15znx5QSdQdASW11nXTLTM2Pg=
github.com/containerd/containerd v1.7.0/go.mod h1:QfR7Efgb/6X2BDpTPJRvPTYDE9rsF0FsXX9J8sIs/sc=
github.com/containerd/stargz-snapshotter/estargz v0.14.3 h1:OqlDCK3ZVUO6C3B/5FSkDwbkEETK84kQgEeFwDC+62k=
github.com/containerd/stargz-snapshotter/estargz v0.14.3/go.mod h1:KY//uOCIkSuNAHhJogcZtrNHdKrA99/FCCRjE3HD36o=
github.com/containerd/containerd v1.6.18 h1:qZbsLvmyu+Vlty0/Ex5xc0z2YtKpIsb5n45mAMI+2Ns=
github.com/containerd/containerd v1.6.18/go.mod h1:1RdCUu95+gc2v9t3IL+zIlpClSmew7/0YS8O5eQZrOw=
github.com/containerd/stargz-snapshotter/estargz v0.12.1 h1:+7nYmHJb0tEkcRaAW+MHqoKaJYZmkikupxCqVtmPuY0=
github.com/containerd/stargz-snapshotter/estargz v0.12.1/go.mod h1:12VUuCq3qPq4y8yUW+l5w3+oXV3cx2Po3KSe/SmPGqw=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/dave/jennifer v1.6.1 h1:T4T/67t6RAA5AIV6+NP8Uk/BIsXgDoqEowgycdQQLuk=
github.com/dave/jennifer v1.6.1/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc=
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
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/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da h1:ZOjWpVsFZ06eIhnh4mkaceTiVoktdU67+M7KDHJ268M=
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da/go.mod h1:B3tI9iGHi4imdLi4Asdha1Sc6feLMTfPLXh9IUYmysk=
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4=
github.com/docker/cli v23.0.5+incompatible h1:ufWmAOuD3Vmr7JP2G5K3cyuNC4YZWiAsuDEvFVVDafE=
github.com/docker/cli v23.0.5+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v24.0.5+incompatible h1:WmgcE4fxyI6EEXxBRxsHnZXrO1pQ3smi0k/jho4HLeY=
github.com/docker/docker v24.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/cli v20.10.20+incompatible h1:lWQbHSHUFs7KraSN2jOJK7zbMS2jNCHI4mt4xUFUVQ4=
github.com/docker/cli v20.10.20+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68=
github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v23.0.1+incompatible h1:vjgvJZxprTTE1A37nm+CLNAdwu6xZekyoiVlUZEINcY=
github.com/docker/docker v23.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A=
github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
@@ -202,13 +161,9 @@ github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY=
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s=
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/edsrzf/mmap-go v1.1.0 h1:6EUwBLQ/Mcr1EYLE4Tn1VdW1A4ckqCQWZBw8Hr0kjpQ=
github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q=
github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819 h1:RIB4cRk+lBqKK3Oy0r2gRX4ui7tuhiZq2SuTtTCi0/0=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
@@ -228,32 +183,13 @@ github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/set v0.2.1 h1:nn2CaJyknWE/6txyUDGwysr3G5QC6xWB/PtVjPBbeaA=
github.com/fatih/set v0.2.1/go.mod h1:+RKtMCH+favT2+3YecHGxcc0b4KyVWA1QWWJUs4E0CI=
github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g=
github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw=
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/gabriel-vasile/mimetype v1.4.0 h1:Cn9dkdYsMIu56tGho+fqzh7XmvY2YyGU0FnbhiOsEro=
github.com/gabriel-vasile/mimetype v1.4.0/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/github/go-spdx/v2 v2.1.2 h1:p+Tv0yMgcuO0/vnMe9Qh4tmUgYhI6AsLVlakZ/Sx+DM=
github.com/github/go-spdx/v2 v2.1.2/go.mod h1:hMCrsFgT0QnCwn7G8gxy/MxMpy67WgZrwFeISTn0o6w=
github.com/gkampitakis/ciinfo v0.2.4 h1:Ip1hf4K7ISRuVlDrheuhaeffg1VOhlyeFGaQ/vTxrtE=
github.com/gkampitakis/ciinfo v0.2.4/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo=
github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M=
github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk=
github.com/gkampitakis/go-snaps v0.4.7 h1:w1hOUdiKELxiTcRySQLmLuV3KW40mIRw3rzcYJGrb3I=
github.com/gkampitakis/go-snaps v0.4.7/go.mod h1:8HW4KX3JKV8M0GSw69CvT+Jqhd1AlBPMPpBfjBI3bdY=
github.com/glebarez/go-sqlite v1.20.3 h1:89BkqGOXR9oRmG58ZrzgoY/Fhy5x0M+/WV48U5zVrZ4=
github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8ix4=
github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f h1:Pz0DHeFij3XFhoBRGUDPzSJ+w2UcK5/0JvF8DRI58r8=
github.com/go-git/go-git/v5 v5.8.1 h1:Zo79E4p7TRk0xoRgMq0RShiTHGKcKI4+DI6BfJc/Q+A=
github.com/go-git/go-git/v5 v5.8.1/go.mod h1:FHFuoD6yGz5OSKEBK+aWN9Oah0q54Jxl0abmj6GnqAo=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
@@ -275,7 +211,6 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
@@ -302,9 +237,8 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
@@ -323,13 +257,12 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-containerregistry v0.15.2 h1:MMkSh+tjSdnmJZO7ljvEqV1DjfekB6VUEAZgy3a+TQE=
github.com/google/go-containerregistry v0.15.2/go.mod h1:wWK+LnOv4jXMM23IT/F1wdYftGWGr47Is8CG+pmHK1Q=
github.com/google/go-containerregistry v0.13.0 h1:y1C7Z3e149OJbOPDBxLYR8ITPz8dTKqQwjErKVHJC8k=
github.com/google/go-containerregistry v0.13.0/go.mod h1:J9FQ+eSS4a1aC2GNZxvNpbWhgp0487v+cgiilB4FqDo=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/licensecheck v0.3.1 h1:QoxgoDkaeC4nFrtGN1jV7IPmDCHFNIVh54e5hSt6sPs=
github.com/google/licensecheck v0.3.1/go.mod h1:ORkR35t/JjW+emNKtfJDII0zlciG9JgbT7SmsohlHmY=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@@ -349,9 +282,6 @@ github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -363,8 +293,8 @@ github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pf
github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
github.com/gookit/color v1.2.5/go.mod h1:AhIE+pS6D4Ql0SQWbBeXPHw7gY0/sjHoA4s/n1KB7xg=
github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
github.com/gookit/color v1.5.2 h1:uLnfXcaFjlrDnQDT+NCBcfhrXqYTx/rcCa6xn01Y8yI=
github.com/gookit/color v1.5.2/go.mod h1:w8h4bGiHeeBpvQVePTutdbERIUf3oJE5lZ8HM0UgXyg=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M=
github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=
@@ -408,17 +338,14 @@ github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0/go.mod h1:N0
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM=
github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/invopop/jsonschema v0.7.0 h1:2vgQcBz1n256N+FpX3Jq7Y17AjYt46Ig3zIWyy770So=
github.com/invopop/jsonschema v0.7.0/go.mod h1:O9uiLokuu0+MGFlyiaqtWxwqJm41/+8Nj0lD7A36YH0=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg=
github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
@@ -428,44 +355,36 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
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/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI=
github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/compress v1.15.11 h1:Lcadnb3RKGin4FYM/orgq0qde+nc15E5Cbqg4B9Sx9c=
github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE=
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/knqyf263/go-rpmdb v0.0.0-20230301153543-ba94b245509b h1:boYyvL3tbUuKcMN029mpCl7oYYJ7yIXujLj+fiW4Alc=
github.com/knqyf263/go-rpmdb v0.0.0-20230301153543-ba94b245509b/go.mod h1:9LQcoMCMQ9vrF7HcDtXfvqGO4+ddxFQ8+YF/0CVGDww=
github.com/knqyf263/go-rpmdb v0.0.0-20221030135625-4082a22221ce h1:/w0hAcauo/FBVaBvNMQdPZgKjTu5Ip3jvGIM1+VUE7o=
github.com/knqyf263/go-rpmdb v0.0.0-20221030135625-4082a22221ce/go.mod h1:zp6SMcRd0GB+uwNJjr+DkrNZdQZ4er2HMO6KyD0vIGU=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381 h1:bqDmpDG49ZRnB5PcgP0RXtQvnMSgIF14M7CBd2shtXs=
github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A=
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
@@ -474,21 +393,19 @@ github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.6/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75 h1:P8UmIzZMYDR+NGImiFvErt6VWfIRPuGM+vyjiEdkmIw=
github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.12 h1:TJ1bhYJPV44phC+IMu1u2K/i5RriLTPe+yc68XDJ1Z0=
github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
@@ -516,52 +433,38 @@ github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA=
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc=
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70 h1:kMlmsLSbjkikxQJ1IPwaM+7LJ9ltFu/fi8CRzvSnQmA=
github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho=
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
github.com/muesli/termenv v0.15.1 h1:UzuTb/+hhlBugQz28rpzey4ZuKcZ03MeKsoG7IJZIxs=
github.com/muesli/termenv v0.15.1/go.mod h1:HeAQPTzpfs016yGtA4g00CsdYnVLJvxsS4ANqrZs2sQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nwaples/rardecode v1.1.0 h1:vSxaY8vQhOcVr4mm5e8XllHWTiM4JF507A0Katqw7MQ=
github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.0-rc3 h1:fzg1mXZFj8YdPeNkRXMg+zb88BFV0Ys52cJydRwBkb8=
github.com/opencontainers/image-spec v1.1.0-rc3/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8=
github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034=
github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pborman/indent v1.2.1 h1:lFiviAbISHv3Rf0jcuh489bi06hj98JsVMtIDZQb9yM=
github.com/pborman/indent v1.2.1/go.mod h1:FitS+t35kIYtB5xWTZAPhnmrxcciEEOdbyrrpz5K6Vw=
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU=
github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0=
github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
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.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA=
github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -580,21 +483,17 @@ github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8b
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/saferwall/pe v1.4.4 h1:Ml++7/2/Z1iKwV4zCsd1nIqTEAdUQKAetwbbcCarhOg=
github.com/saferwall/pe v1.4.4/go.mod h1:SNzv3cdgk8SBI0UwHfyTcdjawfdnN+nbydnEL7GZ25s=
github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig=
github.com/sassoftware/go-rpmutils v0.2.0 h1:pKW0HDYMFWQ5b4JQPiI3WI12hGsVoW0V8+GMoZiI/JE=
github.com/sassoftware/go-rpmutils v0.2.0/go.mod h1:TJJQYtLe/BeEmEjelI3b7xNZjzAukEkeWKmoakvaOoI=
@@ -612,33 +511,31 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/skeema/knownhosts v1.2.0 h1:h9r9cf0+u7wSE+M183ZtMGgOJKiL96brpaz5ekfJCpM=
github.com/skeema/knownhosts v1.2.0/go.mod h1:g4fPeYpque7P0xefxtGzV81ihjC8sX2IqpAoNkjxbMo=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spdx/gordf v0.0.0-20201111095634-7098f93598fb/go.mod h1:uKWaldnbMnjsSAXRurWqqrdyZen1R7kxl8TkmWk2OyM=
github.com/spdx/tools-golang v0.5.3 h1:ialnHeEYUC4+hkm5vJm4qz2x+oEJbS0mAMFrNXdQraY=
github.com/spdx/tools-golang v0.5.3/go.mod h1:/ETOahiAo96Ob0/RAIBmFZw6XN0yTnyr/uFZm2NTMhI=
github.com/spdx/tools-golang v0.5.0-rc1 h1:ooCSe48QatlidqEFd+nSI308tyeNTR6NJvauUj3ApX8=
github.com/spdx/tools-golang v0.5.0-rc1/go.mod h1:LI6onw172PdO57Ob/hgnLDD4Y2PMnroeNT3wO/2WJJI=
github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM=
github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk=
github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA=
github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48=
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4=
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
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.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM=
github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc=
github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg=
github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU=
github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jHOQLA=
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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
@@ -653,48 +550,35 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
github.com/sylabs/sif/v2 v2.11.5 h1:7ssPH3epSonsTrzbS1YxeJ9KuqAN7ISlSM61a7j/mQM=
github.com/sylabs/sif/v2 v2.11.5/go.mod h1:GBoZs9LU3e4yJH1dcZ3Akf/jsqYgy5SeguJQC+zd75Y=
github.com/sylabs/sif/v2 v2.8.1 h1:whr4Vz12RXfLnYyVGHoD/rD/hbF2g9OW7BJHa+WIqW8=
github.com/sylabs/sif/v2 v2.8.1/go.mod h1:LQOdYXC9a8i7BleTKRw9lohi0rTbXkJOeS9u0ebvgyM=
github.com/sylabs/squashfs v0.6.1 h1:4hgvHnD9JGlYWwT0bPYNt9zaz23mAV3Js+VEgQoRGYQ=
github.com/sylabs/squashfs v0.6.1/go.mod h1:ZwpbPCj0ocIvMy2br6KZmix6Gzh6fsGQcCnydMF+Kx8=
github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw=
github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY=
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8=
github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/vbatts/go-mtree v0.5.3 h1:S/jYlfG8rZ+a0bhZd+RANXejy7M4Js8fq9U+XoWTd5w=
github.com/vbatts/go-mtree v0.5.3/go.mod h1:eXsdoPMdL2jcJx6HweWi9lYQxBsTp4lNhqqAjgkZUg8=
github.com/vbatts/tar-split v0.11.3 h1:hLFqsOLQ1SsppQNTMpkpPXClLDfC2A3Zgy9OUU+RVck=
github.com/vbatts/tar-split v0.11.3/go.mod h1:9QlHN18E+fEH7RdG+QAJJcuya3rqT7eXSTY7wGrAokY=
github.com/vifraa/gopom v0.2.2 h1:zrqoCUVIplcsETouv3xxHPvfI/WV1GUPrdX2+Diahzo=
github.com/vifraa/gopom v0.2.2/go.mod h1:oPa1dcrGrtlO37WPDBm5SqHAT+wTgF8An1Q71Z6Vv4o=
github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651 h1:jIVmlAFIqV3d+DOxazTR9v+zgj8+VYuQBzPgBZvWBHA=
github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651/go.mod h1:b26F2tHLqaoRQf8DywqzVaV1MQ9yvjb0OMcNl7Nxu20=
github.com/wagoodman/go-progress v0.0.0-20230301185719-21920a456ad5 h1:lwgTsTy18nYqASnH58qyfRW/ldj7Gt2zzBvgYPzdA4s=
github.com/wagoodman/go-progress v0.0.0-20230301185719-21920a456ad5/go.mod h1:jLXFoL31zFaHKAAyZUh+sxiTDFe1L1ZHrcK2T1itVKA=
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/vbatts/go-mtree v0.5.2 h1:d8SAbLJiR1cR3pe1J+FBaalRkCQw95gP12/P+a9PUcA=
github.com/vbatts/go-mtree v0.5.2/go.mod h1:e0NDJ+bT3jG7ZINeB9HR5AxTvjskCsOR54+9KoaXyDc=
github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlIME=
github.com/vbatts/tar-split v0.11.2/go.mod h1:vV3ZuO2yWSVsz+pfFzDG/upWH1JhjOiEaWq6kXyQ3VI=
github.com/vifraa/gopom v0.2.1 h1:MYVMAMyiGzXPPy10EwojzKIL670kl5Zbae+o3fFvQEM=
github.com/vifraa/gopom v0.2.1/go.mod h1:oPa1dcrGrtlO37WPDBm5SqHAT+wTgF8An1Q71Z6Vv4o=
github.com/wagoodman/go-partybus v0.0.0-20210627031916-db1f5573bbc5 h1:phTLPgMRDYTizrBSKsNSOa2zthoC2KsJsaY/8sg3rD8=
github.com/wagoodman/go-partybus v0.0.0-20210627031916-db1f5573bbc5/go.mod h1:JPirS5jde/CF5qIjcK4WX+eQmKXdPc6vcZkJ/P0hfPw=
github.com/wagoodman/go-progress v0.0.0-20200731105512-1020f39e6240 h1:r6BlIP7CVZtMlxUQhT40h1IE1TzEgKVqwmsVGuscvdk=
github.com/wagoodman/go-progress v0.0.0-20200731105512-1020f39e6240/go.mod h1:jLXFoL31zFaHKAAyZUh+sxiTDFe1L1ZHrcK2T1itVKA=
github.com/wagoodman/jotframe v0.0.0-20211129225309-56b0d0a4aebb h1:Yz6VVOcLuWLAHYlJzTw7JKnWxdV/WXpug2X0quEzRnY=
github.com/wagoodman/jotframe v0.0.0-20211129225309-56b0d0a4aebb/go.mod h1:nDi3BAC5nEbVbg+WSJDHLbjHv0ZToq8nMPA97XMxF3E=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
@@ -712,13 +596,9 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1 h1:V+UsotZpAVvfj3X/LMoEytoLzSiP6Lg0F7wdVyu9gGg=
github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1/go.mod h1:ly2RBz4mnz1yeuVbQA/VFwGjK3mnHGRj1JuoG336Bis=
go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
go.etcd.io/etcd/client/v2 v2.305.1/go.mod h1:pMEacxZW7o8pg4CrFE7pquyCJJzZvkvdD2RibOCCCGs=
go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 h1:CCriYyAfq1Br1aIYettdHZTy8mBTIPo7We18TuO/bak=
go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
@@ -734,6 +614,7 @@ go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9i
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/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-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@@ -745,13 +626,9 @@ golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWP
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A=
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -792,9 +669,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -838,13 +714,10 @@ golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -919,7 +792,6 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -945,28 +817,21 @@ golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220906165534-d0df966e6959/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c=
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/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.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -977,10 +842,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -996,6 +859,7 @@ golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBn
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@@ -1030,6 +894,7 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
@@ -1042,9 +907,8 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.8.0 h1:vSDcovVPld282ceKgDimkRSC8kpaH1dgyc9UMzlt84Y=
golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4=
golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/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=
@@ -1155,8 +1019,8 @@ google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ6
google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A=
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef h1:uQ2vjV/sHTsWSqdKeLqmwitzgvjMl7o4IdtHwUDXSJY=
google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@@ -1184,8 +1048,8 @@ google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnD
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag=
google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8=
google.golang.org/grpc v1.52.0 h1:kd48UiU7EHsV4rnLyOJRuP/Il/UHE7gdDAQ+SZI7nZk=
google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
@@ -1200,20 +1064,17 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
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/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
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.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@@ -1227,6 +1088,7 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
@@ -1235,30 +1097,41 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw=
modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0=
modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw=
modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY=
lukechampine.com/uint128 v1.1.1 h1:pnxCASz787iMf+02ssImqk6OLt+Z5QHMoZyUXR4z6JU=
lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
modernc.org/cc/v3 v3.36.0 h1:0kmRkTmqNidmu3c7BNDSdVHCxXCkWLmWmCIVX4LUboo=
modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc=
modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw=
modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ=
modernc.org/ccgo/v3 v3.16.6 h1:3l18poV+iUemQ98O3X5OMr97LOqlzis+ytivU4NqGhA=
modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ=
modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk=
modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
modernc.org/libc v1.22.5 h1:91BNch/e5B0uPbJFgqbxXuOnxBQjlS//icfQEGmvyjE=
modernc.org/libc v1.22.5/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY=
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds=
modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/sqlite v1.24.0 h1:EsClRIWHGhLTCX44p+Ri/JLD+vFGo0QGjasg2/F9TlI=
modernc.org/sqlite v1.24.0/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk=
modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY=
modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
modernc.org/tcl v1.15.2 h1:C4ybAYCGJw968e+Me18oW55kD/FexcHbqH2xak1ROSY=
modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg=
modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
modernc.org/z v1.7.3 h1:zDJf6iHjrnB+WRD88stbXokugjyc0/pB91ri1gO6LZY=
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA=
modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A=
modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU=
modernc.org/libc v1.16.7 h1:qzQtHhsZNpVPpeCu+aMIQldXeV1P0vRhSqCL0nOIJOA=
modernc.org/libc v1.16.7/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU=
modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8=
modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/memory v1.1.1 h1:bDOL0DIDLQv7bWhP3gMvIrnoFw+Eo6F7a2QK9HPDiFU=
modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw=
modernc.org/opt v0.1.1 h1:/0RX92k9vwVeDXj+Xn23DKp2VJubL7k8qNffND6qn3A=
modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/sqlite v1.17.3 h1:iE+coC5g17LtByDYDWKpR6m2Z9022YrSh3bumwOnIrI=
modernc.org/sqlite v1.17.3/go.mod h1:10hPVYar9C0kfXuTWGz8s0XtB8uAGymUy51ZzStYe3k=
modernc.org/strutil v1.1.1 h1:xv+J1BXY3Opl2ALrBwyfEikFAj8pmqcpnfmuwUwcozs=
modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw=
modernc.org/tcl v1.13.1 h1:npxzTwFTZYM8ghWicVIX1cRWzj7Nd8i6AqqX2p+IYao=
modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw=
modernc.org/token v1.0.0 h1:a0jaWiNMDhDUtqOj09wvjWWAqd3q7WpBulmL9H2egsk=
modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
modernc.org/z v1.5.1 h1:RTNHdsrOpeoSeOF4FbzTo8gBYByaJ5xT7NgZ9ZqRiJM=
modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
+1 -1
View File
@@ -694,6 +694,6 @@ main() (
set +u
if [ -z "${TEST_INSTALL_SH}" ]; then
set -u
exit $(main "$@")
main "$@"
fi
set -u
+9 -9
View File
@@ -16,20 +16,20 @@ package bus
import "github.com/wagoodman/go-partybus"
var publisher partybus.Publisher
var active bool
// Set sets the singleton event bus publisher. This is optional; if no bus is provided, the library will
// SetPublisher sets the singleton event bus publisher. This is optional; if no bus is provided, the library will
// behave no differently than if a bus had been provided.
func Set(p partybus.Publisher) {
func SetPublisher(p partybus.Publisher) {
publisher = p
}
func Get() partybus.Publisher {
return publisher
if p != nil {
active = true
}
}
// Publish an event onto the bus. If there is no bus set by the calling application, this does nothing.
func Publish(e partybus.Event) {
if publisher != nil {
publisher.Publish(e)
func Publish(event partybus.Event) {
if active {
publisher.Publish(event)
}
}
-32
View File
@@ -1,32 +0,0 @@
package bus
import (
"github.com/wagoodman/go-partybus"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/event"
)
func Exit() {
Publish(partybus.Event{
Type: event.CLIExit,
})
}
func Report(report string) {
if len(report) == 0 {
return
}
report = log.Redactor.RedactString(report)
Publish(partybus.Event{
Type: event.CLIReport,
Value: report,
})
}
func Notify(message string) {
Publish(partybus.Event{
Type: event.CLINotification,
Value: message,
})
}
+27 -90
View File
@@ -3,7 +3,6 @@ package config
import (
"errors"
"fmt"
"os"
"path"
"reflect"
"sort"
@@ -18,9 +17,6 @@ import (
"github.com/anchore/syft/internal"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/pkg/cataloger"
golangCataloger "github.com/anchore/syft/syft/pkg/cataloger/golang"
"github.com/anchore/syft/syft/pkg/cataloger/kernel"
pythonCataloger "github.com/anchore/syft/syft/pkg/cataloger/python"
)
var (
@@ -42,31 +38,25 @@ type Application struct {
ConfigPath string `yaml:"configPath,omitempty" json:"configPath" mapstructure:"config"`
Verbosity uint `yaml:"verbosity,omitempty" json:"verbosity" mapstructure:"verbosity"`
// -q, indicates to not show any status output to stderr (ETUI or logging UI)
Quiet bool `yaml:"quiet" json:"quiet" mapstructure:"quiet"`
Outputs []string `yaml:"output" json:"output" mapstructure:"output"` // -o, the format to use for output
OutputTemplatePath string `yaml:"output-template-path" json:"output-template-path" mapstructure:"output-template-path"` // -t template file to use for output
File string `yaml:"file" json:"file" mapstructure:"file"` // --file, the file to write report output to
CheckForAppUpdate bool `yaml:"check-for-app-update" json:"check-for-app-update" mapstructure:"check-for-app-update"` // whether to check for an application update on start up or not
Dev development `yaml:"dev" json:"dev" mapstructure:"dev"`
Log logging `yaml:"log" json:"log" mapstructure:"log"` // all logging-related options
Catalogers []string `yaml:"catalogers" json:"catalogers" mapstructure:"catalogers"`
Package pkg `yaml:"package" json:"package" mapstructure:"package"`
Golang golang `yaml:"golang" json:"golang" mapstructure:"golang"`
LinuxKernel linuxKernel `yaml:"linux-kernel" json:"linux-kernel" mapstructure:"linux-kernel"`
Python python `yaml:"python" json:"python" mapstructure:"python"`
Attest attest `yaml:"attest" json:"attest" mapstructure:"attest"`
FileMetadata FileMetadata `yaml:"file-metadata" json:"file-metadata" mapstructure:"file-metadata"`
FileClassification fileClassification `yaml:"file-classification" json:"file-classification" mapstructure:"file-classification"`
FileContents fileContents `yaml:"file-contents" json:"file-contents" mapstructure:"file-contents"`
Secrets secrets `yaml:"secrets" json:"secrets" mapstructure:"secrets"`
Registry registry `yaml:"registry" json:"registry" mapstructure:"registry"`
Exclusions []string `yaml:"exclude" json:"exclude" mapstructure:"exclude"`
Platform string `yaml:"platform" json:"platform" mapstructure:"platform"`
Name string `yaml:"name" json:"name" mapstructure:"name"`
Source sourceCfg `yaml:"source" json:"source" mapstructure:"source"`
Parallelism int `yaml:"parallelism" json:"parallelism" mapstructure:"parallelism"` // the number of catalog workers to run in parallel
DefaultImagePullSource string `yaml:"default-image-pull-source" json:"default-image-pull-source" mapstructure:"default-image-pull-source"` // specify default image pull source
BasePath string `yaml:"base-path" json:"base-path" mapstructure:"base-path"` // specify base path for all file paths
Quiet bool `yaml:"quiet" json:"quiet" mapstructure:"quiet"`
Outputs []string `yaml:"output" json:"output" mapstructure:"output"` // -o, the format to use for output
OutputTemplatePath string `yaml:"output-template-path" json:"output-template-path" mapstructure:"output-template-path"` // -t template file to use for output
File string `yaml:"file" json:"file" mapstructure:"file"` // --file, the file to write report output to
CheckForAppUpdate bool `yaml:"check-for-app-update" json:"check-for-app-update" mapstructure:"check-for-app-update"` // whether to check for an application update on start up or not
Dev development `yaml:"dev" json:"dev" mapstructure:"dev"`
Log logging `yaml:"log" json:"log" mapstructure:"log"` // all logging-related options
Catalogers []string `yaml:"catalogers" json:"catalogers" mapstructure:"catalogers"`
Package pkg `yaml:"package" json:"package" mapstructure:"package"`
Attest attest `yaml:"attest" json:"attest" mapstructure:"attest"`
FileMetadata FileMetadata `yaml:"file-metadata" json:"file-metadata" mapstructure:"file-metadata"`
FileClassification fileClassification `yaml:"file-classification" json:"file-classification" mapstructure:"file-classification"`
FileContents fileContents `yaml:"file-contents" json:"file-contents" mapstructure:"file-contents"`
Secrets secrets `yaml:"secrets" json:"secrets" mapstructure:"secrets"`
Registry registry `yaml:"registry" json:"registry" mapstructure:"registry"`
Exclusions []string `yaml:"exclude" json:"exclude" mapstructure:"exclude"`
Platform string `yaml:"platform" json:"platform" mapstructure:"platform"`
Name string `yaml:"name" json:"name" mapstructure:"name"`
Parallelism int `yaml:"parallelism" json:"parallelism" mapstructure:"parallelism"` // the number of catalog workers to run in parallel
}
func (cfg Application) ToCatalogerConfig() cataloger.Config {
@@ -78,18 +68,6 @@ func (cfg Application) ToCatalogerConfig() cataloger.Config {
},
Catalogers: cfg.Catalogers,
Parallelism: cfg.Parallelism,
Golang: golangCataloger.NewGoCatalogerOpts().
WithSearchLocalModCacheLicenses(cfg.Golang.SearchLocalModCacheLicenses).
WithLocalModCacheDir(cfg.Golang.LocalModCacheDir).
WithSearchRemoteLicenses(cfg.Golang.SearchRemoteLicenses).
WithProxy(cfg.Golang.Proxy).
WithNoProxy(cfg.Golang.NoProxy),
LinuxKernel: kernel.LinuxCatalogerConfig{
CatalogModules: cfg.LinuxKernel.CatalogModules,
},
Python: pythonCataloger.CatalogerConfig{
GuessUnpinnedRequirements: cfg.Python.GuessUnpinnedRequirements,
},
}
}
@@ -145,19 +123,6 @@ func (cfg *Application) parseConfigValues() error {
return err
}
}
if err := checkDefaultSourceValues(cfg.DefaultImagePullSource); err != nil {
return err
}
if cfg.Name != "" {
log.Warnf("name parameter is deprecated. please use: source-name. name will be removed in a future version")
if cfg.Source.Name == "" {
cfg.Source.Name = cfg.Name
}
}
// check for valid default source options
// parse nested config options
// for each field in the configuration struct, see if the field implements the parser interface
// note: the app config is a pointer, so we need to grab the elements explicitly (to traverse the address)
@@ -220,7 +185,6 @@ func loadDefaultValues(v *viper.Viper) {
v.SetDefault("check-for-app-update", true)
v.SetDefault("catalogers", nil)
v.SetDefault("parallelism", 1)
v.SetDefault("default-image-pull-source", "")
// for each field in the configuration struct, see if the field implements the defaultValueLoader interface and invoke it if it does
value := reflect.ValueOf(Application{})
@@ -244,7 +208,6 @@ func (cfg Application) String() string {
return string(appaStr)
}
// nolint:funlen
func loadConfig(v *viper.Viper, configPath string) error {
var err error
// use explicitly the given user config
@@ -260,26 +223,13 @@ func loadConfig(v *viper.Viper, configPath string) error {
// start searching for valid configs in order...
// 1. look for .<appname>.yaml (in the current directory)
confFilePath := "." + internal.ApplicationName
// TODO: Remove this before v1.0.0
// See syft #1634
v.AddConfigPath(".")
v.SetConfigName(confFilePath)
// check if config.yaml exists in the current directory
// DEPRECATED: this will be removed in v1.0.0
if _, err := os.Stat("config.yaml"); err == nil {
log.Warn("DEPRECATED: ./config.yaml as a configuration file is deprecated and will be removed as an option in v1.0.0, please rename to .syft.yaml")
}
if _, err := os.Stat(confFilePath + ".yaml"); err == nil {
if err = v.ReadInConfig(); err == nil {
v.Set("config", v.ConfigFileUsed())
return nil
} else if !errors.As(err, &viper.ConfigFileNotFoundError{}) {
return fmt.Errorf("unable to parse config=%q: %w", v.ConfigFileUsed(), err)
}
v.SetConfigName("." + internal.ApplicationName)
if err = v.ReadInConfig(); err == nil {
v.Set("config", v.ConfigFileUsed())
return nil
} else if !errors.As(err, &viper.ConfigFileNotFoundError{}) {
return fmt.Errorf("unable to parse config=%q: %w", v.ConfigFileUsed(), err)
}
// 2. look for .<appname>/config.yaml (in the current directory)
@@ -306,12 +256,11 @@ func loadConfig(v *viper.Viper, configPath string) error {
}
// 4. look for <appname>/config.yaml in xdg locations (starting with xdg home config dir, then moving upwards)
v.SetConfigName("config")
configPath = path.Join(xdg.ConfigHome, internal.ApplicationName)
v.AddConfigPath(configPath)
v.AddConfigPath(path.Join(xdg.ConfigHome, internal.ApplicationName))
for _, dir := range xdg.ConfigDirs {
v.AddConfigPath(path.Join(dir, internal.ApplicationName))
}
v.SetConfigName("config")
if err = v.ReadInConfig(); err == nil {
v.Set("config", v.ConfigFileUsed())
return nil
@@ -320,15 +269,3 @@ func loadConfig(v *viper.Viper, configPath string) error {
}
return nil
}
var validDefaultSourceValues = []string{"registry", "docker", "podman", ""}
func checkDefaultSourceValues(source string) error {
validValues := internal.NewStringSet(validDefaultSourceValues...)
if !validValues.Contains(source) {
validValuesString := strings.Join(validDefaultSourceValues, ", ")
return fmt.Errorf("%s is not a valid default source; please use one of the following: %s''", source, validValuesString)
}
return nil
}
-129
View File
@@ -1,129 +0,0 @@
package config
import (
"fmt"
"os"
"path"
"testing"
"github.com/adrg/xdg"
"github.com/mitchellh/go-homedir"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// TODO: set negative case when config.yaml is no longer a valid option
func TestApplicationConfig(t *testing.T) {
// disable homedir package cache for testing
originalCacheOpt := homedir.DisableCache
homedir.DisableCache = true
t.Cleanup(func() {
homedir.DisableCache = originalCacheOpt
})
// ensure we have no side effects for xdg package for future tests
originalXDG := os.Getenv("XDG_CONFIG_HOME")
t.Cleanup(func() {
// note: we're not using t.Setenv since the effect we're trying to eliminate is within the xdg package
require.NoError(t, os.Setenv("XDG_CONFIG_HOME", originalXDG))
xdg.Reload()
})
// config is picked up at desired configuration paths
// VALID: .syft.yaml, .syft/config.yaml, ~/.syft.yaml, <XDG_CONFIG_HOME>/syft/config.yaml
// DEPRECATED: config.yaml is currently supported by
tests := []struct {
name string
setup func(t *testing.T) string
assertions func(t *testing.T, app *Application)
cleanup func()
}{
{
name: "explicit config",
setup: func(t *testing.T) string {
return "./test-fixtures/.syft.yaml"
}, // no-op for explicit config
assertions: func(t *testing.T, app *Application) {
assert.Equal(t, "test-explicit-config", app.File)
},
},
{
name: "current working directory named config",
setup: func(t *testing.T) string {
err := os.Chdir("./test-fixtures/config-wd-file") // change application cwd to test-fixtures
require.NoError(t, err)
return ""
},
assertions: func(t *testing.T, app *Application) {
assert.Equal(t, "test-wd-named-config", app.File)
},
},
{
name: "current working directory syft dir config",
setup: func(t *testing.T) string {
err := os.Chdir("./test-fixtures/config-dir-test") // change application cwd to test-fixtures
require.NoError(t, err)
return ""
},
assertions: func(t *testing.T, app *Application) {
assert.Equal(t, "test-dir-config", app.File)
},
},
{
name: "home directory file config",
setup: func(t *testing.T) string {
// Because Setenv affects the whole process, it cannot be used in parallel tests or
// tests with parallel ancestors: see separate XDG test for consequence of this
t.Setenv("HOME", "./test-fixtures/config-home-test/config-file")
return ""
},
assertions: func(t *testing.T, app *Application) {
assert.Equal(t, "test-home-config", app.File)
},
},
{
name: "XDG file config",
setup: func(t *testing.T) string {
wd, err := os.Getwd()
require.NoError(t, err)
configDir := path.Join(wd, "./test-fixtures/config-home-test") // set HOME to testdata
// note: this explicitly has multiple XDG paths, make certain we use the first VALID one (not the first one)
t.Setenv("XDG_CONFIG_DIRS", fmt.Sprintf("/another/foo/bar:%s", configDir))
xdg.Reload()
return ""
},
assertions: func(t *testing.T, app *Application) {
assert.Equal(t, "test-home-XDG-config", app.File)
},
cleanup: func() {
require.NoError(t, os.Unsetenv("XDG_CONFIG_DIRS"))
xdg.Reload()
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
if test.cleanup != nil {
t.Cleanup(test.cleanup)
}
wd, err := os.Getwd()
require.NoError(t, err)
defer os.Chdir(wd) // reset working directory after test
application := &Application{}
viperInstance := viper.New()
// this will override home in case you are running this test locally and DO have a syft config
// in your home directory... now it will be ignored. Same for XDG_CONFIG_DIRS.
t.Setenv("HOME", "/foo/bar")
t.Setenv("XDG_CONFIG_DIRS", "/foo/bar")
xdg.Reload()
configPath := test.setup(t)
err = application.LoadAllValues(viperInstance, configPath)
require.NoError(t, err)
test.assertions(t, application)
})
}
}
-19
View File
@@ -1,19 +0,0 @@
package config
import "github.com/spf13/viper"
type golang struct {
SearchLocalModCacheLicenses bool `json:"search-local-mod-cache-licenses" yaml:"search-local-mod-cache-licenses" mapstructure:"search-local-mod-cache-licenses"`
LocalModCacheDir string `json:"local-mod-cache-dir" yaml:"local-mod-cache-dir" mapstructure:"local-mod-cache-dir"`
SearchRemoteLicenses bool `json:"search-remote-licenses" yaml:"search-remote-licenses" mapstructure:"search-remote-licenses"`
Proxy string `json:"proxy" yaml:"proxy" mapstructure:"proxy"`
NoProxy string `json:"no-proxy" yaml:"no-proxy" mapstructure:"no-proxy"`
}
func (cfg golang) loadDefaultValues(v *viper.Viper) {
v.SetDefault("golang.search-local-mod-cache-licenses", false)
v.SetDefault("golang.local-mod-cache-dir", "")
v.SetDefault("golang.search-remote-licenses", false)
v.SetDefault("golang.proxy", "")
v.SetDefault("golang.no-proxy", "")
}
-11
View File
@@ -1,11 +0,0 @@
package config
import "github.com/spf13/viper"
type linuxKernel struct {
CatalogModules bool `json:"catalog-modules" yaml:"catalog-modules" mapstructure:"catalog-modules"`
}
func (cfg linuxKernel) loadDefaultValues(v *viper.Viper) {
v.SetDefault("linux-kernel.catalog-modules", true)
}
-13
View File
@@ -1,13 +0,0 @@
package config
import (
"github.com/spf13/viper"
)
type python struct {
GuessUnpinnedRequirements bool `json:"guess-unpinned-requirements" yaml:"guess-unpinned-requirements" mapstructure:"guess-unpinned-requirements"`
}
func (cfg python) loadDefaultValues(v *viper.Viper) {
v.SetDefault("python.guess-unpinned-requirements", false)
}
-17
View File
@@ -1,17 +0,0 @@
package config
import "github.com/spf13/viper"
type sourceCfg struct {
Name string `json:"name" yaml:"name" mapstructure:"name"`
Version string `json:"version" yaml:"version" mapstructure:"version"`
File fileSource `json:"file" yaml:"file" mapstructure:"file"`
}
type fileSource struct {
Digests []string `json:"digests" yaml:"digests" mapstructure:"digests"`
}
func (cfg sourceCfg) loadDefaultValues(v *viper.Viper) {
v.SetDefault("source.file.digests", []string{"sha256"})
}
-7
View File
@@ -1,7 +0,0 @@
# same as --file; write output report to a file (default is to write to stdout)
file: "test-explicit-config"
package:
cataloger:
scope: "squashed"
# same as --scope; limit the scope of the cataloger to only the specified types
@@ -1,5 +0,0 @@
# same as --file; write output report to a file (default is to write to stdout)
file: "test-dir-config"
package:
cataloger:
scope: "squashed"
@@ -1,5 +0,0 @@
# same as --file; write output report to a file (default is to write to stdout)
file: "test-home-config"
package:
cataloger:
scope: "squashed"
@@ -1,5 +0,0 @@
# same as --file; write output report to a file (default is to write to stdout)
file: "test-home-XDG-config"
package:
cataloger:
scope: "squashed"
@@ -1,5 +0,0 @@
# same as --file; write output report to a file (default is to write to stdout)
file: "test-wd-named-config"
package:
cataloger:
scope: "squashed"
+1 -1
View File
@@ -6,5 +6,5 @@ const (
// JSONSchemaVersion is the current schema version output by the JSON encoder
// This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment.
JSONSchemaVersion = "10.0.0"
JSONSchemaVersion = "6.2.0"
)
-76
View File
@@ -1,76 +0,0 @@
package file
import (
"crypto"
"fmt"
"hash"
"io"
"strings"
"github.com/anchore/syft/syft/file"
)
func supportedHashAlgorithms() []crypto.Hash {
return []crypto.Hash{
crypto.MD5,
crypto.SHA1,
crypto.SHA224,
crypto.SHA256,
crypto.SHA384,
crypto.SHA512,
}
}
func NewDigestsFromFile(closer io.ReadCloser, hashes []crypto.Hash) ([]file.Digest, error) {
// create a set of hasher objects tied together with a single writer to feed content into
hashers := make([]hash.Hash, len(hashes))
writers := make([]io.Writer, len(hashes))
for idx, hashObj := range hashes {
hashers[idx] = hashObj.New()
writers[idx] = hashers[idx]
}
size, err := io.Copy(io.MultiWriter(writers...), closer)
if err != nil {
return nil, err
}
if size == 0 {
return make([]file.Digest, 0), nil
}
result := make([]file.Digest, len(hashes))
// only capture digests when there is content. It is important to do this based on SIZE and not
// FILE TYPE. The reasoning is that it is possible for a tar to be crafted with a header-only
// file type but a body is still allowed.
for idx, hasher := range hashers {
result[idx] = file.Digest{
Algorithm: CleanDigestAlgorithmName(hashes[idx].String()),
Value: fmt.Sprintf("%+x", hasher.Sum(nil)),
}
}
return result, nil
}
func Hashers(names ...string) ([]crypto.Hash, error) {
hashByName := make(map[string]crypto.Hash)
for _, h := range supportedHashAlgorithms() {
hashByName[CleanDigestAlgorithmName(h.String())] = h
}
var hashers []crypto.Hash
for _, hashStr := range names {
hashObj, ok := hashByName[CleanDigestAlgorithmName(hashStr)]
if !ok {
return nil, fmt.Errorf("unsupported hash algorithm: %s", hashStr)
}
hashers = append(hashers, hashObj)
}
return hashers, nil
}
func CleanDigestAlgorithmName(name string) string {
lower := strings.ToLower(name)
return strings.ReplaceAll(lower, "-", "")
}
-132
View File
@@ -1,132 +0,0 @@
package file
import (
"crypto"
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/anchore/syft/syft/file"
)
func TestCleanDigestAlgorithmName(t *testing.T) {
tests := []struct {
name string
input string
want string
}{
{
name: "go case",
input: "SHA-256",
want: "sha256",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.want, CleanDigestAlgorithmName(tt.input))
})
}
}
func TestNewDigestsFromFile(t *testing.T) {
require.NotEmpty(t, supportedHashAlgorithms())
tests := []struct {
name string
fixture string
hashes []crypto.Hash
want []file.Digest
wantErr require.ErrorAssertionFunc
}{
{
name: "check supported hash algorithms",
fixture: "test-fixtures/digest.txt",
hashes: supportedHashAlgorithms(),
want: []file.Digest{
{
Algorithm: "md5",
Value: "e8818a24402ae7f8b874cdd9350c1b51",
},
{
Algorithm: "sha1",
Value: "eea4671d168c81fd52e615ed9fb3531a526f4748",
},
{
Algorithm: "sha224",
Value: "fd993e84c7afb449d34bcae7c5ee118f5c73b50170da05171523b22c",
},
{
Algorithm: "sha256",
Value: "cbf1a703b7e4a67529d6e17114880dfa9f879f3749872e1a9d4a20ac509165ad",
},
{
Algorithm: "sha384",
Value: "1eaded3f17fb8d7b731c9175a0f355d3a35575c3cb6cdda46a5272b632968d7257a5e6437d0efae599a81a1b2dcc81ba",
},
{
Algorithm: "sha512",
Value: "b49d5995456edba144dce750eaa8eae12af8fd08c076d401fcf78aac4172080feb70baaa5ed8c1b05046ec278446330fbf77e8ca9e60c03945ded761a641a7e1",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.wantErr == nil {
tt.wantErr = require.NoError
}
fh, err := os.Open(tt.fixture)
require.NoError(t, err)
got, err := NewDigestsFromFile(fh, tt.hashes)
tt.wantErr(t, err)
if err != nil {
return
}
assert.Equal(t, tt.want, got)
})
}
}
func TestHashers(t *testing.T) {
tests := []struct {
name string
names []string
want []crypto.Hash
wantErr require.ErrorAssertionFunc
}{
{
name: "check supported hash algorithms",
names: []string{"MD-5", "shA1", "sHa224", "sha---256", "sha384", "sha512"},
want: []crypto.Hash{
crypto.MD5,
crypto.SHA1,
crypto.SHA224,
crypto.SHA256,
crypto.SHA384,
crypto.SHA512,
},
},
{
name: "error on unsupported hash algorithm",
names: []string{"made-up"},
wantErr: require.Error,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.wantErr == nil {
tt.wantErr = require.NoError
}
got, err := Hashers(tt.names...)
tt.wantErr(t, err)
if err != nil {
return
}
assert.Equal(t, tt.want, got)
})
}
}
+3 -3
View File
@@ -32,12 +32,12 @@ func ExtractGlobsFromTarToUniqueTempFile(archivePath, dir string, globs ...strin
}
// we have a file we want to extract....
tempFilePrefix := filepath.Base(filepath.Clean(file.Name())) + "-"
tempFile, err := os.CreateTemp(dir, tempFilePrefix)
tempfilePrefix := filepath.Base(filepath.Clean(file.Name())) + "-"
tempFile, err := os.CreateTemp(dir, tempfilePrefix)
if err != nil {
return fmt.Errorf("unable to create temp file: %w", err)
}
// we shouldn't try and keep the tempFile open as the returned result may have several files, which takes up
// we shouldn't try and keep the tempfile open as the returned result may have several files, which takes up
// resources (leading to "too many open files"). Instead we'll return a file opener to the caller which
// provides a ReadCloser. It is up to the caller to handle closing the file explicitly.
defer tempFile.Close()
-1
View File
@@ -1 +0,0 @@
hello, file!

Some files were not shown because too many files have changed in this diff Show More