Repository: libp2p/go-libp2p
Pull request #2471 (2023-08-11 06:44:18)

ci: copy new Unified CI templates and bump go.mod to Go 1.20
This PR was created automatically by the @web3-bot as a part of the [Unified CI](https://github.com/pl-strflt/uci) project.

galargh: > go: command not found

There's no default Go installed on self-hosted runners. I'm on it. Will put it in draft as I resolve all the issues.
galargh: Alright! It's ready for review again. I was able to resolve the issue with using self-hosted runners from reusable workflows across organizations (see https://github.com/libp2p/go-libp2p/pull/2471#discussion_r1291304814 for details).

Before we can proceed with the merge, we have 2 conversations that should be resolved:

#### [What should the jobs of Go test workflow be called?](https://github.com/libp2p/go-libp2p/pull/2471#discussion_r1290973799)

1. **Option 1**: _ubuntu (go this)_, _ubuntu (go next)_, ...
2. **Option 2**: _ubuntu (go 1.20.x)_, _ubuntu (go 1.21.x)_, ...

The advantage of _Option 1_ is that we won't have to touch Go test workflow when we upgrade Go version in go.mod. Also, it makes it possible to mark Go test workflow jobs as required checks since the names are stable.

#### [What triggers should we use for Go test and Go check workflows?](https://github.com/libp2p/go-libp2p/pull/2471#discussion_r1291080450)

We cannot leave the _branches_ filter as-is because it's unmaintainable. 

1. **Option 1**: Remove _branches_ filter entirely
2. **Option 2**: Set _branches_ filter to `["master","release-v0[0-9][0-9]"]`

_Option 1_ ensures that each PR is tested both as-is and as-if it was merged. _Option 2_, on the other hand, reduces the number of triggered workflows.

---

@marten-seemann Please let me know what you think 😄 As soon as we resolve these, I'll use @web3-bot to update to Go 1.21.x here.
marten-seemann: > Before we can proceed with the merge, we have 2 conversations that should be resolved:
> 
> #### [What should the jobs of Go test workflow be called?](https://github.com/libp2p/go-libp2p/pull/2471#discussion_r1290973799)
> 1. **Option 1**: _ubuntu (go this)_, _ubuntu (go next)_, ...
> 2. **Option 2**: _ubuntu (go 1.20.x)_, _ubuntu (go 1.21.x)_, ...

I think it pays off to be explicit here (i.e. option 2). I've run into quite a lot of situations where builds failed with one Go version, but not the other. Having another level of mental indirection here makes debugging more difficult. Is there any way to dynamically set the name of the job?

ymmv, but I don't care much about required checks. We don't merge any PR that has _any_ CI failure, so there's little value in explicitly marking any particular check as required. They all are (implicitly).
galargh: > Is there any way to dynamically set the name of the job?

Unfortunately not, we can only set the name before the job starts. We could parse the version strings in a another job that runs before all the others, but I think that would be a waste of a job. 

I restored the explicit setting of go-versions for the go-test job. This should reflect in the checks shortly.

I also updated the push triggers to reflect the protected branches' patterns. If you want me to remove the filter entirely, let me know.
galargh: I added the Go version bump to this branch too not to delay the process :) 
galargh: Go Checks is failing because `go fix` that we run as part of the Go version bump modified proto generated code. I'll revert the changes to the generated code and remove go fix from the Go update procedure. Why the proto generate code is not go fix compliant is a whole different story. 
galargh: And I updated `quic-go` with `go get -u github.com/quic-go/quic-go`. Disclaimer, I haven't looked if any additional changes are required.
galargh: As discussed with @marten-seemann, I reverted the Go version upgrade changes because they're being worked on separately.

Patch:
diff --git a/.github/workflows/go-check.yml b/.github/workflows/go-check.yml
index cc65ce68..c469d491 100644
--- a/.github/workflows/go-check.yml
+++ b/.github/workflows/go-check.yml
@@ -1,67 +1,18 @@
-# File managed by web3-bot. DO NOT EDIT.
-# See https://github.com/protocol/.github/ for details.
-
-on: [push, pull_request]
 name: Go Checks
 
+on:
+  pull_request:
+  push:
+    branches: ["master","release-v0[0-9][0-9]"]
+  workflow_dispatch:
+
+permissions:
+  contents: read
+
+concurrency:
+  group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event_name == 'push' && github.sha || github.ref }}
+  cancel-in-progress: true
+
 jobs:
-  unit:
-    runs-on: ubuntu-latest
-    name: All
-    steps:
-      - uses: actions/checkout@v3
-        with:
-          submodules: recursive
-      - id: config
-        uses: protocol/.github/.github/actions/read-config@master
-      - uses: actions/setup-go@v3
-        with:
-          go-version: 1.20.x
-      - name: Run repo-specific setup
-        uses: ./.github/actions/go-check-setup
-        if: hashFiles('./.github/actions/go-check-setup') != ''
-      - name: Install staticcheck
-        run: go install honnef.co/go/tools/cmd/staticcheck@4970552d932f48b71485287748246cf3237cebdf # 2023.1 (v0.4.0)
-      - name: Check that go.mod is tidy
-        uses: protocol/multiple-go-modules@v1.2
-        with:
-          run: |
-            go mod tidy
-            if [[ -n $(git ls-files --other --exclude-standard --directory -- go.sum) ]]; then
-              echo "go.sum was added by go mod tidy"
-              exit 1
-            fi
-            git diff --exit-code -- go.sum go.mod
-      - name: gofmt
-        if: success() || failure() # run this step even if the previous one failed
-        run: |
-          out=$(gofmt -s -l .)
-          if [[ -n "$out" ]]; then
-            echo $out | awk '{print "::error file=" $0 ",line=0,col=0::File is not gofmt-ed."}'
-            exit 1
-          fi
-      - name: go vet
-        if: success() || failure() # run this step even if the previous one failed
-        uses: protocol/multiple-go-modules@v1.2
-        with:
-          run: go vet ./...
-      - name: staticcheck
-        if: success() || failure() # run this step even if the previous one failed
-        uses: protocol/multiple-go-modules@v1.2
-        with:
-          run: |
-            set -o pipefail
-            staticcheck ./... | sed -e 's@\(.*\)\.go@./\1.go@g'
-      - name: go generate
-        uses: protocol/multiple-go-modules@v1.2
-        if: (success() || failure()) && fromJSON(steps.config.outputs.json).gogenerate == true
-        with:
-          run: |
-            git clean -fd # make sure there aren't untracked files / directories
-            go generate -x ./...
-            # check if go generate modified or added any files
-            if ! $(git add . && git diff-index HEAD --exit-code --quiet); then
-              echo "go generated caused changes to the repository:"
-              git status --short
-              exit 1
-            fi
+  go-check:
+    uses: pl-strflt/uci/.github/workflows/go-check.yml@v0.0
diff --git a/.github/workflows/release-check.yml b/.github/workflows/release-check.yml
index e2408e37..bda61600 100644
--- a/.github/workflows/release-check.yml
+++ b/.github/workflows/release-check.yml
@@ -1,13 +1,19 @@
-# File managed by web3-bot. DO NOT EDIT.
-# See https://github.com/protocol/.github/ for details.
-
 name: Release Checker
+
 on:
   pull_request_target:
     paths: [ 'version.json' ]
+    types: [ opened, synchronize, reopened, labeled, unlabeled ]
+  workflow_dispatch:
+
+permissions:
+  contents: write
+  pull-requests: write
+
+concurrency:
+  group: ${{ github.workflow }}-${{ github.ref }}
+  cancel-in-progress: true
 
 jobs:
   release-check:
-    uses: protocol/.github/.github/workflows/release-check.yml@master
-    with:
-      go-version: 1.20.x
+    uses: pl-strflt/uci/.github/workflows/release-check.yml@v0.0
diff --git a/.github/workflows/releaser.yml b/.github/workflows/releaser.yml
index cdccbf87..dd8081b9 100644
--- a/.github/workflows/releaser.yml
+++ b/.github/workflows/releaser.yml
@@ -1,11 +1,17 @@
-# File managed by web3-bot. DO NOT EDIT.
-# See https://github.com/protocol/.github/ for details.
-
 name: Releaser
+
 on:
   push:
     paths: [ 'version.json' ]
+  workflow_dispatch:
+
+permissions:
+  contents: write
+
+concurrency:
+  group: ${{ github.workflow }}-${{ github.sha }}
+  cancel-in-progress: true
 
 jobs:
   releaser:
-    uses: protocol/.github/.github/workflows/releaser.yml@master
+    uses: pl-strflt/uci/.github/workflows/releaser.yml@v0.0
diff --git a/.github/workflows/tagpush.yml b/.github/workflows/tagpush.yml
index d8499618..59de8cb9 100644
--- a/.github/workflows/tagpush.yml
+++ b/.github/workflows/tagpush.yml
@@ -1,12 +1,18 @@
-# File managed by web3-bot. DO NOT EDIT.
-# See https://github.com/protocol/.github/ for details.
-
 name: Tag Push Checker
+
 on:
   push:
     tags:
       - v*
 
+permissions:
+  contents: read
+  issues: write
+
+concurrency:
+  group: ${{ github.workflow }}-${{ github.ref }}
+  cancel-in-progress: true
+
 jobs:
   releaser:
-    uses: protocol/.github/.github/workflows/tagpush.yml@master
+    uses: pl-strflt/uci/.github/workflows/tagpush.yml@v0.0
diff --git a/examples/go.mod b/examples/go.mod
index feb9b156..9c2988b0 100644
--- a/examples/go.mod
+++ b/examples/go.mod
@@ -1,6 +1,6 @@
 module github.com/libp2p/go-libp2p/examples
 
-go 1.19
+go 1.20
 
 require (
 	github.com/gogo/protobuf v1.3.2
diff --git a/examples/ipfs-camp-2019/go.mod b/examples/ipfs-camp-2019/go.mod
index 2bcabeb2..fcb2b2a1 100644
--- a/examples/ipfs-camp-2019/go.mod
+++ b/examples/ipfs-camp-2019/go.mod
@@ -1,6 +1,6 @@
 module github.com/libp2p/go-libp2p/examples/ipfs-camp-2019
 
-go 1.19
+go 1.20
 
 require (
 	github.com/gogo/protobuf v1.3.2
diff --git a/examples/pubsub/basic-chat-with-rendezvous/go.mod b/examples/pubsub/basic-chat-with-rendezvous/go.mod
index f9cf1ec6..b5816aba 100644
--- a/examples/pubsub/basic-chat-with-rendezvous/go.mod
+++ b/examples/pubsub/basic-chat-with-rendezvous/go.mod
@@ -1,6 +1,6 @@
 module github.com/libp2p/go-libp2p/examples/pubsub/chat
 
-go 1.19
+go 1.20
 
 require (
 	github.com/libp2p/go-libp2p v0.28.0
diff --git a/examples/pubsub/chat/go.mod b/examples/pubsub/chat/go.mod
index 11b1eb10..a3b0b4e3 100644
--- a/examples/pubsub/chat/go.mod
+++ b/examples/pubsub/chat/go.mod
@@ -1,6 +1,6 @@
 module github.com/libp2p/go-libp2p/examples/pubsub/chat
 
-go 1.19
+go 1.20
 
 require (
 	github.com/gdamore/tcell/v2 v2.5.2
diff --git a/go.mod b/go.mod
index 5c00422b..f0879c14 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
 module github.com/libp2p/go-libp2p
 
-go 1.19
+go 1.20
 
 retract v0.26.1 // Tag was applied incorrectly due to a bug in the release workflow.
 


Test patch:
diff --git a/.github/workflows/go-test.yml b/.github/workflows/go-test.yml
index 8ffbc8c0..1907fa24 100644
--- a/.github/workflows/go-test.yml
+++ b/.github/workflows/go-test.yml
@@ -1,76 +1,20 @@
-# File managed by web3-bot. DO NOT EDIT.
-# See https://github.com/protocol/.github/ for details.
-
-on: [push, pull_request]
 name: Go Test
 
+on:
+  pull_request:
+  push:
+    branches: ["master","release-v0[0-9][0-9]"]
+  workflow_dispatch:
+
+permissions:
+  contents: read
+
+concurrency:
+  group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event_name == 'push' && github.sha || github.ref }}
+  cancel-in-progress: true
+
 jobs:
-  unit:
-    strategy:
-      fail-fast: false
-      matrix:
-        os: ["ubuntu", "windows", "macos"]
-        go: ["1.20.x", "1.21.x"]
-    env:
-      COVERAGES: ""
-    runs-on: ${{ fromJSON(vars[format('UCI_GO_TEST_RUNNER_{0}', matrix.os)] || format('"{0}-latest"', matrix.os)) }}
-    name: ${{ matrix.os }} (go ${{ matrix.go }})
-    steps:
-      - uses: actions/checkout@v3
-        with:
-          submodules: recursive
-      - id: config
-        uses: protocol/.github/.github/actions/read-config@master
-      - uses: actions/setup-go@v3
-        with:
-          go-version: ${{ matrix.go }}
-      - name: Go information
-        run: |
-          go version
-          go env
-      - name: Use msys2 on windows
-        if: matrix.os == 'windows'
-        shell: bash
-        # The executable for msys2 is also called bash.cmd
-        #   https://github.com/actions/virtual-environments/blob/main/images/win/Windows2019-Readme.md#shells
-        # If we prepend its location to the PATH
-        #   subsequent 'shell: bash' steps will use msys2 instead of gitbash
-        run: echo "C:/msys64/usr/bin" >> $GITHUB_PATH
-      - name: Run repo-specific setup
-        uses: ./.github/actions/go-test-setup
-        if: hashFiles('./.github/actions/go-test-setup') != ''
-      - name: Run tests
-        if: contains(fromJSON(steps.config.outputs.json).skipOSes, matrix.os) == false
-        uses: protocol/multiple-go-modules@v1.2
-        with:
-          # Use -coverpkg=./..., so that we include cross-package coverage.
-          # If package ./A imports ./B, and ./A's tests also cover ./B,
-          # this means ./B's coverage will be significantly higher than 0%.
-          run: go test -v -shuffle=on -coverprofile=module-coverage.txt -coverpkg=./... ./...
-      - name: Run tests (32 bit)
-        # can't run 32 bit tests on OSX.
-        if: matrix.os != 'macos' &&
-          fromJSON(steps.config.outputs.json).skip32bit != true &&
-          contains(fromJSON(steps.config.outputs.json).skipOSes, matrix.os) == false
-        uses: protocol/multiple-go-modules@v1.2
-        env:
-          GOARCH: 386
-        with:
-          run: |
-            export "PATH=$PATH_386:$PATH"
-            go test -v -shuffle=on ./...
-      - name: Run tests with race detector
-        # speed things up. Windows and OSX VMs are slow
-        if: matrix.os == 'ubuntu' &&
-          contains(fromJSON(steps.config.outputs.json).skipOSes, matrix.os) == false
-        uses: protocol/multiple-go-modules@v1.2
-        with:
-          run: go test -v -race ./...
-      - name: Collect coverage files
-        shell: bash
-        run: echo "COVERAGES=$(find . -type f -name 'module-coverage.txt' | tr -s '\n' ',' | sed 's/,$//')" >> $GITHUB_ENV
-      - name: Upload coverage to Codecov
-        uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1
-        with:
-          files: '${{ env.COVERAGES }}'
-          env_vars: OS=${{ matrix.os }}, GO=${{ matrix.go }}
+  go-test:
+    uses: libp2p/uci/.github/workflows/go-test.yml@v0.0
+    with:
+      go-versions: '["1.20.x","1.21.x"]'
diff --git a/p2p/test/transport/deadline_test.go b/p2p/test/transport/deadline_test.go
new file mode 100644
index 00000000..5c8bcf18
--- /dev/null
+++ b/p2p/test/transport/deadline_test.go
@@ -0,0 +1,90 @@
+package transport_integration
+
+import (
+	"context"
+	"net"
+	"testing"
+	"time"
+
+	"github.com/libp2p/go-libp2p/core/network"
+	"github.com/libp2p/go-libp2p/core/peer"
+	"github.com/stretchr/testify/require"
+)
+
+func TestReadWriteDeadlines(t *testing.T) {
+	// Send a lot of data so that writes have to flush (can't just buffer it all)
+	sendBuf := make([]byte, 10<<20)
+	for _, tc := range transportsToTest {
+		t.Run(tc.Name, func(t *testing.T) {
+			listener := tc.HostGenerator(t, TransportTestCaseOpts{})
+			defer listener.Close()
+			dialer := tc.HostGenerator(t, TransportTestCaseOpts{NoListen: true})
+			defer dialer.Close()
+
+			require.NoError(t, dialer.Connect(context.Background(), peer.AddrInfo{
+				ID:    listener.ID(),
+				Addrs: listener.Addrs(),
+			}))
+
+			// This simply stalls
+			listener.SetStreamHandler("/stall", func(s network.Stream) {
+				time.Sleep(time.Hour)
+				s.Close()
+			})
+
+			t.Run("ReadDeadline", func(t *testing.T) {
+				s, err := dialer.NewStream(context.Background(), listener.ID(), "/stall")
+				require.NoError(t, err)
+				defer s.Close()
+
+				start := time.Now()
+				// Set a deadline
+				s.SetReadDeadline(time.Now().Add(10 * time.Millisecond))
+				buf := make([]byte, 1)
+				_, err = s.Read(buf)
+				require.Error(t, err)
+				require.True(t, err.(net.Error).Timeout())
+				require.Less(t, time.Since(start), 1*time.Second)
+			})
+
+			t.Run("WriteDeadline", func(t *testing.T) {
+				s, err := dialer.NewStream(context.Background(), listener.ID(), "/stall")
+				require.NoError(t, err)
+				defer s.Close()
+
+				// Set a deadline
+				s.SetWriteDeadline(time.Now().Add(10 * time.Millisecond))
+				start := time.Now()
+				_, err = s.Write(sendBuf)
+				require.Error(t, err)
+				require.True(t, err.(net.Error).Timeout())
+				require.Less(t, time.Since(start), 1*time.Second)
+			})
+
+			// Like the above, but with SetDeadline
+			t.Run("SetDeadline", func(t *testing.T) {
+				for _, op := range []string{"Read", "Write"} {
+					t.Run(op, func(t *testing.T) {
+						s, err := dialer.NewStream(context.Background(), listener.ID(), "/stall")
+						require.NoError(t, err)
+						defer s.Close()
+
+						// Set a deadline
+						s.SetDeadline(time.Now().Add(10 * time.Millisecond))
+						start := time.Now()
+
+						if op == "Read" {
+							buf := make([]byte, 1)
+							_, err = s.Read(buf)
+						} else {
+							_, err = s.Write(sendBuf)
+						}
+						require.Error(t, err)
+						require.True(t, err.(net.Error).Timeout())
+						require.Less(t, time.Since(start), 1*time.Second)
+					})
+				}
+			})
+		})
+	}
+}
diff --git a/test-plans/go.mod b/test-plans/go.mod
index a6907d44..676394e7 100644
--- a/test-plans/go.mod
+++ b/test-plans/go.mod
@@ -1,6 +1,6 @@
 module github.com/libp2p/go-libp2p/test-plans/m/v2
 
-go 1.19
+go 1.20
 
 require (
 	github.com/go-redis/redis/v8 v8.11.5