From 3b9783cee634545d9757a5c28c511b8435ed28ef Mon Sep 17 00:00:00 2001 From: ptrus Date: Mon, 16 Oct 2023 17:14:45 +0200 Subject: [PATCH] tools/gomod-updater: Fix Go Dependabot PR's automatically --- .github/workflows/ci-dependabot-fixup.yml | 93 +++++++++++++++++++++++ .github/workflows/ci-test.yaml | 4 + .gitignore | 1 + tools/gomod-updater/go.mod | 13 ++++ tools/gomod-updater/go.sum | 12 +++ tools/gomod-updater/main.go | 77 +++++++++++++++++++ 6 files changed, 200 insertions(+) create mode 100644 .github/workflows/ci-dependabot-fixup.yml create mode 100644 tools/gomod-updater/go.mod create mode 100644 tools/gomod-updater/go.sum create mode 100644 tools/gomod-updater/main.go diff --git a/.github/workflows/ci-dependabot-fixup.yml b/.github/workflows/ci-dependabot-fixup.yml new file mode 100644 index 0000000000..b078bd3ca2 --- /dev/null +++ b/.github/workflows/ci-dependabot-fixup.yml @@ -0,0 +1,93 @@ +# NOTE: This name appears in GitHub's Checks API and in workflow's status badge. +name: ci-dependabot-fixup + +# Trigger the workflow when: +on: + # When a pull request event occurs for a pull request against one of the + # matched branches. + pull_request: + types: [opened, synchronize, reopened] + branches: + - main + +# Cancel in-progress jobs on same branch. +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + check-dependabot: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.ref }} + fetch-depth: "0" + + - name: Ensure Dependebot author + id: check + run: | + # Ensure Dependabot author. + if [ "${{ github.event.pull_request.user.login }}" != "dependabot[bot]" ]; then + echo "This PR was not created by Dependabot. No further action is being taken." + echo "::set-output name=skip::true" + exit 0; + fi + + # Ensure only Dependabot commits. + git fetch --no-tags origin +refs/heads/${BASE_BRANCH}:refs/remotes/origin/${BASE_BRANCH} + if git log origin/${BASE_BRANCH}..HEAD --pretty=format:'%an' | grep -v '^dependabot\[bot\]$' | grep -q . + then + echo "This PR has commits not by Dependabot." + echo "::set-output name=skip::true" + exit 0; + fi + + echo "All commits are by Dependabot." + env: + BASE_BRANCH: ${{ github.base_ref }} + + - name: Set up Go + if: steps.check.outputs.skip != 'true' + uses: actions/setup-go@v4 + with: + go-version: "1.21.x" + + - name: Build gomod updater + if: steps.check.outputs.skip != 'true' + working-directory: tools/gomod-updater + run: go build + + # Dependabot titles are: + # : bump from to in + # e.g. client-sdk/go: bump github.com/ethereum/go-ethereum from 1.12.1 to 1.13.3 in /client-sdk/go + # as long as the (configurable) is without whitespace, the bellow parsing should work. + - name: Try extracting package name and version + if: steps.check.outputs.skip != 'true' + id: extract + run: | + title="${{ github.event.pull_request.title }}" + repo=$(echo $title | awk '{print $3}') + version=$(echo $title | awk '{print $7}') + + # Set the output variables for subsequent steps + echo "::set-output name=repo::$repo" + echo "::set-output name=version::$version" + + - name: Run gomod updater + if: steps.check.outputs.skip != 'true' + run: | + file_list=$(find . -type f -name 'go.mod' | awk -vORS=, '{ print $1 }' | sed 's/,$/\n/') + tools/gomod-updater/gomod-updater ${{ steps.extract.outputs.repo }} ${{ steps.extract.outputs.version }} --packages "$file_list" + + - name: Commit and push all changed files + if: steps.check.outputs.skip != 'true' + env: + CI_COMMIT_MESSAGE: Dependabot dependencies fixup 👷 + CI_COMMIT_AUTHOR: Dependabot Corrector + run: | + git config --global user.name "${{ env.CI_COMMIT_AUTHOR }}" + git config --global user.email "ptrus@users.noreply.github.com" + git commit -a -m "${{ env.CI_COMMIT_MESSAGE }}" + git push diff --git a/.github/workflows/ci-test.yaml b/.github/workflows/ci-test.yaml index 920fd692c1..83cf5e5747 100644 --- a/.github/workflows/ci-test.yaml +++ b/.github/workflows/ci-test.yaml @@ -126,6 +126,10 @@ jobs: working-directory: tools/gen_runtime_vectors run: go build + - name: Test build gomod updater + working-directory: tools/gomod-updater + run: go build + typecheck: # NOTE: This name appears in GitHub's Checks API. name: typecheck diff --git a/.gitignore b/.gitignore index f9d5e66eb6..4d4542da11 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,4 @@ tests/benchmark/benchmark client-sdk/ts-web/core/reflect-go/reflect-go tools/gen_runtime_vectors/gen_runtime_vectors tools/orc/orc +tools/gomod-updater/gomod-updater diff --git a/tools/gomod-updater/go.mod b/tools/gomod-updater/go.mod new file mode 100644 index 0000000000..5cd60d88c1 --- /dev/null +++ b/tools/gomod-updater/go.mod @@ -0,0 +1,13 @@ +module github.com/oasisprotocol/oasis-sdk/tools/gomod-updater + +go 1.21 + +toolchain go1.21.2 + +require ( + github.com/spf13/cobra v1.7.0 + github.com/spf13/pflag v1.0.5 + golang.org/x/mod v0.13.0 +) + +require github.com/inconshreveable/mousetrap v1.1.0 // indirect diff --git a/tools/gomod-updater/go.sum b/tools/gomod-updater/go.sum new file mode 100644 index 0000000000..a54a80db4b --- /dev/null +++ b/tools/gomod-updater/go.sum @@ -0,0 +1,12 @@ +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +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/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= +golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/tools/gomod-updater/main.go b/tools/gomod-updater/main.go new file mode 100644 index 0000000000..40aef03772 --- /dev/null +++ b/tools/gomod-updater/main.go @@ -0,0 +1,77 @@ +package main + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + + "github.com/spf13/cobra" + flag "github.com/spf13/pflag" + "golang.org/x/mod/modfile" +) + +var ( + packages []string + + rootCmd = &cobra.Command{ + Use: "updater [--packages ,...]]", + Short: "Utility for updating go packages in the oasis-sdk repo", + Version: "0.1.0", + Args: cobra.ExactArgs(2), + Run: func(cmd *cobra.Command, args []string) { + pkg, version := args[0], args[1] + + // Go through all packages and update the dependency (if it exists). + for _, path := range packages { + data, err := os.ReadFile(path) + if err != nil { + cobra.CheckErr(fmt.Errorf("failed to read go.mod file: %w", err)) + } + file, err := modfile.ParseLax(path, data, nil) + if err != nil { + cobra.CheckErr(fmt.Errorf("failed to parse go.mod file: %w", err)) + } + var requiresPkg bool + for _, req := range file.Require { + if !req.Indirect && req.Mod.Path == pkg { + requiresPkg = true + break + } + } + if !requiresPkg { + // Nothing to do. + continue + } + fmt.Println("Updating", path) + + // Update the dependency. + cmd := exec.Command("go", "get", "-u", pkg+"@v"+version) + cmd.Dir = filepath.Dir(path) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + err = cmd.Run() + if err != nil { + cobra.CheckErr(fmt.Errorf("failed to update dependency: %w", err)) + } + // Tidy. + cmd = exec.Command("go", "mod", "tidy") + cmd.Dir = filepath.Dir(path) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + err = cmd.Run() + if err != nil { + cobra.CheckErr(fmt.Errorf("failed to run go mod tidy: %w", err)) + } + } + }, + } +) + +func main() { + flags := flag.NewFlagSet("", flag.ContinueOnError) + flags.StringSliceVar(&packages, "packages", []string{"./go.mod"}, "go.mod files to update") + rootCmd.Flags().AddFlagSet(flags) + + _ = rootCmd.Execute() +}