This is an automated email from the ASF dual-hosted git repository.

klesh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git


The following commit(s) were added to refs/heads/main by this push:
     new c1b617f60 chore: upgrade go to 1.26 (#8875)
c1b617f60 is described below

commit c1b617f60a947a90ea0f1b06f12a70dd66640521
Author: Dan Crews <[email protected]>
AuthorDate: Wed May 20 22:48:20 2026 -0700

    chore: upgrade go to 1.26 (#8875)
    
    * chore: upgrade go to 1.26
    
    * ci: update github action versions
    
    * refactor: replace deprecated go APIs
    
    ---------
    
    Co-authored-by: Klesh Wong <[email protected]>
---
 .devcontainer/Dockerfile                           |   2 +-
 .github/workflows/NOTICE-year-check.yml            |   2 +-
 .github/workflows/asf-header-check.yml             |   2 +-
 .github/workflows/auto-cherry-pick.yml             |   2 +-
 .github/workflows/build-builder.yml                |   2 +-
 .github/workflows/build.yml                        |  18 +-
 .github/workflows/codespell.yml                    |   2 +-
 .github/workflows/commit-msg.yml                   |   4 +-
 .github/workflows/config-ui.yml                    |   4 +-
 .github/workflows/golangci-lint.yml                |   8 +-
 .github/workflows/grafana-dashboards-check.yml     |   2 +-
 .github/workflows/migration-script-lint.yml        |   2 +-
 .github/workflows/stale.yml                        |   2 +-
 .github/workflows/test-e2e.yml                     |   8 +-
 .github/workflows/test.yml                         |   4 +-
 .github/workflows/yaml-lint.yml                    |   2 +-
 backend/.golangci.yaml                             | 541 +++++++++------------
 backend/Dockerfile                                 |   4 +-
 backend/Dockerfile.local                           |   4 +-
 backend/Makefile                                   |   4 +-
 backend/core/models/common/iso8601time_test.go     |   4 +-
 backend/core/utils/network_helper.go               |   5 +-
 backend/go.mod                                     |   2 +-
 backend/helpers/unithelper/table_info_checker.go   |  49 +-
 backend/plugins/bitbucket/api/blueprint_v200.go    |   5 +-
 .../plugins/bitbucket_server/api/blueprint_v200.go |   5 +-
 backend/plugins/github/api/blueprint_v200.go       |   5 +-
 backend/plugins/icla/api/swagger.go                |   4 +-
 backend/python/DevelopmentSetup.md                 |   7 +-
 .../server/services/remote/models/conversion.go    |   2 +-
 backend/test/Readme.md                             |  23 +
 devops/docker/lake-builder/Dockerfile              |   6 +-
 32 files changed, 364 insertions(+), 372 deletions(-)

diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
index f48cdbef2..5909efd37 100644
--- a/.devcontainer/Dockerfile
+++ b/.devcontainer/Dockerfile
@@ -13,7 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-FROM mcr.microsoft.com/devcontainers/go:1-1.23-bookworm
+FROM mcr.microsoft.com/devcontainers/go:1-1.26-bookworm
 
 ENV PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib:/usr/local/lib/pkgconfig
 ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
diff --git a/.github/workflows/NOTICE-year-check.yml 
b/.github/workflows/NOTICE-year-check.yml
index 2fff55c63..07be9765f 100644
--- a/.github/workflows/NOTICE-year-check.yml
+++ b/.github/workflows/NOTICE-year-check.yml
@@ -22,6 +22,6 @@ jobs:
   notice-year-check:
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v6
       - name: check the NOTICE file
         run: grep $(date +"%Y") NOTICE
diff --git a/.github/workflows/asf-header-check.yml 
b/.github/workflows/asf-header-check.yml
index c47e94402..dcd9fb7d6 100644
--- a/.github/workflows/asf-header-check.yml
+++ b/.github/workflows/asf-header-check.yml
@@ -27,7 +27,7 @@ jobs:
     runs-on: ubuntu-24.04
     steps:
       - name: Checkout repository
-        uses: actions/checkout@v2
+        uses: actions/checkout@v6
 
       - name: Check License Header
         uses: apache/skywalking-eyes@main
diff --git a/.github/workflows/auto-cherry-pick.yml 
b/.github/workflows/auto-cherry-pick.yml
index 39de99faa..4668a0bc4 100644
--- a/.github/workflows/auto-cherry-pick.yml
+++ b/.github/workflows/auto-cherry-pick.yml
@@ -28,7 +28,7 @@ jobs:
     if: github.event.pull_request.merged == true
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v6
         with:
           fetch-depth: 0
       - name: Auto Cherry Pick
diff --git a/.github/workflows/build-builder.yml 
b/.github/workflows/build-builder.yml
index abdc25f2c..2c01e63c4 100644
--- a/.github/workflows/build-builder.yml
+++ b/.github/workflows/build-builder.yml
@@ -25,7 +25,7 @@ jobs:
     name: Build lake-builder image
     runs-on: ubuntu-20.04
     steps:
-    - uses: actions/checkout@v2
+    - uses: actions/checkout@v6
     - uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
       with:
         username: ${{ secrets.DOCKER_REGISTRY_USERNAME }}
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 6e385137d..32733df61 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -41,7 +41,7 @@ jobs:
           sudo rm -rf /opt/hostedtoolcache/CodeQL
           docker system prune -af
           docker volume prune -f
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v6
       - name: Set up QEMU
         uses: 
docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a  # v4.0.0
       - name: Set up Docker Buildx
@@ -73,7 +73,7 @@ jobs:
           sudo rm -rf /opt/hostedtoolcache/CodeQL
           docker system prune -af
           docker volume prune -f
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v6
       - name: Set up QEMU
         uses: 
docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a  # v4.0.0
       - name: Set up Docker Buildx
@@ -109,7 +109,7 @@ jobs:
           sudo rm -rf /opt/hostedtoolcache/CodeQL
           docker system prune -af
           docker volume prune -f
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v6
       - name: Get short sha
         id: get_short_sha
         run: echo "SHORT_SHA=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
@@ -122,7 +122,7 @@ jobs:
         with:
           username: ${{ env.DOCKERHUB_USERNAME }}
           password: ${{ env.DOCKERHUB_TOKEN }}
-      - uses: actions/cache@v3
+      - uses: actions/cache@v5
         with:
           path: /tmp/devlake-build-cache-${{ matrix.platform }}
           key: buildx-devlake-build-cache-${{ github.run_id }}-${{ 
matrix.platform }}
@@ -161,7 +161,7 @@ jobs:
           sudo rm -rf /opt/hostedtoolcache/CodeQL
           docker system prune -af
           docker volume prune -f
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v6
       - name: Get short sha
         id: get_short_sha
         run: echo "SHORT_SHA=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
@@ -174,11 +174,11 @@ jobs:
         with:
           username: ${{ env.DOCKERHUB_USERNAME }}
           password: ${{ env.DOCKERHUB_TOKEN }}
-      - uses: actions/cache@v3
+      - uses: actions/cache@v5
         with:
           path: /tmp/devlake-build-cache-amd64
           key: buildx-devlake-build-cache-${{ github.run_id }}-amd64
-      - uses: actions/cache@v3
+      - uses: actions/cache@v5
         with:
           path: /tmp/devlake-build-cache-arm64
           key: buildx-devlake-build-cache-${{ github.run_id }}-arm64
@@ -209,7 +209,7 @@ jobs:
             TAG=${{ github.ref_name }}
             SHA=${{ steps.get_short_sha.outputs.SHORT_SHA }}
       - name: Clear cache
-        uses: actions/github-script@v6
+        uses: actions/github-script@v9
         if: always()
         with:
           script: |
@@ -248,7 +248,7 @@ jobs:
           sudo rm -rf /opt/hostedtoolcache/CodeQL
           docker system prune -af
           docker volume prune -f
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v6
       - name: Get short sha
         id: get_short_sha
         run: echo "SHORT_SHA=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml
index 7697e7bd0..be4686233 100644
--- a/.github/workflows/codespell.yml
+++ b/.github/workflows/codespell.yml
@@ -34,6 +34,6 @@ jobs:
 
     steps:
       - name: Checkout
-        uses: actions/checkout@v4
+        uses: actions/checkout@v6
       - name: Codespell
         uses: codespell-project/actions-codespell@v2
diff --git a/.github/workflows/commit-msg.yml b/.github/workflows/commit-msg.yml
index 18fefb2b3..a9d30b59f 100644
--- a/.github/workflows/commit-msg.yml
+++ b/.github/workflows/commit-msg.yml
@@ -22,12 +22,12 @@ jobs:
   commit-msg:
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@v2
+      - uses: actions/checkout@v6
         with:
           fetch-depth: 0
       - name: Cache lint-commit-message
         id: cache-lint-commit-message
-        uses: actions/cache@v3
+        uses: actions/cache@v5
         with:
           path: lint-commit-message
           key: ${{ runner.os }}-lint-commit-message
diff --git a/.github/workflows/config-ui.yml b/.github/workflows/config-ui.yml
index d4152860d..fcf52a19e 100644
--- a/.github/workflows/config-ui.yml
+++ b/.github/workflows/config-ui.yml
@@ -25,9 +25,9 @@ jobs:
   lint:
     runs-on: ubuntu-latest
     steps:
-    - uses: actions/checkout@v2
+    - uses: actions/checkout@v6
     - name: Set up Node.js
-      uses: actions/setup-node@v3
+      uses: actions/setup-node@v6
       with:
         node-version: '18'
         cache: 'yarn'
diff --git a/.github/workflows/golangci-lint.yml 
b/.github/workflows/golangci-lint.yml
index 7084ba598..98d6deebd 100644
--- a/.github/workflows/golangci-lint.yml
+++ b/.github/workflows/golangci-lint.yml
@@ -29,10 +29,10 @@ jobs:
     runs-on: ubuntu-latest
     container: mericodev/lake-builder:latest
     steps:
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v6
       - name: Cache golangci-lint
         id: cache-golangci-lint
-        uses: actions/cache@v3
+        uses: actions/cache@v5
         with:
           path: golangci-lint
           key: ${{ runner.os }}-backend-golangci-lint
@@ -42,7 +42,7 @@ jobs:
           cd backend
           make mock
       - name: golangci-lint
-        uses: golangci/golangci-lint-action@v6
+        uses: golangci/golangci-lint-action@v9
         with:
-          version: v1.53.3
+          version: v2.12.2
           working-directory: ./backend
diff --git a/.github/workflows/grafana-dashboards-check.yml 
b/.github/workflows/grafana-dashboards-check.yml
index a25bf4b1d..e46ecf9fb 100644
--- a/.github/workflows/grafana-dashboards-check.yml
+++ b/.github/workflows/grafana-dashboards-check.yml
@@ -28,7 +28,7 @@ jobs:
     runs-on: ubuntu-24.04
     steps:
       - name: Checkout repository
-        uses: actions/checkout@v2
+        uses: actions/checkout@v6
       - name: Check grafana dashboards whether using mysql uid
         run: |
           if grep '"type": "mysql"' grafana/dashboards/*; then
diff --git a/.github/workflows/migration-script-lint.yml 
b/.github/workflows/migration-script-lint.yml
index 0429f0379..bba0befda 100644
--- a/.github/workflows/migration-script-lint.yml
+++ b/.github/workflows/migration-script-lint.yml
@@ -29,7 +29,7 @@ jobs:
     runs-on: ubuntu-latest
     container: mericodev/lake-builder:latest
     steps:
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v6
       - name: migration script linting
         run: |
           go version
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
index 17fcb20b5..e4a112391 100644
--- a/.github/workflows/stale.yml
+++ b/.github/workflows/stale.yml
@@ -27,7 +27,7 @@ jobs:
   stale:
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/stale@v4
+      - uses: actions/stale@v10
         with:
           # Stale Issues
           days-before-issue-stale: 60
diff --git a/.github/workflows/test-e2e.yml b/.github/workflows/test-e2e.yml
index 4b078f8f6..a37635165 100644
--- a/.github/workflows/test-e2e.yml
+++ b/.github/workflows/test-e2e.yml
@@ -48,7 +48,7 @@ jobs:
     container: mericodev/lake-builder:latest
     steps:
       - name: Checkout code
-        uses: actions/checkout@v3
+        uses: actions/checkout@v6
       - run: git config --global --add safe.directory $(pwd)
       - name: Build Python
         run: |
@@ -57,7 +57,7 @@ jobs:
           make build-python
       - name: Cache test-e2e
         id: cache-test-e2e
-        uses: actions/cache@v3
+        uses: actions/cache@v5
         with:
           path: |
             ~/.cache/go-build
@@ -88,7 +88,7 @@ jobs:
     container: mericodev/lake-builder:latest
     steps:
       - name: Checkout code
-        uses: actions/checkout@v3
+        uses: actions/checkout@v6
       - run: git config --global --add safe.directory $(pwd)
       - name: Build Python
         run: |
@@ -97,7 +97,7 @@ jobs:
           make build-python
       - name: Cache test-e2e-pg
         id: cache-test-e2e-pg
-        uses: actions/cache@v3
+        uses: actions/cache@v5
         with:
           path: |
             ~/.cache/go-build
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 2ef9da145..967d5ab7d 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -44,7 +44,7 @@ jobs:
     container: mericodev/lake-builder:latest
     steps:
     - name: Checkout code
-      uses: actions/checkout@v3
+      uses: actions/checkout@v6
     - name: Set git config
       run: git config --global --add safe.directory $(pwd)
     - name: Build Python
@@ -54,7 +54,7 @@ jobs:
         make build-python
     - name: Cache unit-test
       id: cache-unit-test
-      uses: actions/cache@v3
+      uses: actions/cache@v5
       with:
         path: |
           ~/.cache/go-build
diff --git a/.github/workflows/yaml-lint.yml b/.github/workflows/yaml-lint.yml
index 6c9dde504..ce2555f58 100644
--- a/.github/workflows/yaml-lint.yml
+++ b/.github/workflows/yaml-lint.yml
@@ -26,7 +26,7 @@ jobs:
     name: lint for yamls
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v6
       - uses: github/super-linter/slim@v4
         env:
           FILTER_REGEX_INCLUDE: 
workspace/(docker-compose.yml|deployment/k8s/k8s-deploy.yaml)
diff --git a/backend/.golangci.yaml b/backend/.golangci.yaml
index c0b2bad2c..4eb8ec0dc 100644
--- a/backend/.golangci.yaml
+++ b/backend/.golangci.yaml
@@ -1,3 +1,4 @@
+#
 # Licensed to the Apache Software Foundation (ASF) under one or more
 # contributor license agreements.  See the NOTICE file distributed with
 # this work for additional information regarding copyright ownership.
@@ -14,322 +15,240 @@
 # limitations under the License.
 #
 
-# https://golangci-lint.run/usage/linters/
-# if you complain about it, you can communicate with us after reading 
https://github.com/golang/go/wiki/CodeReviewComments.
+version: "2"
+run:
+  concurrency: 4
+  issues-exit-code: 2
+  tests: false
 linters:
-  disable-all: true
+  default: none
   enable:
-    # - deadcode
     - errcheck
-    - gosimple
+    - goheader
     - govet
+    - importas
     - ineffassign
+    - makezero
+    - revive
     - staticcheck
-    # - structcheck
-    - typecheck
     - unused
-    # - varcheck
-    #    - exhaustive
-    #    - funlen
-    #    - goconst
-    - gofmt
-    - goheader
-    #    - goimports
-    #    - gomnd
-    #    - gosec
-    - importas
-    #    - lll
-    #    - misspell
-    #    - nestif
-    #    - prealloc
-    #    - tagliatelle
-    - revive
-    #    - dupl
-    - makezero
-    - stylecheck
-
-linters-settings:
-  goheader:
-    template-path: .golangci-goheader.template
-
-  stylecheck:
-    # https://staticcheck.io/docs/options#checks
-    # Default: ["*"]
-    checks: [ "all", "-ST1000", "-ST1003", "-ST1005" ]
-
-  revive:
-    # Maximum number of open files at the same time.
-    # See https://github.com/mgechev/revive#command-line-flags
-    # Defaults to unlimited.
-    max-open-files: 2048
-    # Sets the default severity.
-    # See https://github.com/mgechev/revive#configuration
-    # Default: warning
-    severity: error
-
+  settings:
+    goheader:
+      template-path: .golangci-goheader.template
+    govet:
+      disable:
+        - inline
+    revive:
+      max-open-files: 2048
+      severity: error
+      rules:
+        - name: atomic
+          severity: warning
+          disabled: false
+        - name: bare-return
+          severity: warning
+          disabled: true
+        - name: blank-imports
+          severity: warning
+          disabled: false
+        - name: bool-literal-in-expr
+          severity: warning
+          disabled: false
+        - name: call-to-gc
+          severity: warning
+          disabled: false
+        - name: confusing-naming
+          severity: warning
+          disabled: false
+        - name: confusing-results
+          severity: warning
+          disabled: false
+        - name: constant-logical-expr
+          severity: warning
+          disabled: false
+        - name: context-as-argument
+          severity: warning
+          disabled: false
+        - name: context-keys-type
+          severity: warning
+          disabled: false
+        - name: deep-exit
+          severity: warning
+          disabled: false
+        - name: defer
+          arguments:
+            - - call-chain
+          severity: warning
+          disabled: false
+        - name: dot-imports
+          severity: warning
+          disabled: false
+        - name: duplicated-imports
+          severity: warning
+          disabled: false
+        - name: empty-block
+          severity: warning
+          disabled: false
+        - name: empty-lines
+          severity: warning
+          disabled: false
+        - name: error-naming
+          severity: warning
+          disabled: false
+        - name: error-return
+          severity: warning
+          disabled: false
+        - name: error-strings
+          severity: warning
+          disabled: false
+        - name: errorf
+          severity: warning
+          disabled: false
+        - name: exported
+          arguments:
+            - disableStutteringCheck
+          severity: warning
+          disabled: true
+        - name: get-return
+          severity: warning
+          disabled: false
+        - name: identical-branches
+          severity: warning
+          disabled: false
+        - name: import-shadowing
+          severity: warning
+          disabled: true
+        - name: modifies-parameter
+          severity: warning
+          disabled: false
+        - name: modifies-value-receiver
+          severity: warning
+          disabled: false
+        - name: optimize-operands-order
+          severity: warning
+          disabled: false
+        - name: package-comments
+          severity: warning
+          disabled: true
+        - name: range
+          severity: warning
+          disabled: false
+        - name: range-val-in-closure
+          severity: warning
+          disabled: false
+        - name: range-val-address
+          severity: warning
+          disabled: false
+        - name: receiver-naming
+          severity: warning
+          disabled: false
+        - name: redefines-builtin-id
+          severity: warning
+          disabled: false
+        - name: string-of-int
+          severity: warning
+          disabled: false
+        - name: string-format
+          arguments:
+            - - core.WriteError[1].Message
+              - /^([^A-Z]|$)/
+              - must not start with a capital letter
+            - - fmt.Errorf[0]
+              - /(^|[^\.!?])$/
+              - must not end in punctuation
+            - - panic
+              - /^[^\n]*$/
+              - must not contain line breaks
+          severity: warning
+          disabled: false
+        - name: struct-tag
+          severity: warning
+          disabled: false
+        - name: superfluous-else
+          severity: warning
+          disabled: false
+        - name: time-equal
+          severity: warning
+          disabled: false
+        - name: time-naming
+          severity: warning
+          disabled: false
+        - name: var-naming
+          arguments:
+            - []
+            - []
+          severity: warning
+          disabled: true
+        - name: var-declaration
+          severity: warning
+          disabled: false
+        - name: unconditional-recursion
+          severity: warning
+          disabled: false
+        - name: unexported-return
+          severity: warning
+          disabled: false
+        - name: unhandled-error
+          arguments:
+            - fmt.Printf
+            - fmt.Println
+            - fmt.Fprintln
+            - res.Body.Close
+            - body.Close
+          severity: warning
+          disabled: false
+        - name: unnecessary-stmt
+          severity: warning
+          disabled: false
+        - name: unreachable-code
+          severity: warning
+          disabled: false
+        - name: useless-break
+          severity: warning
+          disabled: false
+        - name: waitgroup-by-value
+          severity: warning
+          disabled: false
+    staticcheck:
+      checks:
+        - all
+        - -ST1000
+        - -ST1003
+        - -ST1005
+        - -ST1020
+        - -ST1021
+        - -ST1022
+        - -QF1001
+        - -QF1003
+        - -QF1004
+        - -QF1006
+        - -QF1007
+        - -QF1008
+        - -QF1009
+        - -QF1012
+  exclusions:
+    generated: lax
+    presets:
+      - common-false-positives
+      - legacy
+      - std-error-handling
     rules:
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#atomic
-      - name: atomic
-        severity: warning
-        disabled: false
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#bare-return
-      - name: bare-return
-        severity: warning
-        disabled: true
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#blank-imports
-      - name: blank-imports
-        severity: warning
-        disabled: false
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#bool-literal-in-expr
-      - name: bool-literal-in-expr
-        severity: warning
-        disabled: false
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#call-to-gc
-      - name: call-to-gc
-        severity: warning
-        disabled: false
-      #      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#cognitive-complexity
-      #      - name: cognitive-complexity
-      #        severity: warning
-      #        disabled: false
-      #        arguments: [20]
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#confusing-naming
-      - name: confusing-naming
-        severity: warning
-        disabled: false
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#confusing-results
-      - name: confusing-results
-        severity: warning
-        disabled: false
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#constant-logical-expr
-      - name: constant-logical-expr
-        severity: warning
-        disabled: false
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#context-as-argument
-      - name: context-as-argument
-        severity: warning
-        disabled: false
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#context-keys-type
-      - name: context-keys-type
-        severity: warning
-        disabled: false
-      #      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#cyclomatic
-      #      - name: cyclomatic
-      #        severity: warning
-      #        disabled: false
-      #        arguments: [20]
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#deep-exit
-      - name: deep-exit
-        severity: warning
-        disabled: false
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#defer
-      - name: defer
-        severity: warning
-        disabled: false
-        arguments:
-          - [ "call-chain" ]
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#dot-imports
-      - name: dot-imports
-        severity: warning
-        disabled: false
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#duplicated-imports
-      - name: duplicated-imports
-        severity: warning
-        disabled: false
-      #      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#early-return
-      #      - name: early-return
-      #        severity: warning
-      #        disabled: false
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#empty-block
-      - name: empty-block
-        severity: warning
-        disabled: false
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#empty-lines
-      - name: empty-lines
-        severity: warning
-        disabled: false
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#error-naming
-      - name: error-naming
-        severity: warning
-        disabled: false
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#error-return
-      - name: error-return
-        severity: warning
-        disabled: false
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#error-strings
-      - name: error-strings
-        severity: warning
-        disabled: false
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#errorf
-      - name: errorf
-        severity: warning
-        disabled: false
-      #      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#exported
-      - name: exported
-        severity: warning
-        disabled: true
-        arguments:
-          - "disableStutteringCheck"
-      #          - "checkPrivateReceivers"
-      #          - "sayRepetitiveInsteadOfStutters"
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#get-return
-      - name: get-return
-        severity: warning
-        disabled: false
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#identical-branches
-      - name: identical-branches
-        severity: warning
-        disabled: false
-      #      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#indent-error-flow
-      #      - name: indent-error-flow
-      #        severity: warning
-      #        disabled: false
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#import-shadowing
-      - name: import-shadowing
-        severity: warning
-        disabled: false
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#modifies-parameter
-      - name: modifies-parameter
-        severity: warning
-        disabled: false
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#modifies-value-receiver
-      - name: modifies-value-receiver
-        severity: warning
-        disabled: false
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#optimize-operands-order
-      - name: optimize-operands-order
-        severity: warning
-        disabled: false
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#package-comments
-      - name: package-comments
-        severity: warning
-        disabled: false
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#range
-      - name: range
-        severity: warning
-        disabled: false
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#range-val-in-closure
-      - name: range-val-in-closure
-        severity: warning
-        disabled: false
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#range-val-address
-      - name: range-val-address
-        severity: warning
-        disabled: false
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#receiver-naming
-      - name: receiver-naming
-        severity: warning
-        disabled: false
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#redefines-builtin-id
-      - name: redefines-builtin-id
-        severity: warning
-        disabled: false
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#string-of-int
-      - name: string-of-int
-        severity: warning
-        disabled: false
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#string-format
-      - name: string-format
-        severity: warning
-        disabled: false
-        arguments:
-          - - 'core.WriteError[1].Message'
-            - '/^([^A-Z]|$)/'
-            - must not start with a capital letter
-          - - 'fmt.Errorf[0]'
-            - '/(^|[^\.!?])$/'
-            - must not end in punctuation
-          - - panic
-            - '/^[^\n]*$/'
-            - must not contain line breaks
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#struct-tag
-      - name: struct-tag
-        severity: warning
-        disabled: false
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#superfluous-else
-      - name: superfluous-else
-        severity: warning
-        disabled: false
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#time-equal
-      - name: time-equal
-        severity: warning
-        disabled: false
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#time-naming
-      - name: time-naming
-        severity: warning
-        disabled: false
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#var-naming
-      - name: var-naming
-        severity: warning
-        disabled: true
-        arguments:
-          - [ ] # AllowList
-          - [ ] # DenyList
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#var-declaration
-      - name: var-declaration
-        severity: warning
-        disabled: false
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unconditional-recursion
-      - name: unconditional-recursion
-        severity: warning
-        disabled: false
-      #      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unexported-naming
-      #      - name: unexported-naming
-      #        severity: warning
-      #        disabled: false
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unexported-return
-      - name: unexported-return
-        severity: warning
-        disabled: false
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unhandled-error
-      - name: unhandled-error
-        severity: warning
-        disabled: false
-        arguments:
-          - "fmt.Printf"
-          - "fmt.Println"
-          - "fmt.Fprintln"
-          - "res.Body.Close"
-          - "body.Close"
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unnecessary-stmt
-      - name: unnecessary-stmt
-        severity: warning
-        disabled: false
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unreachable-code
-      - name: unreachable-code
-        severity: warning
-        disabled: false
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#useless-break
-      - name: useless-break
-        severity: warning
-        disabled: false
-      # 
https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#waitgroup-by-value
-      - name: waitgroup-by-value
-        severity: warning
-        disabled: false
-
-issues:
-  include:
-    - EXC0012  # EXC0012 revive: Annoying issue about not having a comment. 
The rare codebase has such comments
-    - EXC0014  # EXC0014 revive: Annoying issue about not having a comment. 
The rare codebase has such comments
-  exclude-rules:
-    - path: models/|api/|migration/|errors/|logger/
-      linters:
-        - revive
-    - path: 
plugins/([^hc].*|.[^eo].*|..[^lr].*|...[^pe].*|....[^e].*|.....[^r].*)
-      linters:
-        - revive
-
-# Options for analysis running.
-run:
-  # The default concurrency value is the number of available CPU.
-  concurrency: 4
-  # Timeout for analysis, e.g. 30s, 5m.
-  # Default: 1m
-  timeout: 5m
-  # Exit code when at least one issue was found.
-  # Default: 1
-  issues-exit-code: 2
-  # Include test files or not.
-  # Default: true
-  tests: false
+      - linters:
+          - revive
+        path: models/|api/|migration/|errors/|logger/
+      - linters:
+          - revive
+        path: 
plugins/([^hc].*|.[^eo].*|..[^lr].*|...[^pe].*|....[^e].*|.....[^r].*)
+    paths:
+      - third_party$
+      - builtin$
+      - examples$
+formatters:
+  enable:
+    - gofmt
+  exclusions:
+    generated: lax
+    paths:
+      - third_party$
+      - builtin$
+      - examples$
diff --git a/backend/Dockerfile b/backend/Dockerfile
index 4d8fc489e..4246ae5f9 100644
--- a/backend/Dockerfile
+++ b/backend/Dockerfile
@@ -31,7 +31,7 @@ FROM --platform=linux/arm64 debian:bookworm as debian-arm64
 RUN apt-get update
 RUN apt-get install -y libssh2-1-dev libssl-dev zlib1g-dev
 
-FROM --platform=$BUILDPLATFORM golang:1.20.5-bookworm as builder
+FROM --platform=$BUILDPLATFORM golang:1.26-bookworm as builder
 
 # docker build --build-arg GOPROXY=https://goproxy.io,direct -t mericodev/lake 
.
 ARG GOPROXY=
@@ -49,7 +49,7 @@ RUN if [ "$(arch)" != "x86_64" ] ; then \
         apt-get install -y gcc-x86-64-linux-gnu binutils-x86-64-linux-gnu ; \
     fi
 
-RUN go install github.com/vektra/mockery/[email protected]
+RUN go install github.com/vektra/mockery/[email protected]
 RUN go install github.com/swaggo/swag/cmd/[email protected]
 
 COPY --from=debian-amd64 /usr/include /rootfs-amd64/usr/include
diff --git a/backend/Dockerfile.local b/backend/Dockerfile.local
index d4dbb0c07..ee306dd7e 100644
--- a/backend/Dockerfile.local
+++ b/backend/Dockerfile.local
@@ -23,7 +23,7 @@
 #While incubation status is not necessarily a reflection of the completeness 
or stability of the code,
 #it does indicate that the project has yet to be fully endorsed by the ASF.
 
-FROM golang:1.20.5-bookworm as builder
+FROM golang:1.26-bookworm as builder
 
 ARG GOPROXY=
 ARG HTTP_PROXY=
@@ -47,7 +47,7 @@ RUN mkdir -p /tmp/build && cd /tmp/build && \
     make -j$(nproc) install && \
     ldconfig
 
-RUN go install github.com/vektra/mockery/[email protected]
+RUN go install github.com/vektra/mockery/[email protected]
 RUN go install github.com/swaggo/swag/cmd/[email protected]
 
 WORKDIR /app
diff --git a/backend/Makefile b/backend/Makefile
index 17f166e0b..369e1490f 100644
--- a/backend/Makefile
+++ b/backend/Makefile
@@ -27,9 +27,9 @@ PYTHON_DIR ?= "./python"
 all: build
 
 go-dep:
-       go install github.com/vektra/mockery/[email protected]
+       go install github.com/vektra/mockery/[email protected]
        go install github.com/swaggo/swag/cmd/[email protected]
-       go install github.com/golangci/golangci-lint/cmd/[email protected]
+       go install 
github.com/golangci/golangci-lint/v2/cmd/[email protected]
 
 go-dev-tools:
        # go install github.com/atombender/go-jsonschema/cmd/gojsonschema@latest
diff --git a/backend/core/models/common/iso8601time_test.go 
b/backend/core/models/common/iso8601time_test.go
index 4307345da..84a33e893 100644
--- a/backend/core/models/common/iso8601time_test.go
+++ b/backend/core/models/common/iso8601time_test.go
@@ -167,7 +167,7 @@ func TestConvertStringToTime(t *testing.T) {
        for _, tc := range testCases {
                t.Run(tc.name, func(t *testing.T) {
                        output, err := ConvertStringToTime(tc.input)
-                       if !reflect.DeepEqual(tc.output, output) {
+                       if !tc.output.Equal(output) {
                                t.Errorf("Expected output to be %v, but got 
%v", tc.output, output)
                        }
                        assert.Equal(t, fmt.Sprintf("%v", err), 
fmt.Sprintf("%v", tc.err), "Expected error to be %v, but got %v", tc.err, err)
@@ -322,7 +322,7 @@ func TestConvertStringToTimeInLoc(t *testing.T) {
        for _, tc := range testCases {
                t.Run(tc.name, func(t *testing.T) {
                        output, err := ConvertStringToTimeInLoc(tc.input, 
tc.loc)
-                       if !reflect.DeepEqual(tc.output, output) {
+                       if !tc.output.Equal(output) {
                                t.Errorf("Expected output to be %v, but got 
%v", tc.output, output)
                        }
                        assert.Equal(t, fmt.Sprintf("%v", err), 
fmt.Sprintf("%v", tc.err), "Expected error to be %v, but got %v", tc.err, err)
diff --git a/backend/core/utils/network_helper.go 
b/backend/core/utils/network_helper.go
index a781b80b7..aba084527 100644
--- a/backend/core/utils/network_helper.go
+++ b/backend/core/utils/network_helper.go
@@ -19,9 +19,10 @@ package utils
 
 import (
        "fmt"
-       "github.com/apache/incubator-devlake/core/errors"
        "net"
        "time"
+
+       "github.com/apache/incubator-devlake/core/errors"
 )
 
 // CheckDNS FIXME ...
@@ -38,7 +39,7 @@ func CheckDNS(domain string) errors.Error {
 
 // CheckNetwork FIXME ...
 func CheckNetwork(host, port string, timeout time.Duration) errors.Error {
-       var target = fmt.Sprintf("%s:%s", host, port)
+       var target = net.JoinHostPort(host, port)
        _, err := net.DialTimeout("tcp", target, timeout)
        if err != nil {
                return errors.Convert(err)
diff --git a/backend/go.mod b/backend/go.mod
index 47e7ad31c..dd212b463 100644
--- a/backend/go.mod
+++ b/backend/go.mod
@@ -1,6 +1,6 @@
 module github.com/apache/incubator-devlake
 
-go 1.20
+go 1.26
 
 require (
        github.com/aws/aws-sdk-go v1.55.6
diff --git a/backend/helpers/unithelper/table_info_checker.go 
b/backend/helpers/unithelper/table_info_checker.go
index bece5b31f..27ba975b8 100644
--- a/backend/helpers/unithelper/table_info_checker.go
+++ b/backend/helpers/unithelper/table_info_checker.go
@@ -49,6 +49,11 @@ type TableInfoCheckerConfig struct {
        IgnoreTables        []string
 }
 
+type parsedPackage struct {
+       name  string
+       files map[string]*ast.File
+}
+
 func NewTableInfoChecker(cfg TableInfoCheckerConfig) *TableInfoChecker {
        return &TableInfoChecker{
                tables:               make(map[string]struct{}),
@@ -112,8 +117,8 @@ func (checker *TableInfoChecker) Verify() errors.Error {
        return errors.Default.New(sb.String())
 }
 
-func (checker *TableInfoChecker) parseDirRecursively(modelsDir string, 
additionalIgnorablePackages ...string) (map[string]*ast.Package, error) {
-       packagesMap := make(map[string]*ast.Package)
+func (checker *TableInfoChecker) parseDirRecursively(modelsDir string, 
additionalIgnorablePackages ...string) (map[string]*parsedPackage, error) {
+       packagesMap := make(map[string]*parsedPackage)
        ignorablePackages := append(checker.ignoredPackages, 
additionalIgnorablePackages...)
        err := filepath.WalkDir(modelsDir, func(path string, d fs2.DirEntry, 
err error) error {
                if err != nil {
@@ -122,7 +127,7 @@ func (checker *TableInfoChecker) 
parseDirRecursively(modelsDir string, additiona
                if !d.IsDir() {
                        return nil
                }
-               packs, err := parser.ParseDir(token.NewFileSet(), path, nil, 0)
+               packs, err := parsePackagesInDir(path, 0)
                if err != nil {
                        return err
                }
@@ -143,10 +148,40 @@ func (checker *TableInfoChecker) 
parseDirRecursively(modelsDir string, additiona
        return packagesMap, nil
 }
 
-func (checker *TableInfoChecker) getTableNameFuncs(pks 
map[string]*ast.Package) []*ast.FuncDecl {
+func parsePackagesInDir(dir string, mode parser.Mode) 
(map[string]*parsedPackage, error) {
+       entries, err := os.ReadDir(dir)
+       if err != nil {
+               return nil, err
+       }
+       packs := make(map[string]*parsedPackage)
+       fileSet := token.NewFileSet()
+       for _, entry := range entries {
+               if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".go") {
+                       continue
+               }
+               filename := filepath.Join(dir, entry.Name())
+               file, err := parser.ParseFile(fileSet, filename, nil, mode)
+               if err != nil {
+                       return nil, err
+               }
+               packageName := file.Name.Name
+               pack := packs[packageName]
+               if pack == nil {
+                       pack = &parsedPackage{
+                               name:  packageName,
+                               files: make(map[string]*ast.File),
+                       }
+                       packs[packageName] = pack
+               }
+               pack.files[filename] = file
+       }
+       return packs, nil
+}
+
+func (checker *TableInfoChecker) getTableNameFuncs(pks 
map[string]*parsedPackage) []*ast.FuncDecl {
        var funcs []*ast.FuncDecl
        for _, pack := range pks {
-               for _, f := range pack.Files {
+               for _, f := range pack.files {
                        for _, d := range f.Decls {
                                if fn, isFn := d.(*ast.FuncDecl); isFn && 
fn.Name.String() == "TableName" {
                                        funcs = append(funcs, fn)
@@ -180,12 +215,12 @@ func (checker *TableInfoChecker) ensureCoverage() 
errors.Error {
                if strings.Count(path, string(os.PathSeparator)) != 0 {
                        return fs2.SkipDir
                }
-               packs, err := parser.ParseDir(token.NewFileSet(), path, nil, 
parser.PackageClauseOnly)
+               packs, err := parsePackagesInDir(path, parser.PackageClauseOnly)
                if err != nil {
                        return err
                }
                for _, pk := range packs {
-                       if pk.Name == "main" {
+                       if pk.name == "main" {
                                packagesFound++
                                return fs2.SkipDir
                        }
diff --git a/backend/plugins/bitbucket/api/blueprint_v200.go 
b/backend/plugins/bitbucket/api/blueprint_v200.go
index 6fedb0df2..2ee774457 100644
--- a/backend/plugins/bitbucket/api/blueprint_v200.go
+++ b/backend/plugins/bitbucket/api/blueprint_v200.go
@@ -75,8 +75,11 @@ func makeDataSourcePipelinePlanV200(
        scopeDetails []*srvhelper.ScopeDetail[models.BitbucketRepo, 
models.BitbucketScopeConfig],
        connection *models.BitbucketConnection,
 ) (coreModels.PipelinePlan, errors.Error) {
-       plan := make(coreModels.PipelinePlan, len(scopeDetails))
+       plan := make(coreModels.PipelinePlan, 0, len(scopeDetails)+1)
        for i, scopeDetail := range scopeDetails {
+               if i == len(plan) {
+                       plan = append(plan, nil)
+               }
                bitbucketRepo, scopeConfig := scopeDetail.Scope, 
scopeDetail.ScopeConfig
                stage := plan[i]
                if stage == nil {
diff --git a/backend/plugins/bitbucket_server/api/blueprint_v200.go 
b/backend/plugins/bitbucket_server/api/blueprint_v200.go
index 04bd90814..0b0ae74c6 100644
--- a/backend/plugins/bitbucket_server/api/blueprint_v200.go
+++ b/backend/plugins/bitbucket_server/api/blueprint_v200.go
@@ -75,8 +75,11 @@ func makeDataSourcePipelinePlanV200(
        scopeDetails []*srvhelper.ScopeDetail[models.BitbucketServerRepo, 
models.BitbucketServerScopeConfig],
        connection *models.BitbucketServerConnection,
 ) (coreModels.PipelinePlan, errors.Error) {
-       plan := make(coreModels.PipelinePlan, len(scopeDetails))
+       plan := make(coreModels.PipelinePlan, 0, len(scopeDetails)+1)
        for i, scopeDetail := range scopeDetails {
+               if i == len(plan) {
+                       plan = append(plan, nil)
+               }
                bitbucketRepo, scopeConfig := scopeDetail.Scope, 
scopeDetail.ScopeConfig
                stage := plan[i]
                if stage == nil {
diff --git a/backend/plugins/github/api/blueprint_v200.go 
b/backend/plugins/github/api/blueprint_v200.go
index 6515f24b7..1af333cb2 100644
--- a/backend/plugins/github/api/blueprint_v200.go
+++ b/backend/plugins/github/api/blueprint_v200.go
@@ -80,8 +80,11 @@ func makeDataSourcePipelinePlanV200(
        scopeDetails []*srvhelper.ScopeDetail[models.GithubRepo, 
models.GithubScopeConfig],
        connection *models.GithubConnection,
 ) (coreModels.PipelinePlan, errors.Error) {
-       plan := make(coreModels.PipelinePlan, len(scopeDetails))
+       plan := make(coreModels.PipelinePlan, 0, len(scopeDetails)+1)
        for i, scopeDetail := range scopeDetails {
+               if i == len(plan) {
+                       plan = append(plan, nil)
+               }
                githubRepo, scopeConfig := scopeDetail.Scope, 
scopeDetail.ScopeConfig
                stage := plan[i]
                if stage == nil {
diff --git a/backend/plugins/icla/api/swagger.go 
b/backend/plugins/icla/api/swagger.go
index 8f9a0f607..06db41f1f 100644
--- a/backend/plugins/icla/api/swagger.go
+++ b/backend/plugins/icla/api/swagger.go
@@ -25,7 +25,7 @@ package api
 // @Router /blueprints/icla/blueprint-setting [post]
 func _() {}
 
-//nolint:unused,deadcode
+//nolint:unused
 type iclaBlueprintSetting []struct {
        Version     string `json:"version" example:"1.0.0"`
        Connections []struct {
@@ -41,7 +41,7 @@ type iclaBlueprintSetting []struct {
 // @Router /pipelines/icla/pipeline-plan [post]
 func _() {}
 
-//nolint:unused,deadcode
+//nolint:unused
 type iclaPipelinePlan [][]struct {
        Plugin string `json:"plugin" example:"icla"`
 }
diff --git a/backend/python/DevelopmentSetup.md 
b/backend/python/DevelopmentSetup.md
index f026fe760..4bc885a96 100644
--- a/backend/python/DevelopmentSetup.md
+++ b/backend/python/DevelopmentSetup.md
@@ -14,7 +14,12 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 
or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 -->
-# For `make e2e-test` to run properly, the following steps must be taken:
+# Python prerequisites for `make e2e-test`
+
+`make e2e-test` also requires a test database and, for some service tests, 
`libgit2`. See
+[`backend/test/Readme.md`](../test/Readme.md) for the full local E2E test flow.
+
+For the Python plugin tests to run properly, the following steps must be taken:
 
 1. The following packages are required for Ubuntu: `libffi-dev 
default-libmysqlclient-dev libpq-dev`
 2. `python3.9` is required by the time of this document. 
diff --git a/backend/server/services/remote/models/conversion.go 
b/backend/server/services/remote/models/conversion.go
index c3a3e0c6d..7c44a1b9d 100644
--- a/backend/server/services/remote/models/conversion.go
+++ b/backend/server/services/remote/models/conversion.go
@@ -179,7 +179,7 @@ func getGoType(schema utils.JsonObject, required bool) 
(reflect.Type, errors.Err
                        if required {
                                return timeType, nil
                        } else {
-                               return reflect.PtrTo(timeType), nil
+                               return reflect.PointerTo(timeType), nil
                        }
                } else {
                        return stringType, nil
diff --git a/backend/test/Readme.md b/backend/test/Readme.md
index 4987c9b0d..046bce5c9 100644
--- a/backend/test/Readme.md
+++ b/backend/test/Readme.md
@@ -18,3 +18,26 @@ limitations under the License.
 
 * <i>e2e/</i>: Contains DevLake-server tests that interact with either fake 
plugins or no plugins at all.
 * <i>integration/</i>: Contains DevLake-server tests written against real 
data-sources which contain test data.
+
+### Running E2E tests locally
+
+Start a MySQL instance and create a test database that the `merico` user can 
access. The database URL must use
+`127.0.0.1` instead of `localhost` because some Python dependencies do not 
resolve `localhost` correctly.
+
+```bash
+export 
E2E_DB_URL='mysql://merico:[email protected]:3306/lake_test?charset=utf8mb4&parseTime=True'
+
+cd backend
+go run ./test/init.go
+make e2e-test
+```
+
+The service E2E tests compile the `gitextractor` plugin, which links against 
`libgit2`. If `libgit2` is installed in
+a non-standard location, expose it through `pkg-config` and add an rpath so 
the test binary can load the dynamic
+library at runtime:
+
+```bash
+export PKG_CONFIG_PATH=/path/to/libgit2/build
+export CGO_LDFLAGS='-L/path/to/libgit2/build -Wl,-rpath,/path/to/libgit2/build'
+make e2e-test
+```
diff --git a/devops/docker/lake-builder/Dockerfile 
b/devops/docker/lake-builder/Dockerfile
index bcd56208f..be6e31f3d 100644
--- a/devops/docker/lake-builder/Dockerfile
+++ b/devops/docker/lake-builder/Dockerfile
@@ -18,7 +18,7 @@ FROM --platform=linux/amd64 debian:bullseye as debian-amd64
 RUN apt-get -y update && apt -y upgrade &&\
     apt-get install -y libssh2-1-dev libssl-dev zlib1g-dev
 
-FROM golang:1.20.4-bullseye as builder
+FROM golang:1.26-bullseye as builder
 
 # Base dependencies
 RUN apt-get -y update && apt -y upgrade &&\
@@ -55,14 +55,14 @@ COPY --from=builder /tmp/deps/include/ /usr/include/
 ENV PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig
 
 # Install Golang
-RUN curl -L https://git.io/vQhTU | bash -s -- --version 1.20.4
+RUN curl -L https://git.io/vQhTU | bash -s -- --version 1.26.2
 RUN mv /root/go /go &&\
     mv /root/.go /usr/local/go &&\
     ln -sf /usr/local/go/bin/* /usr/bin
 
 # Install Golang Tools
 RUN export GOPATH=/go &&  \
-    go install github.com/vektra/mockery/[email protected] &&  \
+    go install github.com/vektra/mockery/[email protected] &&  \
     go install github.com/swaggo/swag/cmd/[email protected]
 
 # Golang Env


Reply via email to