diff --git a/.github/workflows/makefile.yml b/.github/workflows/makefile.yml index c9a45e6..f23c9d0 100644 --- a/.github/workflows/makefile.yml +++ b/.github/workflows/makefile.yml @@ -8,12 +8,9 @@ on: jobs: build: - runs-on: ubuntu-latest - steps: - uses: actions/checkout@v3 - - name: Build Static run: make ociv - name: upload binary @@ -21,4 +18,9 @@ jobs: with: name: ociv path: ociv - + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Lint Go Code + run: make lint diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8b6e13e --- /dev/null +++ b/.gitignore @@ -0,0 +1,26 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Intellij +.idea/ +*.iml + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +tools/ +.gofmt +.lint +.build +coverage-all.txt +coverage.txt +ociv +log.txt \ No newline at end of file diff --git a/Makefile b/Makefile index 8bf3927..f781276 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,22 @@ +TOP_LEVEL=$(shell git rev-parse --show-toplevel) LDFLAGS=--ldflags '-extldflags "-static"' - GOTAGS= -tags containers_image_openpgp +TOOLSDIR := $(TOP_LEVEL)/tools +GOLINTER_VERSION := v1.54.2 +GOLINTER := $(TOOLSDIR)/bin/golangci-lint + +$(GOLINTER): + mkdir -p $(TOOLSDIR)/bin + curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(TOOLSDIR)/bin $(GOLINTER_VERSION) + $(GOLINTER) version ociv: *.go CGO_ENABLED=0 go build ${LDFLAGS} ${GOTAGS} -o ociv ./... +lint: *.go $(GOLINTER) + $(GOLINTER) run --out-format=colored-line-number + test-image: stacker build -f example-stacker.yaml diff --git a/README.md b/README.md index 8fe09f6..0a391db 100644 --- a/README.md +++ b/README.md @@ -1,31 +1,32 @@ # ociv - Interactive TUI OCI layout inspector -Run it with the name of a directory that has OCI layouts somewhere under it. It will display all layouts and images in the entire tree. +Run it with the name of a directory that has OCI layouts somewhere under it. It +will display all layouts and images in the entire tree. -``` +```bash ociv . ``` ![image](https://github.com/project-machine/oci-viewer/assets/1768106/c9ffd36c-1f4b-4acf-824f-38ae856f9e6b) - use the `j,k,n,p` keys or the mouse to move through the tree in the left pane. -Control-S highlights the search panel which updates the tree with matches as you type. -The first match will be selected automatically. Hit enter to refocus on the tree and select other matches. +Control-S highlights the search panel which updates the tree with matches as +you type. The first match will be selected automatically. Hit enter to refocus +on the tree and select other matches. Control-Q exits. ## known layer name display -ociv will look for a file called `known-layers.json` in `$HOME/.cache/ociv/` ( -or `~$SUDO_USER/.cache/ociv/` if running as root, e.g. to look at an OCI layout +ociv will look for a file called `known-layers.json` in `$HOME/.cache/ociv/` +(or `~$SUDO_USER/.cache/ociv/` if running as root, e.g. to look at an OCI layout in a mounted squashfs filesystem) to annotate any displayed layer hashes with the tags in that file, so as to identify base images easily. The format of that file is a list of `Name,Hash` string pairs like this: -``` json +```json [{"Name": "c3/bird:1.0.56-squashfs", "Hash": "e6539655d80241d1a43ea9b00ba2e56b3cccd2a55027c21ad44f359cded63dea"}, {"Name": "c3/bird:1.0.56", "Hash": "8b54d9ceaa3d8a957e4dcb1c7ff96eb4e39bdd8847a1e0752ef7c0b4f6128b36"}, {"Name": "c3/bird:1.0.57-squashfs", "Hash": "0254746330bb206cc49589b25eb6c4d45430b502ff4318f6bb1225e602a40358"} @@ -36,20 +37,17 @@ The included python script `get-published-layers.py` can be used to generate this file from a registry, or update an existing file when new sets of images are published, for example: -``` +```bash ./get-published-layers.py http://my.registry.tld/v2 myrepoprefix ./known-layers.json ``` -## Summary of base images used in all images in a directory - -If you select a directory or a layout, the display shows summary info about all the images in there, and what base images are or are not in common between them. +## Summary of base images used in all images in a directory +If you select a directory or a layout, the display shows summary info about all +the images in there, and what base images are or are not in common between them. TODO: public screenshot needed - ## Shows OCI Artifacts, Referrers and Notary Signatures ![image](https://github.com/project-machine/oci-viewer/assets/1768106/8b374ce1-e1ec-4179-9497-a064cb373711) - - diff --git a/knowntags.go b/knowntags.go index be14487..1d88cfe 100644 --- a/knowntags.go +++ b/knowntags.go @@ -49,9 +49,7 @@ func getShortStringForNames(names []string) []string { } func getNamesForHash(digest string) []string { - layerNames := []string{} - var ok bool - layerNames, ok = LayerNameMap[digest] + layerNames, ok := LayerNameMap[digest] if !ok { layerNames = []string{"?"} } @@ -77,7 +75,7 @@ func setupWellKnownLayerNames() { jsonBytes, err := os.ReadFile(fname) if err != nil { - log.Printf("WARN: can't read %s: %w", fname, err) + log.Printf("WARN: can't read %s: %s", fname, err.Error()) return } diff --git a/main.go b/main.go index a1a735b..305e864 100644 --- a/main.go +++ b/main.go @@ -40,7 +40,7 @@ func addContentsOfLayout(target *tview.TreeNode, path string) ([]imageInfo, []su oci, err := umoci.OpenLayout(path) if err != nil { - log.Printf("error opening layout at path '%s': %w", path, err) + log.Printf("error opening layout at path '%s': %s", path, err.Error()) return imageInfos, subIndexInfos } defer oci.Close() @@ -94,7 +94,7 @@ func addContentsOfLayout(target *tview.TreeNode, path string) ([]imageInfo, []su SetSelectable(true) referrers, err := getReferrersForImage(oci, path, &imageInfo.manifestDescriptor) if err != nil { - log.Printf("error getting referrers:%w\n", err) + log.Printf("error getting referrers: %s\n", err.Error()) } for _, referrerDescriptor := range referrers.Manifests { @@ -597,8 +597,6 @@ func doTViewStuff(ctxt *cli.Context) error { infoPane.SetText("error") } } - return - } tree.SetSelectedFunc(selfunc) tree.SetChangedFunc(selfunc) diff --git a/ociutils.go b/ociutils.go index 7fade1f..d8e866f 100644 --- a/ociutils.go +++ b/ociutils.go @@ -162,7 +162,7 @@ func getImageInfoString(ref imageref, info imageInfo) string { if info.err != nil { errmsg := fmt.Sprintf("\nERROR reading image: %v\n", info.err) hdr += fmt.Sprintf("\n[red:yellow]ERROR reading image: %v[white:-]\n", info.err) - log.Printf(errmsg) + log.Println(errmsg) } subjectHash, subjectName := info.getSubjectInfo() @@ -181,7 +181,7 @@ func getImageInfoString(ref imageref, info imageInfo) string { uncompressedSizeAnnotation := "missing" if val, ok := layer.Annotations[UmociUncompressedSizeAnnotation]; ok { - uncompressedSizeAnnotation = fmt.Sprintf("%d", val) + uncompressedSizeAnnotation = val } var layerNames = getShortStringForNames(getNamesForHash(digest)) @@ -249,7 +249,7 @@ func getImageInfoString(ref imageref, info imageInfo) string { switch info.configBlob.Descriptor.MediaType { case "application/vnd.oci.image.manifest.v1+json": - configInfo = fmt.Sprintf("got a manifest configblob mediatype, expected?") + configInfo = "got a manifest configblob mediatype, expected?" case "application/vnd.dev.cosign.artifact.sig.v1+json": configInfo = "TODO: cosign artifact" @@ -387,8 +387,8 @@ func loadImageManifest(oci casext.Engine, ref imageref, manifestDescriptor ispec info.displayLabel = fmt.Sprintf("🔒 Notary Signature %s", ref.hash) info.displayName = fmt.Sprintf("Notary Signature %s", ref.hash) case "application/vnd.oci.image.index.v1+json": - info.displayLabel = fmt.Sprintf("🗂 Notary Signature Index") - info.displayName = fmt.Sprintf("Notary Signature Index") + info.displayLabel = "🗂 Notary Signature Index" + info.displayName = "Notary Signature Index" default: info.displayLabel = fmt.Sprintf("unknown mediatype. %s %s", configBlob.Descriptor.MediaType, ref.hash) info.displayName = configBlob.Descriptor.MediaType