From 9ea429faafd9e7732edb0be3bc2a75a7b4d3808e Mon Sep 17 00:00:00 2001 From: Joe Chen Date: Fri, 23 Jan 2026 11:09:31 -0500 Subject: [PATCH] Fix CI setup and errors --- .github/workflows/codeball.yml | 35 ---- .github/workflows/codeql.yml | 8 +- .github/workflows/docker.yml | 304 +++++++++++++++++++++++++++---- .github/workflows/go.yml | 62 +++---- .github/workflows/lock.yml | 2 +- .github/workflows/shell.yml | 4 +- .golangci.yml | 59 +++--- gen.go | 4 +- internal/db/mocks_test.go | 2 +- internal/db/ssh_key_test.go | 6 - internal/db/two_factors_test.go | 26 +-- internal/route/lfs/mocks_test.go | 128 ++++++++++++- 12 files changed, 485 insertions(+), 155 deletions(-) delete mode 100644 .github/workflows/codeball.yml diff --git a/.github/workflows/codeball.yml b/.github/workflows/codeball.yml deleted file mode 100644 index 6b6c7a802..000000000 --- a/.github/workflows/codeball.yml +++ /dev/null @@ -1,35 +0,0 @@ -# Docs: https://github.com/sturdy-dev/codeball-action -name: Codeball -on: [ pull_request ] - -permissions: - contents: read - issues: write - pull-requests: write - -jobs: - codeball: - runs-on: ubuntu-latest - name: Codeball - steps: - - # Start a new Codeball review job - # This step is asynchronous and will return a job id - - name: Trigger Codeball - id: codeball_baller - uses: sturdy-dev/codeball-action/baller@v2 - - - # Wait for Codeball to return the status - - name: Get Status - id: codeball_status - uses: sturdy-dev/codeball-action/status@v2 - with: - codeball-job-id: ${{ steps.codeball_baller.outputs.codeball-job-id }} - - # If Codeball approved the contribution, approve the PR - - name: Approve PR - uses: sturdy-dev/codeball-action/approver@v2 - if: ${{ steps.codeball_status.outputs.approved == 'true' }} - with: - message: "Codeball: LGTM! :+1:" diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 6bd2d0e5a..24a551e90 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -39,7 +39,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 with: # We must fetch at least the immediate parents so that if this is # a pull request then we can checkout the head. @@ -47,7 +47,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@fdbfb4d2750291e159f0156def62b853c2798ca2 # v3.28.3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -58,7 +58,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v1 + uses: github/codeql-action/autobuild@fdbfb4d2750291e159f0156def62b853c2798ca2 # v3.28.3 # â„šī¸ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -72,4 +72,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@fdbfb4d2750291e159f0156def62b853c2798ca2 # v3.28.3 diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 492b9e0c8..57564d926 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -5,37 +5,36 @@ on: - main pull_request: paths: + - '.trivy.yaml' - 'Dockerfile' + - 'Dockerfile.next' - 'docker/**' + - 'docker-next/**' - '.github/workflows/docker.yml' release: types: [ published ] jobs: buildx: - if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && github.repository == 'gogs/gogs' }} + concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true runs-on: ubuntu-latest permissions: actions: write contents: read packages: write steps: - - name: Canel previous runs - uses: styfle/cancel-workflow-action@0.9.1 - with: - all_but_latest: true - access_token: ${{ secrets.GITHUB_TOKEN }} - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 - name: Set up QEMU - uses: docker/setup-qemu-action@v1 + uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0 + with: + platforms: linux/amd64,linux/arm64,linux/arm/v7 - name: Set up Docker Buildx id: buildx - uses: docker/setup-buildx-action@v1 - with: - config-inline: | - [worker.oci] - max-parallelism = 2 + uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1 - name: Inspect builder run: | echo "Name: ${{ steps.buildx.outputs.name }}" @@ -44,18 +43,18 @@ jobs: echo "Flags: ${{ steps.buildx.outputs.flags }}" echo "Platforms: ${{ steps.buildx.outputs.platforms }}" - name: Login to Docker Hub - uses: docker/login-action@v1 + uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Login to GitHub Container registry - uses: docker/login-action@v1 + uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push images - uses: docker/build-push-action@v2 + uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 with: context: . platforms: linux/amd64,linux/arm64,linux/arm/v7 @@ -63,14 +62,126 @@ jobs: tags: | gogs/gogs:latest ghcr.io/gogs/gogs:latest - registry.digitalocean.com/gogs/gogs:latest - name: Scan for container vulnerabilities - uses: aquasecurity/trivy-action@master + uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 # 0.33.1 with: image-ref: gogs/gogs:latest exit-code: '1' - name: Send email on failure - uses: dawidd6/action-send-mail@v3 + uses: dawidd6/action-send-mail@2cea9617b09d79a095af21254fbcb7ae95903dde # v3.12.0 + if: ${{ failure() }} + with: + server_address: smtp.mailgun.org + server_port: 465 + username: ${{ secrets.SMTP_USERNAME }} + password: ${{ secrets.SMTP_PASSWORD }} + subject: GitHub Actions (${{ github.repository }}) job result + to: github-actions-8ce6454@unknwon.io + from: GitHub Actions (${{ github.repository }}) + reply_to: noreply@unknwon.io + body: | + The job "${{ github.job }}" of ${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }} completed with "${{ job.status }}". + + View the job run at: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + + buildx-next: + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && github.repository == 'gogs/gogs' }} + concurrency: + group: ${{ github.workflow }}-next-${{ github.ref }} + cancel-in-progress: true + runs-on: ubuntu-latest + permissions: + actions: write + contents: read + packages: write + steps: + - name: Checkout code + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - name: Set up QEMU + uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0 + with: + platforms: linux/amd64,linux/arm64,linux/arm/v7 + - name: Set up Docker Buildx + id: buildx + uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1 + - name: Inspect builder + run: | + echo "Name: ${{ steps.buildx.outputs.name }}" + echo "Endpoint: ${{ steps.buildx.outputs.endpoint }}" + echo "Status: ${{ steps.buildx.outputs.status }}" + echo "Flags: ${{ steps.buildx.outputs.flags }}" + echo "Platforms: ${{ steps.buildx.outputs.platforms }}" + - name: Login to Docker Hub + uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Login to GitHub Container registry + uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Login to DigitalOcean Container registry + uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 + with: + registry: registry.digitalocean.com + username: ${{ secrets.DIGITALOCEAN_USERNAME }} + password: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }} + - name: Build and push next-gen images + uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 + with: + context: . + file: Dockerfile.next + platforms: linux/amd64,linux/arm64,linux/arm/v7 + push: true + tags: | + gogs/gogs:next-latest + ghcr.io/gogs/gogs:next-latest + registry.digitalocean.com/gogs/gogs:next-latest + - name: Scan for container vulnerabilities + uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 # 0.33.1 + with: + image-ref: gogs/gogs:next-latest + exit-code: '1' + - name: Send email on failure + uses: dawidd6/action-send-mail@2cea9617b09d79a095af21254fbcb7ae95903dde # v3.12.0 + if: ${{ failure() }} + with: + server_address: smtp.mailgun.org + server_port: 465 + username: ${{ secrets.SMTP_USERNAME }} + password: ${{ secrets.SMTP_PASSWORD }} + subject: GitHub Actions (${{ github.repository }}) job result + to: github-actions-8ce6454@unknwon.io + from: GitHub Actions (${{ github.repository }}) + reply_to: noreply@unknwon.io + body: | + The job "${{ github.job }}" of ${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }} completed with "${{ job.status }}". + + View the job run at: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + + deploy-demo: + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && github.repository == 'gogs/gogs' }} + needs: buildx-next + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Configure kubectl + run: | + mkdir -p ~/.kube + echo "${KUBECONFIG}" | base64 -d > ~/.kube/config + env: + KUBECONFIG: ${{ secrets.DIGITALOCEAN_K8S_CLUSTER_KUBECONFIG }} + - name: Restart gogs-demo deployment + timeout-minutes: 5 + run: | + set -ex + kubectl rollout restart deployment gogs-demo -n gogs + kubectl rollout status deployment gogs-demo -n gogs + - name: Send email on failure + uses: dawidd6/action-send-mail@2cea9617b09d79a095af21254fbcb7ae95903dde # v3.12.0 if: ${{ failure() }} with: server_address: smtp.mailgun.org @@ -93,10 +204,10 @@ jobs: contents: read steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 - name: Set up Docker Buildx id: buildx - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1 with: config-inline: | [worker.oci] @@ -110,19 +221,59 @@ jobs: echo "Platforms: ${{ steps.buildx.outputs.platforms }}" - name: Compute short commit SHA id: short-sha - uses: benjlevesque/short-sha@v2.1 + uses: benjlevesque/short-sha@599815c8ee942a9616c92bcfb4f947a3b670ab0b # v3.0 - name: Build and push images - uses: docker/build-push-action@v2 + uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 with: context: . platforms: linux/amd64 push: true tags: | - ttl.sh/gogs/gogs-${{ steps.short-sha.outputs.sha }}:1d + ttl.sh/gogs/gogs-${{ steps.short-sha.outputs.sha }}:7d - name: Scan for container vulnerabilities - uses: aquasecurity/trivy-action@master + uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 # 0.33.1 with: - image-ref: ttl.sh/gogs/gogs-${{ steps.short-sha.outputs.sha }}:1d + image-ref: ttl.sh/gogs/gogs-${{ steps.short-sha.outputs.sha }}:7d + exit-code: '1' + + buildx-next-pull-request: + if: ${{ github.event_name == 'pull_request'}} + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Checkout code + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - name: Set up Docker Buildx + id: buildx + uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1 + with: + config-inline: | + [worker.oci] + max-parallelism = 2 + - name: Inspect builder + run: | + echo "Name: ${{ steps.buildx.outputs.name }}" + echo "Endpoint: ${{ steps.buildx.outputs.endpoint }}" + echo "Status: ${{ steps.buildx.outputs.status }}" + echo "Flags: ${{ steps.buildx.outputs.flags }}" + echo "Platforms: ${{ steps.buildx.outputs.platforms }}" + - name: Compute short commit SHA + id: short-sha + uses: benjlevesque/short-sha@599815c8ee942a9616c92bcfb4f947a3b670ab0b # v3.0 + - name: Build and push next-gen images + uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 + with: + context: . + file: Dockerfile.next + platforms: linux/amd64 + push: true + tags: | + ttl.sh/gogs/gogs-next-${{ steps.short-sha.outputs.sha }}:7d + - name: Scan for container vulnerabilities + uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 # 0.33.1 + with: + image-ref: ttl.sh/gogs/gogs-next-${{ steps.short-sha.outputs.sha }}:7d exit-code: '1' # Updates to the following section needs to be synced to all release branches within their lifecycles. @@ -137,16 +288,14 @@ jobs: - name: Compute image tag name run: echo "IMAGE_TAG=$(echo $GITHUB_REF_NAME | cut -c 2-)" >> $GITHUB_ENV - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 - name: Set up QEMU - uses: docker/setup-qemu-action@v1 + uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0 + with: + platforms: linux/amd64,linux/arm64,linux/arm/v7 - name: Set up Docker Buildx id: buildx - uses: docker/setup-buildx-action@v1 - with: - config-inline: | - [worker.oci] - max-parallelism = 2 + uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1 - name: Inspect builder run: | echo "Name: ${{ steps.buildx.outputs.name }}" @@ -155,18 +304,18 @@ jobs: echo "Flags: ${{ steps.buildx.outputs.flags }}" echo "Platforms: ${{ steps.buildx.outputs.platforms }}" - name: Login to Docker Hub - uses: docker/login-action@v1 + uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Login to GitHub Container registry - uses: docker/login-action@v1 + uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push images - uses: docker/build-push-action@v2 + uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 with: context: . platforms: linux/amd64,linux/arm64,linux/arm/v7 @@ -175,7 +324,7 @@ jobs: gogs/gogs:${{ env.IMAGE_TAG }} ghcr.io/gogs/gogs:${{ env.IMAGE_TAG }} - name: Send email on failure - uses: dawidd6/action-send-mail@v3 + uses: dawidd6/action-send-mail@2cea9617b09d79a095af21254fbcb7ae95903dde # v3.12.0 if: ${{ failure() }} with: server_address: smtp.mailgun.org @@ -190,3 +339,84 @@ jobs: The job "${{ github.job }}" of ${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }} completed with "${{ job.status }}". View the job run at: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + + # Updates to the following section needs to be synced to all release branches within their lifecycles. + buildx-next-release: + if: ${{ github.event_name == 'release' }} + runs-on: ubuntu-latest + permissions: + actions: write + contents: read + packages: write + steps: + - name: Compute image tag name + run: echo "IMAGE_TAG=$(echo $GITHUB_REF_NAME | cut -c 2-)" >> $GITHUB_ENV + - name: Checkout code + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - name: Set up QEMU + uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0 + with: + platforms: linux/amd64,linux/arm64,linux/arm/v7 + - name: Set up Docker Buildx + id: buildx + uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1 + - name: Inspect builder + run: | + echo "Name: ${{ steps.buildx.outputs.name }}" + echo "Endpoint: ${{ steps.buildx.outputs.endpoint }}" + echo "Status: ${{ steps.buildx.outputs.status }}" + echo "Flags: ${{ steps.buildx.outputs.flags }}" + echo "Platforms: ${{ steps.buildx.outputs.platforms }}" + - name: Login to Docker Hub + uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Login to GitHub Container registry + uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build and push next-gen images + uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 + with: + context: . + file: Dockerfile.next + platforms: linux/amd64,linux/arm64,linux/arm/v7 + push: true + tags: | + gogs/gogs:next-${{ env.IMAGE_TAG }} + ghcr.io/gogs/gogs:next-${{ env.IMAGE_TAG }} + - name: Send email on failure + uses: dawidd6/action-send-mail@2cea9617b09d79a095af21254fbcb7ae95903dde # v3.12.0 + if: ${{ failure() }} + with: + server_address: smtp.mailgun.org + server_port: 465 + username: ${{ secrets.SMTP_USERNAME }} + password: ${{ secrets.SMTP_PASSWORD }} + subject: GitHub Actions (${{ github.repository }}) job result + to: github-actions-8ce6454@unknwon.io + from: GitHub Actions (${{ github.repository }}) + reply_to: noreply@unknwon.io + body: | + The job "${{ github.job }}" of ${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }} completed with "${{ job.status }}". + + View the job run at: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + + digitalocean-gc: + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && github.repository == 'gogs/gogs' }} + needs: buildx-next + permissions: + contents: read + uses: ./.github/workflows/digitalocean_gc.yml + secrets: inherit + + digitalocean-gc-pull-request: + if: ${{ github.event_name == 'pull_request' && github.repository == 'gogs/gogs' }} + needs: buildx-next-pull-request + permissions: + contents: read + uses: ./.github/workflows/digitalocean_gc.yml + secrets: inherit diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index d145a5f6e..42e0d6929 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -30,13 +30,13 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 - name: Install Go - uses: actions/setup-go@v5 + uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 with: - go-version: 1.23.x + go-version: 1.25.x - name: Install Task - uses: arduino/setup-task@v2 + uses: arduino/setup-task@b91d5d2c96a56797b48ac1e0e89220bf64044611 # v2.0.0 with: repo-token: ${{ secrets.GITHUB_TOKEN }} - name: Check Go module tidiness and generated files @@ -52,7 +52,7 @@ jobs: exit 1 fi - name: Run golangci-lint - uses: golangci/golangci-lint-action@v4 + uses: golangci/golangci-lint-action@9fae48acfc02a90574d7c304a1758ef9895495fa # v7.0.1 with: version: latest args: --timeout=30m @@ -61,25 +61,25 @@ jobs: name: Test strategy: matrix: - go-version: [ 1.23.x ] + go-version: [ 1.25.x ] platform: [ ubuntu-latest, macos-latest ] runs-on: ${{ matrix.platform }} steps: + - name: Checkout code + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 - name: Install Go - uses: actions/setup-go@v2 + uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 with: go-version: ${{ matrix.go-version }} - - name: Checkout code - uses: actions/checkout@v2 - name: Run tests with coverage run: go test -shuffle=on -v -race -coverprofile=coverage -covermode=atomic ./... - name: Upload coverage report to Codecov - uses: codecov/codecov-action@v1.5.0 + uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1 with: file: ./coverage flags: unittests - name: Send email on failure - uses: dawidd6/action-send-mail@v3 + uses: dawidd6/action-send-mail@2cea9617b09d79a095af21254fbcb7ae95903dde # v3.12.0 if: ${{ failure() && github.event_name == 'push' && github.ref == 'refs/heads/main' }} with: server_address: smtp.mailgun.org @@ -98,28 +98,28 @@ jobs: # Running tests with race detection consumes too much memory on Windows, # see https://github.com/golang/go/issues/46099 for details. test-windows: - name: Test + name: Test Windows strategy: matrix: - go-version: [ 1.23.x ] + go-version: [ 1.25.x ] platform: [ windows-latest ] runs-on: ${{ matrix.platform }} steps: + - name: Checkout code + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 - name: Install Go - uses: actions/setup-go@v2 + uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 with: go-version: ${{ matrix.go-version }} - - name: Checkout code - uses: actions/checkout@v2 - name: Run tests with coverage run: go test -shuffle=on -v -coverprofile=coverage -covermode=atomic ./... - name: Upload coverage report to Codecov - uses: codecov/codecov-action@v1.5.0 + uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1 with: file: ./coverage flags: unittests - name: Send email on failure - uses: dawidd6/action-send-mail@v3 + uses: dawidd6/action-send-mail@2cea9617b09d79a095af21254fbcb7ae95903dde # v3.12.0 if: ${{ failure() && github.event_name == 'push' && github.ref == 'refs/heads/main' }} with: server_address: smtp.mailgun.org @@ -139,7 +139,7 @@ jobs: name: Postgres strategy: matrix: - go-version: [ 1.23.x ] + go-version: [ 1.25.x ] platform: [ ubuntu-latest ] runs-on: ${{ matrix.platform }} services: @@ -155,12 +155,12 @@ jobs: ports: - 5432:5432 steps: + - name: Checkout code + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 - name: Install Go - uses: actions/setup-go@v2 + uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 with: go-version: ${{ matrix.go-version }} - - name: Checkout code - uses: actions/checkout@v2 - name: Run tests with coverage run: go test -shuffle=on -v -race -coverprofile=coverage -covermode=atomic ./internal/db/... env: @@ -175,18 +175,18 @@ jobs: name: MySQL strategy: matrix: - go-version: [ 1.23.x ] - platform: [ ubuntu-22.04 ] + go-version: [ 1.25.x ] + platform: [ ubuntu-22.04 ] # Use the lowest version possible for backwards compatibility runs-on: ${{ matrix.platform }} steps: - name: Start MySQL server run: sudo systemctl start mysql + - name: Checkout code + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 - name: Install Go - uses: actions/setup-go@v2 + uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 with: go-version: ${{ matrix.go-version }} - - name: Checkout code - uses: actions/checkout@v2 - name: Run tests with coverage run: go test -shuffle=on -v -race -coverprofile=coverage -covermode=atomic ./internal/db/... env: @@ -200,16 +200,16 @@ jobs: name: SQLite - Go strategy: matrix: - go-version: [ 1.23.x ] + go-version: [ 1.25.x ] platform: [ ubuntu-latest ] runs-on: ${{ matrix.platform }} steps: + - name: Checkout code + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 - name: Install Go - uses: actions/setup-go@v2 + uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 with: go-version: ${{ matrix.go-version }} - - name: Checkout code - uses: actions/checkout@v2 - name: Run tests with coverage run: go test -shuffle=on -v -race -parallel=1 -coverprofile=coverage -covermode=atomic ./internal/db/... env: diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml index 45169f85c..431efde3d 100644 --- a/.github/workflows/lock.yml +++ b/.github/workflows/lock.yml @@ -16,7 +16,7 @@ jobs: action: runs-on: ubuntu-latest steps: - - uses: dessant/lock-threads@v3 + - uses: dessant/lock-threads@1bf7ec25051fe7c00bdd17e6a7cf3d7bfb7dc771 # v5.0.1 with: github-token: ${{ github.token }} issue-inactive-days: '90' diff --git a/.github/workflows/shell.yml b/.github/workflows/shell.yml index 7031950e5..d7c0e0b46 100644 --- a/.github/workflows/shell.yml +++ b/.github/workflows/shell.yml @@ -12,6 +12,6 @@ jobs: name: Shellcheck runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 - name: Run ShellCheck - uses: ludeeus/action-shellcheck@master + uses: ludeeus/action-shellcheck@00cae500b08a931fb5698e11e79bfbd38e612a38 # 2.0.0 diff --git a/.golangci.yml b/.golangci.yml index 65a5ab089..c426559c4 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,29 +1,42 @@ -linters-settings: - staticcheck: - checks: [ - "all", - "-SA1019" # There are valid use cases of strings.Title - ] - nakedret: - max-func-lines: 0 # Disallow any unnamed return statement - govet: - disable: - # printf: non-constant format string in call to fmt.Errorf (govet) - # showing up since golangci-lint version 1.60.1 - - printf - +version: "2" linters: enable: - - unused - - errcheck - - gosimple - - govet - - ineffassign - - staticcheck - - typecheck - nakedret - - gofmt - rowserrcheck - unconvert - - goimports - unparam + settings: + govet: + disable: + # printf: non-constant format string in call to fmt.Errorf (govet) + # showing up since golangci-lint version 1.60.1 + - printf + staticcheck: + checks: + - all + - "-SA1019" # This project is under active refactoring and not all code is up to date. + - "-QF1001" # I'm a math noob + - "-ST1016" # Some legit code uses this pattern + nakedret: + max-func-lines: 0 # Disallow any unnamed return statement + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling + paths: + - third_party$ + - builtin$ + - examples$ +formatters: + enable: + - gofmt + - goimports + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ diff --git a/gen.go b/gen.go index deaa7eae8..ef6cfd705 100644 --- a/gen.go +++ b/gen.go @@ -4,5 +4,5 @@ package main -//go:generate go install golang.org/x/tools/cmd/goimports@v0.17.0 -//go:generate go run github.com/derision-test/go-mockgen/v2/cmd/go-mockgen@v2.0.1 +//go:generate go install golang.org/x/tools/cmd/goimports@v0.33.0 +//go:generate go run github.com/unknwon/go-mockgen/cmd/go-mockgen@v0.0.0-20251002032800-a9a94b119e3b diff --git a/internal/db/mocks_test.go b/internal/db/mocks_test.go index 6ba677706..27d91abc2 100644 --- a/internal/db/mocks_test.go +++ b/internal/db/mocks_test.go @@ -1,4 +1,4 @@ -// Code generated by go-mockgen 1.3.7; DO NOT EDIT. +// Code generated by go-mockgen 2.1.1; DO NOT EDIT. // // This file was generated by running `go-mockgen` at the root of this repository. // To add additional mocks to this or another package, add a new entry to the diff --git a/internal/db/ssh_key_test.go b/internal/db/ssh_key_test.go index ceaf48c2a..5b2600e10 100644 --- a/internal/db/ssh_key_test.go +++ b/internal/db/ssh_key_test.go @@ -21,12 +21,6 @@ func Test_SSHParsePublicKey(t *testing.T) { expType string expLength int }{ - { - name: "dsa-1024", - content: "ssh-dss AAAAB3NzaC1kc3MAAACBAOChCC7lf6Uo9n7BmZ6M8St19PZf4Tn59NriyboW2x/DZuYAz3ibZ2OkQ3S0SqDIa0HXSEJ1zaExQdmbO+Ux/wsytWZmCczWOVsaszBZSl90q8UnWlSH6P+/YA+RWJm5SFtuV9PtGIhyZgoNuz5kBQ7K139wuQsecdKktISwTakzAAAAFQCzKsO2JhNKlL+wwwLGOcLffoAmkwAAAIBpK7/3xvduajLBD/9vASqBQIHrgK2J+wiQnIb/Wzy0UsVmvfn8A+udRbBo+csM8xrSnlnlJnjkJS3qiM5g+eTwsLIV1IdKPEwmwB+VcP53Cw6lSyWyJcvhFb0N6s08NZysLzvj0N+ZC/FnhKTLzIyMtkHf/IrPCwlM+pV/M/96YgAAAIEAqQcGn9CKgzgPaguIZooTAOQdvBLMI5y0bQjOW6734XOpqQGf/Kra90wpoasLKZjSYKNPjE+FRUOrStLrxcNs4BeVKhy2PYTRnybfYVk1/dmKgH6P1YSRONsGKvTsH6c5IyCRG0ncCgYeF8tXppyd642982daopE7zQ/NPAnJfag= nocomment", - expType: "dsa", - expLength: 1024, - }, { name: "rsa-1024", content: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDAu7tvIvX6ZHrRXuZNfkR3XLHSsuCK9Zn3X58lxBcQzuo5xZgB6vRwwm/QtJuF+zZPtY5hsQILBLmF+BZ5WpKZp1jBeSjH2G7lxet9kbcH+kIVj0tPFEoyKI9wvWqIwC4prx/WVk2wLTJjzBAhyNxfEq7C9CeiX9pQEbEqJfkKCQ== nocomment", diff --git a/internal/db/two_factors_test.go b/internal/db/two_factors_test.go index 0471c9e6c..135fc6cf5 100644 --- a/internal/db/two_factors_test.go +++ b/internal/db/two_factors_test.go @@ -141,54 +141,56 @@ func twoFactorsIsEnabled(t *testing.T, db *twoFactors) { assert.False(t, db.IsEnabled(ctx, 2)) } -func twoFactorsUseRecoveryCode(t *testing.T, ctx context.Context, s *TwoFactorsStore) { +func twoFactorsUseRecoveryCode(t *testing.T, db *twoFactors) { + ctx := context.Background() + // Create 2FA tokens for two users - err := s.Create(ctx, 1, "secure-key", "secure-secret") + err := db.Create(ctx, 1, "secure-key", "secure-secret") require.NoError(t, err) - err = s.Create(ctx, 2, "secure-key", "secure-secret") + err = db.Create(ctx, 2, "secure-key", "secure-secret") require.NoError(t, err) // Get recovery codes for both users var user1Codes []TwoFactorRecoveryCode - err = s.db.Where("user_id = ?", 1).Find(&user1Codes).Error + err = db.DB.Where("user_id = ?", 1).Find(&user1Codes).Error require.NoError(t, err) require.NotEmpty(t, user1Codes) var user2Codes []TwoFactorRecoveryCode - err = s.db.Where("user_id = ?", 2).Find(&user2Codes).Error + err = db.DB.Where("user_id = ?", 2).Find(&user2Codes).Error require.NoError(t, err) require.NotEmpty(t, user2Codes) // User 1 should be able to use their own recovery code - err = s.UseRecoveryCode(ctx, 1, user1Codes[0].Code) + err = db.UseRecoveryCode(ctx, 1, user1Codes[0].Code) require.NoError(t, err) // Verify the code is now marked as used var usedCode TwoFactorRecoveryCode - err = s.db.Where("id = ?", user1Codes[0].ID).First(&usedCode).Error + err = db.DB.Where("id = ?", user1Codes[0].ID).First(&usedCode).Error require.NoError(t, err) assert.True(t, usedCode.IsUsed) // User 1 should NOT be able to use user 2's recovery code // This is the key security test - recovery codes must be scoped by user - err = s.UseRecoveryCode(ctx, 1, user2Codes[0].Code) + err = db.UseRecoveryCode(ctx, 1, user2Codes[0].Code) assert.True(t, IsTwoFactorRecoveryCodeNotFound(err), "expected recovery code not found error when using another user's code") // User 2's code should still be unused var user2Code TwoFactorRecoveryCode - err = s.db.Where("id = ?", user2Codes[0].ID).First(&user2Code).Error + err = db.DB.Where("id = ?", user2Codes[0].ID).First(&user2Code).Error require.NoError(t, err) assert.False(t, user2Code.IsUsed, "user 2's recovery code should not be marked as used") // User 2 should be able to use their own code - err = s.UseRecoveryCode(ctx, 2, user2Codes[0].Code) + err = db.UseRecoveryCode(ctx, 2, user2Codes[0].Code) require.NoError(t, err) // Using an already-used code should fail - err = s.UseRecoveryCode(ctx, 1, user1Codes[0].Code) + err = db.UseRecoveryCode(ctx, 1, user1Codes[0].Code) assert.True(t, IsTwoFactorRecoveryCodeNotFound(err), "expected error when reusing a recovery code") // Using a non-existent code should fail - err = s.UseRecoveryCode(ctx, 1, "invalid-code") + err = db.UseRecoveryCode(ctx, 1, "invalid-code") assert.True(t, IsTwoFactorRecoveryCodeNotFound(err), "expected error for invalid recovery code") } diff --git a/internal/route/lfs/mocks_test.go b/internal/route/lfs/mocks_test.go index e4f013a64..226c252d4 100644 --- a/internal/route/lfs/mocks_test.go +++ b/internal/route/lfs/mocks_test.go @@ -1,4 +1,4 @@ -// Code generated by go-mockgen 1.3.7; DO NOT EDIT. +// Code generated by go-mockgen 2.1.1; DO NOT EDIT. // // This file was generated by running `go-mockgen` at the root of this repository. // To add additional mocks to this or another package, add a new entry to the @@ -2780,6 +2780,9 @@ type MockTwoFactorsStore struct { // IsEnabledFunc is an instance of a mock function object controlling // the behavior of the method IsEnabled. IsEnabledFunc *TwoFactorsStoreIsEnabledFunc + // UseRecoveryCodeFunc is an instance of a mock function object + // controlling the behavior of the method UseRecoveryCode. + UseRecoveryCodeFunc *TwoFactorsStoreUseRecoveryCodeFunc } // NewMockTwoFactorsStore creates a new mock of the TwoFactorsStore @@ -2802,6 +2805,11 @@ func NewMockTwoFactorsStore() *MockTwoFactorsStore { return }, }, + UseRecoveryCodeFunc: &TwoFactorsStoreUseRecoveryCodeFunc{ + defaultHook: func(context.Context, int64, string) (r0 error) { + return + }, + }, } } @@ -2824,6 +2832,11 @@ func NewStrictMockTwoFactorsStore() *MockTwoFactorsStore { panic("unexpected invocation of MockTwoFactorsStore.IsEnabled") }, }, + UseRecoveryCodeFunc: &TwoFactorsStoreUseRecoveryCodeFunc{ + defaultHook: func(context.Context, int64, string) error { + panic("unexpected invocation of MockTwoFactorsStore.UseRecoveryCode") + }, + }, } } @@ -2841,6 +2854,9 @@ func NewMockTwoFactorsStoreFrom(i db.TwoFactorsStore) *MockTwoFactorsStore { IsEnabledFunc: &TwoFactorsStoreIsEnabledFunc{ defaultHook: i.IsEnabled, }, + UseRecoveryCodeFunc: &TwoFactorsStoreUseRecoveryCodeFunc{ + defaultHook: i.UseRecoveryCode, + }, } } @@ -3168,6 +3184,116 @@ func (c TwoFactorsStoreIsEnabledFuncCall) Results() []interface{} { return []interface{}{c.Result0} } +// TwoFactorsStoreUseRecoveryCodeFunc describes the behavior when the +// UseRecoveryCode method of the parent MockTwoFactorsStore instance is +// invoked. +type TwoFactorsStoreUseRecoveryCodeFunc struct { + defaultHook func(context.Context, int64, string) error + hooks []func(context.Context, int64, string) error + history []TwoFactorsStoreUseRecoveryCodeFuncCall + mutex sync.Mutex +} + +// UseRecoveryCode delegates to the next hook function in the queue and +// stores the parameter and result values of this invocation. +func (m *MockTwoFactorsStore) UseRecoveryCode(v0 context.Context, v1 int64, v2 string) error { + r0 := m.UseRecoveryCodeFunc.nextHook()(v0, v1, v2) + m.UseRecoveryCodeFunc.appendCall(TwoFactorsStoreUseRecoveryCodeFuncCall{v0, v1, v2, r0}) + return r0 +} + +// SetDefaultHook sets function that is called when the UseRecoveryCode +// method of the parent MockTwoFactorsStore instance is invoked and the hook +// queue is empty. +func (f *TwoFactorsStoreUseRecoveryCodeFunc) SetDefaultHook(hook func(context.Context, int64, string) error) { + f.defaultHook = hook +} + +// PushHook adds a function to the end of hook queue. Each invocation of the +// UseRecoveryCode method of the parent MockTwoFactorsStore instance invokes +// the hook at the front of the queue and discards it. After the queue is +// empty, the default hook function is invoked for any future action. +func (f *TwoFactorsStoreUseRecoveryCodeFunc) PushHook(hook func(context.Context, int64, string) error) { + f.mutex.Lock() + f.hooks = append(f.hooks, hook) + f.mutex.Unlock() +} + +// SetDefaultReturn calls SetDefaultHook with a function that returns the +// given values. +func (f *TwoFactorsStoreUseRecoveryCodeFunc) SetDefaultReturn(r0 error) { + f.SetDefaultHook(func(context.Context, int64, string) error { + return r0 + }) +} + +// PushReturn calls PushHook with a function that returns the given values. +func (f *TwoFactorsStoreUseRecoveryCodeFunc) PushReturn(r0 error) { + f.PushHook(func(context.Context, int64, string) error { + return r0 + }) +} + +func (f *TwoFactorsStoreUseRecoveryCodeFunc) nextHook() func(context.Context, int64, string) error { + f.mutex.Lock() + defer f.mutex.Unlock() + + if len(f.hooks) == 0 { + return f.defaultHook + } + + hook := f.hooks[0] + f.hooks = f.hooks[1:] + return hook +} + +func (f *TwoFactorsStoreUseRecoveryCodeFunc) appendCall(r0 TwoFactorsStoreUseRecoveryCodeFuncCall) { + f.mutex.Lock() + f.history = append(f.history, r0) + f.mutex.Unlock() +} + +// History returns a sequence of TwoFactorsStoreUseRecoveryCodeFuncCall +// objects describing the invocations of this function. +func (f *TwoFactorsStoreUseRecoveryCodeFunc) History() []TwoFactorsStoreUseRecoveryCodeFuncCall { + f.mutex.Lock() + history := make([]TwoFactorsStoreUseRecoveryCodeFuncCall, len(f.history)) + copy(history, f.history) + f.mutex.Unlock() + + return history +} + +// TwoFactorsStoreUseRecoveryCodeFuncCall is an object that describes an +// invocation of method UseRecoveryCode on an instance of +// MockTwoFactorsStore. +type TwoFactorsStoreUseRecoveryCodeFuncCall struct { + // Arg0 is the value of the 1st argument passed to this method + // invocation. + Arg0 context.Context + // Arg1 is the value of the 2nd argument passed to this method + // invocation. + Arg1 int64 + // Arg2 is the value of the 3rd argument passed to this method + // invocation. + Arg2 string + // Result0 is the value of the 1st result returned from this method + // invocation. + Result0 error +} + +// Args returns an interface slice containing the arguments of this +// invocation. +func (c TwoFactorsStoreUseRecoveryCodeFuncCall) Args() []interface{} { + return []interface{}{c.Arg0, c.Arg1, c.Arg2} +} + +// Results returns an interface slice containing the results of this +// invocation. +func (c TwoFactorsStoreUseRecoveryCodeFuncCall) Results() []interface{} { + return []interface{}{c.Result0} +} + // MockUsersStore is a mock implementation of the UsersStore interface (from // the package gogs.io/gogs/internal/db) used for unit testing. type MockUsersStore struct {