diff --git a/.github/workflows/cache-seeder.yml b/.github/workflows/cache-seeder.yml index b0f3ca97de..733077cc80 100644 --- a/.github/workflows/cache-seeder.yml +++ b/.github/workflows/cache-seeder.yml @@ -54,8 +54,6 @@ jobs: matrix: include: - { job: lint-backend, tags: "bindata", target: "lint-backend" } - - { job: lint-go-windows, tags: "bindata", target: "lint-go-windows" } - - { job: lint-go-gogit, tags: "bindata gogit", target: "lint-go" } steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 diff --git a/.github/workflows/cron-renovate.yml b/.github/workflows/cron-renovate.yml index 9ebe1fec32..7e9a00450b 100644 --- a/.github/workflows/cron-renovate.yml +++ b/.github/workflows/cron-renovate.yml @@ -21,7 +21,7 @@ jobs: timeout-minutes: 30 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: renovatebot/github-action@f66d8679fcfcfa051abde6e7a623007173bf5164 # v46.1.12 + - uses: renovatebot/github-action@79dc0ba74dc3de28db0a7aeb1d0b95d5bf5fde2a # v46.1.13 with: renovate-version: ${{ env.RENOVATE_VERSION }} configurationFile: renovate.json5 diff --git a/.github/workflows/pull-compliance.yml b/.github/workflows/pull-compliance.yml index 6a3ec2d73e..51e1c805e6 100644 --- a/.github/workflows/pull-compliance.yml +++ b/.github/workflows/pull-compliance.yml @@ -64,48 +64,6 @@ jobs: - if: needs.files-changed.outputs.actions == 'true' run: make lint-actions - lint-go-windows: - if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true' - needs: files-changed - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 - with: - go-version-file: go.mod - check-latest: true - cache: false - - uses: ./.github/actions/go-cache - with: - cache-name: lint-go-windows - lint-cache: "true" - - run: make deps-backend deps-tools - - run: make lint-go-windows - env: - TAGS: bindata - GOOS: windows - GOARCH: amd64 - - lint-go-gogit: - if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true' - needs: files-changed - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 - with: - go-version-file: go.mod - check-latest: true - cache: false - - uses: ./.github/actions/go-cache - with: - cache-name: lint-go-gogit - lint-cache: "true" - - run: make deps-backend deps-tools - - run: make lint-go - env: - TAGS: bindata gogit - checks-backend: if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true' needs: files-changed diff --git a/.github/workflows/pull-db-tests.yml b/.github/workflows/pull-db-tests.yml index b750a9c264..a3f4efdc16 100644 --- a/.github/workflows/pull-db-tests.yml +++ b/.github/workflows/pull-db-tests.yml @@ -105,7 +105,7 @@ jobs: runs-on: ubuntu-latest services: elasticsearch: - image: docker.elastic.co/elasticsearch/elasticsearch:8.19.14 + image: docker.elastic.co/elasticsearch/elasticsearch:8.19.15 env: discovery.type: single-node xpack.security.enabled: false @@ -184,7 +184,7 @@ jobs: options: >- --mount type=tmpfs,destination=/bitnami/mysql/data elasticsearch: - image: docker.elastic.co/elasticsearch/elasticsearch:8.19.14 + image: docker.elastic.co/elasticsearch/elasticsearch:8.19.15 env: discovery.type: single-node xpack.security.enabled: false diff --git a/.github/workflows/pull-labeler.yml b/.github/workflows/pull-labeler.yml index f9e2e5e07b..b27fc32cdb 100644 --- a/.github/workflows/pull-labeler.yml +++ b/.github/workflows/pull-labeler.yml @@ -15,6 +15,6 @@ jobs: contents: read pull-requests: write steps: - - uses: actions/labeler@634933edcd8ababfe52f92936142cc22ac488b1b # v6.0.1 + - uses: actions/labeler@f27b608878404679385c85cfa523b85ccb86e213 # v6.1.0 with: sync-labels: true diff --git a/.github/workflows/release-nightly.yml b/.github/workflows/release-nightly.yml index e1fd87b759..be216a48e8 100644 --- a/.github/workflows/release-nightly.yml +++ b/.github/workflows/release-nightly.yml @@ -52,7 +52,7 @@ jobs: echo "Cleaned name is ${REF_NAME}" echo "branch=${REF_NAME}-nightly" >> "$GITHUB_OUTPUT" - name: configure aws - uses: aws-actions/configure-aws-credentials@ec61189d14ec14c8efccab744f656cffd0e33f37 # v6.1.0 + uses: aws-actions/configure-aws-credentials@d979d5b3a71173a29b74b5b88418bfda9437d885 # v6.1.1 with: aws-region: ${{ secrets.AWS_REGION }} aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} diff --git a/.github/workflows/release-tag-rc.yml b/.github/workflows/release-tag-rc.yml index b7d5cbcd22..07102acc39 100644 --- a/.github/workflows/release-tag-rc.yml +++ b/.github/workflows/release-tag-rc.yml @@ -53,7 +53,7 @@ jobs: echo "Cleaned name is ${REF_NAME}" echo "branch=${REF_NAME}" >> "$GITHUB_OUTPUT" - name: configure aws - uses: aws-actions/configure-aws-credentials@ec61189d14ec14c8efccab744f656cffd0e33f37 # v6.1.0 + uses: aws-actions/configure-aws-credentials@d979d5b3a71173a29b74b5b88418bfda9437d885 # v6.1.1 with: aws-region: ${{ secrets.AWS_REGION }} aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} diff --git a/.github/workflows/release-tag-version.yml b/.github/workflows/release-tag-version.yml index 82a1930f0a..44cf49b78c 100644 --- a/.github/workflows/release-tag-version.yml +++ b/.github/workflows/release-tag-version.yml @@ -56,7 +56,7 @@ jobs: echo "Cleaned name is ${REF_NAME}" echo "branch=${REF_NAME}" >> "$GITHUB_OUTPUT" - name: configure aws - uses: aws-actions/configure-aws-credentials@ec61189d14ec14c8efccab744f656cffd0e33f37 # v6.1.0 + uses: aws-actions/configure-aws-credentials@d979d5b3a71173a29b74b5b88418bfda9437d885 # v6.1.1 with: aws-region: ${{ secrets.AWS_REGION }} aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} diff --git a/.golangci.yml b/.golangci.yml index 570942bdd3..056a6c2a08 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -13,7 +13,6 @@ linters: - forbidigo - gocheckcompilerdirectives - gocritic - - goheader - govet - ineffassign - mirror @@ -118,11 +117,6 @@ linters: enable: - nilness - unusedwrite - goheader: - values: - regexp: - HEADER: '((Copyright [^\n]+|All rights reserved\.)\n)*Copyright \d{4} (The (Gogs|Gitea) Authors|Gitea Authors|Gitea)\.( All rights reserved\.)?(\n(Copyright [^\n]+|All rights reserved\.))*\nSPDX-License-Identifier: [\w.-]+' - template: '{{ HEADER }}' exclusions: generated: lax presets: diff --git a/Makefile b/Makefile index 1087dc8410..27b2c30295 100644 --- a/Makefile +++ b/Makefile @@ -332,18 +332,11 @@ lint-spell-fix: ## lint spelling and fix issues .PHONY: lint-go lint-go: ## lint go files - $(GO) run $(GOLANGCI_LINT_PACKAGE) run + GO=$(GO) GOLANGCI_LINT_PACKAGE=$(GOLANGCI_LINT_PACKAGE) $(GO) run ./tools/lint-go-all.go .PHONY: lint-go-fix lint-go-fix: ## lint go files and fix issues - $(GO) run $(GOLANGCI_LINT_PACKAGE) run --fix - -# workaround step for the lint-go-windows CI task because 'go run' can not -# have distinct GOOS/GOARCH for its build and run steps -.PHONY: lint-go-windows -lint-go-windows: - @GOOS= GOARCH= $(GO) install $(GOLANGCI_LINT_PACKAGE) - golangci-lint run + GO=$(GO) GOLANGCI_LINT_PACKAGE=$(GOLANGCI_LINT_PACKAGE) $(GO) run ./tools/lint-go-all.go --fix .PHONY: lint-editorconfig lint-editorconfig: diff --git a/build/generate-gitignores.go b/build/generate-gitignores.go index 1e09c83a6a..7a498f548e 100644 --- a/build/generate-gitignores.go +++ b/build/generate-gitignores.go @@ -1,3 +1,6 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + //go:build ignore package main diff --git a/go.mod b/go.mod index 20d26abc23..bf16f648c3 100644 --- a/go.mod +++ b/go.mod @@ -53,8 +53,8 @@ require ( github.com/go-chi/cors v1.2.2 github.com/go-co-op/gocron/v2 v2.21.1 github.com/go-enry/go-enry/v2 v2.9.6 - github.com/go-git/go-billy/v5 v5.8.0 - github.com/go-git/go-git/v5 v5.18.0 + github.com/go-git/go-billy/v5 v5.9.0 + github.com/go-git/go-git/v5 v5.19.0 github.com/go-ldap/ldap/v3 v3.4.13 github.com/go-redsync/redsync/v4 v4.16.0 github.com/go-sql-driver/mysql v1.10.0 @@ -115,7 +115,7 @@ require ( golang.org/x/net v0.53.0 golang.org/x/oauth2 v0.36.0 golang.org/x/sync v0.20.0 - golang.org/x/sys v0.43.0 + golang.org/x/sys v0.44.0 golang.org/x/text v0.36.0 google.golang.org/grpc v1.81.0 google.golang.org/protobuf v1.36.11 @@ -251,7 +251,7 @@ require ( github.com/perimeterx/marshmallow v1.1.5 // indirect github.com/philhofer/fwd v1.2.0 // indirect github.com/pierrec/lz4/v4 v4.1.26 // indirect - github.com/pjbgf/sha1cd v0.5.0 // indirect + github.com/pjbgf/sha1cd v0.6.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_model v0.6.2 // indirect @@ -285,7 +285,6 @@ require ( go.yaml.in/yaml/v2 v2.4.4 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect go4.org v0.0.0-20260112195520-a5071408f32f // indirect - golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b // indirect golang.org/x/mod v0.35.0 // indirect golang.org/x/time v0.15.0 // indirect golang.org/x/tools v0.44.0 // indirect diff --git a/go.sum b/go.sum index 82849a19f6..b2df3d0408 100644 --- a/go.sum +++ b/go.sum @@ -300,12 +300,12 @@ github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e h1:oRq/fiirun5Hql github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= -github.com/go-git/go-billy/v5 v5.8.0 h1:I8hjc3LbBlXTtVuFNJuwYuMiHvQJDq1AT6u4DwDzZG0= -github.com/go-git/go-billy/v5 v5.8.0/go.mod h1:RpvI/rw4Vr5QA+Z60c6d6LXH0rYJo0uD5SqfmrrheCY= +github.com/go-git/go-billy/v5 v5.9.0 h1:jItGXszUDRtR/AlferWPTMN4j38BQ88XnXKbilmmBPA= +github.com/go-git/go-billy/v5 v5.9.0/go.mod h1:jCnQMLj9eUgGU7+ludSTYoZL/GGmii14RxKFj7ROgHw= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= -github.com/go-git/go-git/v5 v5.18.0 h1:O831KI+0PR51hM2kep6T8k+w0/LIAD490gvqMCvL5hM= -github.com/go-git/go-git/v5 v5.18.0/go.mod h1:pW/VmeqkanRFqR6AljLcs7EA7FbZaN5MQqO7oZADXpo= +github.com/go-git/go-git/v5 v5.19.0 h1:+WkVUQZSy/F1Gb13udrMKjIM2PrzsNfDKFSfo5tkMtc= +github.com/go-git/go-git/v5 v5.19.0/go.mod h1:Pb1v0c7/g8aGQJwx9Us09W85yGoyvSwuhEGMH7zjDKQ= github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA= @@ -607,8 +607,8 @@ github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM= github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= github.com/pierrec/lz4/v4 v4.1.26 h1:GrpZw1gZttORinvzBdXPUXATeqlJjqUG/D87TKMnhjY= github.com/pierrec/lz4/v4 v4.1.26/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4= -github.com/pjbgf/sha1cd v0.5.0 h1:a+UkboSi1znleCDUNT3M5YxjOnN1fz2FhN48FlwCxs0= -github.com/pjbgf/sha1cd v0.5.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM= +github.com/pjbgf/sha1cd v0.6.0 h1:3WJ8Wz8gvDz29quX1OcEmkAlUg9diU4GxJHqs0/XiwU= +github.com/pjbgf/sha1cd v0.6.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -799,8 +799,8 @@ golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ss golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI= golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q= -golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b h1:DXr+pvt3nC887026GRP39Ej11UATqWDmWuS99x26cD0= -golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b/go.mod h1:4QTo5u+SEIbbKW1RacMZq1YEfOBqeXa19JeshGi+zc4= +golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f h1:W3F4c+6OLc6H2lb//N1q4WpJkhzJCK5J6kUi1NTVXfM= +golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f/go.mod h1:J1xhfL/vlindoeF/aINzNzt2Bket5bjo9sdOYzOsU80= golang.org/x/image v0.39.0 h1:skVYidAEVKgn8lZ602XO75asgXBgLj9G/FE3RbuPFww= golang.org/x/image v0.39.0/go.mod h1:sIbmppfU+xFLPIG0FoVUTvyBMmgng1/XAMhQ2ft0hpA= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= @@ -877,8 +877,8 @@ golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= -golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ= +golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= diff --git a/models/db/driver_sqlite_mattn.go b/models/db/driver_sqlite_mattn.go index 1fe2e35cba..a25cb09990 100644 --- a/models/db/driver_sqlite_mattn.go +++ b/models/db/driver_sqlite_mattn.go @@ -1,8 +1,8 @@ -//go:build sqlite_mattn && sqlite_unlock_notify - // Copyright 2026 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT +//go:build sqlite_mattn && sqlite_unlock_notify + package db import ( diff --git a/models/db/driver_sqlite_modernc.go b/models/db/driver_sqlite_modernc.go index 8202d5f6f4..6bd5498a55 100644 --- a/models/db/driver_sqlite_modernc.go +++ b/models/db/driver_sqlite_modernc.go @@ -1,8 +1,8 @@ -//go:build !sqlite_mattn - // Copyright 2026 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT +//go:build !sqlite_mattn + // modernc driver is chosen as the default one (compared to mattn, ncruces) // * mattn was used as default, but it requires CGO // * the CI times are almost the same for these three (race detector must be disabled) diff --git a/modules/actions/commit_status_info.go b/modules/actions/commit_status_info.go new file mode 100644 index 0000000000..af06d35a2a --- /dev/null +++ b/modules/actions/commit_status_info.go @@ -0,0 +1,70 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package actions + +import ( + "context" + "maps" + "slices" + + actions_model "code.gitea.io/gitea/models/actions" + "code.gitea.io/gitea/models/db" + git_model "code.gitea.io/gitea/models/git" + repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/modules/log" +) + +// CommitActionsStatusMap maps CommitStatus.ID to the live ActionRunJob status +// for Gitea Actions rows. +type CommitActionsStatusMap map[int64]actions_model.Status + +// IconStatus returns the action status name to route the icon through +// repo/icons/action_status, or "" when the row isn't from Gitea Actions. +func (m CommitActionsStatusMap) IconStatus(s *git_model.CommitStatus) string { + if status, ok := m[s.ID]; ok { + return status.String() + } + return "" +} + +// GetCommitActionsStatusMap resolves the live ActionRunJob.Status for every +// CommitStatus row backed by Gitea Actions. Rows from other sources (external +// CIs, API) are left untouched and rendered from their stored State. +func GetCommitActionsStatusMap(ctx context.Context, statuses []*git_model.CommitStatus) CommitActionsStatusMap { + if len(statuses) == 0 { + return nil + } + statusByJobID := make(map[int64]*git_model.CommitStatus) + repoByID := make(map[int64]*repo_model.Repository) + for _, status := range statuses { + if status == nil || status.TargetURL == "" { + continue + } + if status.Repo == nil { + status.Repo = repoByID[status.RepoID] + } + // ParseGiteaActionsTargetURL lazy-loads status.Repo on miss; cache the + // outcome so later entries with the same RepoID skip that load. + _, jobID, ok := status.ParseGiteaActionsTargetURL(ctx) + repoByID[status.RepoID] = status.Repo + if ok { + statusByJobID[jobID] = status + } + } + if len(statusByJobID) == 0 { + return nil + } + jobs := make(map[int64]*actions_model.ActionRunJob, len(statusByJobID)) + if err := db.GetEngine(ctx).In("id", slices.Collect(maps.Keys(statusByJobID))).Cols("id", "status").Find(&jobs); err != nil { + log.Error("db.Find: failed to find action run jobs: %v", err) + return nil + } + info := make(CommitActionsStatusMap, len(jobs)) + for jobID, status := range statusByJobID { + if job, ok := jobs[jobID]; ok { + info[status.ID] = job.Status + } + } + return info +} diff --git a/modules/auth/pam/pam_test.go b/modules/auth/pam/pam_test.go index d4ab058ec7..1180e75d88 100644 --- a/modules/auth/pam/pam_test.go +++ b/modules/auth/pam/pam_test.go @@ -1,8 +1,8 @@ -//go:build pam - // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT +//go:build pam + package pam import ( diff --git a/modules/templates/util_actions.go b/modules/templates/util_actions.go new file mode 100644 index 0000000000..ad7c695185 --- /dev/null +++ b/modules/templates/util_actions.go @@ -0,0 +1,23 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package templates + +import ( + "context" + + git_model "code.gitea.io/gitea/models/git" + actions_module "code.gitea.io/gitea/modules/actions" +) + +type ActionsUtils struct { + ctx context.Context +} + +func NewActionsUtils(ctx context.Context) *ActionsUtils { + return &ActionsUtils{ctx: ctx} +} + +func (a *ActionsUtils) CommitStatusesToActionsStatuses(statuses []*git_model.CommitStatus) actions_module.CommitActionsStatusMap { + return actions_module.GetCommitActionsStatusMap(a.ctx, statuses) +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e8ba191bd5..edb9fc7481 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3818,6 +3818,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.8.0: + resolution: {integrity: sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==} + engines: {node: '>=10'} + hasBin: true + seroval-plugins@1.5.3: resolution: {integrity: sha512-LhVh4KjjkKmCxOUjoaUwtqbDjyMfnA535yEmmGDuwZcIYtw8ns6tZmeszNTECeUg/3sJpnEjsz/KhQrcPXPw1Q==} engines: {node: '>=10'} @@ -8198,6 +8203,8 @@ snapshots: semver@7.7.4: {} + semver@7.8.0: {} + seroval-plugins@1.5.3(seroval@1.5.3): dependencies: seroval: 1.5.3 @@ -8694,7 +8701,7 @@ snapshots: eslint-visitor-keys: 5.0.1 espree: 11.2.0 esquery: 1.7.0 - semver: 7.7.4 + semver: 7.8.0 transitivePeerDependencies: - supports-color diff --git a/routers/api/actions/artifactsv4.go b/routers/api/actions/artifactsv4.go index 8bd3fb7e2b..f1f33424ed 100644 --- a/routers/api/actions/artifactsv4.go +++ b/routers/api/actions/artifactsv4.go @@ -104,7 +104,6 @@ import ( actions_model "code.gitea.io/gitea/models/actions" "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/actions" "code.gitea.io/gitea/modules/httplib" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/optional" @@ -112,6 +111,7 @@ import ( "code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/actions" "code.gitea.io/gitea/services/context" "google.golang.org/protobuf/encoding/protojson" diff --git a/routers/api/v1/repo/action.go b/routers/api/v1/repo/action.go index 09b0cc5b2d..d23fc849ac 100644 --- a/routers/api/v1/repo/action.go +++ b/routers/api/v1/repo/action.go @@ -21,7 +21,6 @@ import ( "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" secret_model "code.gitea.io/gitea/models/secret" - "code.gitea.io/gitea/modules/actions" "code.gitea.io/gitea/modules/httplib" "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" @@ -1877,7 +1876,7 @@ func GetArtifact(ctx *context.APIContext) { return } - if actions.IsArtifactV4(art) { + if actions_service.IsArtifactV4(art) { convertedArtifact, err := convert.ToActionArtifact(ctx.Repo.Repository, art) if err != nil { ctx.APIErrorInternal(err) @@ -1926,7 +1925,7 @@ func DeleteArtifact(ctx *context.APIContext) { return } - if actions.IsArtifactV4(art) { + if actions_service.IsArtifactV4(art) { if err := actions_model.SetArtifactNeedDeleteByID(ctx, art.ID); err != nil { ctx.APIErrorInternal(err) return @@ -1999,10 +1998,10 @@ func DownloadArtifact(ctx *context.APIContext) { return } - if actions.IsArtifactV4(art) { + if actions_service.IsArtifactV4(art) { // @actions/toolkit asserts that downloaded artifacts of a different runid return 302 // https://github.com/actions/toolkit/blob/44d43b5490b02998bd09b0c4ff369a4cc67876c2/packages/artifact/src/internal/download/download-artifact.ts#L203-L210 - if actions.DownloadArtifactV4ServeDirect(ctx.Base, art) { + if actions_service.DownloadArtifactV4ServeDirect(ctx.Base, art) { return } @@ -2054,8 +2053,8 @@ func DownloadArtifactRaw(ctx *context.APIContext) { ctx.APIError(http.StatusNotFound, "Artifact has expired") return } - if actions.IsArtifactV4(art) { - err := actions.DownloadArtifactV4(ctx.Base, art) + if actions_service.IsArtifactV4(art) { + err := actions_service.DownloadArtifactV4(ctx.Base, art) if err != nil { ctx.APIErrorInternal(err) return diff --git a/routers/web/repo/actions/view.go b/routers/web/repo/actions/view.go index 3ce4337fbc..726ab63e56 100644 --- a/routers/web/repo/actions/view.go +++ b/routers/web/repo/actions/view.go @@ -972,8 +972,8 @@ func ArtifactsDownloadView(ctx *context_module.Context) { // A v4 Artifact may only contain a single file // Multiple files are uploaded as a single file archive // All other cases fall back to the legacy v1–v3 zip handling below - if len(artifacts) == 1 && actions.IsArtifactV4(artifacts[0]) { - err := actions.DownloadArtifactV4(ctx.Base, artifacts[0]) + if len(artifacts) == 1 && actions_service.IsArtifactV4(artifacts[0]) { + err := actions_service.DownloadArtifactV4(ctx.Base, artifacts[0]) if err != nil { ctx.ServerError("DownloadArtifactV4", err) return diff --git a/modules/actions/artifacts.go b/services/actions/artifacts.go similarity index 100% rename from modules/actions/artifacts.go rename to services/actions/artifacts.go diff --git a/services/actions/commit_status.go b/services/actions/commit_status.go index 5a7f8f1f44..76c11da7cb 100644 --- a/services/actions/commit_status.go +++ b/services/actions/commit_status.go @@ -181,11 +181,11 @@ func toCommitStatusDescription(job *actions_model.ActionRunJob) string { case actions_model.StatusFailure: return fmt.Sprintf("Failing after %s", job.Duration()) case actions_model.StatusCancelled: - return "Has been cancelled" + return fmt.Sprintf("Cancelled after %s", job.Duration()) case actions_model.StatusSkipped: - return "Has been skipped" + return "Skipped" case actions_model.StatusRunning: - return "Has started running" + return "In progress" case actions_model.StatusWaiting: return "Waiting to run" case actions_model.StatusBlocked: diff --git a/services/actions/commit_status_test.go b/services/actions/commit_status_test.go index 6ff9393318..fa95a46383 100644 --- a/services/actions/commit_status_test.go +++ b/services/actions/commit_status_test.go @@ -11,13 +11,36 @@ import ( git_model "code.gitea.io/gitea/models/git" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" + actions_module "code.gitea.io/gitea/modules/actions" "code.gitea.io/gitea/modules/commitstatus" "code.gitea.io/gitea/modules/gitrepo" + "code.gitea.io/gitea/modules/timeutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) +func TestCommitStatusDescription(t *testing.T) { + cases := []struct { + status actions_model.Status + started, stopped timeutil.TimeStamp + want string + }{ + {actions_model.StatusSuccess, 100, 102, "Successful in 2s"}, + {actions_model.StatusFailure, 100, 130, "Failing after 30s"}, + {actions_model.StatusCancelled, 100, 145, "Cancelled after 45s"}, + {actions_model.StatusSkipped, 0, 0, "Skipped"}, + {actions_model.StatusRunning, 0, 0, "In progress"}, + {actions_model.StatusWaiting, 0, 0, "Waiting to run"}, + {actions_model.StatusBlocked, 0, 0, "Blocked by required conditions"}, + {actions_model.StatusUnknown, 0, 0, "Unknown status: 0"}, + } + for _, tc := range cases { + job := &actions_model.ActionRunJob{Status: tc.status, Started: tc.started, Stopped: tc.stopped} + assert.Equal(t, tc.want, toCommitStatusDescription(job), tc.status.String()) + } +} + func TestCreateCommitStatus_Dedupe(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) @@ -61,7 +84,7 @@ func TestCreateCommitStatus_Dedupe(t *testing.T) { require.Len(t, statuses, 2) assert.Equal(t, "Waiting to run", statuses[0].Description) assert.Equal(t, commitstatus.CommitStatusPending, statuses[1].State) - assert.Equal(t, "Has started running", statuses[1].Description) + assert.Equal(t, "In progress", statuses[1].Description) assert.Equal(t, expectedTargetURL, statuses[1].TargetURL) require.NoError(t, createCommitStatus(t.Context(), repo, "push", commit.ID.String(), run, job)) @@ -75,6 +98,53 @@ func TestCreateCommitStatus_Dedupe(t *testing.T) { assert.Equal(t, commitstatus.CommitStatusSuccess, statuses[2].State) } +func TestGetCommitActionsStatusMap(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4}) + branch := unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo.ID, Name: repo.DefaultBranch}) + + run := &actions_model.ActionRun{ + RepoID: repo.ID, Repo: repo, OwnerID: repo.OwnerID, TriggerUserID: repo.OwnerID, + WorkflowID: "test.yaml", CommitSHA: branch.CommitID, + } + require.NoError(t, db.Insert(t.Context(), run)) + + cases := []struct { + jobName string + status actions_model.Status + }{ + {"running-job", actions_model.StatusRunning}, + {"waiting-job", actions_model.StatusWaiting}, + {"unknown-job", actions_model.StatusUnknown}, + } + for _, tc := range cases { + job := &actions_model.ActionRunJob{ + RunID: run.ID, RepoID: repo.ID, OwnerID: repo.OwnerID, Name: tc.jobName, Status: tc.status, + } + require.NoError(t, db.Insert(t.Context(), job)) + require.NoError(t, createCommitStatus(t.Context(), repo, "push", branch.CommitID, run, job)) + } + + statuses, err := git_model.GetLatestCommitStatus(t.Context(), repo.ID, branch.CommitID, db.ListOptionsAll) + require.NoError(t, err) + + info := actions_module.GetCommitActionsStatusMap(t.Context(), statuses) + got := map[string]string{} + for _, s := range statuses { + got[s.Context] = info.IconStatus(s) + } + for _, tc := range cases { + key := "test.yaml / " + tc.jobName + " (push)" + want := tc.status.String() + assert.Equal(t, want, got[key], "icon status for %s", tc.jobName) + } + + // Nil receiver returns "" without panicking — used by callers that skip enrichment. + var nilInfo actions_module.CommitActionsStatusMap + assert.Empty(t, nilInfo.IconStatus(statuses[0])) +} + func findCommitStatusesForContext(t *testing.T, repoID int64, sha, context string) []*git_model.CommitStatus { t.Helper() diff --git a/services/context/context.go b/services/context/context.go index e8b1663b22..b4e9904cd4 100644 --- a/services/context/context.go +++ b/services/context/context.go @@ -104,6 +104,7 @@ func NewTemplateContextForWeb(ctx reqctx.RequestContext, req *http.Request, loca tmplCtx["AvatarUtils"] = templates.NewAvatarUtils(ctx) tmplCtx["RenderUtils"] = templates.NewRenderUtils(ctx) tmplCtx["MiscUtils"] = templates.NewMiscUtils(ctx) + tmplCtx["ActionsUtils"] = templates.NewActionsUtils(ctx) tmplCtx["RootData"] = ctx.GetData() tmplCtx["Consts"] = map[string]any{ "RepoUnitTypeCode": unit.TypeCode, diff --git a/templates/repo/actions/runs_list.tmpl b/templates/repo/actions/runs_list.tmpl index f60d480f8a..e3a73c9739 100644 --- a/templates/repo/actions/runs_list.tmpl +++ b/templates/repo/actions/runs_list.tmpl @@ -8,7 +8,9 @@ {{range $run := .Runs}}
- {{template "repo/actions/status" (dict "status" $run.Status.String)}} + + {{template "repo/icons/action_status" (dict "Status" $run.Status.String "IconVariant" "circle-fill")}} +
diff --git a/templates/repo/actions/status.tmpl b/templates/repo/actions/status.tmpl deleted file mode 100644 index f44245086e..0000000000 --- a/templates/repo/actions/status.tmpl +++ /dev/null @@ -1,23 +0,0 @@ - -{{- $size := Iif .size .size 16 -}} -{{- $className := Iif .className .className "" -}} - -{{if eq .status "success"}} - {{svg "octicon-check-circle-fill" $size (printf "tw-text-green %s" $className)}} -{{else if eq .status "skipped"}} - {{svg "octicon-skip" $size (printf "tw-text-text-light %s" $className)}} -{{else if eq .status "cancelled"}} - {{svg "octicon-stop" $size (printf "tw-text-text-light %s" $className)}} -{{else if eq .status "waiting"}} - {{svg "octicon-circle" $size (printf "tw-text-text-light %s" $className)}} -{{else if eq .status "blocked"}} - {{svg "octicon-blocked" $size (printf "tw-text-yellow %s" $className)}} -{{else if eq .status "running"}} - {{svg "gitea-running" $size (printf "tw-text-yellow rotate-clockwise %s" $className)}} -{{else}}{{/*failure, unknown*/}} - {{svg "octicon-x-circle-fill" $size (printf "tw-text-red %s" $className)}} -{{end}} - diff --git a/templates/repo/commit_statuses.tmpl b/templates/repo/commit_statuses.tmpl index 4575827ceb..fb1e6fbf9b 100644 --- a/templates/repo/commit_statuses.tmpl +++ b/templates/repo/commit_statuses.tmpl @@ -1,11 +1,11 @@ {{if .Statuses}} {{if and (eq (len .Statuses) 1) .Status.TargetURL}} - {{template "repo/commit_status" .Status}} + {{template "repo/icons/commit_status" .Status}} {{else}} - {{template "repo/commit_status" .Status}} + {{template "repo/icons/commit_status" .Status}} {{end}}
diff --git a/templates/repo/icons/action_status.tmpl b/templates/repo/icons/action_status.tmpl new file mode 100644 index 0000000000..4f381bdb0e --- /dev/null +++ b/templates/repo/icons/action_status.tmpl @@ -0,0 +1,28 @@ +{{/* Status icons used for runs, jobs and steps. + +Template Attributes: +* Status: one of success, skipped, waiting, blocked, running, failure, cancelled, unknown +* Size: icon size in pixels (default 16) +* ClassName: additional CSS classes +* IconVariant: "circle-fill" → octicon-check-circle-fill / octicon-x-circle-fill + +Keep this template in sync with web_src/js/components/ActionStatusIcon.vue. +*/}} +{{- $size := or .Size 16 -}} +{{- $className := or .ClassName "" -}} +{{- $circleFill := eq .IconVariant "circle-fill" -}} +{{if eq .Status "success"}} + {{svg (Iif $circleFill "octicon-check-circle-fill" "octicon-check") $size (printf "tw-text-green %s" $className)}} +{{else if eq .Status "skipped"}} + {{svg "octicon-skip" $size (printf "tw-text-text-light %s" $className)}} +{{else if eq .Status "cancelled"}} + {{svg "octicon-stop" $size (printf "tw-text-text-light %s" $className)}} +{{else if eq .Status "waiting"}} + {{svg "octicon-circle" $size (printf "tw-text-text-light %s" $className)}} +{{else if eq .Status "blocked"}} + {{svg "octicon-blocked" $size (printf "tw-text-yellow %s" $className)}} +{{else if eq .Status "running"}} + {{svg "gitea-running" $size (printf "tw-text-yellow rotate-clockwise %s" $className)}} +{{else}}{{/*failure, unknown*/}} + {{svg (Iif $circleFill "octicon-x-circle-fill" "octicon-x") $size (printf "tw-text-red %s" $className)}} +{{end}} diff --git a/templates/repo/commit_status.tmpl b/templates/repo/icons/commit_status.tmpl similarity index 100% rename from templates/repo/commit_status.tmpl rename to templates/repo/icons/commit_status.tmpl diff --git a/templates/repo/pulls/status_items.tmpl b/templates/repo/pulls/status_items.tmpl index b4f8307395..415f9a8a32 100644 --- a/templates/repo/pulls/status_items.tmpl +++ b/templates/repo/pulls/status_items.tmpl @@ -3,10 +3,16 @@ * StatusCheckData: optional, additional status check data, see backend pullCommitStatusCheckData struct */}} {{$statusCheckData := $.StatusCheckData}} +{{$commitActionsStatuses := ctx.ActionsUtils.CommitStatusesToActionsStatuses $.CommitStatuses}} {{range $cs := $.CommitStatuses}}
- {{template "repo/commit_status" $cs}} + {{$actionStatus := $commitActionsStatuses.IconStatus $cs}} + {{if $actionStatus}} + {{template "repo/icons/action_status" (dict "Status" $actionStatus "Size" 18 "ClassName" "commit-status icon")}} + {{else}} + {{template "repo/icons/commit_status" $cs}} + {{end}}
{{$cs.Context}} {{$cs.Description}}
diff --git a/tools/lint-go-all.go b/tools/lint-go-all.go new file mode 100644 index 0000000000..b2071f1703 --- /dev/null +++ b/tools/lint-go-all.go @@ -0,0 +1,101 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package main + +import ( + "fmt" + "io" + "io/fs" + "os" + "os/exec" + "path/filepath" + "regexp" + "strings" +) + +func lintGoHeader() bool { + headerRE := regexp.MustCompile(`^(// (Copyright [^\n]+|All rights reserved\.)\n)*// Copyright \d{4} (The Gogs Authors|The Gitea Authors|Gitea Authors|Gitea)\.( All rights reserved\.)?\n(// (Copyright [^\n]+|All rights reserved\.)\n)*// SPDX-License-Identifier: [\w.-]+`) + generatedRE := regexp.MustCompile(`(?m)^// (Code|This file is) [Gg]enerated.*DO NOT EDIT`) + skipDirs := map[string]bool{ + ".git": true, + ".venv": true, + "node_modules": true, + "public": true, + "vendor": true, + "web_src": true, + } + root, bad := ".", 0 + err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + if d.IsDir() { + if rel, _ := filepath.Rel(root, path); skipDirs[filepath.ToSlash(rel)] { + return fs.SkipDir + } + return nil + } + if !strings.HasSuffix(path, ".go") { + return nil + } + f, err := os.Open(path) + if err != nil { + return err + } + data, err := io.ReadAll(io.LimitReader(f, 512)) + _ = f.Close() + if err != nil { + return err + } + if generatedRE.Match(data) { + return nil + } + if !headerRE.Match(data) { + _, _ = fmt.Fprintf(os.Stderr, "%s: missing or invalid copyright header\n", path) + bad++ + } + return nil + }) + if err != nil { + _, _ = fmt.Fprintln(os.Stderr, err) + } + return err == nil && bad == 0 +} + +func runCmd(env []string, name string, args []string) bool { + cmd := exec.Command(name, args...) + cmd.Env = append(os.Environ(), env...) + cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr + if err := cmd.Run(); err != nil { + _, _ = fmt.Fprintln(os.Stderr, err) + return false + } + return true +} + +func main() { + // 'go run' can not have distinct GOOS/GOARCH for its build and run steps, + // so install a pre-compiled binary and run it for different target platforms. + _, _ = os.Unsetenv("GOOS"), os.Unsetenv("GOARCH") + + envGolangciLintPackage := os.Getenv("GOLANGCI_LINT_PACKAGE") + envGo := os.Getenv("GO") + if envGo == "" || envGolangciLintPackage == "" { + _, _ = fmt.Fprintln(os.Stderr, "Environment variables GO and GOLANGCI_LINT_PACKAGE must be set") + os.Exit(1) + } + if !runCmd(nil, envGo, []string{"install", envGolangciLintPackage}) { + os.Exit(1) + } + + _, _ = fmt.Fprintln(os.Stdout, "lint go header ...") + succeed := lintGoHeader() + _, _ = fmt.Fprintln(os.Stdout, "lint for linux ...") + succeed = runCmd([]string{"GOOS=linux", "TAGS=bindata"}, "golangci-lint", append([]string{"run"}, os.Args[1:]...)) && succeed + _, _ = fmt.Fprintln(os.Stdout, "lint for windows ...") + succeed = runCmd([]string{"GOOS=windows", "TAGS=gogit"}, "golangci-lint", append([]string{"run"}, os.Args[1:]...)) && succeed + if !succeed { + os.Exit(1) + } +} diff --git a/web_src/js/components/ActionRunJobView.vue b/web_src/js/components/ActionRunJobView.vue index a78385b6b4..8bae33e71b 100644 --- a/web_src/js/components/ActionRunJobView.vue +++ b/web_src/js/components/ActionRunJobView.vue @@ -1,14 +1,14 @@ diff --git a/web_src/js/components/RepoActionView.vue b/web_src/js/components/RepoActionView.vue index 4d20392dac..725a996028 100644 --- a/web_src/js/components/RepoActionView.vue +++ b/web_src/js/components/RepoActionView.vue @@ -1,6 +1,6 @@