diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..216bccd --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +/.idea +/*.iml + +/dist +/coverage.txt +/go-mod-outdated diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ccaf671..9dc5668 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,83 +1,160 @@ -name: CI Workflow -on: [pull_request] +name: ci + +on: + push: + branches: + - 'master' + tags: + - 'v*' + pull_request: jobs: - golangci: + validate: strategy: + fail-fast: false matrix: - go-version: [1.16.x] - platform: [ubuntu-latest] - name: golangci-lint - runs-on: ${{ matrix.platform }} + target: + - lint + - vendor-validate + runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 1 - - uses: actions/setup-go@v2 + - + name: Checkout + uses: actions/checkout@v2 + - + name: Validate + uses: docker/bake-action@v1 with: - go-version: ${{ matrix.go-version }} - - run: | - docker run --rm -v $(pwd):/app -w /app golangci/golangci-lint:v1.37.1 golangci-lint run -v - docker: - strategy: - matrix: - go-version: [1.16.x] - platform: [ubuntu-latest] - name: docker - runs-on: ${{ matrix.platform }} + targets: ${{ matrix.target }} + + coverage: + runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - + name: Checkout + uses: actions/checkout@v2 with: - fetch-depth: 1 - - uses: actions/setup-go@v2 + fetch-depth: 0 + - + name: Test + uses: docker/bake-action@v1 with: - go-version: ${{ matrix.go-version }} - - run: | - docker build -t psampaz/go-mod-outdated . - go list -u -m -json all | docker run --rm -i psampaz/go-mod-outdated - tests: + targets: test + - + name: Upload coverage + uses: codecov/codecov-action@v2 + with: + file: ./coverage.txt + + e2e: + runs-on: ${{ matrix.os }} strategy: matrix: - go-version: [1.14.x, 1.15.x, 1.16.x] - platform: [ubuntu-latest, macos-latest] - name: tests - runs-on: ${{ matrix.platform }} + os: + - ubuntu-latest + - macos-latest + go-version: + - 1.14 + - 1.15 + - 1.16 steps: - - uses: actions/checkout@v2 + - + name: Checkout + uses: actions/checkout@v2 with: fetch-depth: 1 - - uses: actions/setup-go@v2 + - + name: Set up Go + uses: actions/setup-go@v2 with: go-version: ${{ matrix.go-version }} - - run: | + - + name: Test + run: | go get github.com/mfridman/tparse go test -v -race -cover -json ./... | $(go env GOPATH)/bin/tparse -all - - run: | + - + name: Integration + run: | go install - mkdir hugo + mkdir hugo cp internal/runner/testdata/hugo_0_53_go.mod hugo/go.mod cd hugo - go list -u -m -json all | $(go env GOPATH)/bin/go-mod-outdated - coverage: - strategy: - matrix: - go-version: [1.16.x] - platform: [ubuntu-latest] - name: coverage - runs-on: ${{ matrix.platform }} + go list -u -m -json all | $(go env GOPATH)/bin/go-mod-outdated + + build: + runs-on: ubuntu-latest + needs: + - validate + - coverage + env: + DOCKERHUB_SLUG: psampaz/go-mod-outdated steps: - - uses: actions/checkout@v2 + - + name: Checkout + uses: actions/checkout@v2 with: - fetch-depth: 1 - - uses: actions/setup-go@v2 + fetch-depth: 0 + - + name: Docker meta + id: meta + uses: docker/metadata-action@v3 with: - go-version: ${{ matrix.go-version }} - - run: | - go test -v -race -cover -coverprofile=coverage.txt -covermode=atomic ./... - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v1 + images: | + ${{ env.DOCKERHUB_SLUG }} + tags: | + type=semver,pattern={{version}} + type=ref,event=pr + type=edge + labels: | + org.opencontainers.image.title=go-mod-outdated + org.opencontainers.image.description=Find outdated dependencies of your Go projects + - + name: Set up QEMU + uses: docker/setup-qemu-action@v1 + - + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + - + name: Login to DockerHub + if: github.event_name != 'pull_request' + uses: docker/login-action@v1 with: - token: ${{secrets.CODECOV_TOKEN}} - file: ./coverage.txt - flags: unittests - name: codecov-umbrella + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - + name: Build artifacts + uses: docker/bake-action@v1 + with: + targets: artifact-all + - + name: Move artifacts + run: | + mv ./dist/**/* ./dist/ + - + name: Upload artifacts + uses: actions/upload-artifact@v2 + with: + name: go-mod-outdated + path: ./dist/* + if-no-files-found: error + - + name: Build image + uses: docker/bake-action@v1 + with: + files: | + ./docker-bake.hcl + ${{ steps.meta.outputs.bake-file }} + targets: image-all + push: ${{ github.event_name != 'pull_request' }} + - + name: GitHub Release + uses: softprops/action-gh-release@v1 + if: startsWith(github.ref, 'refs/tags/') + with: + draft: true + files: | + dist/*.tar.gz + dist/*.zip + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index a1debdf..216bccd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ -.idea -vendor -go-mod-outdated \ No newline at end of file +/.idea +/*.iml + +/dist +/coverage.txt +/go-mod-outdated diff --git a/Dockerfile b/Dockerfile index d1c372c..4f412fe 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,33 @@ -FROM golang:1.16.3-alpine3.13 +# syntax=docker/dockerfile:1.3 +ARG GO_VERSION + +FROM --platform=$BUILDPLATFORM crazymax/goreleaser-xx:latest AS goreleaser-xx +FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine AS base RUN apk add --no-cache git -WORKDIR /home -COPY ./ . -RUN CGO_ENABLED=0 GOOS=linux go build -ldflags "-s -w" -o go-mod-outdated . +COPY --from=goreleaser-xx / / +WORKDIR /src + +FROM base AS build +ARG TARGETPLATFORM +ARG GIT_REF +RUN --mount=type=bind,target=/src,rw \ + --mount=type=cache,target=/root/.cache/go-build \ + --mount=target=/go/pkg/mod,type=cache \ + goreleaser-xx --debug \ + --name "go-mod-outdated" \ + --dist "/out" \ + --hooks="go mod tidy" \ + --hooks="go mod download" \ + --ldflags="-s -w" \ + --files="CHANGELOG.md" \ + --files="LICENSE" \ + --files="README.md" + +FROM scratch AS artifacts +COPY --from=build /out/*.tar.gz / +COPY --from=build /out/*.zip / FROM scratch WORKDIR /home/ -COPY --from=0 /home/go-mod-outdated . +COPY --from=build /usr/local/bin/go-mod-outdated . ENTRYPOINT ["./go-mod-outdated"] diff --git a/README.md b/README.md index f52b101..10667b2 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ -![Build Status](https://github.com/psampaz/go-mod-outdated/workflows/CI%20Workflow/badge.svg) +[![Build Status](https://github.com/psampaz/go-mod-outdated/workflows/ci/badge.svg)](https://github.com/psampaz/go-mod-outdated/actions?query=workflow%3Aci) [![codecov](https://codecov.io/gh/psampaz/go-mod-outdated/branch/master/graph/badge.svg)](https://codecov.io/gh/psampaz/go-mod-outdated) [![Go Report Card](https://goreportcard.com/badge/github.com/psampaz/go-mod-outdated)](https://goreportcard.com/report/github.com/psampaz/go-mod-outdated) [![GoDoc](https://godoc.org/github.com/psampaz/go-mod-outdated?status.svg)](https://pkg.go.dev/github.com/psampaz/go-mod-outdated) - +[![Docker Pulls](https://img.shields.io/docker/pulls/psampaz/go-mod-outdated.svg?logo=docker)](https://hub.docker.com/r/psampaz/go-mod-outdated/) # go-mod-outdated An easy way to find outdated dependencies of your Go projects. -go-mod-outdated provides a table view of the **go list -u -m -json all** command which lists all dependencies of a Go project and their available minor and patch updates. It also provides a way to filter indirect dependencies and dependencies without updates. +go-mod-outdated provides a table view of the `go list -u -m -json all` command which lists all dependencies of a Go project and their available minor and patch updates. It also provides a way to filter indirect dependencies and dependencies without updates. In short it turns this: @@ -42,24 +42,39 @@ into this | github.com/BurntSushi/toml | v0.0.0-20170626110600-a368813c5e64 | v0.3.1 | true | true | +-------------------------------------------+--------------------------------------+------------------------------------+--------+--------+---------+ ``` + +___ + +* [Installation](#installation) +* [Usage](#usage) + * [Docker](#docker) + * [CI pipelines](#ci-pipelines) + * [Help](#help) + * [Shortcut](#shortcut) +* [Invalid timestamps](#invalid-timestamps) +* [Important note](#important-note) +* [Build](#build) +* [Real example](#real-example) + ## Installation -``` +```shell go get -u github.com/psampaz/go-mod-outdated ``` or -```go +```shell go install github.com/psampaz/go-mod-outdated@v0.8.0 ``` if you are on go 1.16 ## Usage + In the folder where your go.mod lives run -``` +```shell go list -u -m -json all | go-mod-outdated ``` @@ -67,25 +82,25 @@ to see all modules in table view. If you want to see only the modules with updates run -``` +```shell go list -u -m -json all | go-mod-outdated -update ``` If you want to see only the direct depedencies run -``` +```shell go list -u -m -json all | go-mod-outdated -direct ``` If you want to see only the direct depedencies that have updates run -``` +```shell go list -u -m -json all | go-mod-outdated -update -direct ``` To output a markdown compatible table, pass the `-style markdown` option -``` +```shell go list -u -m -json all | go-mod-outdated -style markdown ``` @@ -96,42 +111,51 @@ you will get the following error: ``` $ go list -u -m -json all - + go list -m: can't determine available upgrades using the vendor directory (Use -mod=mod or -mod=readonly to bypass.) ``` The following will work: -``` - go list -u -m -mod=mod -json all | go-mod-outdated +```shell +go list -u -m -mod=mod -json all | go-mod-outdated ``` -``` - go list -u -m -mod=readonly -json all | go-mod-outdated +```shell +go list -u -m -mod=readonly -json all | go-mod-outdated ``` ### Docker -In the folder where your go.mod lives run -``` + +In the folder where your `go.mod` lives run + +```shell go list -u -m -json all | docker run -i psampaz/go-mod-outdated ``` + To use parameters just append -``` + +```shell go list -u -m -json all | docker run -i psampaz/go-mod-outdated -update ``` + ### CI pipelines Using the -ci flag will the make the command exit with none zero code, breaking this way your ci pipelines. If you want to make your CI pipeline fail if **any direct or indirect** dependency is outdated use the following: -``` + +```shell go list -u -m -json all | go-mod-outdated -ci ``` + If you want to make your CI pipeline fail **only if a direct** dependency is outdated use the following: -``` + +```shell go list -u -m -json all | go-mod-outdated -direct -ci ``` + ### Help In order to see details about the usage of the command use the **-h** or **-help** flag @@ -152,10 +176,10 @@ Usage of go-mod-outdated: ### Shortcut -If **go list -u -m -json all | go-mod-outdated -update -direct** seems too difficult to use or remember you can create +If `go list -u -m -json all | go-mod-outdated -update -direct` seems too difficult to use or remember you can create a shortcut using an alias. In linux try one of the following: -``` +```shell alias gmo="go list -u -m -json all | go-mod-outdated" alias gmod="go list -u -m -json all | go-mod-outdated -direct" @@ -179,23 +203,33 @@ to indicate breaking changes, but still everything relies on each module develop there is a fully automated way to detect breaking changes in a codebase, a good practice to avoid surpises is to write tests and avoid dependencies on modules not well maintained and documented. +## Build -## Supported Go versions +```shell +git clone https://github.com/psampaz/go-mod-outdated.git +cd go-mod-outdated -- 1.13.x -- 1.14.x -- 1.15.x +# validate (golangci-lint, vendor) +docker buildx bake validate -## Supported operating systems +# test with coverage +docker buildx bake test -- linux -- osx +# build docker image and output to docker with psampaz/go-mod-outdated:local tag (default) +docker buildx bake + +# build multi-platform image +docker buildx bake image-all + +# create artifacts in ./dist +docker buildx bake artifact-all +``` ## Real Example The following example is based on Hugo's go.mod (v0.53) (https://raw.githubusercontent.com/gohugoio/hugo/v0.53/go.mod) -### Json output of go list -u -m json all command +### Json output of `go list -u -m json all` command ``` $ go list -u -m -json all @@ -284,7 +318,8 @@ $ go list -u -m -json all } ``` -### Table view of go list -u -m -json all command using go-mod-outdated +### Table view of `go list -u -m -json all` command using go-mod-outdated + ``` $ go list -u -m -json all | go-mod-outdated +-------------------------------------------+--------------------------------------+------------------------------------+--------+------------------+ @@ -375,7 +410,7 @@ $ go list -u -m -json all | go-mod-outdated +-------------------------------------------+--------------------------------------+------------------------------------+--------+------------------+ ``` -### Table view of go list -u -m -json all command using go-mod-outdated (only dependencies with updates) +### Table view of `go list -u -m -json all` command using go-mod-outdated (only dependencies with updates) ``` $ go list -u -m -json all | go-mod-outdated -update @@ -421,7 +456,8 @@ $ go list -u -m -json all | go-mod-outdated -update | golang.org/x/sys | v0.0.0-20181206074257-70b957f3b65e | v0.0.0-20190419153524-e8e3143a4f4a | false | true | +-------------------------------------------+--------------------------------------+------------------------------------+--------+------------------+ ``` -### Table view of go list -u -m -json all command using go-mod-outdated (only direct dependencies with updates) + +### Table view of `go list -u -m -json all` command using go-mod-outdated (only direct dependencies with updates) ``` $ go list -u -m -json all | go-mod-outdated -update -direct @@ -451,7 +487,8 @@ $ go list -u -m -json all | go-mod-outdated -update -direct | golang.org/x/sync | v0.0.0-20180314180146-1d60e4601c6f | v0.0.0-20190412183630-56d357773e84 | true | true | +------------------------------------+--------------------------------------+------------------------------------+--------+------------------+ ``` -### Table view of go list -u -m -json all command using go-mod-outdated (with -ci flag, only direct dependencies with updates) + +### Table view of `go list -u -m -json all` command using go-mod-outdated (with -ci flag, only direct dependencies with updates) ``` $ go list -u -m -json all | go-mod-outdated -update -direct -ci diff --git a/docker-bake.hcl b/docker-bake.hcl new file mode 100644 index 0000000..a670def --- /dev/null +++ b/docker-bake.hcl @@ -0,0 +1,96 @@ +// Go version +variable "GO_VERSION" { + default = "1.16.3" +} + +// GitHub reference as defined in GitHub Actions (eg. refs/head/master)) +variable "GITHUB_REF" { + default = "" +} + +// Common args for inheritance +target "_common" { + args = { + GO_VERSION = GO_VERSION + GIT_REF = GITHUB_REF + } +} + +// Special target: https://github.com/docker/metadata-action#bake-definition +target "docker-metadata-action" { + tags = ["psampaz/go-mod-outdated:local"] +} + +group "default" { + targets = ["image-local"] +} + +group "validate" { + targets = ["lint", "vendor-validate"] +} + +target "lint" { + inherits = ["_common"] + dockerfile = "./hack/lint.Dockerfile" + target = "lint" + output = ["type=cacheonly"] +} + +target "vendor-validate" { + inherits = ["_common"] + dockerfile = "./hack/vendor.Dockerfile" + target = "validate" + output = ["type=cacheonly"] +} + +target "vendor-update" { + inherits = ["_common"] + dockerfile = "./hack/vendor.Dockerfile" + target = "update" + output = ["."] +} + +target "test" { + inherits = ["_common"] + dockerfile = "./hack/test.Dockerfile" + target = "test-coverage" + output = ["."] +} + +target "artifact" { + inherits = ["_common"] + target = "artifacts" + output = ["./dist"] +} + +target "artifact-all" { + inherits = ["artifact"] + platforms = [ + "darwin/amd64", + "darwin/arm64", + "linux/amd64", + "linux/arm/v6", + "linux/arm/v7", + "linux/arm64", + "windows/amd64" + ] +} + +target "image" { + inherits = ["_common", "docker-metadata-action"] +} + +target "image-local" { + inherits = ["image"] + output = ["type=docker"] +} + +target "image-all" { + inherits = ["image"] + platforms = [ + "linux/amd64", + "linux/arm/v6", + "linux/arm/v7", + "linux/arm64" + ] +} diff --git a/go.sum b/go.sum index c23b7bf..1475730 100644 --- a/go.sum +++ b/go.sum @@ -3,7 +3,6 @@ github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRR github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= 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/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= diff --git a/hack/lint.Dockerfile b/hack/lint.Dockerfile new file mode 100644 index 0000000..729fe2a --- /dev/null +++ b/hack/lint.Dockerfile @@ -0,0 +1,14 @@ +# syntax=docker/dockerfile:1.3 +ARG GO_VERSION + +FROM golang:${GO_VERSION}-alpine AS base +RUN apk add --no-cache gcc linux-headers musl-dev +WORKDIR /src + +FROM golangci/golangci-lint:v1.37.1-alpine AS golangci-lint +FROM base AS lint +RUN --mount=type=bind,target=. \ + --mount=type=cache,target=/root/.cache/go-build \ + --mount=type=cache,target=/root/.cache/golangci-lint \ + --mount=from=golangci-lint,source=/usr/bin/golangci-lint,target=/usr/bin/golangci-lint \ + golangci-lint run --timeout 10m0s ./... diff --git a/hack/test.Dockerfile b/hack/test.Dockerfile new file mode 100644 index 0000000..49b4298 --- /dev/null +++ b/hack/test.Dockerfile @@ -0,0 +1,21 @@ +# syntax=docker/dockerfile:1.2 +ARG GO_VERSION + +FROM golang:${GO_VERSION}-alpine AS base +RUN apk add --no-cache gcc linux-headers musl-dev +WORKDIR /src + +FROM base AS gomod +RUN --mount=type=bind,target=.,rw \ + --mount=type=cache,target=/go/pkg/mod \ + go mod tidy && go mod download + +FROM gomod AS test +RUN --mount=type=bind,target=. \ + --mount=type=cache,target=/go/pkg/mod \ + --mount=type=cache,target=/root/.cache/go-build \ + go test -v -coverprofile=/tmp/coverage.txt -covermode=atomic -race ./... && \ + go tool cover -func=/tmp/coverage.txt + +FROM scratch AS test-coverage +COPY --from=test /tmp/coverage.txt /coverage.txt diff --git a/hack/vendor.Dockerfile b/hack/vendor.Dockerfile new file mode 100644 index 0000000..1705ff5 --- /dev/null +++ b/hack/vendor.Dockerfile @@ -0,0 +1,24 @@ +# syntax=docker/dockerfile:1.2 +ARG GO_VERSION + +FROM golang:${GO_VERSION}-alpine AS base +RUN apk add --no-cache git linux-headers musl-dev +WORKDIR /src + +FROM base AS vendored +RUN --mount=type=bind,target=.,rw \ + --mount=type=cache,target=/go/pkg/mod \ + go mod tidy && go mod download && \ + mkdir /out && cp go.mod go.sum /out + +FROM scratch AS update +COPY --from=vendored /out / + +FROM vendored AS validate +RUN --mount=type=bind,target=.,rw \ + git add -A && cp -rf /out/* .; \ + if [ -n "$(git status --porcelain -- go.mod go.sum)" ]; then \ + echo >&2 'ERROR: Vendor result differs. Please vendor your package with "docker buildx bake vendor-update"'; \ + git status --porcelain -- go.mod go.sum; \ + exit 1; \ + fi