1
0
Fork 0
mirror of https://codeberg.org/forgejo/forgejo.git synced 2025-09-15 18:56:59 +00:00

Merge branch 'forgejo' into timeout-config-to-remove-resolved-reports

This commit is contained in:
Leni Kadali 2025-06-25 12:09:38 +02:00
commit 7d03d7dd1e
632 changed files with 19250 additions and 6456 deletions

View file

@ -13,6 +13,13 @@ forgejo.org/models
IsErrSHANotFound IsErrSHANotFound
IsErrMergeDivergingFastForwardOnly IsErrMergeDivergingFastForwardOnly
forgejo.org/models/activities
GetActivityByID
NewFederatedUserActivity
CreateUserActivity
GetFollowingFeeds
FederatedUserActivity.loadActor
forgejo.org/models/auth forgejo.org/models/auth
WebAuthnCredentials WebAuthnCredentials
@ -54,9 +61,17 @@ forgejo.org/models/user
IsErrExternalLoginUserAlreadyExist IsErrExternalLoginUserAlreadyExist
IsErrExternalLoginUserNotExist IsErrExternalLoginUserNotExist
NewFederatedUser NewFederatedUser
NewFederatedUserFollower
IsErrUserSettingIsNotExist IsErrUserSettingIsNotExist
GetUserAllSettings GetUserAllSettings
DeleteUserSetting DeleteUserSetting
GetFederatedUser
GetFederatedUserByUserID
UpdateFederatedUser
GetFollowersForUser
AddFollower
RemoveFollower
IsFollowingAp
forgejo.org/modules/activitypub forgejo.org/modules/activitypub
NewContext NewContext
@ -87,12 +102,24 @@ forgejo.org/modules/eventsource
Event.String Event.String
forgejo.org/modules/forgefed forgejo.org/modules/forgefed
NewForgeFollowFromAp
NewForgeFollow
ForgeFollow.MarshalJSON
ForgeFollow.UnmarshalJSON
ForgeFollow.Validate
NewForgeUndoLike NewForgeUndoLike
ForgeUndoLike.UnmarshalJSON ForgeUndoLike.UnmarshalJSON
ForgeUndoLike.Validate ForgeUndoLike.Validate
NewForgeUserActivityFromAp
NewForgeUserActivity
ForgeUserActivity.Validate
NewPersonIDFromModel
GetItemByType GetItemByType
JSONUnmarshalerFn JSONUnmarshalerFn
NotEmpty NotEmpty
NewForgeUserActivityNoteFromAp
newNote
ForgeUserActivityNote.Validate
ToRepository ToRepository
OnRepository OnRepository
@ -204,6 +231,7 @@ forgejo.org/modules/util/filebuffer
forgejo.org/modules/validation forgejo.org/modules/validation
IsErrNotValid IsErrNotValid
ValidateIDExists
forgejo.org/modules/web forgejo.org/modules/web
RouteMock RouteMock

View file

@ -6,7 +6,7 @@
"ghcr.io/devcontainers/features/node:1": { "ghcr.io/devcontainers/features/node:1": {
"version": "22" "version": "22"
}, },
"ghcr.io/devcontainers/features/git-lfs:1.2.3": {}, "ghcr.io/devcontainers/features/git-lfs:1.2.4": {},
"ghcr.io/warrenbuckley/codespace-features/sqlite:1": {} "ghcr.io/warrenbuckley/codespace-features/sqlite:1": {}
}, },
"customizations": { "customizations": {

View file

@ -37,13 +37,9 @@ coverage.all
coverage/ coverage/
cpu.out cpu.out
/modules/migration/bindata.go
/modules/migration/bindata.go.hash /modules/migration/bindata.go.hash
/modules/options/bindata.go
/modules/options/bindata.go.hash /modules/options/bindata.go.hash
/modules/public/bindata.go
/modules/public/bindata.go.hash /modules/public/bindata.go.hash
/modules/templates/bindata.go
/modules/templates/bindata.go.hash /modules/templates/bindata.go.hash
*.db *.db

View file

@ -6,7 +6,7 @@ body:
- type: markdown - type: markdown
attributes: attributes:
value: | value: |
**NOTE: If your issue is a security concern, please email <security@forgejo.org> (GPG: `A4676E79`) instead of opening a public issue.** **NOTE: If your issue is a security concern, please email <security@forgejo.org> ([security.txt](https://forgejo.org/.well-known/security.txt)) instead of opening a public issue.**
- type: markdown - type: markdown
attributes: attributes:
value: | value: |

View file

@ -6,7 +6,7 @@ body:
- type: markdown - type: markdown
attributes: attributes:
value: | value: |
**NOTE: If your issue is a security concern, please email <security@forgejo.org> (GPG: `A4676E79`) instead of opening a public issue.** **NOTE: If your issue is a security concern, please email <security@forgejo.org> ([security.txt](https://forgejo.org/.well-known/security.txt)) instead of opening a public issue.**
- type: markdown - type: markdown
attributes: attributes:
value: | value: |

View file

@ -1,4 +1,4 @@
FROM data.forgejo.org/oci/alpine:3.21 FROM data.forgejo.org/oci/alpine:3.22
ARG RELEASE_VERSION=unkown ARG RELEASE_VERSION=unkown
LABEL maintainer="contact@forgejo.org" \ LABEL maintainer="contact@forgejo.org" \
org.opencontainers.image.version="${RELEASE_VERSION}" org.opencontainers.image.version="${RELEASE_VERSION}"

View file

@ -18,7 +18,7 @@ runs:
- name: install packages - name: install packages
run: | run: |
apt-get update -qq apt-get update -qq
apt-get -q install -qq -y ${PACKAGES} apt-get -q install --allow-downgrades -qq -y ${PACKAGES}
env: env:
PACKAGES: ${{inputs.packages}} PACKAGES: ${{inputs.packages}}
- name: remove temporary package list to prevent using it in other steps - name: remove temporary package list to prevent using it in other steps

View file

@ -164,7 +164,7 @@ jobs:
- name: build container & release - name: build container & release
if: ${{ secrets.TOKEN != '' }} if: ${{ secrets.TOKEN != '' }}
uses: https://data.forgejo.org/forgejo/forgejo-build-publish/build@v5.3.4 uses: https://data.forgejo.org/forgejo/forgejo-build-publish/build@v5.3.5
with: with:
forgejo: "${{ env.GITHUB_SERVER_URL }}" forgejo: "${{ env.GITHUB_SERVER_URL }}"
owner: "${{ env.GITHUB_REPOSITORY_OWNER }}" owner: "${{ env.GITHUB_REPOSITORY_OWNER }}"
@ -183,7 +183,7 @@ jobs:
- name: build rootless container - name: build rootless container
if: ${{ secrets.TOKEN != '' }} if: ${{ secrets.TOKEN != '' }}
uses: https://data.forgejo.org/forgejo/forgejo-build-publish/build@v5.3.4 uses: https://data.forgejo.org/forgejo/forgejo-build-publish/build@v5.3.5
with: with:
forgejo: "${{ env.GITHUB_SERVER_URL }}" forgejo: "${{ env.GITHUB_SERVER_URL }}"
owner: "${{ env.GITHUB_REPOSITORY_OWNER }}" owner: "${{ env.GITHUB_REPOSITORY_OWNER }}"

View file

@ -44,7 +44,7 @@ jobs:
- uses: https://data.forgejo.org/actions/checkout@v4 - uses: https://data.forgejo.org/actions/checkout@v4
- name: copy & sign - name: copy & sign
uses: https://data.forgejo.org/forgejo/forgejo-build-publish/publish@v5.3.4 uses: https://data.forgejo.org/forgejo/forgejo-build-publish/publish@v5.3.5
with: with:
from-forgejo: ${{ vars.FORGEJO }} from-forgejo: ${{ vars.FORGEJO }}
to-forgejo: ${{ vars.FORGEJO }} to-forgejo: ${{ vars.FORGEJO }}

View file

@ -28,7 +28,7 @@ jobs:
runs-on: docker runs-on: docker
container: container:
image: data.forgejo.org/renovate/renovate:40.11.19 image: data.forgejo.org/renovate/renovate:41.1.4
steps: steps:
- name: Load renovate repo cache - name: Load renovate repo cache

View file

@ -0,0 +1,71 @@
#
# Additional integration tests designed to run once a day when
# `mirror.yml` pushes to https://codeberg.org/forgejo-integration/forgejo
# and send a notification via email should they fail.
#
# For debug purposes:
#
# - uncomment [on].pull_request
# - swap 'forgejo-integration' and 'forgejo-coding'
# - open a pull request at https://codeberg.org/forgejo/forgejo and fix things
# - swap 'forgejo-integration' and 'forgejo-coding'
# - comment [on].pull_request
#
name: testing-integration
on:
# pull_request:
push:
tags: 'v[0-9]+.[0-9]+.*'
branches:
- 'forgejo'
- 'v*/forgejo'
jobs:
test-unit:
# if: vars.ROLE == 'forgejo-coding'
if: vars.ROLE == 'forgejo-integration'
runs-on: docker
container:
image: 'data.forgejo.org/oci/node:22-bookworm'
options: --tmpfs /tmp:exec,noatime
steps:
- uses: https://data.forgejo.org/actions/checkout@v4
- uses: ./.forgejo/workflows-composite/setup-env
- name: install git 2.30
uses: ./.forgejo/workflows-composite/apt-install-from
with:
packages: git/bullseye git-lfs/bullseye
release: bullseye
- uses: ./.forgejo/workflows-composite/build-backend
- run: |
su forgejo -c 'make test-backend test-check'
timeout-minutes: 120
env:
RACE_ENABLED: 'true'
TAGS: bindata
test-sqlite:
# if: vars.ROLE == 'forgejo-coding'
if: vars.ROLE == 'forgejo-integration'
runs-on: docker
container:
image: 'data.forgejo.org/oci/node:22-bookworm'
options: --tmpfs /tmp:exec,noatime
steps:
- uses: https://data.forgejo.org/actions/checkout@v4
- uses: ./.forgejo/workflows-composite/setup-env
- name: install git 2.30
uses: ./.forgejo/workflows-composite/apt-install-from
with:
packages: git/bullseye git-lfs/bullseye
release: bullseye
- uses: ./.forgejo/workflows-composite/build-backend
- run: |
su forgejo -c 'make test-sqlite-migration test-sqlite'
timeout-minutes: 120
env:
TAGS: sqlite sqlite_unlock_notify
RACE_ENABLED: true
TEST_TAGS: sqlite sqlite_unlock_notify
USE_REPO_TEST_DIR: 1

View file

@ -91,6 +91,7 @@ jobs:
RACE_ENABLED: 'true' RACE_ENABLED: 'true'
TAGS: bindata TAGS: bindata
TEST_ELASTICSEARCH_URL: http://elasticsearch:9200 TEST_ELASTICSEARCH_URL: http://elasticsearch:9200
TEST_MINIO_ENDPOINT: minio:9000
test-e2e: test-e2e:
if: vars.ROLE == 'forgejo-coding' || vars.ROLE == 'forgejo-testing' if: vars.ROLE == 'forgejo-coding' || vars.ROLE == 'forgejo-testing'
runs-on: docker runs-on: docker
@ -114,6 +115,11 @@ jobs:
run: | run: |
su forgejo -c 'make deps-frontend frontend' su forgejo -c 'make deps-frontend frontend'
- uses: ./.forgejo/workflows-composite/build-backend - uses: ./.forgejo/workflows-composite/build-backend
- name: Decide to run all tests
id: run-all
if: contains(github.event.pull_request.labels.*.name, 'run-all-playwright-tests') || contains(github.event.pull_request.title, 'playwright')
run: |
echo "all=1" >> "$GITHUB_OUTPUT"
- name: Get changed files - name: Get changed files
id: changed-files id: changed-files
uses: https://data.forgejo.org/tj-actions/changed-files@v46 uses: https://data.forgejo.org/tj-actions/changed-files@v46
@ -126,6 +132,7 @@ jobs:
USE_REPO_TEST_DIR: 1 USE_REPO_TEST_DIR: 1
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
CHANGED_FILES: ${{steps.changed-files.outputs.all_changed_files}} CHANGED_FILES: ${{steps.changed-files.outputs.all_changed_files}}
RUN_ALL: ${{steps.run-all.all}}
- name: Upload test artifacts on failure - name: Upload test artifacts on failure
if: failure() if: failure()
uses: https://data.forgejo.org/forgejo/upload-artifact@v4 uses: https://data.forgejo.org/forgejo/upload-artifact@v4

View file

@ -10,6 +10,7 @@
# Javascript and CSS code. # Javascript and CSS code.
web_src/.* @beowulf @gusted web_src/.* @beowulf @gusted
web_src/css/.* @0ko
# HTML templates used by the backend. # HTML templates used by the backend.
templates/.* @beowulf @gusted templates/.* @beowulf @gusted

View file

@ -1,6 +1,6 @@
FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/xx AS xx FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/xx AS xx
FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/golang:1.24-alpine3.21 AS build-env FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/golang:1.24-alpine3.22 AS build-env
ARG GOPROXY ARG GOPROXY
ENV GOPROXY=${GOPROXY:-https://proxy.golang.org,direct} ENV GOPROXY=${GOPROXY:-https://proxy.golang.org,direct}
@ -33,10 +33,10 @@ RUN apk --no-cache add build-base git nodejs npm
COPY . ${GOPATH}/src/forgejo.org COPY . ${GOPATH}/src/forgejo.org
WORKDIR ${GOPATH}/src/forgejo.org WORKDIR ${GOPATH}/src/forgejo.org
RUN make clean RUN make clean-no-bindata
RUN make frontend RUN make frontend
RUN go build contrib/environment-to-ini/environment-to-ini.go && xx-verify environment-to-ini RUN go build contrib/environment-to-ini/environment-to-ini.go && xx-verify environment-to-ini
RUN LDFLAGS="-buildid=" make RELEASE_VERSION=$RELEASE_VERSION GOFLAGS="-trimpath" go-check generate-backend static-executable && xx-verify gitea RUN LDFLAGS="-buildid=" make FORGEJO_GENERATE_SKIP_HASH=true RELEASE_VERSION=$RELEASE_VERSION GOFLAGS="-trimpath" go-check generate-backend static-executable && xx-verify gitea
# Copy local files # Copy local files
COPY docker/root /tmp/local COPY docker/root /tmp/local
@ -51,7 +51,7 @@ RUN chmod 755 /tmp/local/usr/bin/entrypoint \
/go/src/forgejo.org/environment-to-ini /go/src/forgejo.org/environment-to-ini
RUN chmod 644 /go/src/forgejo.org/contrib/autocompletion/bash_autocomplete RUN chmod 644 /go/src/forgejo.org/contrib/autocompletion/bash_autocomplete
FROM data.forgejo.org/oci/alpine:3.21 FROM data.forgejo.org/oci/alpine:3.22
ARG RELEASE_VERSION ARG RELEASE_VERSION
LABEL maintainer="contact@forgejo.org" \ LABEL maintainer="contact@forgejo.org" \
org.opencontainers.image.authors="Forgejo" \ org.opencontainers.image.authors="Forgejo" \

View file

@ -1,6 +1,6 @@
FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/xx AS xx FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/xx AS xx
FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/golang:1.24-alpine3.21 AS build-env FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/golang:1.24-alpine3.22 AS build-env
ARG GOPROXY ARG GOPROXY
ENV GOPROXY=${GOPROXY:-https://proxy.golang.org,direct} ENV GOPROXY=${GOPROXY:-https://proxy.golang.org,direct}
@ -33,10 +33,10 @@ RUN apk --no-cache add build-base git nodejs npm
COPY . ${GOPATH}/src/forgejo.org COPY . ${GOPATH}/src/forgejo.org
WORKDIR ${GOPATH}/src/forgejo.org WORKDIR ${GOPATH}/src/forgejo.org
RUN make clean RUN make clean-no-bindata
RUN make frontend RUN make frontend
RUN go build contrib/environment-to-ini/environment-to-ini.go && xx-verify environment-to-ini RUN go build contrib/environment-to-ini/environment-to-ini.go && xx-verify environment-to-ini
RUN make RELEASE_VERSION=$RELEASE_VERSION go-check generate-backend static-executable && xx-verify gitea RUN make FORGEJO_GENERATE_SKIP_HASH=true RELEASE_VERSION=$RELEASE_VERSION go-check generate-backend static-executable && xx-verify gitea
# Copy local files # Copy local files
COPY docker/rootless /tmp/local COPY docker/rootless /tmp/local
@ -49,7 +49,7 @@ RUN chmod 755 /tmp/local/usr/local/bin/docker-entrypoint.sh \
/go/src/forgejo.org/environment-to-ini /go/src/forgejo.org/environment-to-ini
RUN chmod 644 /go/src/forgejo.org/contrib/autocompletion/bash_autocomplete RUN chmod 644 /go/src/forgejo.org/contrib/autocompletion/bash_autocomplete
FROM data.forgejo.org/oci/alpine:3.21 FROM data.forgejo.org/oci/alpine:3.22
ARG RELEASE_VERSION ARG RELEASE_VERSION
LABEL maintainer="contact@forgejo.org" \ LABEL maintainer="contact@forgejo.org" \
org.opencontainers.image.authors="Forgejo" \ org.opencontainers.image.authors="Forgejo" \

View file

@ -37,19 +37,17 @@ endif
XGO_VERSION := go-1.21.x XGO_VERSION := go-1.21.x
AIR_PACKAGE ?= github.com/air-verse/air@v1 # renovate: datasource=go AIR_PACKAGE ?= github.com/air-verse/air@v1 # renovate: datasource=go
EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3.2.1 # renovate: datasource=go EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3.3.0 # renovate: datasource=go
GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.8.0 # renovate: datasource=go GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.8.0 # renovate: datasource=go
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.1.6 # renovate: datasource=go GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.1.6 # renovate: datasource=go
GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11 # renovate: datasource=go GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11 # renovate: datasource=go
MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.6.0 # renovate: datasource=go
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.31.0 # renovate: datasource=go SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.31.0 # renovate: datasource=go
XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest
GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1.6.0 # renovate: datasource=go GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1.6.0 # renovate: datasource=go
GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1 # renovate: datasource=go GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1 # renovate: datasource=go
DEADCODE_PACKAGE ?= golang.org/x/tools/cmd/deadcode@v0.32.0 # renovate: datasource=go DEADCODE_PACKAGE ?= golang.org/x/tools/cmd/deadcode@v0.34.0 # renovate: datasource=go
GOMOCK_PACKAGE ?= go.uber.org/mock/mockgen@v0.5.1 # renovate: datasource=go GOMOCK_PACKAGE ?= go.uber.org/mock/mockgen@v0.5.2 # renovate: datasource=go
GOPLS_PACKAGE ?= golang.org/x/tools/gopls@v0.18.1 # renovate: datasource=go RENOVATE_NPM_PACKAGE ?= renovate@41.1.4 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate
RENOVATE_NPM_PACKAGE ?= renovate@40.11.19 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate
# https://github.com/disposable-email-domains/disposable-email-domains/commits/main/ # https://github.com/disposable-email-domains/disposable-email-domains/commits/main/
DISPOSABLE_EMAILS_SHA ?= 0c27e671231d27cf66370034d7f6818037416989 # renovate: ... DISPOSABLE_EMAILS_SHA ?= 0c27e671231d27cf66370034d7f6818037416989 # renovate: ...
@ -130,7 +128,7 @@ WEBPACK_CONFIGS := webpack.config.js tailwind.config.js
WEBPACK_DEST := public/assets/js/index.js public/assets/css/index.css WEBPACK_DEST := public/assets/js/index.js public/assets/css/index.css
WEBPACK_DEST_ENTRIES := public/assets/js public/assets/css public/assets/fonts WEBPACK_DEST_ENTRIES := public/assets/js public/assets/css public/assets/fonts
BINDATA_DEST := modules/public/bindata.go modules/options/bindata.go modules/templates/bindata.go BINDATA_DEST := modules/migration/bindata.go modules/public/bindata.go modules/options/bindata.go modules/templates/bindata.go
BINDATA_HASH := $(addsuffix .hash,$(BINDATA_DEST)) BINDATA_HASH := $(addsuffix .hash,$(BINDATA_DEST))
GENERATED_GO_DEST := modules/charset/invisible_gen.go modules/charset/ambiguous_gen.go GENERATED_GO_DEST := modules/charset/invisible_gen.go modules/charset/ambiguous_gen.go
@ -223,7 +221,6 @@ help:
@echo " - lint-go lint go files" @echo " - lint-go lint go files"
@echo " - lint-go-fix lint go files and fix issues" @echo " - lint-go-fix lint go files and fix issues"
@echo " - lint-go-vet lint go files with vet" @echo " - lint-go-vet lint go files with vet"
@echo " - lint-go-gopls lint go files with gopls"
@echo " - lint-js lint js files" @echo " - lint-js lint js files"
@echo " - lint-js-fix lint js files and fix issues" @echo " - lint-js-fix lint js files and fix issues"
@echo " - lint-css lint css files" @echo " - lint-css lint css files"
@ -326,8 +323,12 @@ clean-all: clean
rm -rf $(WEBPACK_DEST_ENTRIES) node_modules rm -rf $(WEBPACK_DEST_ENTRIES) node_modules
.PHONY: clean .PHONY: clean
clean: clean: clean-no-bindata
rm -rf $(EXECUTABLE) $(DIST) $(BINDATA_DEST) $(BINDATA_HASH) \ rm -rf $(BINDATA_DEST) $(BINDATA_HASH)
.PHONY: clean-no-bindata
clean-no-bindata:
rm -rf $(EXECUTABLE) $(DIST) \
integrations*.test \ integrations*.test \
e2e*.test \ e2e*.test \
tests/integration/gitea-integration-* \ tests/integration/gitea-integration-* \
@ -484,11 +485,6 @@ lint-go-vet:
@echo "Running go vet..." @echo "Running go vet..."
@$(GO) vet ./... @$(GO) vet ./...
.PHONY: lint-go-gopls
lint-go-gopls:
@echo "Running gopls check..."
@GO=$(GO) GOPLS_PACKAGE=$(GOPLS_PACKAGE) tools/lint-go-gopls.sh $(GO_SOURCES_NO_BINDATA)
.PHONY: lint-editorconfig .PHONY: lint-editorconfig
lint-editorconfig: lint-editorconfig:
$(GO) run $(EDITORCONFIG_CHECKER_PACKAGE) templates .forgejo/workflows $(GO) run $(EDITORCONFIG_CHECKER_PACKAGE) templates .forgejo/workflows
@ -557,7 +553,7 @@ test-check:
.PHONY: test\#% .PHONY: test\#%
test\#%: test\#%:
@echo "Running go test with -tags '$(TEST_TAGS)'..." @echo "Running go test with $(GOTESTFLAGS) -tags '$(TEST_TAGS)'..."
@$(GOTEST) $(GOTESTFLAGS) -tags='$(TEST_TAGS)' -run $(subst .,/,$*) $(GO_TEST_PACKAGES) @$(GOTEST) $(GOTESTFLAGS) -tags='$(TEST_TAGS)' -run $(subst .,/,$*) $(GO_TEST_PACKAGES)
.PHONY: coverage .PHONY: coverage
@ -924,13 +920,11 @@ deps-tools:
$(GO) install $(GOFUMPT_PACKAGE) $(GO) install $(GOFUMPT_PACKAGE)
$(GO) install $(GOLANGCI_LINT_PACKAGE) $(GO) install $(GOLANGCI_LINT_PACKAGE)
$(GO) install $(GXZ_PACKAGE) $(GO) install $(GXZ_PACKAGE)
$(GO) install $(MISSPELL_PACKAGE)
$(GO) install $(SWAGGER_PACKAGE) $(GO) install $(SWAGGER_PACKAGE)
$(GO) install $(XGO_PACKAGE) $(GO) install $(XGO_PACKAGE)
$(GO) install $(GO_LICENSES_PACKAGE) $(GO) install $(GO_LICENSES_PACKAGE)
$(GO) install $(GOVULNCHECK_PACKAGE) $(GO) install $(GOVULNCHECK_PACKAGE)
$(GO) install $(GOMOCK_PACKAGE) $(GO) install $(GOMOCK_PACKAGE)
$(GO) install $(GOPLS_PACKAGE)
node_modules: package-lock.json node_modules: package-lock.json
npm install --no-save npm install --no-save

File diff suppressed because one or more lines are too long

View file

@ -1,14 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
//go:build vendor
package main
// Libraries that are included to vendor utilities used during build.
// These libraries will not be included in a normal compilation.
import (
// for embed
_ "github.com/shurcooL/vfsgen"
)

View file

@ -1,5 +1,6 @@
// Copyright 2020 The Gitea Authors. All rights reserved. // Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT // Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: GPL-3.0-or-later
//go:build ignore //go:build ignore
@ -7,30 +8,40 @@ package main
import ( import (
"bytes" "bytes"
"crypto/sha1" "crypto/sha256"
"fmt" "fmt"
"io"
"io/fs"
"log" "log"
"net/http"
"os" "os"
"path"
"path/filepath" "path/filepath"
"strconv" "strconv"
"text/template"
"github.com/shurcooL/vfsgen" "github.com/klauspost/compress/zstd"
) )
func needsUpdate(dir, filename string) (bool, []byte) { func fileExists(filename string) bool {
needRegen := false
_, err := os.Stat(filename) _, err := os.Stat(filename)
if err != nil { if err == nil {
needRegen = true return true
} }
if os.IsNotExist(err) {
return false
}
panic(err)
}
func needsUpdate(dir, filename string) (bool, []byte) {
needRegen := !fileExists(filename)
oldHash, err := os.ReadFile(filename + ".hash") oldHash, err := os.ReadFile(filename + ".hash")
if err != nil { if err != nil {
oldHash = []byte{} oldHash = []byte{}
} }
hasher := sha1.New() hasher := sha256.New()
err = filepath.WalkDir(dir, func(path string, d os.DirEntry, err error) error { err = filepath.WalkDir(dir, func(path string, d os.DirEntry, err error) error {
if err != nil { if err != nil {
@ -51,7 +62,7 @@ func needsUpdate(dir, filename string) (bool, []byte) {
newHash := hasher.Sum([]byte{}) newHash := hasher.Sum([]byte{})
if bytes.Compare(oldHash, newHash) != 0 { if !bytes.Equal(oldHash, newHash) {
return true, newHash return true, newHash
} }
@ -69,24 +80,280 @@ func main() {
useGlobalModTime, _ = strconv.ParseBool(os.Args[4]) useGlobalModTime, _ = strconv.ParseBool(os.Args[4])
} }
update, newHash := needsUpdate(dir, filename) if os.Getenv("FORGEJO_GENERATE_SKIP_HASH") == "true" && fileExists(filename) {
fmt.Printf("bindata %s already exists and FORGEJO_GENERATE_SKIP_HASH=true\n", packageName)
return
}
update, newHash := needsUpdate(dir, filename)
if !update { if !update {
fmt.Printf("bindata for %s already up-to-date\n", packageName) fmt.Printf("bindata %s already exists and the checksum is a match\n", packageName)
return return
} }
fmt.Printf("generating bindata for %s\n", packageName) fmt.Printf("generating bindata for %s\n", packageName)
var fsTemplates http.FileSystem = http.Dir(dir)
err := vfsgen.Generate(fsTemplates, vfsgen.Options{ root, err := os.OpenRoot(dir)
PackageName: packageName,
BuildTags: "bindata",
VariableName: "Assets",
Filename: filename,
UseGlobalModTime: useGlobalModTime,
})
if err != nil { if err != nil {
log.Fatalf("%v\n", err) log.Fatal(err)
}
out, err := os.OpenFile(filename, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o644)
if err != nil {
log.Fatal(err)
}
defer out.Close()
if err := generate(root.FS(), packageName, useGlobalModTime, out); err != nil {
log.Fatal(err)
} }
_ = os.WriteFile(filename+".hash", newHash, 0o666) _ = os.WriteFile(filename+".hash", newHash, 0o666)
} }
type file struct {
Path string
Name string
UncompressedSize int
CompressedData []byte
UncompressedData []byte
}
type direntry struct {
Name string
IsDir bool
}
func generate(fsRoot fs.FS, packageName string, globalTime bool, output io.Writer) error {
enc, err := zstd.NewWriter(nil, zstd.WithEncoderLevel(zstd.SpeedBestCompression))
if err != nil {
return err
}
files := []file{}
dirs := map[string][]direntry{}
if err := fs.WalkDir(fsRoot, ".", func(filePath string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() {
entries, err := fs.ReadDir(fsRoot, filePath)
if err != nil {
return err
}
dirEntries := make([]direntry, 0, len(entries))
for _, entry := range entries {
dirEntries = append(dirEntries, direntry{Name: entry.Name(), IsDir: entry.IsDir()})
}
dirs[filePath] = dirEntries
return nil
}
src, err := fs.ReadFile(fsRoot, filePath)
if err != nil {
return err
}
dst := enc.EncodeAll(src, nil)
if len(dst) < len(src) {
files = append(files, file{
Path: filePath,
Name: path.Base(filePath),
UncompressedSize: len(src),
CompressedData: dst,
})
} else {
files = append(files, file{
Path: filePath,
Name: path.Base(filePath),
UncompressedData: src,
})
}
return nil
}); err != nil {
return err
}
return generatedTmpl.Execute(output, map[string]any{
"Packagename": packageName,
"GlobalTime": globalTime,
"Files": files,
"Dirs": dirs,
})
}
var generatedTmpl = template.Must(template.New("").Parse(`// Code generated by efs-gen. DO NOT EDIT.
//go:build bindata
package {{.Packagename}}
import (
"bytes"
"time"
"io"
"io/fs"
"github.com/klauspost/compress/zstd"
)
type normalFile struct {
name string
content []byte
}
type compressedFile struct {
name string
uncompressedSize int64
data []byte
}
var files = map[string]any{
{{- range .Files}}
"{{.Path}}": {{if .CompressedData}}compressedFile{"{{.Name}}", {{.UncompressedSize}}, []byte({{printf "%+q" .CompressedData}})}{{else}}normalFile{"{{.Name}}", []byte({{printf "%+q" .UncompressedData}})}{{end}},
{{- end}}
}
var dirs = map[string][]fs.DirEntry{
{{- range $key, $entry := .Dirs}}
"{{$key}}": {
{{- range $entry}}
direntry{"{{.Name}}", {{.IsDir}}},
{{- end}}
},
{{- end}}
}
type assets struct{}
var Assets = assets{}
func (a assets) Open(name string) (fs.File, error) {
f, ok := files[name]
if !ok {
return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrNotExist}
}
switch f := f.(type) {
case normalFile:
return file{name: f.name, size: int64(len(f.content)), data: bytes.NewReader(f.content)}, nil
case compressedFile:
r, _ := zstd.NewReader(bytes.NewReader(f.data))
return &compressFile{name: f.name, size: f.uncompressedSize, data: r, content: f.data}, nil
default:
panic("unknown file type")
}
}
func (a assets) ReadDir(name string) ([]fs.DirEntry, error) {
d, ok := dirs[name]
if !ok {
return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrNotExist}
}
return d, nil
}
type file struct {
name string
size int64
data io.ReadSeeker
}
var _ io.ReadSeeker = (*file)(nil)
func (f file) Stat() (fs.FileInfo, error) {
return fileinfo{name: f.name, size: f.size}, nil
}
func (f file) Read(p []byte) (int, error) {
return f.data.Read(p)
}
func (f file) Seek(offset int64, whence int) (int64, error) {
return f.data.Seek(offset, whence)
}
func (f file) Close() error { return nil }
type compressFile struct {
name string
size int64
data *zstd.Decoder
content []byte
zstdPos int64
seekPos int64
}
var _ io.ReadSeeker = (*compressFile)(nil)
func (f *compressFile) Stat() (fs.FileInfo, error) {
return fileinfo{name: f.name, size: f.size}, nil
}
func (f *compressFile) Read(p []byte) (int, error) {
if f.zstdPos > f.seekPos {
if err := f.data.Reset(bytes.NewReader(f.content)); err != nil {
return 0, err
}
f.zstdPos = 0
}
if f.zstdPos < f.seekPos {
if _, err := io.CopyN(io.Discard, f.data, f.seekPos - f.zstdPos); err != nil {
return 0, err
}
f.zstdPos = f.seekPos
}
n, err := f.data.Read(p)
f.zstdPos += int64(n)
f.seekPos = f.zstdPos
return n, err
}
func (f *compressFile) Seek(offset int64, whence int) (int64, error) {
switch whence {
case io.SeekStart:
f.seekPos = 0 + offset
case io.SeekCurrent:
f.seekPos += offset
case io.SeekEnd:
f.seekPos = f.size + offset
}
return f.seekPos, nil
}
func (f *compressFile) Close() error {
f.data.Close()
return nil
}
func (f *compressFile) ZstdBytes() []byte { return f.content }
type fileinfo struct {
name string
size int64
}
func (f fileinfo) Name() string { return f.name }
func (f fileinfo) Size() int64 { return f.size }
func (f fileinfo) Mode() fs.FileMode { return 0o444 }
func (f fileinfo) ModTime() time.Time { return {{if .GlobalTime}}GlobalModTime(f.name){{else}}time.Unix(0, 0){{end}} }
func (f fileinfo) IsDir() bool { return false }
func (f fileinfo) Sys() any { return nil }
type direntry struct {
name string
isDir bool
}
func (d direntry) Name() string { return d.name }
func (d direntry) IsDir() bool { return d.isDir }
func (d direntry) Type() fs.FileMode {
if d.isDir {
return 0o755 | fs.ModeDir
}
return 0o444
}
func (direntry) Info() (fs.FileInfo, error) { return nil, fs.ErrNotExist }
`))

View file

@ -52,7 +52,7 @@ func initBlueMondayPolicy() {
policy.AllowAttrs("id").Matching(positionalPlaceholderRe).OnElements("code") policy.AllowAttrs("id").Matching(positionalPlaceholderRe).OnElements("code")
// Allowed elements with no attributes. Must be a recognized tagname. // Allowed elements with no attributes. Must be a recognized tagname.
policy.AllowElements("strong", "br", "b", "strike", "code", "i") policy.AllowElements("strong", "br", "b", "strike", "code", "i", "kbd")
// TODO: Remove <c> in `actions.workflow.dispatch.trigger_found`. // TODO: Remove <c> in `actions.workflow.dispatch.trigger_found`.
policy.AllowNoAttrs().OnElements("c") policy.AllowNoAttrs().OnElements("c")

View file

@ -37,6 +37,7 @@ func TestLocalizationPolicy(t *testing.T) {
assert.Empty(t, checkLocaleContent([]byte("teams.specific_repositories_helper = Members will only have access to repositories explicitly added to the team. Selecting this <strong>will not</strong> automatically remove repositories already added with <i>All repositories</i>."))) assert.Empty(t, checkLocaleContent([]byte("teams.specific_repositories_helper = Members will only have access to repositories explicitly added to the team. Selecting this <strong>will not</strong> automatically remove repositories already added with <i>All repositories</i>.")))
assert.Empty(t, checkLocaleContent([]byte("sqlite_helper = File path for the SQLite3 database.<br>Enter an absolute path if you run Forgejo as a service."))) assert.Empty(t, checkLocaleContent([]byte("sqlite_helper = File path for the SQLite3 database.<br>Enter an absolute path if you run Forgejo as a service.")))
assert.Empty(t, checkLocaleContent([]byte("hi_user_x = Hi <b>%s</b>,"))) assert.Empty(t, checkLocaleContent([]byte("hi_user_x = Hi <b>%s</b>,")))
assert.Empty(t, checkLocaleContent([]byte("key = Press <kbd>Shift</kbd>")))
assert.Equal(t, []string{"error404: The page you are trying to reach either <strong\x1b[31m title='aaa'\x1b[0m>does not exist</strong> or <strong>you are not authorized</strong> to view it."}, checkLocaleContent([]byte("error404 = The page you are trying to reach either <strong title='aaa'>does not exist</strong> or <strong>you are not authorized</strong> to view it."))) assert.Equal(t, []string{"error404: The page you are trying to reach either <strong\x1b[31m title='aaa'\x1b[0m>does not exist</strong> or <strong>you are not authorized</strong> to view it."}, checkLocaleContent([]byte("error404 = The page you are trying to reach either <strong title='aaa'>does not exist</strong> or <strong>you are not authorized</strong> to view it.")))
}) })

View file

@ -4,25 +4,28 @@
package cmd package cmd
import ( import (
"context"
"fmt" "fmt"
"forgejo.org/modules/private" "forgejo.org/modules/private"
"forgejo.org/modules/setting" "forgejo.org/modules/setting"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v3"
) )
var (
// CmdActions represents the available actions sub-commands. // CmdActions represents the available actions sub-commands.
CmdActions = &cli.Command{ func cmdActions() *cli.Command {
return &cli.Command{
Name: "actions", Name: "actions",
Usage: "Manage Forgejo Actions", Usage: "Manage Forgejo Actions",
Subcommands: []*cli.Command{ Commands: []*cli.Command{
subcmdActionsGenRunnerToken, subcmdActionsGenRunnerToken(),
}, },
} }
}
subcmdActionsGenRunnerToken = &cli.Command{ func subcmdActionsGenRunnerToken() *cli.Command {
return &cli.Command{
Name: "generate-runner-token", Name: "generate-runner-token",
Usage: "Generate a new token for a runner to use to register with the server", Usage: "Generate a new token for a runner to use to register with the server",
Action: runGenerateActionsRunnerToken, Action: runGenerateActionsRunnerToken,
@ -36,10 +39,10 @@ var (
}, },
}, },
} }
) }
func runGenerateActionsRunnerToken(c *cli.Context) error { func runGenerateActionsRunnerToken(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals() ctx, cancel := installSignals(ctx)
defer cancel() defer cancel()
setting.MustInstalled() setting.MustInstalled()

View file

@ -15,56 +15,64 @@ import (
"forgejo.org/modules/log" "forgejo.org/modules/log"
repo_module "forgejo.org/modules/repository" repo_module "forgejo.org/modules/repository"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v3"
) )
var (
// CmdAdmin represents the available admin sub-command. // CmdAdmin represents the available admin sub-command.
CmdAdmin = &cli.Command{ func cmdAdmin() *cli.Command {
return &cli.Command{
Name: "admin", Name: "admin",
Usage: "Perform common administrative operations", Usage: "Perform common administrative operations",
Subcommands: []*cli.Command{ Commands: []*cli.Command{
subcmdUser, subcmdUser(),
subcmdRepoSyncReleases, subcmdRepoSyncReleases(),
subcmdRegenerate, subcmdRegenerate(),
subcmdAuth, subcmdAuth(),
subcmdSendMail, subcmdSendMail(),
}, },
} }
}
subcmdRepoSyncReleases = &cli.Command{ func subcmdRepoSyncReleases() *cli.Command {
return &cli.Command{
Name: "repo-sync-releases", Name: "repo-sync-releases",
Usage: "Synchronize repository releases with tags", Usage: "Synchronize repository releases with tags",
Action: runRepoSyncReleases, Action: runRepoSyncReleases,
} }
}
subcmdRegenerate = &cli.Command{ func subcmdRegenerate() *cli.Command {
return &cli.Command{
Name: "regenerate", Name: "regenerate",
Usage: "Regenerate specific files", Usage: "Regenerate specific files",
Subcommands: []*cli.Command{ Commands: []*cli.Command{
microcmdRegenHooks, microcmdRegenHooks,
microcmdRegenKeys, microcmdRegenKeys,
}, },
} }
subcmdAuth = &cli.Command{
Name: "auth",
Usage: "Modify external auth providers",
Subcommands: []*cli.Command{
microcmdAuthAddOauth,
microcmdAuthUpdateOauth,
microcmdAuthAddLdapBindDn,
microcmdAuthUpdateLdapBindDn,
microcmdAuthAddLdapSimpleAuth,
microcmdAuthUpdateLdapSimpleAuth,
microcmdAuthAddSMTP,
microcmdAuthUpdateSMTP,
microcmdAuthList,
microcmdAuthDelete,
},
} }
subcmdSendMail = &cli.Command{ func subcmdAuth() *cli.Command {
return &cli.Command{
Name: "auth",
Usage: "Modify external auth providers",
Commands: []*cli.Command{
microcmdAuthAddOauth(),
microcmdAuthUpdateOauth(),
microcmdAuthAddLdapBindDn(),
microcmdAuthUpdateLdapBindDn(),
microcmdAuthAddLdapSimpleAuth(),
microcmdAuthUpdateLdapSimpleAuth(),
microcmdAuthAddSMTP(),
microcmdAuthUpdateSMTP(),
microcmdAuthList(),
microcmdAuthDelete(),
},
}
}
func subcmdSendMail() *cli.Command {
return &cli.Command{
Name: "sendmail", Name: "sendmail",
Usage: "Send a message to all users", Usage: "Send a message to all users",
Action: runSendMail, Action: runSendMail,
@ -86,15 +94,17 @@ var (
}, },
}, },
} }
}
idFlag = &cli.Int64Flag{ func idFlag() *cli.Int64Flag {
return &cli.Int64Flag{
Name: "id", Name: "id",
Usage: "ID of authentication source", Usage: "ID of authentication source",
} }
) }
func runRepoSyncReleases(_ *cli.Context) error { func runRepoSyncReleases(ctx context.Context, _ *cli.Command) error {
ctx, cancel := installSignals() ctx, cancel := installSignals(ctx)
defer cancel() defer cancel()
if err := initDB(ctx); err != nil { if err := initDB(ctx); err != nil {

View file

@ -4,6 +4,7 @@
package cmd package cmd
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
"os" "os"
@ -13,17 +14,20 @@ import (
"forgejo.org/models/db" "forgejo.org/models/db"
auth_service "forgejo.org/services/auth" auth_service "forgejo.org/services/auth"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v3"
) )
var ( func microcmdAuthDelete() *cli.Command {
microcmdAuthDelete = &cli.Command{ return &cli.Command{
Name: "delete", Name: "delete",
Usage: "Delete specific auth source", Usage: "Delete specific auth source",
Flags: []cli.Flag{idFlag}, Flags: []cli.Flag{idFlag()},
Action: runDeleteAuth, Action: runDeleteAuth,
} }
microcmdAuthList = &cli.Command{ }
func microcmdAuthList() *cli.Command {
return &cli.Command{
Name: "list", Name: "list",
Usage: "List auth sources", Usage: "List auth sources",
Action: runListAuth, Action: runListAuth,
@ -54,10 +58,10 @@ var (
}, },
}, },
} }
) }
func runListAuth(c *cli.Context) error { func runListAuth(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals() ctx, cancel := installSignals(ctx)
defer cancel() defer cancel()
if err := initDB(ctx); err != nil { if err := initDB(ctx); err != nil {
@ -81,7 +85,7 @@ func runListAuth(c *cli.Context) error {
// loop through each source and print // loop through each source and print
w := tabwriter.NewWriter(os.Stdout, c.Int("min-width"), c.Int("tab-width"), c.Int("padding"), padChar, flags) w := tabwriter.NewWriter(os.Stdout, c.Int("min-width"), c.Int("tab-width"), c.Int("padding"), padChar, flags)
fmt.Fprintf(w, "ID\tName\tType\tEnabled\n") fmt.Fprint(w, "ID\tName\tType\tEnabled\n")
for _, source := range authSources { for _, source := range authSources {
fmt.Fprintf(w, "%d\t%s\t%s\t%t\n", source.ID, source.Name, source.Type.String(), source.IsActive) fmt.Fprintf(w, "%d\t%s\t%s\t%t\n", source.ID, source.Name, source.Type.String(), source.IsActive)
} }
@ -90,12 +94,12 @@ func runListAuth(c *cli.Context) error {
return nil return nil
} }
func runDeleteAuth(c *cli.Context) error { func runDeleteAuth(ctx context.Context, c *cli.Command) error {
if !c.IsSet("id") { if !c.IsSet("id") {
return errors.New("--id flag is missing") return errors.New("--id flag is missing")
} }
ctx, cancel := installSignals() ctx, cancel := installSignals(ctx)
defer cancel() defer cancel()
if err := initDB(ctx); err != nil { if err := initDB(ctx); err != nil {

View file

@ -11,7 +11,7 @@ import (
"forgejo.org/models/auth" "forgejo.org/models/auth"
"forgejo.org/services/auth/source/ldap" "forgejo.org/services/auth/source/ldap"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v3"
) )
type ( type (
@ -23,8 +23,8 @@ type (
} }
) )
var ( func commonLdapCLIFlags() []cli.Flag {
commonLdapCLIFlags = []cli.Flag{ return []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "name", Name: "name",
Usage: "Authentication name.", Usage: "Authentication name.",
@ -102,8 +102,10 @@ var (
Usage: "The attribute of the users LDAP record containing the users avatar.", Usage: "The attribute of the users LDAP record containing the users avatar.",
}, },
} }
}
ldapBindDnCLIFlags = append(commonLdapCLIFlags, func ldapBindDnCLIFlags() []cli.Flag {
return append(commonLdapCLIFlags(),
&cli.StringFlag{ &cli.StringFlag{
Name: "bind-dn", Name: "bind-dn",
Usage: "The DN to bind to the LDAP server with when searching for the user.", Usage: "The DN to bind to the LDAP server with when searching for the user.",
@ -128,49 +130,59 @@ var (
Name: "page-size", Name: "page-size",
Usage: "Search page size.", Usage: "Search page size.",
}) })
}
ldapSimpleAuthCLIFlags = append(commonLdapCLIFlags, func ldapSimpleAuthCLIFlags() []cli.Flag {
return append(commonLdapCLIFlags(),
&cli.StringFlag{ &cli.StringFlag{
Name: "user-dn", Name: "user-dn",
Usage: "The user's DN.", Usage: "The user's DN.",
}) })
}
microcmdAuthAddLdapBindDn = &cli.Command{ func microcmdAuthAddLdapBindDn() *cli.Command {
return &cli.Command{
Name: "add-ldap", Name: "add-ldap",
Usage: "Add new LDAP (via Bind DN) authentication source", Usage: "Add new LDAP (via Bind DN) authentication source",
Action: func(c *cli.Context) error { Action: func(ctx context.Context, cli *cli.Command) error {
return newAuthService().addLdapBindDn(c) return newAuthService().addLdapBindDn(ctx, cli)
}, },
Flags: ldapBindDnCLIFlags, Flags: ldapBindDnCLIFlags(),
}
} }
microcmdAuthUpdateLdapBindDn = &cli.Command{ func microcmdAuthUpdateLdapBindDn() *cli.Command {
return &cli.Command{
Name: "update-ldap", Name: "update-ldap",
Usage: "Update existing LDAP (via Bind DN) authentication source", Usage: "Update existing LDAP (via Bind DN) authentication source",
Action: func(c *cli.Context) error { Action: func(ctx context.Context, cli *cli.Command) error {
return newAuthService().updateLdapBindDn(c) return newAuthService().updateLdapBindDn(ctx, cli)
}, },
Flags: append([]cli.Flag{idFlag}, ldapBindDnCLIFlags...), Flags: append([]cli.Flag{idFlag()}, ldapBindDnCLIFlags()...),
}
} }
microcmdAuthAddLdapSimpleAuth = &cli.Command{ func microcmdAuthAddLdapSimpleAuth() *cli.Command {
return &cli.Command{
Name: "add-ldap-simple", Name: "add-ldap-simple",
Usage: "Add new LDAP (simple auth) authentication source", Usage: "Add new LDAP (simple auth) authentication source",
Action: func(c *cli.Context) error { Action: func(ctx context.Context, cli *cli.Command) error {
return newAuthService().addLdapSimpleAuth(c) return newAuthService().addLdapSimpleAuth(ctx, cli)
}, },
Flags: ldapSimpleAuthCLIFlags, Flags: ldapSimpleAuthCLIFlags(),
}
} }
microcmdAuthUpdateLdapSimpleAuth = &cli.Command{ func microcmdAuthUpdateLdapSimpleAuth() *cli.Command {
return &cli.Command{
Name: "update-ldap-simple", Name: "update-ldap-simple",
Usage: "Update existing LDAP (simple auth) authentication source", Usage: "Update existing LDAP (simple auth) authentication source",
Action: func(c *cli.Context) error { Action: func(ctx context.Context, cli *cli.Command) error {
return newAuthService().updateLdapSimpleAuth(c) return newAuthService().updateLdapSimpleAuth(ctx, cli)
}, },
Flags: append([]cli.Flag{idFlag}, ldapSimpleAuthCLIFlags...), Flags: append([]cli.Flag{idFlag()}, ldapSimpleAuthCLIFlags()...),
}
} }
)
// newAuthService creates a service with default functions. // newAuthService creates a service with default functions.
func newAuthService() *authService { func newAuthService() *authService {
@ -183,7 +195,7 @@ func newAuthService() *authService {
} }
// parseAuthSource assigns values on authSource according to command line flags. // parseAuthSource assigns values on authSource according to command line flags.
func parseAuthSource(c *cli.Context, authSource *auth.Source) { func parseAuthSource(c *cli.Command, authSource *auth.Source) {
if c.IsSet("name") { if c.IsSet("name") {
authSource.Name = c.String("name") authSource.Name = c.String("name")
} }
@ -202,7 +214,7 @@ func parseAuthSource(c *cli.Context, authSource *auth.Source) {
} }
// parseLdapConfig assigns values on config according to command line flags. // parseLdapConfig assigns values on config according to command line flags.
func parseLdapConfig(c *cli.Context, config *ldap.Source) error { func parseLdapConfig(c *cli.Command, config *ldap.Source) error {
if c.IsSet("name") { if c.IsSet("name") {
config.Name = c.String("name") config.Name = c.String("name")
} }
@ -289,7 +301,7 @@ func findLdapSecurityProtocolByName(name string) (ldap.SecurityProtocol, bool) {
// getAuthSource gets the login source by its id defined in the command line flags. // getAuthSource gets the login source by its id defined in the command line flags.
// It returns an error if the id is not set, does not match any source or if the source is not of expected type. // It returns an error if the id is not set, does not match any source or if the source is not of expected type.
func (a *authService) getAuthSource(ctx context.Context, c *cli.Context, authType auth.Type) (*auth.Source, error) { func (a *authService) getAuthSource(ctx context.Context, c *cli.Command, authType auth.Type) (*auth.Source, error) {
if err := argsSet(c, "id"); err != nil { if err := argsSet(c, "id"); err != nil {
return nil, err return nil, err
} }
@ -307,12 +319,12 @@ func (a *authService) getAuthSource(ctx context.Context, c *cli.Context, authTyp
} }
// addLdapBindDn adds a new LDAP via Bind DN authentication source. // addLdapBindDn adds a new LDAP via Bind DN authentication source.
func (a *authService) addLdapBindDn(c *cli.Context) error { func (a *authService) addLdapBindDn(ctx context.Context, c *cli.Command) error {
if err := argsSet(c, "name", "security-protocol", "host", "port", "user-search-base", "user-filter", "email-attribute"); err != nil { if err := argsSet(c, "name", "security-protocol", "host", "port", "user-search-base", "user-filter", "email-attribute"); err != nil {
return err return err
} }
ctx, cancel := installSignals() ctx, cancel := installSignals(ctx)
defer cancel() defer cancel()
if err := a.initDB(ctx); err != nil { if err := a.initDB(ctx); err != nil {
@ -336,8 +348,8 @@ func (a *authService) addLdapBindDn(c *cli.Context) error {
} }
// updateLdapBindDn updates a new LDAP via Bind DN authentication source. // updateLdapBindDn updates a new LDAP via Bind DN authentication source.
func (a *authService) updateLdapBindDn(c *cli.Context) error { func (a *authService) updateLdapBindDn(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals() ctx, cancel := installSignals(ctx)
defer cancel() defer cancel()
if err := a.initDB(ctx); err != nil { if err := a.initDB(ctx); err != nil {
@ -358,12 +370,12 @@ func (a *authService) updateLdapBindDn(c *cli.Context) error {
} }
// addLdapSimpleAuth adds a new LDAP (simple auth) authentication source. // addLdapSimpleAuth adds a new LDAP (simple auth) authentication source.
func (a *authService) addLdapSimpleAuth(c *cli.Context) error { func (a *authService) addLdapSimpleAuth(ctx context.Context, c *cli.Command) error {
if err := argsSet(c, "name", "security-protocol", "host", "port", "user-dn", "user-filter", "email-attribute"); err != nil { if err := argsSet(c, "name", "security-protocol", "host", "port", "user-dn", "user-filter", "email-attribute"); err != nil {
return err return err
} }
ctx, cancel := installSignals() ctx, cancel := installSignals(ctx)
defer cancel() defer cancel()
if err := a.initDB(ctx); err != nil { if err := a.initDB(ctx); err != nil {
@ -387,8 +399,8 @@ func (a *authService) addLdapSimpleAuth(c *cli.Context) error {
} }
// updateLdapSimpleAuth updates a new LDAP (simple auth) authentication source. // updateLdapSimpleAuth updates a new LDAP (simple auth) authentication source.
func (a *authService) updateLdapSimpleAuth(c *cli.Context) error { func (a *authService) updateLdapSimpleAuth(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals() ctx, cancel := installSignals(ctx)
defer cancel() defer cancel()
if err := a.initDB(ctx); err != nil { if err := a.initDB(ctx); err != nil {

View file

@ -13,7 +13,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v3"
) )
func TestAddLdapBindDn(t *testing.T) { func TestAddLdapBindDn(t *testing.T) {
@ -225,12 +225,12 @@ func TestAddLdapBindDn(t *testing.T) {
} }
// Create a copy of command to test // Create a copy of command to test
app := cli.NewApp() app := cli.Command{}
app.Flags = microcmdAuthAddLdapBindDn.Flags app.Flags = microcmdAuthAddLdapBindDn().Flags
app.Action = service.addLdapBindDn app.Action = service.addLdapBindDn
// Run it // Run it
err := app.Run(c.args) err := app.Run(t.Context(), c.args)
if c.errMsg != "" { if c.errMsg != "" {
assert.EqualError(t, err, c.errMsg, "case %d: error should match", n) assert.EqualError(t, err, c.errMsg, "case %d: error should match", n)
} else { } else {
@ -454,12 +454,12 @@ func TestAddLdapSimpleAuth(t *testing.T) {
} }
// Create a copy of command to test // Create a copy of command to test
app := cli.NewApp() app := cli.Command{}
app.Flags = microcmdAuthAddLdapSimpleAuth.Flags app.Flags = microcmdAuthAddLdapSimpleAuth().Flags
app.Action = service.addLdapSimpleAuth app.Action = service.addLdapSimpleAuth
// Run it // Run it
err := app.Run(c.args) err := app.Run(t.Context(), c.args)
if c.errMsg != "" { if c.errMsg != "" {
assert.EqualError(t, err, c.errMsg, "case %d: error should match", n) assert.EqualError(t, err, c.errMsg, "case %d: error should match", n)
} else { } else {
@ -915,12 +915,12 @@ func TestUpdateLdapBindDn(t *testing.T) {
} }
// Create a copy of command to test // Create a copy of command to test
app := cli.NewApp() app := cli.Command{}
app.Flags = microcmdAuthUpdateLdapBindDn.Flags app.Flags = microcmdAuthUpdateLdapBindDn().Flags
app.Action = service.updateLdapBindDn app.Action = service.updateLdapBindDn
// Run it // Run it
err := app.Run(c.args) err := app.Run(t.Context(), c.args)
if c.errMsg != "" { if c.errMsg != "" {
assert.EqualError(t, err, c.errMsg, "case %d: error should match", n) assert.EqualError(t, err, c.errMsg, "case %d: error should match", n)
} else { } else {
@ -1303,12 +1303,12 @@ func TestUpdateLdapSimpleAuth(t *testing.T) {
} }
// Create a copy of command to test // Create a copy of command to test
app := cli.NewApp() app := cli.Command{}
app.Flags = microcmdAuthUpdateLdapSimpleAuth.Flags app.Flags = microcmdAuthUpdateLdapSimpleAuth().Flags
app.Action = service.updateLdapSimpleAuth app.Action = service.updateLdapSimpleAuth
// Run it // Run it
err := app.Run(c.args) err := app.Run(t.Context(), c.args)
if c.errMsg != "" { if c.errMsg != "" {
assert.EqualError(t, err, c.errMsg, "case %d: error should match", n) assert.EqualError(t, err, c.errMsg, "case %d: error should match", n)
} else { } else {

View file

@ -4,6 +4,7 @@
package cmd package cmd
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
"net/url" "net/url"
@ -11,11 +12,11 @@ import (
auth_model "forgejo.org/models/auth" auth_model "forgejo.org/models/auth"
"forgejo.org/services/auth/source/oauth2" "forgejo.org/services/auth/source/oauth2"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v3"
) )
var ( func oauthCLIFlags() []cli.Flag {
oauthCLIFlags = []cli.Flag{ return []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "name", Name: "name",
Value: "", Value: "",
@ -120,23 +121,27 @@ var (
Usage: "Activate automatic team membership removal depending on groups", Usage: "Activate automatic team membership removal depending on groups",
}, },
} }
}
microcmdAuthAddOauth = &cli.Command{ func microcmdAuthAddOauth() *cli.Command {
return &cli.Command{
Name: "add-oauth", Name: "add-oauth",
Usage: "Add new Oauth authentication source", Usage: "Add new Oauth authentication source",
Action: runAddOauth, Action: runAddOauth,
Flags: oauthCLIFlags, Flags: oauthCLIFlags(),
}
} }
microcmdAuthUpdateOauth = &cli.Command{ func microcmdAuthUpdateOauth() *cli.Command {
return &cli.Command{
Name: "update-oauth", Name: "update-oauth",
Usage: "Update existing Oauth authentication source", Usage: "Update existing Oauth authentication source",
Action: runUpdateOauth, Action: runUpdateOauth,
Flags: append(oauthCLIFlags[:1], append([]cli.Flag{idFlag}, oauthCLIFlags[1:]...)...), Flags: append(oauthCLIFlags()[:1], append([]cli.Flag{idFlag()}, oauthCLIFlags()[1:]...)...),
}
} }
)
func parseOAuth2Config(c *cli.Context) *oauth2.Source { func parseOAuth2Config(_ context.Context, c *cli.Command) *oauth2.Source {
var customURLMapping *oauth2.CustomURLMapping var customURLMapping *oauth2.CustomURLMapping
if c.IsSet("use-custom-urls") { if c.IsSet("use-custom-urls") {
customURLMapping = &oauth2.CustomURLMapping{ customURLMapping = &oauth2.CustomURLMapping{
@ -168,15 +173,15 @@ func parseOAuth2Config(c *cli.Context) *oauth2.Source {
} }
} }
func runAddOauth(c *cli.Context) error { func runAddOauth(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals() ctx, cancel := installSignals(ctx)
defer cancel() defer cancel()
if err := initDB(ctx); err != nil { if err := initDB(ctx); err != nil {
return err return err
} }
config := parseOAuth2Config(c) config := parseOAuth2Config(ctx, c)
if config.Provider == "openidConnect" { if config.Provider == "openidConnect" {
discoveryURL, err := url.Parse(config.OpenIDConnectAutoDiscoveryURL) discoveryURL, err := url.Parse(config.OpenIDConnectAutoDiscoveryURL)
if err != nil || (discoveryURL.Scheme != "http" && discoveryURL.Scheme != "https") { if err != nil || (discoveryURL.Scheme != "http" && discoveryURL.Scheme != "https") {
@ -192,12 +197,12 @@ func runAddOauth(c *cli.Context) error {
}) })
} }
func runUpdateOauth(c *cli.Context) error { func runUpdateOauth(ctx context.Context, c *cli.Command) error {
if !c.IsSet("id") { if !c.IsSet("id") {
return errors.New("--id flag is missing") return errors.New("--id flag is missing")
} }
ctx, cancel := installSignals() ctx, cancel := installSignals(ctx)
defer cancel() defer cancel()
if err := initDB(ctx); err != nil { if err := initDB(ctx); err != nil {

View file

@ -4,6 +4,7 @@
package cmd package cmd
import ( import (
"context"
"errors" "errors"
"strings" "strings"
@ -11,11 +12,11 @@ import (
"forgejo.org/modules/util" "forgejo.org/modules/util"
"forgejo.org/services/auth/source/smtp" "forgejo.org/services/auth/source/smtp"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v3"
) )
var ( func smtpCLIFlags() []cli.Flag {
smtpCLIFlags = []cli.Flag{ return []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "name", Name: "name",
Value: "", Value: "",
@ -71,23 +72,27 @@ var (
Value: true, Value: true,
}, },
} }
}
microcmdAuthAddSMTP = &cli.Command{ func microcmdAuthAddSMTP() *cli.Command {
return &cli.Command{
Name: "add-smtp", Name: "add-smtp",
Usage: "Add new SMTP authentication source", Usage: "Add new SMTP authentication source",
Action: runAddSMTP, Action: runAddSMTP,
Flags: smtpCLIFlags, Flags: smtpCLIFlags(),
}
} }
microcmdAuthUpdateSMTP = &cli.Command{ func microcmdAuthUpdateSMTP() *cli.Command {
return &cli.Command{
Name: "update-smtp", Name: "update-smtp",
Usage: "Update existing SMTP authentication source", Usage: "Update existing SMTP authentication source",
Action: runUpdateSMTP, Action: runUpdateSMTP,
Flags: append(smtpCLIFlags[:1], append([]cli.Flag{idFlag}, smtpCLIFlags[1:]...)...), Flags: append(smtpCLIFlags()[:1], append([]cli.Flag{idFlag()}, smtpCLIFlags()[1:]...)...),
}
} }
)
func parseSMTPConfig(c *cli.Context, conf *smtp.Source) error { func parseSMTPConfig(c *cli.Command, conf *smtp.Source) error {
if c.IsSet("auth-type") { if c.IsSet("auth-type") {
conf.Auth = c.String("auth-type") conf.Auth = c.String("auth-type")
validAuthTypes := []string{"PLAIN", "LOGIN", "CRAM-MD5"} validAuthTypes := []string{"PLAIN", "LOGIN", "CRAM-MD5"}
@ -123,8 +128,8 @@ func parseSMTPConfig(c *cli.Context, conf *smtp.Source) error {
return nil return nil
} }
func runAddSMTP(c *cli.Context) error { func runAddSMTP(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals() ctx, cancel := installSignals(ctx)
defer cancel() defer cancel()
if err := initDB(ctx); err != nil { if err := initDB(ctx); err != nil {
@ -163,12 +168,12 @@ func runAddSMTP(c *cli.Context) error {
}) })
} }
func runUpdateSMTP(c *cli.Context) error { func runUpdateSMTP(ctx context.Context, c *cli.Command) error {
if !c.IsSet("id") { if !c.IsSet("id") {
return errors.New("--id flag is missing") return errors.New("--id flag is missing")
} }
ctx, cancel := installSignals() ctx, cancel := installSignals(ctx)
defer cancel() defer cancel()
if err := initDB(ctx); err != nil { if err := initDB(ctx); err != nil {

View file

@ -4,11 +4,13 @@
package cmd package cmd
import ( import (
"context"
asymkey_model "forgejo.org/models/asymkey" asymkey_model "forgejo.org/models/asymkey"
"forgejo.org/modules/graceful" "forgejo.org/modules/graceful"
repo_service "forgejo.org/services/repository" repo_service "forgejo.org/services/repository"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v3"
) )
var ( var (
@ -25,8 +27,8 @@ var (
} }
) )
func runRegenerateHooks(_ *cli.Context) error { func runRegenerateHooks(ctx context.Context, _ *cli.Command) error {
ctx, cancel := installSignals() ctx, cancel := installSignals(ctx)
defer cancel() defer cancel()
if err := initDB(ctx); err != nil { if err := initDB(ctx); err != nil {
@ -35,8 +37,8 @@ func runRegenerateHooks(_ *cli.Context) error {
return repo_service.SyncRepositoryHooks(graceful.GetManager().ShutdownContext()) return repo_service.SyncRepositoryHooks(graceful.GetManager().ShutdownContext())
} }
func runRegenerateKeys(_ *cli.Context) error { func runRegenerateKeys(ctx context.Context, _ *cli.Command) error {
ctx, cancel := installSignals() ctx, cancel := installSignals(ctx)
defer cancel() defer cancel()
if err := initDB(ctx); err != nil { if err := initDB(ctx); err != nil {

View file

@ -4,18 +4,21 @@
package cmd package cmd
import ( import (
"github.com/urfave/cli/v2" "github.com/urfave/cli/v3"
) )
var subcmdUser = &cli.Command{ func subcmdUser() *cli.Command {
return &cli.Command{
Name: "user", Name: "user",
Usage: "Modify users", Usage: "Modify users",
Subcommands: []*cli.Command{ Commands: []*cli.Command{
microcmdUserCreate, microcmdUserCreate(),
microcmdUserList, microcmdUserList(),
microcmdUserChangePassword, microcmdUserChangePassword(),
microcmdUserDelete, microcmdUserDelete(),
microcmdUserGenerateAccessToken, microcmdUserGenerateAccessToken(),
microcmdUserMustChangePassword, microcmdUserMustChangePassword(),
microcmdUserResetMFA(),
}, },
} }
}

View file

@ -4,6 +4,7 @@
package cmd package cmd
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
@ -13,10 +14,11 @@ import (
"forgejo.org/modules/setting" "forgejo.org/modules/setting"
user_service "forgejo.org/services/user" user_service "forgejo.org/services/user"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v3"
) )
var microcmdUserChangePassword = &cli.Command{ func microcmdUserChangePassword() *cli.Command {
return &cli.Command{
Name: "change-password", Name: "change-password",
Usage: "Change a user's password", Usage: "Change a user's password",
Action: runChangePassword, Action: runChangePassword,
@ -40,13 +42,14 @@ var microcmdUserChangePassword = &cli.Command{
}, },
}, },
} }
}
func runChangePassword(c *cli.Context) error { func runChangePassword(ctx context.Context, c *cli.Command) error {
if err := argsSet(c, "username", "password"); err != nil { if err := argsSet(c, "username", "password"); err != nil {
return err return err
} }
ctx, cancel := installSignals() ctx, cancel := installSignals(ctx)
defer cancel() defer cancel()
if err := initDB(ctx); err != nil { if err := initDB(ctx); err != nil {

View file

@ -4,6 +4,7 @@
package cmd package cmd
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
"strings" "strings"
@ -15,10 +16,11 @@ import (
"forgejo.org/modules/optional" "forgejo.org/modules/optional"
"forgejo.org/modules/setting" "forgejo.org/modules/setting"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v3"
) )
var microcmdUserCreate = &cli.Command{ func microcmdUserCreate() *cli.Command {
return &cli.Command{
Name: "create", Name: "create",
Usage: "Create a new user in database", Usage: "Create a new user in database",
Action: runCreateUser, Action: runCreateUser,
@ -51,7 +53,6 @@ var microcmdUserCreate = &cli.Command{
Name: "must-change-password", Name: "must-change-password",
Usage: "Set this option to false to prevent forcing the user to change their password after initial login", Usage: "Set this option to false to prevent forcing the user to change their password after initial login",
Value: true, Value: true,
DisableDefaultText: true,
}, },
&cli.IntFlag{ &cli.IntFlag{
Name: "random-password-length", Name: "random-password-length",
@ -82,8 +83,9 @@ var microcmdUserCreate = &cli.Command{
}, },
}, },
} }
}
func runCreateUser(c *cli.Context) error { func runCreateUser(ctx context.Context, c *cli.Command) error {
// this command highly depends on the many setting options (create org, visibility, etc.), so it must have a full setting load first // this command highly depends on the many setting options (create org, visibility, etc.), so it must have a full setting load first
// duplicate setting loading should be safe at the moment, but it should be refactored & improved in the future. // duplicate setting loading should be safe at the moment, but it should be refactored & improved in the future.
setting.LoadSettings() setting.LoadSettings()
@ -108,10 +110,10 @@ func runCreateUser(c *cli.Context) error {
username = c.String("username") username = c.String("username")
} else { } else {
username = c.String("name") username = c.String("name")
_, _ = fmt.Fprintf(c.App.ErrWriter, "--name flag is deprecated. Use --username instead.\n") _, _ = fmt.Fprint(c.Root().ErrWriter, "--name flag is deprecated. Use --username instead.\n")
} }
ctx, cancel := installSignals() ctx, cancel := installSignals(ctx)
defer cancel() defer cancel()
if err := initDB(ctx); err != nil { if err := initDB(ctx); err != nil {

View file

@ -4,6 +4,7 @@
package cmd package cmd
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
"strings" "strings"
@ -12,10 +13,11 @@ import (
"forgejo.org/modules/storage" "forgejo.org/modules/storage"
user_service "forgejo.org/services/user" user_service "forgejo.org/services/user"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v3"
) )
var microcmdUserDelete = &cli.Command{ func microcmdUserDelete() *cli.Command {
return &cli.Command{
Name: "delete", Name: "delete",
Usage: "Delete specific user by id, name or email", Usage: "Delete specific user by id, name or email",
Flags: []cli.Flag{ Flags: []cli.Flag{
@ -40,13 +42,14 @@ var microcmdUserDelete = &cli.Command{
}, },
Action: runDeleteUser, Action: runDeleteUser,
} }
}
func runDeleteUser(c *cli.Context) error { func runDeleteUser(ctx context.Context, c *cli.Command) error {
if !c.IsSet("id") && !c.IsSet("username") && !c.IsSet("email") { if !c.IsSet("id") && !c.IsSet("username") && !c.IsSet("email") {
return errors.New("You must provide the id, username or email of a user to delete") return errors.New("You must provide the id, username or email of a user to delete")
} }
ctx, cancel := installSignals() ctx, cancel := installSignals(ctx)
defer cancel() defer cancel()
if err := initDB(ctx); err != nil { if err := initDB(ctx); err != nil {

View file

@ -4,16 +4,18 @@
package cmd package cmd
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
auth_model "forgejo.org/models/auth" auth_model "forgejo.org/models/auth"
user_model "forgejo.org/models/user" user_model "forgejo.org/models/user"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v3"
) )
var microcmdUserGenerateAccessToken = &cli.Command{ func microcmdUserGenerateAccessToken() *cli.Command {
return &cli.Command{
Name: "generate-access-token", Name: "generate-access-token",
Usage: "Generate an access token for a specific user", Usage: "Generate an access token for a specific user",
Flags: []cli.Flag{ Flags: []cli.Flag{
@ -40,13 +42,14 @@ var microcmdUserGenerateAccessToken = &cli.Command{
}, },
Action: runGenerateAccessToken, Action: runGenerateAccessToken,
} }
}
func runGenerateAccessToken(c *cli.Context) error { func runGenerateAccessToken(ctx context.Context, c *cli.Command) error {
if !c.IsSet("username") { if !c.IsSet("username") {
return errors.New("you must provide a username to generate a token for") return errors.New("you must provide a username to generate a token for")
} }
ctx, cancel := installSignals() ctx, cancel := installSignals(ctx)
defer cancel() defer cancel()
if err := initDB(ctx); err != nil { if err := initDB(ctx); err != nil {

View file

@ -4,16 +4,18 @@
package cmd package cmd
import ( import (
"context"
"fmt" "fmt"
"os" "os"
"text/tabwriter" "text/tabwriter"
user_model "forgejo.org/models/user" user_model "forgejo.org/models/user"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v3"
) )
var microcmdUserList = &cli.Command{ func microcmdUserList() *cli.Command {
return &cli.Command{
Name: "list", Name: "list",
Usage: "List users", Usage: "List users",
Action: runListUsers, Action: runListUsers,
@ -24,9 +26,10 @@ var microcmdUserList = &cli.Command{
}, },
}, },
} }
}
func runListUsers(c *cli.Context) error { func runListUsers(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals() ctx, cancel := installSignals(ctx)
defer cancel() defer cancel()
if err := initDB(ctx); err != nil { if err := initDB(ctx); err != nil {
@ -41,7 +44,7 @@ func runListUsers(c *cli.Context) error {
w := tabwriter.NewWriter(os.Stdout, 5, 0, 1, ' ', 0) w := tabwriter.NewWriter(os.Stdout, 5, 0, 1, ' ', 0)
if c.IsSet("admin") { if c.IsSet("admin") {
fmt.Fprintf(w, "ID\tUsername\tEmail\tIsActive\n") fmt.Fprint(w, "ID\tUsername\tEmail\tIsActive\n")
for _, u := range users { for _, u := range users {
if u.IsAdmin { if u.IsAdmin {
fmt.Fprintf(w, "%d\t%s\t%s\t%t\n", u.ID, u.Name, u.Email, u.IsActive) fmt.Fprintf(w, "%d\t%s\t%s\t%t\n", u.ID, u.Name, u.Email, u.IsActive)
@ -49,7 +52,7 @@ func runListUsers(c *cli.Context) error {
} }
} else { } else {
twofa := user_model.UserList(users).GetTwoFaStatus(ctx) twofa := user_model.UserList(users).GetTwoFaStatus(ctx)
fmt.Fprintf(w, "ID\tUsername\tEmail\tIsActive\tIsAdmin\t2FA\n") fmt.Fprint(w, "ID\tUsername\tEmail\tIsActive\tIsAdmin\t2FA\n")
for _, u := range users { for _, u := range users {
fmt.Fprintf(w, "%d\t%s\t%s\t%t\t%t\t%t\n", u.ID, u.Name, u.Email, u.IsActive, u.IsAdmin, twofa[u.ID]) fmt.Fprintf(w, "%d\t%s\t%s\t%t\t%t\t%t\n", u.ID, u.Name, u.Email, u.IsActive, u.IsAdmin, twofa[u.ID])
} }

View file

@ -4,15 +4,17 @@
package cmd package cmd
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
user_model "forgejo.org/models/user" user_model "forgejo.org/models/user"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v3"
) )
var microcmdUserMustChangePassword = &cli.Command{ func microcmdUserMustChangePassword() *cli.Command {
return &cli.Command{
Name: "must-change-password", Name: "must-change-password",
Usage: "Set the must change password flag for the provided users or all users", Usage: "Set the must change password flag for the provided users or all users",
Action: runMustChangePassword, Action: runMustChangePassword,
@ -33,9 +35,10 @@ var microcmdUserMustChangePassword = &cli.Command{
}, },
}, },
} }
}
func runMustChangePassword(c *cli.Context) error { func runMustChangePassword(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals() ctx, cancel := installSignals(ctx)
defer cancel() defer cancel()
if c.NArg() == 0 && !c.IsSet("all") { if c.NArg() == 0 && !c.IsSet("all") {

View file

@ -0,0 +1,73 @@
// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package cmd
import (
"context"
"fmt"
auth_model "forgejo.org/models/auth"
user_model "forgejo.org/models/user"
"github.com/urfave/cli/v3"
)
func microcmdUserResetMFA() *cli.Command {
return &cli.Command{
Name: "reset-mfa",
Usage: "Remove all two-factor authentication configurations for a user",
Action: runResetMFA,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "username",
Aliases: []string{"u"},
Value: "",
Usage: "The user to update",
},
},
}
}
func runResetMFA(ctx context.Context, c *cli.Command) error {
if err := argsSet(c, "username"); err != nil {
return err
}
ctx, cancel := installSignals(ctx)
defer cancel()
if err := initDB(ctx); err != nil {
return err
}
user, err := user_model.GetUserByName(ctx, c.String("username"))
if err != nil {
return err
}
webAuthnList, err := auth_model.GetWebAuthnCredentialsByUID(ctx, user.ID)
if err != nil {
return err
}
for _, credential := range webAuthnList {
if _, err := auth_model.DeleteCredential(ctx, credential.ID, user.ID); err != nil {
return err
}
}
tfaModes, err := auth_model.GetTwoFactorByUID(ctx, user.ID)
if err == nil && tfaModes != nil {
if err := auth_model.DeleteTwoFactorByID(ctx, tfaModes.ID, user.ID); err != nil {
return err
}
} else {
if _, is := err.(auth_model.ErrTwoFactorNotEnrolled); !is {
return err
}
}
fmt.Printf("%s's two-factor authentication settings have been removed!\n", user.Name)
return nil
}

View file

@ -6,6 +6,7 @@
package cmd package cmd
import ( import (
"context"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/elliptic" "crypto/elliptic"
"crypto/rand" "crypto/rand"
@ -20,11 +21,12 @@ import (
"strings" "strings"
"time" "time"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v3"
) )
// CmdCert represents the available cert sub-command. // CmdCert represents the available cert sub-command.
var CmdCert = &cli.Command{ func cmdCert() *cli.Command {
return &cli.Command{
Name: "cert", Name: "cert",
Usage: "Generate self-signed certificate", Usage: "Generate self-signed certificate",
Description: `Generate a self-signed X.509 certificate for a TLS server. Description: `Generate a self-signed X.509 certificate for a TLS server.
@ -62,6 +64,7 @@ Outputs to 'cert.pem' and 'key.pem' and will overwrite existing files.`,
}, },
}, },
} }
}
func publicKey(priv any) any { func publicKey(priv any) any {
switch k := priv.(type) { switch k := priv.(type) {
@ -89,7 +92,7 @@ func pemBlockForKey(priv any) *pem.Block {
} }
} }
func runCert(c *cli.Context) error { func runCert(ctx context.Context, c *cli.Command) error {
if err := argsSet(c, "host"); err != nil { if err := argsSet(c, "host"); err != nil {
return err return err
} }

View file

@ -20,21 +20,23 @@ import (
"forgejo.org/modules/setting" "forgejo.org/modules/setting"
"forgejo.org/modules/util" "forgejo.org/modules/util"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v3"
) )
// argsSet checks that all the required arguments are set. args is a list of // argsSet checks that all the required arguments are set. args is a list of
// arguments that must be set in the passed Context. // arguments that must be set in the passed Context.
func argsSet(c *cli.Context, args ...string) error { func argsSet(c *cli.Command, args ...string) error {
for _, a := range args { for _, a := range args {
if !c.IsSet(a) { if !c.IsSet(a) {
return errors.New(a + " is not set") return errors.New(a + " is not set")
} }
if util.IsEmptyString(c.String(a)) { if s, ok := c.Value(a).(string); ok {
if util.IsEmptyString(s) {
return errors.New(a + " is required") return errors.New(a + " is required")
} }
} }
}
return nil return nil
} }
@ -73,8 +75,8 @@ If this is the intended configuration file complete the [database] section.`, se
return nil return nil
} }
func installSignals() (context.Context, context.CancelFunc) { func installSignals(ctx context.Context) (context.Context, context.CancelFunc) {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(ctx)
go func() { go func() {
// install notify // install notify
signalChannel := make(chan os.Signal, 1) signalChannel := make(chan os.Signal, 1)
@ -109,7 +111,7 @@ func setupConsoleLogger(level log.Level, colorize bool, out io.Writer) {
log.GetManager().GetLogger(log.DEFAULT).ReplaceAllWriters(writer) log.GetManager().GetLogger(log.DEFAULT).ReplaceAllWriters(writer)
} }
func globalBool(c *cli.Context, name string) bool { func globalBool(c *cli.Command, name string) bool {
for _, ctx := range c.Lineage() { for _, ctx := range c.Lineage() {
if ctx.Bool(name) { if ctx.Bool(name) {
return true return true
@ -120,16 +122,16 @@ func globalBool(c *cli.Context, name string) bool {
// PrepareConsoleLoggerLevel by default, use INFO level for console logger, but some sub-commands (for git/ssh protocol) shouldn't output any log to stdout. // PrepareConsoleLoggerLevel by default, use INFO level for console logger, but some sub-commands (for git/ssh protocol) shouldn't output any log to stdout.
// Any log appears in git stdout pipe will break the git protocol, eg: client can't push and hangs forever. // Any log appears in git stdout pipe will break the git protocol, eg: client can't push and hangs forever.
func PrepareConsoleLoggerLevel(defaultLevel log.Level) func(*cli.Context) error { func PrepareConsoleLoggerLevel(defaultLevel log.Level) func(ctx context.Context, cli *cli.Command) (context.Context, error) {
return func(c *cli.Context) error { return func(ctx context.Context, cli *cli.Command) (context.Context, error) {
level := defaultLevel level := defaultLevel
if globalBool(c, "quiet") { if globalBool(cli, "quiet") {
level = log.FATAL level = log.FATAL
} }
if globalBool(c, "debug") || globalBool(c, "verbose") { if globalBool(cli, "debug") || globalBool(cli, "verbose") {
level = log.TRACE level = log.TRACE
} }
log.SetConsoleLogger(log.DEFAULT, "console-default", level) log.SetConsoleLogger(log.DEFAULT, "console-default", level)
return nil return ctx, nil
} }
} }

View file

@ -1,65 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package cmd
import (
"fmt"
"os"
"strings"
"github.com/urfave/cli/v2"
)
// CmdDocs represents the available docs sub-command.
var CmdDocs = &cli.Command{
Name: "docs",
Usage: "Output CLI documentation",
Description: "A command to output Forgejo's CLI documentation, optionally to a file.",
Action: runDocs,
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "man",
Usage: "Output man pages instead",
},
&cli.StringFlag{
Name: "output",
Aliases: []string{"o"},
Usage: "Path to output to instead of stdout (will overwrite if exists)",
},
},
}
func runDocs(ctx *cli.Context) error {
docs, err := ctx.App.ToMarkdown()
if ctx.Bool("man") {
docs, err = ctx.App.ToMan()
}
if err != nil {
return err
}
if !ctx.Bool("man") {
// Clean up markdown. The following bug was fixed in v2, but is present in v1.
// It affects markdown output (even though the issue is referring to man pages)
// https://github.com/urfave/cli/issues/1040
firstHashtagIndex := strings.Index(docs, "#")
if firstHashtagIndex > 0 {
docs = docs[firstHashtagIndex:]
}
}
out := os.Stdout
if ctx.String("output") != "" {
fi, err := os.Create(ctx.String("output"))
if err != nil {
return err
}
defer fi.Close()
out = fi
}
_, err = fmt.Fprintln(out, docs)
return err
}

View file

@ -4,6 +4,7 @@
package cmd package cmd
import ( import (
"context"
"fmt" "fmt"
golog "log" golog "log"
"os" "os"
@ -19,23 +20,26 @@ import (
"forgejo.org/modules/setting" "forgejo.org/modules/setting"
"forgejo.org/services/doctor" "forgejo.org/services/doctor"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v3"
) )
// CmdDoctor represents the available doctor sub-command. // CmdDoctor represents the available doctor sub-command.
var CmdDoctor = &cli.Command{ func cmdDoctor() *cli.Command {
return &cli.Command{
Name: "doctor", Name: "doctor",
Usage: "Diagnose and optionally fix problems, convert or re-create database tables", Usage: "Diagnose and optionally fix problems, convert or re-create database tables",
Description: "A command to diagnose problems with the current Forgejo instance according to the given configuration. Some problems can optionally be fixed by modifying the database or data storage.", Description: "A command to diagnose problems with the current Forgejo instance according to the given configuration. Some problems can optionally be fixed by modifying the database or data storage.",
Subcommands: []*cli.Command{ Commands: []*cli.Command{
cmdDoctorCheck, cmdDoctorCheck(),
cmdRecreateTable, cmdRecreateTable(),
cmdDoctorConvert, cmdDoctorConvert(),
}, },
} }
}
var cmdDoctorCheck = &cli.Command{ func cmdDoctorCheck() *cli.Command {
return &cli.Command{
Name: "check", Name: "check",
Usage: "Diagnose and optionally fix problems", Usage: "Diagnose and optionally fix problems",
Description: "A command to diagnose problems with the current Forgejo instance according to the given configuration. Some problems can optionally be fixed by modifying the database or data storage.", Description: "A command to diagnose problems with the current Forgejo instance according to the given configuration. Some problems can optionally be fixed by modifying the database or data storage.",
@ -72,8 +76,10 @@ var cmdDoctorCheck = &cli.Command{
}, },
}, },
} }
}
var cmdRecreateTable = &cli.Command{ func cmdRecreateTable() *cli.Command {
return &cli.Command{
Name: "recreate-table", Name: "recreate-table",
Usage: "Recreate tables from XORM definitions and copy the data.", Usage: "Recreate tables from XORM definitions and copy the data.",
ArgsUsage: "[TABLE]... : (TABLEs to recreate - leave blank for all)", ArgsUsage: "[TABLE]... : (TABLEs to recreate - leave blank for all)",
@ -90,9 +96,10 @@ This command will cause Xorm to recreate tables, copying over the data and delet
You should back-up your database before doing this and ensure that your database is up-to-date first.`, You should back-up your database before doing this and ensure that your database is up-to-date first.`,
Action: runRecreateTable, Action: runRecreateTable,
} }
}
func runRecreateTable(ctx *cli.Context) error { func runRecreateTable(stdCtx context.Context, ctx *cli.Command) error {
stdCtx, cancel := installSignals() stdCtx, cancel := installSignals(stdCtx)
defer cancel() defer cancel()
// Redirect the default golog to here // Redirect the default golog to here
@ -143,7 +150,7 @@ func runRecreateTable(ctx *cli.Context) error {
}) })
} }
func setupDoctorDefaultLogger(ctx *cli.Context, colorize bool) { func setupDoctorDefaultLogger(ctx *cli.Command, colorize bool) {
// Silence the default loggers // Silence the default loggers
setupConsoleLogger(log.FATAL, log.CanColorStderr, os.Stderr) setupConsoleLogger(log.FATAL, log.CanColorStderr, os.Stderr)
@ -165,8 +172,8 @@ func setupDoctorDefaultLogger(ctx *cli.Context, colorize bool) {
} }
} }
func runDoctorCheck(ctx *cli.Context) error { func runDoctorCheck(stdCtx context.Context, ctx *cli.Command) error {
stdCtx, cancel := installSignals() stdCtx, cancel := installSignals(stdCtx)
defer cancel() defer cancel()
colorize := log.CanColorStdout colorize := log.CanColorStdout

View file

@ -4,25 +4,28 @@
package cmd package cmd
import ( import (
"context"
"fmt" "fmt"
"forgejo.org/models/db" "forgejo.org/models/db"
"forgejo.org/modules/log" "forgejo.org/modules/log"
"forgejo.org/modules/setting" "forgejo.org/modules/setting"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v3"
) )
// cmdDoctorConvert represents the available convert sub-command. // cmdDoctorConvert represents the available convert sub-command.
var cmdDoctorConvert = &cli.Command{ func cmdDoctorConvert() *cli.Command {
return &cli.Command{
Name: "convert", Name: "convert",
Usage: "Convert the database", Usage: "Convert the database",
Description: "A command to convert an existing MySQL database from utf8 to utf8mb4", Description: "A command to convert an existing MySQL database from utf8 to utf8mb4",
Action: runDoctorConvert, Action: runDoctorConvert,
} }
}
func runDoctorConvert(ctx *cli.Context) error { func runDoctorConvert(stdCtx context.Context, ctx *cli.Command) error {
stdCtx, cancel := installSignals() stdCtx, cancel := installSignals(stdCtx)
defer cancel() defer cancel()
if err := initDB(stdCtx); err != nil { if err := initDB(stdCtx); err != nil {

View file

@ -11,7 +11,7 @@ import (
"forgejo.org/services/doctor" "forgejo.org/services/doctor"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v3"
) )
func TestDoctorRun(t *testing.T) { func TestDoctorRun(t *testing.T) {
@ -22,12 +22,12 @@ func TestDoctorRun(t *testing.T) {
SkipDatabaseInitialization: true, SkipDatabaseInitialization: true,
}) })
app := cli.NewApp() app := cli.Command{}
app.Commands = []*cli.Command{cmdDoctorCheck} app.Commands = []*cli.Command{cmdDoctorCheck()}
err := app.Run([]string{"./gitea", "check", "--run", "test-check"}) err := app.Run(t.Context(), []string{"./gitea", "check", "--run", "test-check"})
require.NoError(t, err) require.NoError(t, err)
err = app.Run([]string{"./gitea", "check", "--run", "no-such"}) err = app.Run(t.Context(), []string{"./gitea", "check", "--run", "no-such"})
require.ErrorContains(t, err, `unknown checks: "no-such"`) require.ErrorContains(t, err, `unknown checks: "no-such"`)
err = app.Run([]string{"./gitea", "check", "--run", "test-check,no-such"}) err = app.Run(t.Context(), []string{"./gitea", "check", "--run", "test-check,no-such"})
require.ErrorContains(t, err, `unknown checks: "no-such"`) require.ErrorContains(t, err, `unknown checks: "no-such"`)
} }

View file

@ -5,6 +5,8 @@
package cmd package cmd
import ( import (
"context"
"errors"
"fmt" "fmt"
"io" "io"
"os" "os"
@ -22,7 +24,7 @@ import (
"code.forgejo.org/go-chi/session" "code.forgejo.org/go-chi/session"
"github.com/mholt/archiver/v3" "github.com/mholt/archiver/v3"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v3"
) )
func addReader(w archiver.Writer, r io.ReadCloser, info os.FileInfo, customName string, verbose bool) error { func addReader(w archiver.Writer, r io.ReadCloser, info os.FileInfo, customName string, verbose bool) error {
@ -83,6 +85,10 @@ func (o *outputType) Set(value string) error {
return fmt.Errorf("allowed values are %s", o.Join()) return fmt.Errorf("allowed values are %s", o.Join())
} }
func (o *outputType) Get() any {
return o.String()
}
func (o outputType) String() string { func (o outputType) String() string {
if o.selected == "" { if o.selected == "" {
return o.Default return o.Default
@ -96,7 +102,8 @@ var outputTypeEnum = &outputType{
} }
// CmdDump represents the available dump sub-command. // CmdDump represents the available dump sub-command.
var CmdDump = &cli.Command{ func cmdDump() *cli.Command {
return &cli.Command{
Name: "dump", Name: "dump",
Usage: "Dump Forgejo files and database", Usage: "Dump Forgejo files and database",
Description: `Dump compresses all related files and database into zip file. Description: `Dump compresses all related files and database into zip file.
@ -170,13 +177,14 @@ It can be used for backup and capture Forgejo server image to send to maintainer
}, },
}, },
} }
}
func fatal(format string, args ...any) { func fatal(format string, args ...any) {
fmt.Fprintf(os.Stderr, format+"\n", args...) fmt.Fprintf(os.Stderr, format+"\n", args...)
log.Fatal(format, args...) log.Fatal(format, args...)
} }
func runDump(ctx *cli.Context) error { func runDump(stdCtx context.Context, ctx *cli.Command) error {
var file *os.File var file *os.File
fileName := ctx.String("file") fileName := ctx.String("file")
outType := ctx.String("type") outType := ctx.String("type")
@ -212,16 +220,16 @@ func runDump(ctx *cli.Context) error {
if !setting.InstallLock { if !setting.InstallLock {
log.Error("Is '%s' really the right config path?\n", setting.CustomConf) log.Error("Is '%s' really the right config path?\n", setting.CustomConf)
return fmt.Errorf("forgejo is not initialized") return errors.New("forgejo is not initialized")
} }
setting.LoadSettings() // cannot access session settings otherwise setting.LoadSettings() // cannot access session settings otherwise
verbose := ctx.Bool("verbose") verbose := ctx.Bool("verbose")
if verbose && ctx.Bool("quiet") { if verbose && ctx.Bool("quiet") {
return fmt.Errorf("--quiet and --verbose cannot both be set") return errors.New("--quiet and --verbose cannot both be set")
} }
stdCtx, cancel := installSignals() stdCtx, cancel := installSignals(stdCtx)
defer cancel() defer cancel()
err := db.InitEngine(stdCtx) err := db.InitEngine(stdCtx)

View file

@ -19,11 +19,12 @@ import (
"forgejo.org/services/convert" "forgejo.org/services/convert"
"forgejo.org/services/migrations" "forgejo.org/services/migrations"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v3"
) )
// CmdDumpRepository represents the available dump repository sub-command. // CmdDumpRepository represents the available dump repository sub-command.
var CmdDumpRepository = &cli.Command{ func cmdDumpRepository() *cli.Command {
return &cli.Command{
Name: "dump-repo", Name: "dump-repo",
Usage: "Dump the repository from git/github/gitea/gitlab", Usage: "Dump the repository from git/github/gitea/gitlab",
Description: "This is a command for dumping the repository data.", Description: "This is a command for dumping the repository data.",
@ -78,9 +79,10 @@ wiki, issues, labels, releases, release_assets, milestones, pull_requests, comme
}, },
}, },
} }
}
func runDumpRepository(ctx *cli.Context) error { func runDumpRepository(stdCtx context.Context, ctx *cli.Command) error {
stdCtx, cancel := installSignals() stdCtx, cancel := installSignals(stdCtx)
defer cancel() defer cancel()
if err := initDB(stdCtx); err != nil { if err := initDB(stdCtx); err != nil {

View file

@ -4,6 +4,7 @@
package cmd package cmd
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
"os" "os"
@ -19,23 +20,25 @@ import (
"forgejo.org/modules/util" "forgejo.org/modules/util"
"github.com/gobwas/glob" "github.com/gobwas/glob"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v3"
) )
// CmdEmbedded represents the available extract sub-command. // CmdEmbedded represents the available extract sub-command.
var ( func cmdEmbedded() *cli.Command {
CmdEmbedded = &cli.Command{ return &cli.Command{
Name: "embedded", Name: "embedded",
Usage: "Extract embedded resources", Usage: "Extract embedded resources",
Description: "A command for extracting embedded resources, like templates and images", Description: "A command for extracting embedded resources, like templates and images",
Subcommands: []*cli.Command{ Commands: []*cli.Command{
subcmdList, subcmdList(),
subcmdView, subcmdView(),
subcmdExtract, subcmdExtract(),
}, },
} }
}
subcmdList = &cli.Command{ func subcmdList() *cli.Command {
return &cli.Command{
Name: "list", Name: "list",
Usage: "List files matching the given pattern", Usage: "List files matching the given pattern",
Action: runList, Action: runList,
@ -47,8 +50,10 @@ var (
}, },
}, },
} }
}
subcmdView = &cli.Command{ func subcmdView() *cli.Command {
return &cli.Command{
Name: "view", Name: "view",
Usage: "View a file matching the given pattern", Usage: "View a file matching the given pattern",
Action: runView, Action: runView,
@ -60,8 +65,10 @@ var (
}, },
}, },
} }
}
subcmdExtract = &cli.Command{ func subcmdExtract() *cli.Command {
return &cli.Command{
Name: "extract", Name: "extract",
Usage: "Extract resources", Usage: "Extract resources",
Action: runExtract, Action: runExtract,
@ -90,9 +97,9 @@ var (
}, },
}, },
} }
}
matchedAssetFiles []assetFile var matchedAssetFiles []assetFile
)
type assetFile struct { type assetFile struct {
fs *assetfs.LayeredFS fs *assetfs.LayeredFS
@ -100,7 +107,7 @@ type assetFile struct {
path string path string
} }
func initEmbeddedExtractor(c *cli.Context) error { func initEmbeddedExtractor(_ context.Context, c *cli.Command) error {
setupConsoleLogger(log.ERROR, log.CanColorStderr, os.Stderr) setupConsoleLogger(log.ERROR, log.CanColorStderr, os.Stderr)
patterns, err := compileCollectPatterns(c.Args().Slice()) patterns, err := compileCollectPatterns(c.Args().Slice())
@ -115,32 +122,32 @@ func initEmbeddedExtractor(c *cli.Context) error {
return nil return nil
} }
func runList(c *cli.Context) error { func runList(ctx context.Context, c *cli.Command) error {
if err := runListDo(c); err != nil { if err := runListDo(ctx, c); err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err) fmt.Fprintf(os.Stderr, "%v\n", err)
return err return err
} }
return nil return nil
} }
func runView(c *cli.Context) error { func runView(ctx context.Context, c *cli.Command) error {
if err := runViewDo(c); err != nil { if err := runViewDo(ctx, c); err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err) fmt.Fprintf(os.Stderr, "%v\n", err)
return err return err
} }
return nil return nil
} }
func runExtract(c *cli.Context) error { func runExtract(ctx context.Context, c *cli.Command) error {
if err := runExtractDo(c); err != nil { if err := runExtractDo(ctx, c); err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err) fmt.Fprintf(os.Stderr, "%v\n", err)
return err return err
} }
return nil return nil
} }
func runListDo(c *cli.Context) error { func runListDo(ctx context.Context, c *cli.Command) error {
if err := initEmbeddedExtractor(c); err != nil { if err := initEmbeddedExtractor(ctx, c); err != nil {
return err return err
} }
@ -151,8 +158,8 @@ func runListDo(c *cli.Context) error {
return nil return nil
} }
func runViewDo(c *cli.Context) error { func runViewDo(ctx context.Context, c *cli.Command) error {
if err := initEmbeddedExtractor(c); err != nil { if err := initEmbeddedExtractor(ctx, c); err != nil {
return err return err
} }
@ -174,8 +181,8 @@ func runViewDo(c *cli.Context) error {
return nil return nil
} }
func runExtractDo(c *cli.Context) error { func runExtractDo(ctx context.Context, c *cli.Command) error {
if err := initEmbeddedExtractor(c); err != nil { if err := initEmbeddedExtractor(ctx, c); err != nil {
return err return err
} }
@ -271,7 +278,7 @@ func extractAsset(d string, a assetFile, overwrite, rename bool) error {
return nil return nil
} }
func collectAssetFilesByPattern(c *cli.Context, globs []glob.Glob, path string, layer *assetfs.Layer) { func collectAssetFilesByPattern(c *cli.Command, globs []glob.Glob, path string, layer *assetfs.Layer) {
fs := assetfs.Layered(layer) fs := assetfs.Layered(layer)
files, err := fs.ListAllFiles(".", true) files, err := fs.ListAllFiles(".", true)
if err != nil { if err != nil {

View file

@ -6,6 +6,7 @@ package forgejo
import ( import (
"context" "context"
"encoding/hex" "encoding/hex"
"errors"
"fmt" "fmt"
"io" "io"
"os" "os"
@ -16,14 +17,14 @@ import (
"forgejo.org/modules/setting" "forgejo.org/modules/setting"
private_routers "forgejo.org/routers/private" private_routers "forgejo.org/routers/private"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v3"
) )
func CmdActions(ctx context.Context) *cli.Command { func CmdActions(ctx context.Context) *cli.Command {
return &cli.Command{ return &cli.Command{
Name: "actions", Name: "actions",
Usage: "Commands for managing Forgejo Actions", Usage: "Commands for managing Forgejo Actions",
Subcommands: []*cli.Command{ Commands: []*cli.Command{
SubcmdActionsGenerateRunnerToken(ctx), SubcmdActionsGenerateRunnerToken(ctx),
SubcmdActionsGenerateRunnerSecret(ctx), SubcmdActionsGenerateRunnerSecret(ctx),
SubcmdActionsRegister(ctx), SubcmdActionsRegister(ctx),
@ -36,7 +37,7 @@ func SubcmdActionsGenerateRunnerToken(ctx context.Context) *cli.Command {
Name: "generate-runner-token", Name: "generate-runner-token",
Usage: "Generate a new token for a runner to use to register with the server", Usage: "Generate a new token for a runner to use to register with the server",
Before: prepareWorkPathAndCustomConf(ctx), Before: prepareWorkPathAndCustomConf(ctx),
Action: func(cliCtx *cli.Context) error { return RunGenerateActionsRunnerToken(ctx, cliCtx) }, Action: RunGenerateActionsRunnerToken,
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "scope", Name: "scope",
@ -52,7 +53,7 @@ func SubcmdActionsGenerateRunnerSecret(ctx context.Context) *cli.Command {
return &cli.Command{ return &cli.Command{
Name: "generate-secret", Name: "generate-secret",
Usage: "Generate a secret suitable for input to the register subcommand", Usage: "Generate a secret suitable for input to the register subcommand",
Action: func(cliCtx *cli.Context) error { return RunGenerateSecret(ctx, cliCtx) }, Action: RunGenerateSecret,
} }
} }
@ -61,7 +62,7 @@ func SubcmdActionsRegister(ctx context.Context) *cli.Command {
Name: "register", Name: "register",
Usage: "Idempotent registration of a runner using a shared secret", Usage: "Idempotent registration of a runner using a shared secret",
Before: prepareWorkPathAndCustomConf(ctx), Before: prepareWorkPathAndCustomConf(ctx),
Action: func(cliCtx *cli.Context) error { return RunRegister(ctx, cliCtx) }, Action: RunRegister,
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "secret", Name: "secret",
@ -105,26 +106,26 @@ func SubcmdActionsRegister(ctx context.Context) *cli.Command {
} }
} }
func readSecret(ctx context.Context, cliCtx *cli.Context) (string, error) { func readSecret(ctx context.Context, cli *cli.Command) (string, error) {
if cliCtx.IsSet("secret") { if cli.IsSet("secret") {
return cliCtx.String("secret"), nil return cli.String("secret"), nil
} }
if cliCtx.IsSet("secret-stdin") { if cli.IsSet("secret-stdin") {
buf, err := io.ReadAll(ContextGetStdin(ctx)) buf, err := io.ReadAll(ContextGetStdin(ctx))
if err != nil { if err != nil {
return "", err return "", err
} }
return string(buf), nil return string(buf), nil
} }
if cliCtx.IsSet("secret-file") { if cli.IsSet("secret-file") {
path := cliCtx.String("secret-file") path := cli.String("secret-file")
buf, err := os.ReadFile(path) buf, err := os.ReadFile(path)
if err != nil { if err != nil {
return "", err return "", err
} }
return string(buf), nil return string(buf), nil
} }
return "", fmt.Errorf("at least one of the --secret, --secret-stdin, --secret-file options is required") return "", errors.New("at least one of the --secret, --secret-stdin, --secret-file options is required")
} }
func validateSecret(secret string) error { func validateSecret(secret string) error {
@ -138,18 +139,18 @@ func validateSecret(secret string) error {
return nil return nil
} }
func getLabels(cliCtx *cli.Context) (*[]string, error) { func getLabels(cli *cli.Command) (*[]string, error) {
if !cliCtx.Bool("keep-labels") { if !cli.Bool("keep-labels") {
lblValue := strings.Split(cliCtx.String("labels"), ",") lblValue := strings.Split(cli.String("labels"), ",")
return &lblValue, nil return &lblValue, nil
} }
if cliCtx.String("labels") != "" { if cli.String("labels") != "" {
return nil, fmt.Errorf("--labels and --keep-labels should not be used together") return nil, errors.New("--labels and --keep-labels should not be used together")
} }
return nil, nil return nil, nil
} }
func RunRegister(ctx context.Context, cliCtx *cli.Context) error { func RunRegister(ctx context.Context, cli *cli.Command) error {
var cancel context.CancelFunc var cancel context.CancelFunc
if !ContextGetNoInit(ctx) { if !ContextGetNoInit(ctx) {
ctx, cancel = installSignals(ctx) ctx, cancel = installSignals(ctx)
@ -161,17 +162,17 @@ func RunRegister(ctx context.Context, cliCtx *cli.Context) error {
} }
setting.MustInstalled() setting.MustInstalled()
secret, err := readSecret(ctx, cliCtx) secret, err := readSecret(ctx, cli)
if err != nil { if err != nil {
return err return err
} }
if err := validateSecret(secret); err != nil { if err := validateSecret(secret); err != nil {
return err return err
} }
scope := cliCtx.String("scope") scope := cli.String("scope")
name := cliCtx.String("name") name := cli.String("name")
version := cliCtx.String("version") version := cli.String("version")
labels, err := getLabels(cliCtx) labels, err := getLabels(cli)
if err != nil { if err != nil {
return err return err
} }
@ -209,7 +210,7 @@ func RunRegister(ctx context.Context, cliCtx *cli.Context) error {
return nil return nil
} }
func RunGenerateSecret(ctx context.Context, cliCtx *cli.Context) error { func RunGenerateSecret(ctx context.Context, cli *cli.Command) error {
runner := actions_model.ActionRunner{} runner := actions_model.ActionRunner{}
if err := runner.GenerateToken(); err != nil { if err := runner.GenerateToken(); err != nil {
return err return err
@ -220,7 +221,7 @@ func RunGenerateSecret(ctx context.Context, cliCtx *cli.Context) error {
return nil return nil
} }
func RunGenerateActionsRunnerToken(ctx context.Context, cliCtx *cli.Context) error { func RunGenerateActionsRunnerToken(ctx context.Context, cli *cli.Command) error {
if !ContextGetNoInit(ctx) { if !ContextGetNoInit(ctx) {
var cancel context.CancelFunc var cancel context.CancelFunc
ctx, cancel = installSignals(ctx) ctx, cancel = installSignals(ctx)
@ -229,7 +230,7 @@ func RunGenerateActionsRunnerToken(ctx context.Context, cliCtx *cli.Context) err
setting.MustInstalled() setting.MustInstalled()
scope := cliCtx.String("scope") scope := cli.String("scope")
respText, extra := private.GenerateActionsRunnerToken(ctx, scope) respText, extra := private.GenerateActionsRunnerToken(ctx, scope)
if extra.HasError() { if extra.HasError() {

View file

@ -4,14 +4,13 @@
package forgejo package forgejo
import ( import (
"context"
"fmt" "fmt"
"testing" "testing"
"forgejo.org/services/context"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v3"
) )
func TestActions_getLabels(t *testing.T) { func TestActions_getLabels(t *testing.T) {
@ -54,21 +53,21 @@ func TestActions_getLabels(t *testing.T) {
}, },
} }
flags := SubcmdActionsRegister(context.Context{}).Flags flags := SubcmdActionsRegister(t.Context()).Flags
for _, c := range cases { for _, c := range cases {
t.Run(fmt.Sprintf("args: %v", c.args), func(t *testing.T) { t.Run(fmt.Sprintf("args: %v", c.args), func(t *testing.T) {
// Create a copy of command to test // Create a copy of command to test
var result *resultType var result *resultType
app := cli.NewApp() app := cli.Command{}
app.Flags = flags app.Flags = flags
app.Action = func(ctx *cli.Context) error { app.Action = func(_ context.Context, ctx *cli.Command) error {
labels, err := getLabels(ctx) labels, err := getLabels(ctx)
result = &resultType{labels, err} result = &resultType{labels, err}
return nil return nil
} }
// Run it // Run it
_ = app.Run(c.args) _ = app.Run(t.Context(), c.args)
// Test the results // Test the results
require.NotNil(t, result) require.NotNil(t, result)

View file

@ -20,7 +20,7 @@ import (
f3_cmd "code.forgejo.org/f3/gof3/v3/cmd" f3_cmd "code.forgejo.org/f3/gof3/v3/cmd"
f3_logger "code.forgejo.org/f3/gof3/v3/logger" f3_logger "code.forgejo.org/f3/gof3/v3/logger"
f3_util "code.forgejo.org/f3/gof3/v3/util" f3_util "code.forgejo.org/f3/gof3/v3/util"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v3"
) )
func CmdF3(ctx context.Context) *cli.Command { func CmdF3(ctx context.Context) *cli.Command {
@ -28,21 +28,21 @@ func CmdF3(ctx context.Context) *cli.Command {
return &cli.Command{ return &cli.Command{
Name: "f3", Name: "f3",
Usage: "F3", Usage: "F3",
Subcommands: []*cli.Command{ Commands: []*cli.Command{
SubcmdF3Mirror(ctx), SubcmdF3Mirror(ctx),
}, },
} }
} }
func SubcmdF3Mirror(ctx context.Context) *cli.Command { func SubcmdF3Mirror(ctx context.Context) *cli.Command {
mirrorCmd := f3_cmd.CreateCmdMirror(ctx) mirrorCmd := f3_cmd.CreateCmdMirror()
mirrorCmd.Before = prepareWorkPathAndCustomConf(ctx) mirrorCmd.Before = prepareWorkPathAndCustomConf(ctx)
f3Action := mirrorCmd.Action f3Action := mirrorCmd.Action
mirrorCmd.Action = func(c *cli.Context) error { return runMirror(ctx, c, f3Action) } mirrorCmd.Action = func(ctx context.Context, cli *cli.Command) error { return runMirror(ctx, cli, f3Action) }
return mirrorCmd return mirrorCmd
} }
func runMirror(ctx context.Context, c *cli.Context, action cli.ActionFunc) error { func runMirror(ctx context.Context, c *cli.Command, action cli.ActionFunc) error {
setting.LoadF3Setting() setting.LoadF3Setting()
if !setting.F3.Enabled { if !setting.F3.Enabled {
return errors.New("F3 is disabled, it is not ready to be used and is only present for development purposes") return errors.New("F3 is disabled, it is not ready to be used and is only present for development purposes")
@ -69,7 +69,7 @@ func runMirror(ctx context.Context, c *cli.Context, action cli.ActionFunc) error
} }
} }
err := action(c) err := action(ctx, c)
if panicError, ok := err.(f3_util.PanicError); ok { if panicError, ok := err.(f3_util.PanicError); ok {
log.Debug("F3 Stack trace\n%s", panicError.Stack()) log.Debug("F3 Stack trace\n%s", panicError.Stack())
} }

View file

@ -16,7 +16,7 @@ import (
"forgejo.org/modules/private" "forgejo.org/modules/private"
"forgejo.org/modules/setting" "forgejo.org/modules/setting"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v3"
) )
type key int type key int
@ -34,7 +34,7 @@ func CmdForgejo(ctx context.Context) *cli.Command {
Name: "forgejo-cli", Name: "forgejo-cli",
Usage: "Forgejo CLI", Usage: "Forgejo CLI",
Flags: []cli.Flag{}, Flags: []cli.Flag{},
Subcommands: []*cli.Command{ Commands: []*cli.Command{
CmdActions(ctx), CmdActions(ctx),
CmdF3(ctx), CmdF3(ctx),
}, },
@ -147,12 +147,12 @@ func handleCliResponseExtra(ctx context.Context, extra private.ResponseExtra) er
return cli.Exit(extra.Error, 1) return cli.Exit(extra.Error, 1)
} }
func prepareWorkPathAndCustomConf(ctx context.Context) func(c *cli.Context) error { func prepareWorkPathAndCustomConf(ctx context.Context) func(ctx context.Context, cli *cli.Command) (context.Context, error) {
return func(c *cli.Context) error { return func(ctx context.Context, cli *cli.Command) (context.Context, error) {
if !ContextGetNoInit(ctx) { if !ContextGetNoInit(ctx) {
var args setting.ArgWorkPathAndCustomConf var args setting.ArgWorkPathAndCustomConf
// from children to parent, check the global flags // from children to parent, check the global flags
for _, curCtx := range c.Lineage() { for _, curCtx := range cli.Lineage() {
if curCtx.IsSet("work-path") && args.WorkPath == "" { if curCtx.IsSet("work-path") && args.WorkPath == "" {
args.WorkPath = curCtx.String("work-path") args.WorkPath = curCtx.String("work-path")
} }
@ -165,6 +165,6 @@ func prepareWorkPathAndCustomConf(ctx context.Context) func(c *cli.Context) erro
} }
setting.InitWorkPathAndCommonConfig(os.Getenv, args) setting.InitWorkPathAndCommonConfig(os.Getenv, args)
} }
return nil return ctx, nil
} }
} }

View file

@ -5,56 +5,65 @@
package cmd package cmd
import ( import (
"context"
"fmt" "fmt"
"os" "os"
"forgejo.org/modules/generate" "forgejo.org/modules/generate"
"github.com/mattn/go-isatty" "github.com/mattn/go-isatty"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v3"
) )
var (
// CmdGenerate represents the available generate sub-command. // CmdGenerate represents the available generate sub-command.
CmdGenerate = &cli.Command{ func cmdGenerate() *cli.Command {
return &cli.Command{
Name: "generate", Name: "generate",
Usage: "Generate Gitea's secrets/keys/tokens", Usage: "Generate Gitea's secrets/keys/tokens",
Subcommands: []*cli.Command{ Commands: []*cli.Command{
subcmdSecret, subcmdSecret(),
}, },
} }
}
subcmdSecret = &cli.Command{ func subcmdSecret() *cli.Command {
return &cli.Command{
Name: "secret", Name: "secret",
Usage: "Generate a secret token", Usage: "Generate a secret token",
Subcommands: []*cli.Command{ Commands: []*cli.Command{
microcmdGenerateInternalToken, microcmdGenerateInternalToken(),
microcmdGenerateLfsJwtSecret, microcmdGenerateLfsJwtSecret(),
microcmdGenerateSecretKey, microcmdGenerateSecretKey(),
}, },
} }
}
microcmdGenerateInternalToken = &cli.Command{ func microcmdGenerateInternalToken() *cli.Command {
return &cli.Command{
Name: "INTERNAL_TOKEN", Name: "INTERNAL_TOKEN",
Usage: "Generate a new INTERNAL_TOKEN", Usage: "Generate a new INTERNAL_TOKEN",
Action: runGenerateInternalToken, Action: runGenerateInternalToken,
} }
}
microcmdGenerateLfsJwtSecret = &cli.Command{ func microcmdGenerateLfsJwtSecret() *cli.Command {
return &cli.Command{
Name: "JWT_SECRET", Name: "JWT_SECRET",
Aliases: []string{"LFS_JWT_SECRET"}, Aliases: []string{"LFS_JWT_SECRET"},
Usage: "Generate a new JWT_SECRET", Usage: "Generate a new JWT_SECRET",
Action: runGenerateLfsJwtSecret, Action: runGenerateLfsJwtSecret,
} }
}
microcmdGenerateSecretKey = &cli.Command{ func microcmdGenerateSecretKey() *cli.Command {
return &cli.Command{
Name: "SECRET_KEY", Name: "SECRET_KEY",
Usage: "Generate a new SECRET_KEY", Usage: "Generate a new SECRET_KEY",
Action: runGenerateSecretKey, Action: runGenerateSecretKey,
} }
) }
func runGenerateInternalToken(c *cli.Context) error { func runGenerateInternalToken(ctx context.Context, c *cli.Command) error {
internalToken, err := generate.NewInternalToken() internalToken, err := generate.NewInternalToken()
if err != nil { if err != nil {
return err return err
@ -63,25 +72,25 @@ func runGenerateInternalToken(c *cli.Context) error {
fmt.Printf("%s", internalToken) fmt.Printf("%s", internalToken)
if isatty.IsTerminal(os.Stdout.Fd()) { if isatty.IsTerminal(os.Stdout.Fd()) {
fmt.Printf("\n") fmt.Println()
} }
return nil return nil
} }
func runGenerateLfsJwtSecret(c *cli.Context) error { func runGenerateLfsJwtSecret(ctx context.Context, c *cli.Command) error {
_, jwtSecretBase64 := generate.NewJwtSecret() _, jwtSecretBase64 := generate.NewJwtSecret()
fmt.Printf("%s", jwtSecretBase64) fmt.Printf("%s", jwtSecretBase64)
if isatty.IsTerminal(os.Stdout.Fd()) { if isatty.IsTerminal(os.Stdout.Fd()) {
fmt.Printf("\n") fmt.Print("\n")
} }
return nil return nil
} }
func runGenerateSecretKey(c *cli.Context) error { func runGenerateSecretKey(ctx context.Context, c *cli.Command) error {
secretKey, err := generate.NewSecretKey() secretKey, err := generate.NewSecretKey()
if err != nil { if err != nil {
return err return err
@ -90,7 +99,7 @@ func runGenerateSecretKey(c *cli.Context) error {
fmt.Printf("%s", secretKey) fmt.Printf("%s", secretKey)
if isatty.IsTerminal(os.Stdout.Fd()) { if isatty.IsTerminal(os.Stdout.Fd()) {
fmt.Printf("\n") fmt.Print("\n")
} }
return nil return nil

View file

@ -21,29 +21,31 @@ import (
repo_module "forgejo.org/modules/repository" repo_module "forgejo.org/modules/repository"
"forgejo.org/modules/setting" "forgejo.org/modules/setting"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v3"
) )
const ( const (
hookBatchSize = 30 hookBatchSize = 30
) )
var (
// CmdHook represents the available hooks sub-command. // CmdHook represents the available hooks sub-command.
CmdHook = &cli.Command{ func cmdHook() *cli.Command {
return &cli.Command{
Name: "hook", Name: "hook",
Usage: "(internal) Should only be called by Git", Usage: "(internal) Should only be called by Git",
Description: "Delegate commands to corresponding Git hooks", Description: "Delegate commands to corresponding Git hooks",
Before: PrepareConsoleLoggerLevel(log.FATAL), Before: PrepareConsoleLoggerLevel(log.FATAL),
Subcommands: []*cli.Command{ Commands: []*cli.Command{
subcmdHookPreReceive, subcmdHookPreReceive(),
subcmdHookUpdate, subcmdHookUpdate(),
subcmdHookPostReceive, subcmdHookPostReceive(),
subcmdHookProcReceive, subcmdHookProcReceive(),
}, },
} }
}
subcmdHookPreReceive = &cli.Command{ func subcmdHookPreReceive() *cli.Command {
return &cli.Command{
Name: "pre-receive", Name: "pre-receive",
Usage: "Delegate pre-receive Git hook", Usage: "Delegate pre-receive Git hook",
Description: "This command should only be called by Git", Description: "This command should only be called by Git",
@ -54,7 +56,10 @@ var (
}, },
}, },
} }
subcmdHookUpdate = &cli.Command{ }
func subcmdHookUpdate() *cli.Command {
return &cli.Command{
Name: "update", Name: "update",
Usage: "Delegate update Git hook", Usage: "Delegate update Git hook",
Description: "This command should only be called by Git", Description: "This command should only be called by Git",
@ -65,7 +70,10 @@ var (
}, },
}, },
} }
subcmdHookPostReceive = &cli.Command{ }
func subcmdHookPostReceive() *cli.Command {
return &cli.Command{
Name: "post-receive", Name: "post-receive",
Usage: "Delegate post-receive Git hook", Usage: "Delegate post-receive Git hook",
Description: "This command should only be called by Git", Description: "This command should only be called by Git",
@ -76,8 +84,11 @@ var (
}, },
}, },
} }
}
// Note: new hook since git 2.29 // Note: new hook since git 2.29
subcmdHookProcReceive = &cli.Command{ func subcmdHookProcReceive() *cli.Command {
return &cli.Command{
Name: "proc-receive", Name: "proc-receive",
Usage: "Delegate proc-receive Git hook", Usage: "Delegate proc-receive Git hook",
Description: "This command should only be called by Git", Description: "This command should only be called by Git",
@ -88,7 +99,7 @@ var (
}, },
}, },
} }
) }
type delayWriter struct { type delayWriter struct {
internal io.Writer internal io.Writer
@ -161,11 +172,11 @@ func (n *nilWriter) WriteString(s string) (int, error) {
return len(s), nil return len(s), nil
} }
func runHookPreReceive(c *cli.Context) error { func runHookPreReceive(ctx context.Context, c *cli.Command) error {
if isInternal, _ := strconv.ParseBool(os.Getenv(repo_module.EnvIsInternal)); isInternal { if isInternal, _ := strconv.ParseBool(os.Getenv(repo_module.EnvIsInternal)); isInternal {
return nil return nil
} }
ctx, cancel := installSignals() ctx, cancel := installSignals(ctx)
defer cancel() defer cancel()
setup(ctx, c.Bool("debug"), true) setup(ctx, c.Bool("debug"), true)
@ -247,7 +258,7 @@ Forgejo or set your environment appropriately.`, "")
newCommitIDs[count] = newCommitID newCommitIDs[count] = newCommitID
refFullNames[count] = refFullName refFullNames[count] = refFullName
count++ count++
fmt.Fprintf(out, "*") fmt.Fprint(out, "*")
if count >= hookBatchSize { if count >= hookBatchSize {
fmt.Fprintf(out, " Checking %d references\n", count) fmt.Fprintf(out, " Checking %d references\n", count)
@ -263,10 +274,10 @@ Forgejo or set your environment appropriately.`, "")
lastline = 0 lastline = 0
} }
} else { } else {
fmt.Fprintf(out, ".") fmt.Fprint(out, ".")
} }
if lastline >= hookBatchSize { if lastline >= hookBatchSize {
fmt.Fprintf(out, "\n") fmt.Fprint(out, "\n")
lastline = 0 lastline = 0
} }
} }
@ -283,7 +294,7 @@ Forgejo or set your environment appropriately.`, "")
return fail(ctx, extra.UserMsg, "HookPreReceive(last) failed: %v", extra.Error) return fail(ctx, extra.UserMsg, "HookPreReceive(last) failed: %v", extra.Error)
} }
} else if lastline > 0 { } else if lastline > 0 {
fmt.Fprintf(out, "\n") fmt.Fprint(out, "\n")
} }
fmt.Fprintf(out, "Checked %d references in total\n", total) fmt.Fprintf(out, "Checked %d references in total\n", total)
@ -291,13 +302,13 @@ Forgejo or set your environment appropriately.`, "")
} }
// runHookUpdate process the update hook: https://git-scm.com/docs/githooks#update // runHookUpdate process the update hook: https://git-scm.com/docs/githooks#update
func runHookUpdate(c *cli.Context) error { func runHookUpdate(ctx context.Context, c *cli.Command) error {
// Now if we're an internal don't do anything else // Now if we're an internal don't do anything else
if isInternal, _ := strconv.ParseBool(os.Getenv(repo_module.EnvIsInternal)); isInternal { if isInternal, _ := strconv.ParseBool(os.Getenv(repo_module.EnvIsInternal)); isInternal {
return nil return nil
} }
ctx, cancel := installSignals() ctx, cancel := installSignals(ctx)
defer cancel() defer cancel()
if c.NArg() != 3 { if c.NArg() != 3 {
@ -323,8 +334,8 @@ func runHookUpdate(c *cli.Context) error {
return fail(ctx, fmt.Sprintf("The modification of %s is skipped as it's an internal reference.", refFullName), "") return fail(ctx, fmt.Sprintf("The modification of %s is skipped as it's an internal reference.", refFullName), "")
} }
func runHookPostReceive(c *cli.Context) error { func runHookPostReceive(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals() ctx, cancel := installSignals(ctx)
defer cancel() defer cancel()
setup(ctx, c.Bool("debug"), true) setup(ctx, c.Bool("debug"), true)
@ -399,7 +410,7 @@ Forgejo or set your environment appropriately.`, "")
continue continue
} }
fmt.Fprintf(out, ".") fmt.Fprint(out, ".")
oldCommitIDs[count] = string(fields[0]) oldCommitIDs[count] = string(fields[0])
newCommitIDs[count] = string(fields[1]) newCommitIDs[count] = string(fields[1])
refFullNames[count] = git.RefName(fields[2]) refFullNames[count] = git.RefName(fields[2])
@ -487,8 +498,8 @@ func hookPrintResults(results []private.HookPostReceiveBranchResult) {
} }
} }
func runHookProcReceive(c *cli.Context) error { func runHookProcReceive(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals() ctx, cancel := installSignals(ctx)
defer cancel() defer cancel()
setup(ctx, c.Bool("debug"), true) setup(ctx, c.Bool("debug"), true)

View file

@ -19,7 +19,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v3"
) )
// Capture what's being written into a standard file descriptor. // Capture what's being written into a standard file descriptor.
@ -134,14 +134,14 @@ func TestDelayWriter(t *testing.T) {
defer ts.Close() defer ts.Close()
defer test.MockVariableValue(&setting.LocalURL, ts.URL+"/")() defer test.MockVariableValue(&setting.LocalURL, ts.URL+"/")()
app := cli.NewApp() app := cli.Command{}
app.Commands = []*cli.Command{subcmdHookPreReceive} app.Commands = []*cli.Command{subcmdHookPreReceive()}
t.Run("Should delay", func(t *testing.T) { t.Run("Should delay", func(t *testing.T) {
defer test.MockVariableValue(&setting.Git.VerbosePushDelay, time.Millisecond*500)() defer test.MockVariableValue(&setting.Git.VerbosePushDelay, time.Millisecond*500)()
finish := captureOutput(t, os.Stdout) finish := captureOutput(t, os.Stdout)
err = app.Run([]string{"./forgejo", "pre-receive"}) err = app.Run(t.Context(), []string{"./forgejo", "pre-receive"})
require.NoError(t, err) require.NoError(t, err)
out := finish() out := finish()
@ -153,7 +153,7 @@ func TestDelayWriter(t *testing.T) {
defer test.MockVariableValue(&setting.Git.VerbosePushDelay, time.Second*5)() defer test.MockVariableValue(&setting.Git.VerbosePushDelay, time.Second*5)()
finish := captureOutput(t, os.Stdout) finish := captureOutput(t, os.Stdout)
err = app.Run([]string{"./forgejo", "pre-receive"}) err = app.Run(t.Context(), []string{"./forgejo", "pre-receive"})
require.NoError(t, err) require.NoError(t, err)
out := finish() out := finish()
@ -163,15 +163,15 @@ func TestDelayWriter(t *testing.T) {
} }
func TestRunHookUpdate(t *testing.T) { func TestRunHookUpdate(t *testing.T) {
app := cli.NewApp() app := cli.Command{}
app.Commands = []*cli.Command{subcmdHookUpdate} app.Commands = []*cli.Command{subcmdHookUpdate()}
t.Run("Removal of internal reference", func(t *testing.T) { t.Run("Removal of internal reference", func(t *testing.T) {
defer test.MockVariableValue(&cli.OsExiter, func(code int) {})() defer test.MockVariableValue(&cli.OsExiter, func(code int) {})()
defer test.MockVariableValue(&setting.IsProd, false)() defer test.MockVariableValue(&setting.IsProd, false)()
finish := captureOutput(t, os.Stderr) finish := captureOutput(t, os.Stderr)
err := app.Run([]string{"./forgejo", "update", "refs/pull/1/head", "0a51ae26bc73c47e2f754560c40904cf14ed51a9", "0000000000000000000000000000000000000000"}) err := app.Run(t.Context(), []string{"./forgejo", "update", "refs/pull/1/head", "0a51ae26bc73c47e2f754560c40904cf14ed51a9", "0000000000000000000000000000000000000000"})
out := finish() out := finish()
require.Error(t, err) require.Error(t, err)
@ -183,7 +183,7 @@ func TestRunHookUpdate(t *testing.T) {
defer test.MockVariableValue(&setting.IsProd, false)() defer test.MockVariableValue(&setting.IsProd, false)()
finish := captureOutput(t, os.Stderr) finish := captureOutput(t, os.Stderr)
err := app.Run([]string{"./forgejo", "update", "refs/pull/1/head", "0a51ae26bc73c47e2f754560c40904cf14ed51a9", "0000000000000000000000000000000000000001"}) err := app.Run(t.Context(), []string{"./forgejo", "update", "refs/pull/1/head", "0a51ae26bc73c47e2f754560c40904cf14ed51a9", "0000000000000000000000000000000000000001"})
out := finish() out := finish()
require.Error(t, err) require.Error(t, err)
@ -191,12 +191,12 @@ func TestRunHookUpdate(t *testing.T) {
}) })
t.Run("Removal of branch", func(t *testing.T) { t.Run("Removal of branch", func(t *testing.T) {
err := app.Run([]string{"./forgejo", "update", "refs/head/main", "0a51ae26bc73c47e2f754560c40904cf14ed51a9", "0000000000000000000000000000000000000000"}) err := app.Run(t.Context(), []string{"./forgejo", "update", "refs/head/main", "0a51ae26bc73c47e2f754560c40904cf14ed51a9", "0000000000000000000000000000000000000000"})
require.NoError(t, err) require.NoError(t, err)
}) })
t.Run("Not enough arguments", func(t *testing.T) { t.Run("Not enough arguments", func(t *testing.T) {
err := app.Run([]string{"./forgejo", "update"}) err := app.Run(t.Context(), []string{"./forgejo", "update"})
require.NoError(t, err) require.NoError(t, err)
}) })
} }

View file

@ -4,6 +4,7 @@
package cmd package cmd
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
"strings" "strings"
@ -11,11 +12,12 @@ import (
"forgejo.org/modules/log" "forgejo.org/modules/log"
"forgejo.org/modules/private" "forgejo.org/modules/private"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v3"
) )
// CmdKeys represents the available keys sub-command // CmdKeys represents the available keys sub-command
var CmdKeys = &cli.Command{ func cmdKeys() *cli.Command {
return &cli.Command{
Name: "keys", Name: "keys",
Usage: "(internal) Should only be called by SSH server", Usage: "(internal) Should only be called by SSH server",
Description: "Queries the Forgejo database to get the authorized command for a given ssh key fingerprint", Description: "Queries the Forgejo database to get the authorized command for a given ssh key fingerprint",
@ -48,8 +50,9 @@ var CmdKeys = &cli.Command{
}, },
}, },
} }
}
func runKeys(c *cli.Context) error { func runKeys(ctx context.Context, c *cli.Command) error {
if !c.IsSet("username") { if !c.IsSet("username") {
return errors.New("No username provided") return errors.New("No username provided")
} }
@ -68,7 +71,7 @@ func runKeys(c *cli.Context) error {
return errors.New("No key type and content provided") return errors.New("No key type and content provided")
} }
ctx, cancel := installSignals() ctx, cancel := installSignals(ctx)
defer cancel() defer cancel()
setup(ctx, c.Bool("debug"), true) setup(ctx, c.Bool("debug"), true)
@ -78,6 +81,6 @@ func runKeys(c *cli.Context) error {
if extra.Error != nil { if extra.Error != nil {
return extra.Error return extra.Error
} }
_, _ = fmt.Fprintln(c.App.Writer, strings.TrimSpace(authorizedString.Text)) _, _ = fmt.Fprintln(c.Root().Writer, strings.TrimSpace(authorizedString.Text))
return nil return nil
} }

View file

@ -4,16 +4,17 @@
package cmd package cmd
import ( import (
"context"
"fmt" "fmt"
"forgejo.org/modules/private" "forgejo.org/modules/private"
"forgejo.org/modules/setting" "forgejo.org/modules/setting"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v3"
) )
func runSendMail(c *cli.Context) error { func runSendMail(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals() ctx, cancel := installSignals(ctx)
defer cancel() defer cancel()
setting.MustInstalled() setting.MustInstalled()

View file

@ -14,7 +14,7 @@ import (
"forgejo.org/modules/log" "forgejo.org/modules/log"
"forgejo.org/modules/setting" "forgejo.org/modules/setting"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v3"
) )
// cmdHelp is our own help subcommand with more information // cmdHelp is our own help subcommand with more information
@ -25,18 +25,18 @@ func cmdHelp() *cli.Command {
Aliases: []string{"h"}, Aliases: []string{"h"},
Usage: "Shows a list of commands or help for one command", Usage: "Shows a list of commands or help for one command",
ArgsUsage: "[command]", ArgsUsage: "[command]",
Action: func(c *cli.Context) (err error) { Action: func(ctx context.Context, c *cli.Command) (err error) {
lineage := c.Lineage() // The order is from child to parent: help, doctor, Gitea, {Command:nil} lineage := c.Lineage() // The order is from child to parent: help, doctor, Forgejo
targetCmdIdx := 0 targetCmdIdx := 0
if c.Command.Name == "help" { if c.Name == "help" {
targetCmdIdx = 1 targetCmdIdx = 1
} }
if lineage[targetCmdIdx+1].Command != nil { if targetCmdIdx+1 < len(lineage) {
err = cli.ShowCommandHelp(lineage[targetCmdIdx+1], lineage[targetCmdIdx].Command.Name) err = cli.ShowCommandHelp(ctx, lineage[targetCmdIdx+1], lineage[targetCmdIdx].Name)
} else { } else {
err = cli.ShowAppHelp(c) err = cli.ShowAppHelp(c)
} }
_, _ = fmt.Fprintf(c.App.Writer, ` _, _ = fmt.Fprintf(c.Root().Writer, `
DEFAULT CONFIGURATION: DEFAULT CONFIGURATION:
AppPath: %s AppPath: %s
WorkPath: %s WorkPath: %s
@ -77,25 +77,25 @@ func appGlobalFlags() []cli.Flag {
} }
} }
func prepareSubcommandWithConfig(command *cli.Command, globalFlags []cli.Flag) { func prepareSubcommandWithConfig(command *cli.Command, globalFlags func() []cli.Flag) {
command.Flags = append(append([]cli.Flag{}, globalFlags...), command.Flags...) command.Flags = append(globalFlags(), command.Flags...)
command.Action = prepareWorkPathAndCustomConf(command.Action) command.Action = prepareWorkPathAndCustomConf(command.Action)
command.HideHelp = true command.HideHelp = true
if command.Name != "help" { if command.Name != "help" {
command.Subcommands = append(command.Subcommands, cmdHelp()) command.Commands = append(command.Commands, cmdHelp())
} }
for i := range command.Subcommands { for i := range command.Commands {
prepareSubcommandWithConfig(command.Subcommands[i], globalFlags) prepareSubcommandWithConfig(command.Commands[i], globalFlags)
} }
} }
// prepareWorkPathAndCustomConf wraps the Action to prepare the work path and custom config // prepareWorkPathAndCustomConf wraps the Action to prepare the work path and custom config
// It can't use "Before", because each level's sub-command's Before will be called one by one, so the "init" would be done multiple times // It can't use "Before", because each level's sub-command's Before will be called one by one, so the "init" would be done multiple times
func prepareWorkPathAndCustomConf(action cli.ActionFunc) func(ctx *cli.Context) error { func prepareWorkPathAndCustomConf(action cli.ActionFunc) func(_ context.Context, _ *cli.Command) error {
return func(ctx *cli.Context) error { return func(ctx context.Context, cli *cli.Command) error {
var args setting.ArgWorkPathAndCustomConf var args setting.ArgWorkPathAndCustomConf
// from children to parent, check the global flags // from children to parent, check the global flags
for _, curCtx := range ctx.Lineage() { for _, curCtx := range cli.Lineage() {
if curCtx.IsSet("work-path") && args.WorkPath == "" { if curCtx.IsSet("work-path") && args.WorkPath == "" {
args.WorkPath = curCtx.String("work-path") args.WorkPath = curCtx.String("work-path")
} }
@ -107,15 +107,15 @@ func prepareWorkPathAndCustomConf(action cli.ActionFunc) func(ctx *cli.Context)
} }
} }
setting.InitWorkPathAndCommonConfig(os.Getenv, args) setting.InitWorkPathAndCommonConfig(os.Getenv, args)
if ctx.Bool("help") || action == nil { if cli.Bool("help") || action == nil {
// the default behavior of "urfave/cli": "nil action" means "show help" // the default behavior of "urfave/cli": "nil action" means "show help"
return cmdHelp().Action(ctx) return cmdHelp().Action(ctx, cli)
} }
return action(ctx) return action(ctx, cli)
} }
} }
func NewMainApp(version, versionExtra string) *cli.App { func NewMainApp(version, versionExtra string) *cli.Command {
path, err := os.Executable() path, err := os.Executable()
if err != nil { if err != nil {
panic(err) panic(err)
@ -124,7 +124,7 @@ func NewMainApp(version, versionExtra string) *cli.App {
subCmdsStandalone := make([]*cli.Command, 0, 10) subCmdsStandalone := make([]*cli.Command, 0, 10)
subCmdWithConfig := make([]*cli.Command, 0, 10) subCmdWithConfig := make([]*cli.Command, 0, 10)
globalFlags := make([]cli.Flag, 0, 10) globalFlags := func() []cli.Flag { return []cli.Flag{} }
// //
// If the executable is forgejo-cli, provide a Forgejo specific CLI // If the executable is forgejo-cli, provide a Forgejo specific CLI
@ -133,14 +133,16 @@ func NewMainApp(version, versionExtra string) *cli.App {
if executable == "forgejo-cli" { if executable == "forgejo-cli" {
subCmdsStandalone = append(subCmdsStandalone, forgejo.CmdActions(context.Background())) subCmdsStandalone = append(subCmdsStandalone, forgejo.CmdActions(context.Background()))
subCmdWithConfig = append(subCmdWithConfig, forgejo.CmdF3(context.Background())) subCmdWithConfig = append(subCmdWithConfig, forgejo.CmdF3(context.Background()))
globalFlags = append(globalFlags, []cli.Flag{ globalFlags = func() []cli.Flag {
return []cli.Flag{
&cli.BoolFlag{ &cli.BoolFlag{
Name: "quiet", Name: "quiet",
}, },
&cli.BoolFlag{ &cli.BoolFlag{
Name: "verbose", Name: "verbose",
}, },
}...) }
}
} else { } else {
// //
// Otherwise provide a Gitea compatible CLI which includes Forgejo // Otherwise provide a Gitea compatible CLI which includes Forgejo
@ -149,55 +151,54 @@ func NewMainApp(version, versionExtra string) *cli.App {
// binary and rename it to forgejo if they want. // binary and rename it to forgejo if they want.
// //
subCmdsStandalone = append(subCmdsStandalone, forgejo.CmdForgejo(context.Background())) subCmdsStandalone = append(subCmdsStandalone, forgejo.CmdForgejo(context.Background()))
subCmdWithConfig = append(subCmdWithConfig, CmdActions) subCmdWithConfig = append(subCmdWithConfig, cmdActions())
} }
return innerNewMainApp(version, versionExtra, subCmdsStandalone, subCmdWithConfig, globalFlags) return innerNewMainApp(version, versionExtra, subCmdsStandalone, subCmdWithConfig, globalFlags)
} }
func innerNewMainApp(version, versionExtra string, subCmdsStandaloneArgs, subCmdWithConfigArgs []*cli.Command, globalFlagsArgs []cli.Flag) *cli.App { func innerNewMainApp(version, versionExtra string, subCmdsStandaloneArgs, subCmdWithConfigArgs []*cli.Command, globalFlagsArgs func() []cli.Flag) *cli.Command {
app := cli.NewApp() app := &cli.Command{}
app.HelpName = "forgejo" app.Name = "forgejo"
app.Name = "Forgejo"
app.Usage = "Beyond coding. We forge." app.Usage = "Beyond coding. We forge."
app.Description = `By default, forgejo will start serving using the web-server with no argument, which can alternatively be run by running the subcommand "web".` app.Description = `By default, forgejo will start serving using the web-server with no argument, which can alternatively be run by running the subcommand "web".`
app.Version = version + versionExtra app.Version = version + versionExtra
app.EnableBashCompletion = true app.EnableShellCompletion = true
// these sub-commands need to use config file // these sub-commands need to use config file
subCmdWithConfig := []*cli.Command{ subCmdWithConfig := []*cli.Command{
cmdHelp(), // the "help" sub-command was used to show the more information for "work path" and "custom config" cmdHelp(), // the "help" sub-command was used to show the more information for "work path" and "custom config"
CmdWeb, cmdWeb(),
CmdServ, cmdServ(),
CmdHook, cmdHook(),
CmdKeys, cmdKeys(),
CmdDump, cmdDump(),
CmdAdmin, cmdAdmin(),
CmdMigrate, cmdMigrate(),
CmdDoctor, cmdDoctor(),
CmdManager, cmdManager(),
CmdEmbedded, cmdEmbedded(),
CmdMigrateStorage, cmdMigrateStorage(),
CmdDumpRepository, cmdDumpRepository(),
CmdRestoreRepository, cmdRestoreRepository(),
} }
subCmdWithConfig = append(subCmdWithConfig, subCmdWithConfigArgs...) subCmdWithConfig = append(subCmdWithConfig, subCmdWithConfigArgs...)
// these sub-commands do not need the config file, and they do not depend on any path or environment variable. // these sub-commands do not need the config file, and they do not depend on any path or environment variable.
subCmdStandalone := []*cli.Command{ subCmdStandalone := []*cli.Command{
CmdCert, cmdCert(),
CmdGenerate, cmdGenerate(),
CmdDocs,
} }
subCmdStandalone = append(subCmdStandalone, subCmdsStandaloneArgs...) subCmdStandalone = append(subCmdStandalone, subCmdsStandaloneArgs...)
app.DefaultCommand = CmdWeb.Name app.DefaultCommand = cmdWeb().Name
globalFlags := appGlobalFlags() globalFlags := func() []cli.Flag {
globalFlags = append(globalFlags, globalFlagsArgs...) return append(appGlobalFlags(), globalFlagsArgs()...)
}
app.Flags = append(app.Flags, cli.VersionFlag) app.Flags = append(app.Flags, cli.VersionFlag)
app.Flags = append(app.Flags, globalFlags...) app.Flags = append(app.Flags, globalFlags()...)
app.HideHelp = true // use our own help action to show helps (with more information like default config) app.HideHelp = true // use our own help action to show helps (with more information like default config)
app.Before = PrepareConsoleLoggerLevel(log.INFO) app.Before = PrepareConsoleLoggerLevel(log.INFO)
for i := range subCmdWithConfig { for i := range subCmdWithConfig {
@ -210,8 +211,8 @@ func innerNewMainApp(version, versionExtra string, subCmdsStandaloneArgs, subCmd
return app return app
} }
func RunMainApp(app *cli.App, args ...string) error { func RunMainApp(app *cli.Command, args ...string) error {
err := app.Run(args) err := app.Run(context.Background(), args)
if err == nil { if err == nil {
return nil return nil
} }
@ -220,7 +221,7 @@ func RunMainApp(app *cli.App, args ...string) error {
cli.OsExiter(1) cli.OsExiter(1)
return err return err
} }
_, _ = fmt.Fprintf(app.ErrWriter, "Command error: %v\n", err) _, _ = fmt.Fprintf(app.Root().ErrWriter, "Command error: %v\n", err)
cli.OsExiter(1) cli.OsExiter(1)
return err return err
} }

View file

@ -4,6 +4,8 @@
package cmd package cmd
import ( import (
"context"
"errors"
"fmt" "fmt"
"io" "io"
"path/filepath" "path/filepath"
@ -16,7 +18,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v3"
) )
func TestMain(m *testing.M) { func TestMain(m *testing.M) {
@ -27,10 +29,10 @@ func makePathOutput(workPath, customPath, customConf string) string {
return fmt.Sprintf("WorkPath=%s\nCustomPath=%s\nCustomConf=%s", workPath, customPath, customConf) return fmt.Sprintf("WorkPath=%s\nCustomPath=%s\nCustomConf=%s", workPath, customPath, customConf)
} }
func newTestApp(testCmdAction func(ctx *cli.Context) error) *cli.App { func newTestApp(testCmdAction func(_ context.Context, ctx *cli.Command) error) *cli.Command {
app := NewMainApp("version", "version-extra") app := NewMainApp("version", "version-extra")
testCmd := &cli.Command{Name: "test-cmd", Action: testCmdAction} testCmd := &cli.Command{Name: "test-cmd", Action: testCmdAction}
prepareSubcommandWithConfig(testCmd, appGlobalFlags()) prepareSubcommandWithConfig(testCmd, appGlobalFlags)
app.Commands = append(app.Commands, testCmd) app.Commands = append(app.Commands, testCmd)
app.DefaultCommand = testCmd.Name app.DefaultCommand = testCmd.Name
return app return app
@ -42,7 +44,7 @@ type runResult struct {
ExitCode int ExitCode int
} }
func runTestApp(app *cli.App, args ...string) (runResult, error) { func runTestApp(app *cli.Command, args ...string) (runResult, error) {
outBuf := new(strings.Builder) outBuf := new(strings.Builder)
errBuf := new(strings.Builder) errBuf := new(strings.Builder)
app.Writer = outBuf app.Writer = outBuf
@ -65,7 +67,6 @@ func TestCliCmd(t *testing.T) {
defaultCustomConf := filepath.Join(defaultCustomPath, "conf/app.ini") defaultCustomConf := filepath.Join(defaultCustomPath, "conf/app.ini")
cli.CommandHelpTemplate = "(command help template)" cli.CommandHelpTemplate = "(command help template)"
cli.AppHelpTemplate = "(app help template)"
cli.SubcommandHelpTemplate = "(subcommand help template)" cli.SubcommandHelpTemplate = "(subcommand help template)"
cases := []struct { cases := []struct {
@ -109,12 +110,17 @@ func TestCliCmd(t *testing.T) {
}, },
} }
app := newTestApp(func(ctx *cli.Context) error {
_, _ = fmt.Fprint(ctx.App.Writer, makePathOutput(setting.AppWorkPath, setting.CustomPath, setting.CustomConf))
return nil
})
for _, c := range cases { for _, c := range cases {
t.Run(c.cmd, func(t *testing.T) { t.Run(c.cmd, func(t *testing.T) {
defer test.MockProtect(&setting.AppWorkPath)()
defer test.MockProtect(&setting.CustomPath)()
defer test.MockProtect(&setting.CustomConf)()
app := newTestApp(func(_ context.Context, ctx *cli.Command) error {
_, _ = fmt.Fprint(ctx.Root().Writer, makePathOutput(setting.AppWorkPath, setting.CustomPath, setting.CustomConf))
return nil
})
for k, v := range c.env { for k, v := range c.env {
t.Setenv(k, v) t.Setenv(k, v)
} }
@ -122,34 +128,34 @@ func TestCliCmd(t *testing.T) {
r, err := runTestApp(app, args...) r, err := runTestApp(app, args...)
require.NoError(t, err, c.cmd) require.NoError(t, err, c.cmd)
assert.NotEmpty(t, c.exp, c.cmd) assert.NotEmpty(t, c.exp, c.cmd)
assert.Contains(t, r.Stdout, c.exp, c.cmd) assert.Contains(t, r.Stdout, c.exp, c.cmd+"\n"+r.Stdout)
}) })
} }
} }
func TestCliCmdError(t *testing.T) { func TestCliCmdError(t *testing.T) {
app := newTestApp(func(ctx *cli.Context) error { return fmt.Errorf("normal error") }) app := newTestApp(func(_ context.Context, ctx *cli.Command) error { return errors.New("normal error") })
r, err := runTestApp(app, "./gitea", "test-cmd") r, err := runTestApp(app, "./gitea", "test-cmd")
require.Error(t, err) require.Error(t, err)
assert.Equal(t, 1, r.ExitCode) assert.Equal(t, 1, r.ExitCode)
assert.Empty(t, r.Stdout) assert.Empty(t, r.Stdout)
assert.Equal(t, "Command error: normal error\n", r.Stderr) assert.Equal(t, "Command error: normal error\n", r.Stderr)
app = newTestApp(func(ctx *cli.Context) error { return cli.Exit("exit error", 2) }) app = newTestApp(func(_ context.Context, ctx *cli.Command) error { return cli.Exit("exit error", 2) })
r, err = runTestApp(app, "./gitea", "test-cmd") r, err = runTestApp(app, "./gitea", "test-cmd")
require.Error(t, err) require.Error(t, err)
assert.Equal(t, 2, r.ExitCode) assert.Equal(t, 2, r.ExitCode)
assert.Empty(t, r.Stdout) assert.Empty(t, r.Stdout)
assert.Equal(t, "exit error\n", r.Stderr) assert.Equal(t, "exit error\n", r.Stderr)
app = newTestApp(func(ctx *cli.Context) error { return nil }) app = newTestApp(func(_ context.Context, ctx *cli.Command) error { return nil })
r, err = runTestApp(app, "./gitea", "test-cmd", "--no-such") r, err = runTestApp(app, "./gitea", "test-cmd", "--no-such")
require.Error(t, err) require.Error(t, err)
assert.Equal(t, 1, r.ExitCode) assert.Equal(t, 1, r.ExitCode)
assert.Equal(t, "Incorrect Usage: flag provided but not defined: -no-such\n\n", r.Stdout) assert.Equal(t, "Incorrect Usage: flag provided but not defined: -no-such\n\n", r.Stderr)
assert.Empty(t, r.Stderr) // the cli package's strange behavior, the error message is not in stderr .... assert.Empty(t, r.Stdout)
app = newTestApp(func(ctx *cli.Context) error { return nil }) app = newTestApp(func(_ context.Context, ctx *cli.Command) error { return nil })
r, err = runTestApp(app, "./gitea", "test-cmd") r, err = runTestApp(app, "./gitea", "test-cmd")
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, -1, r.ExitCode) // the cli.OsExiter is not called assert.Equal(t, -1, r.ExitCode) // the cli.OsExiter is not called

View file

@ -4,30 +4,34 @@
package cmd package cmd
import ( import (
"context"
"os" "os"
"time" "time"
"forgejo.org/modules/private" "forgejo.org/modules/private"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v3"
) )
var (
// CmdManager represents the manager command // CmdManager represents the manager command
CmdManager = &cli.Command{ func cmdManager() *cli.Command {
return &cli.Command{
Name: "manager", Name: "manager",
Usage: "Manage the running forgejo process", Usage: "Manage the running forgejo process",
Description: "This is a command for managing the running forgejo process", Description: "This is a command for managing the running forgejo process",
Subcommands: []*cli.Command{ Commands: []*cli.Command{
subcmdShutdown, subcmdShutdown(),
subcmdRestart, subcmdRestart(),
subcmdReloadTemplates, subcmdReloadTemplates(),
subcmdFlushQueues, subcmdFlushQueues(),
subcmdLogging, subcmdLogging(),
subCmdProcesses, subCmdProcesses(),
}, },
} }
subcmdShutdown = &cli.Command{ }
func subcmdShutdown() *cli.Command {
return &cli.Command{
Name: "shutdown", Name: "shutdown",
Usage: "Gracefully shutdown the running process", Usage: "Gracefully shutdown the running process",
Flags: []cli.Flag{ Flags: []cli.Flag{
@ -37,7 +41,10 @@ var (
}, },
Action: runShutdown, Action: runShutdown,
} }
subcmdRestart = &cli.Command{ }
func subcmdRestart() *cli.Command {
return &cli.Command{
Name: "restart", Name: "restart",
Usage: "Gracefully restart the running process - (not implemented for windows servers)", Usage: "Gracefully restart the running process - (not implemented for windows servers)",
Flags: []cli.Flag{ Flags: []cli.Flag{
@ -47,7 +54,10 @@ var (
}, },
Action: runRestart, Action: runRestart,
} }
subcmdReloadTemplates = &cli.Command{ }
func subcmdReloadTemplates() *cli.Command {
return &cli.Command{
Name: "reload-templates", Name: "reload-templates",
Usage: "Reload template files in the running process", Usage: "Reload template files in the running process",
Flags: []cli.Flag{ Flags: []cli.Flag{
@ -57,7 +67,10 @@ var (
}, },
Action: runReloadTemplates, Action: runReloadTemplates,
} }
subcmdFlushQueues = &cli.Command{ }
func subcmdFlushQueues() *cli.Command {
return &cli.Command{
Name: "flush-queues", Name: "flush-queues",
Usage: "Flush queues in the running process", Usage: "Flush queues in the running process",
Action: runFlushQueues, Action: runFlushQueues,
@ -76,7 +89,10 @@ var (
}, },
}, },
} }
subCmdProcesses = &cli.Command{ }
func subCmdProcesses() *cli.Command {
return &cli.Command{
Name: "processes", Name: "processes",
Usage: "Display running processes within the current process", Usage: "Display running processes within the current process",
Action: runProcesses, Action: runProcesses,
@ -106,10 +122,10 @@ var (
}, },
}, },
} }
) }
func runShutdown(c *cli.Context) error { func runShutdown(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals() ctx, cancel := installSignals(ctx)
defer cancel() defer cancel()
setup(ctx, c.Bool("debug"), false) setup(ctx, c.Bool("debug"), false)
@ -117,8 +133,8 @@ func runShutdown(c *cli.Context) error {
return handleCliResponseExtra(extra) return handleCliResponseExtra(extra)
} }
func runRestart(c *cli.Context) error { func runRestart(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals() ctx, cancel := installSignals(ctx)
defer cancel() defer cancel()
setup(ctx, c.Bool("debug"), false) setup(ctx, c.Bool("debug"), false)
@ -126,8 +142,8 @@ func runRestart(c *cli.Context) error {
return handleCliResponseExtra(extra) return handleCliResponseExtra(extra)
} }
func runReloadTemplates(c *cli.Context) error { func runReloadTemplates(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals() ctx, cancel := installSignals(ctx)
defer cancel() defer cancel()
setup(ctx, c.Bool("debug"), false) setup(ctx, c.Bool("debug"), false)
@ -135,8 +151,8 @@ func runReloadTemplates(c *cli.Context) error {
return handleCliResponseExtra(extra) return handleCliResponseExtra(extra)
} }
func runFlushQueues(c *cli.Context) error { func runFlushQueues(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals() ctx, cancel := installSignals(ctx)
defer cancel() defer cancel()
setup(ctx, c.Bool("debug"), false) setup(ctx, c.Bool("debug"), false)
@ -144,8 +160,8 @@ func runFlushQueues(c *cli.Context) error {
return handleCliResponseExtra(extra) return handleCliResponseExtra(extra)
} }
func runProcesses(c *cli.Context) error { func runProcesses(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals() ctx, cancel := installSignals(ctx)
defer cancel() defer cancel()
setup(ctx, c.Bool("debug"), false) setup(ctx, c.Bool("debug"), false)

View file

@ -4,6 +4,7 @@
package cmd package cmd
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
"os" "os"
@ -11,11 +12,11 @@ import (
"forgejo.org/modules/log" "forgejo.org/modules/log"
"forgejo.org/modules/private" "forgejo.org/modules/private"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v3"
) )
var ( func defaultLoggingFlags() []cli.Flag {
defaultLoggingFlags = []cli.Flag{ return []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "logger", Name: "logger",
Usage: `Logger name - will default to "default"`, Usage: `Logger name - will default to "default"`,
@ -56,11 +57,13 @@ var (
Name: "debug", Name: "debug",
}, },
} }
}
subcmdLogging = &cli.Command{ func subcmdLogging() *cli.Command {
return &cli.Command{
Name: "logging", Name: "logging",
Usage: "Adjust logging commands", Usage: "Adjust logging commands",
Subcommands: []*cli.Command{ Commands: []*cli.Command{
{ {
Name: "pause", Name: "pause",
Usage: "Pause logging (Forgejo will buffer logs up to a certain point and will drop them after that point)", Usage: "Pause logging (Forgejo will buffer logs up to a certain point and will drop them after that point)",
@ -104,11 +107,11 @@ var (
}, { }, {
Name: "add", Name: "add",
Usage: "Add a logger", Usage: "Add a logger",
Subcommands: []*cli.Command{ Commands: []*cli.Command{
{ {
Name: "file", Name: "file",
Usage: "Add a file logger", Usage: "Add a file logger",
Flags: append(defaultLoggingFlags, []cli.Flag{ Flags: append(defaultLoggingFlags(), []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "filename", Name: "filename",
Aliases: []string{"f"}, Aliases: []string{"f"},
@ -152,7 +155,7 @@ var (
}, { }, {
Name: "conn", Name: "conn",
Usage: "Add a net conn logger", Usage: "Add a net conn logger",
Flags: append(defaultLoggingFlags, []cli.Flag{ Flags: append(defaultLoggingFlags(), []cli.Flag{
&cli.BoolFlag{ &cli.BoolFlag{
Name: "reconnect-on-message", Name: "reconnect-on-message",
Aliases: []string{"R"}, Aliases: []string{"R"},
@ -193,10 +196,10 @@ var (
}, },
}, },
} }
) }
func runRemoveLogger(c *cli.Context) error { func runRemoveLogger(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals() ctx, cancel := installSignals(ctx)
defer cancel() defer cancel()
setup(ctx, c.Bool("debug"), false) setup(ctx, c.Bool("debug"), false)
@ -210,8 +213,8 @@ func runRemoveLogger(c *cli.Context) error {
return handleCliResponseExtra(extra) return handleCliResponseExtra(extra)
} }
func runAddConnLogger(c *cli.Context) error { func runAddConnLogger(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals() ctx, cancel := installSignals(ctx)
defer cancel() defer cancel()
setup(ctx, c.Bool("debug"), false) setup(ctx, c.Bool("debug"), false)
@ -237,11 +240,11 @@ func runAddConnLogger(c *cli.Context) error {
if c.IsSet("reconnect-on-message") { if c.IsSet("reconnect-on-message") {
vals["reconnectOnMsg"] = c.Bool("reconnect-on-message") vals["reconnectOnMsg"] = c.Bool("reconnect-on-message")
} }
return commonAddLogger(c, mode, vals) return commonAddLogger(ctx, c, mode, vals)
} }
func runAddFileLogger(c *cli.Context) error { func runAddFileLogger(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals() ctx, cancel := installSignals(ctx)
defer cancel() defer cancel()
setup(ctx, c.Bool("debug"), false) setup(ctx, c.Bool("debug"), false)
@ -270,10 +273,10 @@ func runAddFileLogger(c *cli.Context) error {
if c.IsSet("compression-level") { if c.IsSet("compression-level") {
vals["compressionLevel"] = c.Int("compression-level") vals["compressionLevel"] = c.Int("compression-level")
} }
return commonAddLogger(c, mode, vals) return commonAddLogger(ctx, c, mode, vals)
} }
func commonAddLogger(c *cli.Context, mode string, vals map[string]any) error { func commonAddLogger(ctx context.Context, c *cli.Command, mode string, vals map[string]any) error {
if len(c.String("level")) > 0 { if len(c.String("level")) > 0 {
vals["level"] = log.LevelFromString(c.String("level")).String() vals["level"] = log.LevelFromString(c.String("level")).String()
} }
@ -300,15 +303,15 @@ func commonAddLogger(c *cli.Context, mode string, vals map[string]any) error {
if c.IsSet("writer") { if c.IsSet("writer") {
writer = c.String("writer") writer = c.String("writer")
} }
ctx, cancel := installSignals() ctx, cancel := installSignals(ctx)
defer cancel() defer cancel()
extra := private.AddLogger(ctx, logger, writer, mode, vals) extra := private.AddLogger(ctx, logger, writer, mode, vals)
return handleCliResponseExtra(extra) return handleCliResponseExtra(extra)
} }
func runPauseLogging(c *cli.Context) error { func runPauseLogging(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals() ctx, cancel := installSignals(ctx)
defer cancel() defer cancel()
setup(ctx, c.Bool("debug"), false) setup(ctx, c.Bool("debug"), false)
@ -317,8 +320,8 @@ func runPauseLogging(c *cli.Context) error {
return nil return nil
} }
func runResumeLogging(c *cli.Context) error { func runResumeLogging(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals() ctx, cancel := installSignals(ctx)
defer cancel() defer cancel()
setup(ctx, c.Bool("debug"), false) setup(ctx, c.Bool("debug"), false)
@ -327,8 +330,8 @@ func runResumeLogging(c *cli.Context) error {
return nil return nil
} }
func runReleaseReopenLogging(c *cli.Context) error { func runReleaseReopenLogging(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals() ctx, cancel := installSignals(ctx)
defer cancel() defer cancel()
setup(ctx, c.Bool("debug"), false) setup(ctx, c.Bool("debug"), false)
@ -337,8 +340,8 @@ func runReleaseReopenLogging(c *cli.Context) error {
return nil return nil
} }
func runSetLogSQL(c *cli.Context) error { func runSetLogSQL(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals() ctx, cancel := installSignals(ctx)
defer cancel() defer cancel()
setup(ctx, c.Bool("debug"), false) setup(ctx, c.Bool("debug"), false)

View file

@ -11,19 +11,21 @@ import (
"forgejo.org/modules/log" "forgejo.org/modules/log"
"forgejo.org/modules/setting" "forgejo.org/modules/setting"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v3"
) )
// CmdMigrate represents the available migrate sub-command. // CmdMigrate represents the available migrate sub-command.
var CmdMigrate = &cli.Command{ func cmdMigrate() *cli.Command {
return &cli.Command{
Name: "migrate", Name: "migrate",
Usage: "Migrate the database", Usage: "Migrate the database",
Description: "This is a command for migrating the database, so that you can run 'forgejo admin user create' before starting the server.", Description: "This is a command for migrating the database, so that you can run 'forgejo admin user create' before starting the server.",
Action: runMigrate, Action: runMigrate,
} }
}
func runMigrate(ctx *cli.Context) error { func runMigrate(stdCtx context.Context, ctx *cli.Command) error {
stdCtx, cancel := installSignals() stdCtx, cancel := installSignals(stdCtx)
defer cancel() defer cancel()
if err := initDB(stdCtx); err != nil { if err := initDB(stdCtx); err != nil {

View file

@ -22,12 +22,13 @@ import (
"forgejo.org/modules/setting" "forgejo.org/modules/setting"
"forgejo.org/modules/storage" "forgejo.org/modules/storage"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v3"
"xorm.io/xorm" "xorm.io/xorm"
) )
// CmdMigrateStorage represents the available migrate storage sub-command. // CmdMigrateStorage represents the available migrate storage sub-command.
var CmdMigrateStorage = &cli.Command{ func cmdMigrateStorage() *cli.Command {
return &cli.Command{
Name: "migrate-storage", Name: "migrate-storage",
Usage: "Migrate the storage", Usage: "Migrate the storage",
Description: "Copies stored files from storage configured in app.ini to parameter-configured storage", Description: "Copies stored files from storage configured in app.ini to parameter-configured storage",
@ -96,6 +97,7 @@ var CmdMigrateStorage = &cli.Command{
}, },
}, },
} }
}
func migrateAttachments(ctx context.Context, dstStorage storage.ObjectStorage) error { func migrateAttachments(ctx context.Context, dstStorage storage.ObjectStorage) error {
return db.Iterate(ctx, nil, func(ctx context.Context, attach *repo_model.Attachment) error { return db.Iterate(ctx, nil, func(ctx context.Context, attach *repo_model.Attachment) error {
@ -182,8 +184,8 @@ func migrateActionsArtifacts(ctx context.Context, dstStorage storage.ObjectStora
}) })
} }
func runMigrateStorage(ctx *cli.Context) error { func runMigrateStorage(stdCtx context.Context, ctx *cli.Command) error {
stdCtx, cancel := installSignals() stdCtx, cancel := installSignals(stdCtx)
defer cancel() defer cancel()
if err := initDB(stdCtx); err != nil { if err := initDB(stdCtx); err != nil {

View file

@ -4,16 +4,18 @@
package cmd package cmd
import ( import (
"context"
"strings" "strings"
"forgejo.org/modules/private" "forgejo.org/modules/private"
"forgejo.org/modules/setting" "forgejo.org/modules/setting"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v3"
) )
// CmdRestoreRepository represents the available restore a repository sub-command. // CmdRestoreRepository represents the available restore a repository sub-command.
var CmdRestoreRepository = &cli.Command{ func cmdRestoreRepository() *cli.Command {
return &cli.Command{
Name: "restore-repo", Name: "restore-repo",
Usage: "Restore the repository from disk", Usage: "Restore the repository from disk",
Description: "This is a command for restoring the repository data.", Description: "This is a command for restoring the repository data.",
@ -47,9 +49,10 @@ wiki, issues, labels, releases, release_assets, milestones, pull_requests, comme
}, },
}, },
} }
}
func runRestoreRepository(c *cli.Context) error { func runRestoreRepository(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals() ctx, cancel := installSignals(ctx)
defer cancel() defer cancel()
setting.MustInstalled() setting.MustInstalled()

View file

@ -33,7 +33,7 @@ import (
"github.com/golang-jwt/jwt/v5" "github.com/golang-jwt/jwt/v5"
"github.com/kballard/go-shellquote" "github.com/kballard/go-shellquote"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v3"
) )
const ( const (
@ -41,7 +41,8 @@ const (
) )
// CmdServ represents the available serv sub-command. // CmdServ represents the available serv sub-command.
var CmdServ = &cli.Command{ func cmdServ() *cli.Command {
return &cli.Command{
Name: "serv", Name: "serv",
Usage: "(internal) Should only be called by SSH shell", Usage: "(internal) Should only be called by SSH shell",
Description: "Serv provides access auth for repositories", Description: "Serv provides access auth for repositories",
@ -56,6 +57,7 @@ var CmdServ = &cli.Command{
}, },
}, },
} }
}
func setup(ctx context.Context, debug, gitNeeded bool) { func setup(ctx context.Context, debug, gitNeeded bool) {
if debug { if debug {
@ -131,8 +133,8 @@ func handleCliResponseExtra(extra private.ResponseExtra) error {
return nil return nil
} }
func runServ(c *cli.Context) error { func runServ(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals() ctx, cancel := installSignals(ctx)
defer cancel() defer cancel()
// FIXME: This needs to internationalised // FIXME: This needs to internationalised
@ -194,7 +196,7 @@ func runServ(c *cli.Context) error {
if git.CheckGitVersionAtLeast("2.29") == nil { if git.CheckGitVersionAtLeast("2.29") == nil {
// for AGit Flow // for AGit Flow
if cmd == "ssh_info" { if cmd == "ssh_info" {
fmt.Print(`{"type":"gitea","version":1}`) fmt.Print(`{"type":"agit","version":1}`)
return nil return nil
} }
} }

View file

@ -26,14 +26,15 @@ import (
"forgejo.org/routers/install" "forgejo.org/routers/install"
"github.com/felixge/fgprof" "github.com/felixge/fgprof"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v3"
) )
// PIDFile could be set from build tag // PIDFile could be set from build tag
var PIDFile = "/run/gitea.pid" var PIDFile = "/run/gitea.pid"
// CmdWeb represents the available web sub-command. // CmdWeb represents the available web sub-command.
var CmdWeb = &cli.Command{ func cmdWeb() *cli.Command {
return &cli.Command{
Name: "web", Name: "web",
Usage: "Start the Forgejo web server", Usage: "Start the Forgejo web server",
Description: `The Forgejo web server is the only thing you need to run, Description: `The Forgejo web server is the only thing you need to run,
@ -69,6 +70,7 @@ and it takes care of all the other things for you`,
}, },
}, },
} }
}
func runHTTPRedirector() { func runHTTPRedirector() {
_, _, finished := process.GetManager().AddTypedContext(graceful.GetManager().HammerContext(), "Web: HTTP Redirector", process.SystemProcessType, true) _, _, finished := process.GetManager().AddTypedContext(graceful.GetManager().HammerContext(), "Web: HTTP Redirector", process.SystemProcessType, true)
@ -128,7 +130,7 @@ func showWebStartupMessage(msg string) {
} }
} }
func serveInstall(ctx *cli.Context) error { func serveInstall(_ context.Context, ctx *cli.Command) error {
showWebStartupMessage("Prepare to run install page") showWebStartupMessage("Prepare to run install page")
routers.InitWebInstallPage(graceful.GetManager().HammerContext()) routers.InitWebInstallPage(graceful.GetManager().HammerContext())
@ -161,7 +163,7 @@ func serveInstall(ctx *cli.Context) error {
return nil return nil
} }
func serveInstalled(ctx *cli.Context) error { func serveInstalled(_ context.Context, ctx *cli.Command) error {
setting.InitCfgProvider(setting.CustomConf) setting.InitCfgProvider(setting.CustomConf)
setting.LoadCommonSettings() setting.LoadCommonSettings()
setting.MustInstalled() setting.MustInstalled()
@ -233,7 +235,7 @@ func servePprof() {
finished() finished()
} }
func runWeb(ctx *cli.Context) error { func runWeb(ctx context.Context, cli *cli.Command) error {
defer func() { defer func() {
if panicked := recover(); panicked != nil { if panicked := recover(); panicked != nil {
log.Fatal("PANIC: %v\n%s", panicked, log.Stack(2)) log.Fatal("PANIC: %v\n%s", panicked, log.Stack(2))
@ -251,12 +253,12 @@ func runWeb(ctx *cli.Context) error {
} }
// Set pid file setting // Set pid file setting
if ctx.IsSet("pid") { if cli.IsSet("pid") {
createPIDFile(ctx.String("pid")) createPIDFile(cli.String("pid"))
} }
if !setting.InstallLock { if !setting.InstallLock {
if err := serveInstall(ctx); err != nil { if err := serveInstall(ctx, cli); err != nil {
return err return err
} }
} else { } else {
@ -267,7 +269,7 @@ func runWeb(ctx *cli.Context) error {
go servePprof() go servePprof()
} }
return serveInstalled(ctx) return serveInstalled(ctx, cli)
} }
func setPort(port string) error { func setPort(port string) error {

View file

@ -1,4 +1,3 @@
#! /bin/bash
# Heavily inspired by https://github.com/urfave/cli # Heavily inspired by https://github.com/urfave/cli
_cli_bash_autocomplete() { _cli_bash_autocomplete() {
@ -7,9 +6,9 @@ _cli_bash_autocomplete() {
COMPREPLY=() COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}" cur="${COMP_WORDS[COMP_CWORD]}"
if [[ "$cur" == "-"* ]]; then if [[ "$cur" == "-"* ]]; then
opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} ${cur} --generate-bash-completion ) opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} ${cur} --generate-shell-completion )
else else
opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-bash-completion ) opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-shell-completion )
fi fi
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0 return 0

View file

@ -9,9 +9,9 @@ _cli_zsh_autocomplete() {
local cur local cur
cur=${words[-1]} cur=${words[-1]}
if [[ "$cur" == "-"* ]]; then if [[ "$cur" == "-"* ]]; then
opts=("${(@f)$(_CLI_ZSH_AUTOCOMPLETE_HACK=1 ${words[@]:0:#words[@]-1} ${cur} --generate-bash-completion)}") opts=("${(@f)$(_CLI_ZSH_AUTOCOMPLETE_HACK=1 ${words[@]:0:#words[@]-1} ${cur} --generate-shell-completion)}")
else else
opts=("${(@f)$(_CLI_ZSH_AUTOCOMPLETE_HACK=1 ${words[@]:0:#words[@]-1} --generate-bash-completion)}") opts=("${(@f)$(_CLI_ZSH_AUTOCOMPLETE_HACK=1 ${words[@]:0:#words[@]-1} --generate-shell-completion)}")
fi fi
if [[ "${opts[1]}" != "" ]]; then if [[ "${opts[1]}" != "" ]]; then

View file

@ -1,34 +1,35 @@
Environment To Ini Environment To Ini
================== ==================
Multiple docker users have requested that the Gitea docker is changed This tool allows defining Forgejo's entire configuration via environment
to permit arbitrary configuration via environment variables. variables, mostly geared towards usage in Docker.
Gitea needs to use an ini file for configuration because the running Forgejo needs to use an INI file for configuration because the running
environment that starts the docker may not be the same as that used environment that starts the container may not be the same as the one used
by the hooks. An ini file also gives a good default and means that by the hooks. An INI file also gives a good default and means that
users do not have to completely provide a full environment. users do not have to provide the entire set of environment variables.
With those caveats above, this command provides a generic way of With those caveats above, this command provides a generic way of
converting suitably structured environment variables into any ini converting suitably structured environment variables into any ini
value. value.
To use the command is very simple just run it and the default gitea When run, `environment-to-ini` will write the config files based on the
app.ini will be rewritten to take account of the variables provided, environment variables provided.
however there are various options to give slightly different Check with the `-h` flag for several options to alter this behaviour.
behavior and these can be interrogated with the `-h` option.
The environment variables should be of the form: Environment variables of the form "FORGEJO__SECTION_NAME__KEY_NAME"
will be mapped to the ini section "[section_name]" and the key
"KEY_NAME" with the value as provided.
GITEA__SECTION_NAME__KEY_NAME Environment variables of the form "FORGEJO__SECTION_NAME__KEY_NAME__FILE"
will be mapped to the ini section "[section_name]" and the key
Note, SECTION_NAME in the notation above is case-insensitive. "KEY_NAME" with the value loaded from the specified file.
Environment variables are usually restricted to a reduced character Environment variables are usually restricted to a reduced character
set "0-9A-Z_" - in order to allow the setting of sections with set "0-9A-Z_" - in order to allow the setting of sections with
characters outside of that set, they should be escaped as following: characters outside of that set, they should be escaped as following:
"_0X2E_" for "." and "_0X2D_" for "-". The entire section and key names "_0X2E_" for ".". The entire section and key names can be escaped as
can be escaped as a UTF8 byte string if necessary. E.g. to configure: a UTF8 byte string if necessary. E.g. to configure:
""" """
... ...
@ -38,8 +39,8 @@ can be escaped as a UTF8 byte string if necessary. E.g. to configure:
... ...
""" """
You would set the environment variables: "GITEA__LOG_0x2E_CONSOLE__COLORIZE=false" You would set the environment variables: "FORGEJO__LOG_0x2E_CONSOLE__COLORIZE=false"
and "GITEA__LOG_0x2E_CONSOLE__STDERR=false". Other examples can be found and "FORGEJO__LOG_0x2E_CONSOLE__STDERR=false". Other examples can be found
on the configuration cheat sheet. on the configuration cheat sheet.
To build locally, run: To build locally, run:

View file

@ -4,16 +4,17 @@
package main package main
import ( import (
"context"
"os" "os"
"forgejo.org/modules/log" "forgejo.org/modules/log"
"forgejo.org/modules/setting" "forgejo.org/modules/setting"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v3"
) )
func main() { func main() {
app := cli.NewApp() app := cli.Command{}
app.Name = "environment-to-ini" app.Name = "environment-to-ini"
app.Usage = "Use provided environment to update configuration ini" app.Usage = "Use provided environment to update configuration ini"
app.Description = `As a helper to allow docker users to update the forgejo configuration app.Description = `As a helper to allow docker users to update the forgejo configuration
@ -72,13 +73,13 @@ func main() {
}, },
} }
app.Action = runEnvironmentToIni app.Action = runEnvironmentToIni
err := app.Run(os.Args) err := app.Run(context.Background(), os.Args)
if err != nil { if err != nil {
log.Fatal("Failed to run app with %s: %v", os.Args, err) log.Fatal("Failed to run app with %s: %v", os.Args, err)
} }
} }
func runEnvironmentToIni(c *cli.Context) error { func runEnvironmentToIni(ctx context.Context, c *cli.Command) error {
// the config system may change the environment variables, so get a copy first, to be used later // the config system may change the environment variables, so get a copy first, to be used later
env := append([]string{}, os.Environ()...) env := append([]string{}, os.Environ()...)
setting.InitWorkPathAndCfgProvider(os.Getenv, setting.ArgWorkPathAndCustomConf{ setting.InitWorkPathAndCfgProvider(os.Getenv, setting.ArgWorkPathAndCustomConf{

View file

@ -408,7 +408,7 @@ local addIssueLabelsOverrides(labels) =
regex: '', regex: '',
type: 'query', type: 'query',
multi: true, multi: true,
allValue: '.+' allValue: '.+',
}, },
) )
.addTemplate( .addTemplate(
@ -423,7 +423,7 @@ local addIssueLabelsOverrides(labels) =
regex: '', regex: '',
type: 'query', type: 'query',
multi: true, multi: true,
allValue: '.+' allValue: '.+',
}, },
) )
.addTemplate( .addTemplate(

View file

@ -183,7 +183,7 @@ RUN_USER = ; git
;; ;;
;; For the built-in SSH server, choose the key exchange algorithms to support for SSH connections, ;; For the built-in SSH server, choose the key exchange algorithms to support for SSH connections,
;; for system SSH this setting has no effect ;; for system SSH this setting has no effect
;SSH_SERVER_KEY_EXCHANGES = curve25519-sha256, ecdh-sha2-nistp256, ecdh-sha2-nistp384, ecdh-sha2-nistp521, diffie-hellman-group14-sha256, diffie-hellman-group14-sha1 ;SSH_SERVER_KEY_EXCHANGES = mlkem768x25519-sha256, curve25519-sha256, ecdh-sha2-nistp256, ecdh-sha2-nistp384, ecdh-sha2-nistp521, diffie-hellman-group14-sha256, diffie-hellman-group14-sha1
;; ;;
;; For the built-in SSH server, choose the MACs to support for SSH connections, ;; For the built-in SSH server, choose the MACs to support for SSH connections,
;; for system SSH this setting has no effect ;; for system SSH this setting has no effect
@ -1025,6 +1025,10 @@ LEVEL = Info
;; The set of allowed values and rules are the same as DEFAULT_REPO_UNITS. ;; The set of allowed values and rules are the same as DEFAULT_REPO_UNITS.
;DEFAULT_FORK_REPO_UNITS = repo.code,repo.pulls ;DEFAULT_FORK_REPO_UNITS = repo.code,repo.pulls
;; ;;
;; Comma separated list of default mirror repo units.
;; The set of allowed values and rules are the same as DEFAULT_REPO_UNITS.
;DEFAULT_MIRROR_REPO_UNITS = repo.code,repo.releases,repo.issues,repo.wiki,repo.projects,repo.packages
;;
;; Prefix archive files by placing them in a directory named after the repository ;; Prefix archive files by placing them in a directory named after the repository
;PREFIX_ARCHIVE_FILES = true ;PREFIX_ARCHIVE_FILES = true
;; ;;

View file

@ -1,5 +1,5 @@
import eslintCommunityEslintPluginEslintComments from '@eslint-community/eslint-plugin-eslint-comments'; import eslintCommunityEslintPluginEslintComments from '@eslint-community/eslint-plugin-eslint-comments';
import stylisticEslintPluginJs from '@stylistic/eslint-plugin-js'; import stylisticEslintPlugin from '@stylistic/eslint-plugin';
import vitest from '@vitest/eslint-plugin'; import vitest from '@vitest/eslint-plugin';
import arrayFunc from 'eslint-plugin-array-func'; import arrayFunc from 'eslint-plugin-array-func';
import eslintPluginImportX from 'eslint-plugin-import-x'; import eslintPluginImportX from 'eslint-plugin-import-x';
@ -26,7 +26,7 @@ export default tseslint.config(
{ {
plugins: { plugins: {
'@eslint-community/eslint-comments': eslintCommunityEslintPluginEslintComments, '@eslint-community/eslint-comments': eslintCommunityEslintPluginEslintComments,
'@stylistic/js': stylisticEslintPluginJs, '@stylistic': stylisticEslintPlugin,
'@vitest': vitest, '@vitest': vitest,
'array-func': arrayFunc, 'array-func': arrayFunc,
'no-jquery': noJquery, 'no-jquery': noJquery,
@ -69,62 +69,62 @@ export default tseslint.config(
'@eslint-community/eslint-comments/no-unused-enable': [2], '@eslint-community/eslint-comments/no-unused-enable': [2],
'@eslint-community/eslint-comments/no-use': [0], '@eslint-community/eslint-comments/no-use': [0],
'@eslint-community/eslint-comments/require-description': [0], '@eslint-community/eslint-comments/require-description': [0],
'@stylistic/js/array-bracket-newline': [0], '@stylistic/array-bracket-newline': [0],
'@stylistic/js/array-bracket-spacing': [2, 'never'], '@stylistic/array-bracket-spacing': [2, 'never'],
'@stylistic/js/array-element-newline': [0], '@stylistic/array-element-newline': [0],
'@stylistic/js/arrow-parens': [2, 'always'], '@stylistic/arrow-parens': [2, 'always'],
'@stylistic/js/arrow-spacing': [2, { '@stylistic/arrow-spacing': [2, {
before: true, before: true,
after: true, after: true,
}], }],
'@stylistic/js/block-spacing': [0], '@stylistic/block-spacing': [0],
'@stylistic/js/brace-style': [2, '1tbs', { '@stylistic/brace-style': [2, '1tbs', {
allowSingleLine: true, allowSingleLine: true,
}], }],
'@stylistic/js/comma-dangle': [2, 'always-multiline'], '@stylistic/comma-dangle': [2, 'always-multiline'],
'@stylistic/js/comma-spacing': [2, { '@stylistic/comma-spacing': [2, {
before: false, before: false,
after: true, after: true,
}], }],
'@stylistic/js/comma-style': [2, 'last'], '@stylistic/comma-style': [2, 'last'],
'@stylistic/js/computed-property-spacing': [2, 'never'], '@stylistic/computed-property-spacing': [2, 'never'],
'@stylistic/js/dot-location': [2, 'property'], '@stylistic/dot-location': [2, 'property'],
'@stylistic/js/eol-last': [2], '@stylistic/eol-last': [2],
'@stylistic/js/function-call-spacing': [2, 'never'], '@stylistic/function-call-spacing': [2, 'never'],
'@stylistic/js/function-call-argument-newline': [0], '@stylistic/function-call-argument-newline': [0],
'@stylistic/js/function-paren-newline': [0], '@stylistic/function-paren-newline': [0],
'@stylistic/js/generator-star-spacing': [0], '@stylistic/generator-star-spacing': [0],
'@stylistic/js/implicit-arrow-linebreak': [0], '@stylistic/implicit-arrow-linebreak': [0],
'@stylistic/js/indent': [2, 2, { '@stylistic/indent': [2, 2, {
ignoreComments: true, ignoreComments: true,
SwitchCase: 1, SwitchCase: 1,
}], }],
'@stylistic/js/key-spacing': [2], '@stylistic/key-spacing': [2],
'@stylistic/js/keyword-spacing': [2], '@stylistic/keyword-spacing': [2],
'@stylistic/js/linebreak-style': [2, 'unix'], '@stylistic/linebreak-style': [2, 'unix'],
'@stylistic/js/lines-around-comment': [0], '@stylistic/lines-around-comment': [0],
'@stylistic/js/lines-between-class-members': [0], '@stylistic/lines-between-class-members': [0],
'@stylistic/js/max-len': [0], '@stylistic/max-len': [0],
'@stylistic/js/max-statements-per-line': [0], '@stylistic/max-statements-per-line': [0],
'@stylistic/js/multiline-ternary': [0], '@stylistic/multiline-ternary': [0],
'@stylistic/js/new-parens': [2], '@stylistic/new-parens': [2],
'@stylistic/js/newline-per-chained-call': [0], '@stylistic/newline-per-chained-call': [0],
'@stylistic/js/no-confusing-arrow': [0], '@stylistic/no-confusing-arrow': [0],
'@stylistic/js/no-extra-parens': [0], '@stylistic/no-extra-parens': [0],
'@stylistic/js/no-extra-semi': [2], '@stylistic/no-extra-semi': [2],
'@stylistic/js/no-floating-decimal': [0], '@stylistic/no-floating-decimal': [0],
'@stylistic/js/no-mixed-operators': [0], '@stylistic/no-mixed-operators': [0],
'@stylistic/js/no-mixed-spaces-and-tabs': [2], '@stylistic/no-mixed-spaces-and-tabs': [2],
'@stylistic/js/no-multi-spaces': [2, { '@stylistic/no-multi-spaces': [2, {
ignoreEOLComments: true, ignoreEOLComments: true,
exceptions: { exceptions: {
@ -132,60 +132,60 @@ export default tseslint.config(
}, },
}], }],
'@stylistic/js/no-multiple-empty-lines': [2, { '@stylistic/no-multiple-empty-lines': [2, {
max: 1, max: 1,
maxEOF: 0, maxEOF: 0,
maxBOF: 0, maxBOF: 0,
}], }],
'@stylistic/js/no-tabs': [2], '@stylistic/no-tabs': [2],
'@stylistic/js/no-trailing-spaces': [2], '@stylistic/no-trailing-spaces': [2],
'@stylistic/js/no-whitespace-before-property': [2], '@stylistic/no-whitespace-before-property': [2],
'@stylistic/js/nonblock-statement-body-position': [2], '@stylistic/nonblock-statement-body-position': [2],
'@stylistic/js/object-curly-newline': [0], '@stylistic/object-curly-newline': [0],
'@stylistic/js/object-curly-spacing': [2, 'never'], '@stylistic/object-curly-spacing': [2, 'never'],
'@stylistic/js/object-property-newline': [0], '@stylistic/object-property-newline': [0],
'@stylistic/js/one-var-declaration-per-line': [0], '@stylistic/one-var-declaration-per-line': [0],
'@stylistic/js/operator-linebreak': [2, 'after'], '@stylistic/operator-linebreak': [2, 'after'],
'@stylistic/js/padded-blocks': [2, 'never'], '@stylistic/padded-blocks': [2, 'never'],
'@stylistic/js/padding-line-between-statements': [0], '@stylistic/padding-line-between-statements': [0],
'@stylistic/js/quote-props': [0], '@stylistic/quote-props': [0],
'@stylistic/js/quotes': [2, 'single', { '@stylistic/quotes': [2, 'single', {
avoidEscape: true, avoidEscape: true,
allowTemplateLiterals: true, allowTemplateLiterals: true,
}], }],
'@stylistic/js/rest-spread-spacing': [2, 'never'], '@stylistic/rest-spread-spacing': [2, 'never'],
'@stylistic/js/semi': [2, 'always', { '@stylistic/semi': [2, 'always', {
omitLastInOneLineBlock: true, omitLastInOneLineBlock: true,
}], }],
'@stylistic/js/semi-spacing': [2, { '@stylistic/semi-spacing': [2, {
before: false, before: false,
after: true, after: true,
}], }],
'@stylistic/js/semi-style': [2, 'last'], '@stylistic/semi-style': [2, 'last'],
'@stylistic/js/space-before-blocks': [2, 'always'], '@stylistic/space-before-blocks': [2, 'always'],
'@stylistic/js/space-before-function-paren': [2, { '@stylistic/space-before-function-paren': [2, {
anonymous: 'ignore', anonymous: 'ignore',
named: 'never', named: 'never',
asyncArrow: 'always', asyncArrow: 'always',
}], }],
'@stylistic/js/space-in-parens': [2, 'never'], '@stylistic/space-in-parens': [2, 'never'],
'@stylistic/js/space-infix-ops': [2], '@stylistic/space-infix-ops': [2],
'@stylistic/js/space-unary-ops': [2], '@stylistic/space-unary-ops': [2],
'@stylistic/js/spaced-comment': [2, 'always'], '@stylistic/spaced-comment': [2, 'always'],
'@stylistic/js/switch-colon-spacing': [2], '@stylistic/switch-colon-spacing': [2],
'@stylistic/js/template-curly-spacing': [2, 'never'], '@stylistic/template-curly-spacing': [2, 'never'],
'@stylistic/js/template-tag-spacing': [2, 'never'], '@stylistic/template-tag-spacing': [2, 'never'],
'@stylistic/js/wrap-iife': [2, 'inside'], '@stylistic/wrap-iife': [2, 'inside'],
'@stylistic/js/wrap-regex': [0], '@stylistic/wrap-regex': [0],
'@stylistic/js/yield-star-spacing': [2, 'after'], '@stylistic/yield-star-spacing': [2, 'after'],
'accessor-pairs': [2], 'accessor-pairs': [2],
'array-callback-return': [2, { 'array-callback-return': [2, {

6
flake.lock generated
View file

@ -20,11 +20,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1733392399, "lastModified": 1749285348,
"narHash": "sha256-kEsTJTUQfQFIJOcLYFt/RvNxIK653ZkTBIs4DG+cBns=", "narHash": "sha256-frdhQvPbmDYaScPFiCnfdh3B/Vh81Uuoo0w5TkWmmjU=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "d0797a04b81caeae77bcff10a9dde78bc17f5661", "rev": "3e3afe5174c561dee0df6f2c2b2236990146329f",
"type": "github" "type": "github"
}, },
"original": { "original": {

View file

@ -3,35 +3,20 @@
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
flake-utils.url = "github:numtide/flake-utils"; flake-utils.url = "github:numtide/flake-utils";
}; };
outputs = { outputs =
{
nixpkgs, nixpkgs,
flake-utils, flake-utils,
... ...
}: }:
flake-utils.lib.eachDefaultSystem ( flake-utils.lib.eachDefaultSystem (
system: let system:
let
pkgs = nixpkgs.legacyPackages.${system}; pkgs = nixpkgs.legacyPackages.${system};
in { in
devShells.default = pkgs.mkShell { {
buildInputs = with pkgs; [ devShells.default = import ./shell.nix { inherit pkgs; };
# generic formatter = pkgs.nixfmt-rfc-style;
git
git-lfs
gnumake
gnused
gnutar
gzip
# frontend
nodejs_20
# backend
gofumpt
sqlite
go
gopls
];
};
} }
); );
} }

68
go.mod
View file

@ -2,30 +2,31 @@ module forgejo.org
go 1.24 go 1.24
toolchain go1.24.3 toolchain go1.24.4
require ( require (
code.forgejo.org/f3/gof3/v3 v3.10.6 code.forgejo.org/f3/gof3/v3 v3.11.0
code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251 code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251
code.forgejo.org/forgejo/go-rpmutils v1.0.0
code.forgejo.org/forgejo/levelqueue v1.0.0 code.forgejo.org/forgejo/levelqueue v1.0.0
code.forgejo.org/forgejo/reply v1.0.2 code.forgejo.org/forgejo/reply v1.0.2
code.forgejo.org/go-chi/binding v1.0.0 code.forgejo.org/go-chi/binding v1.0.1
code.forgejo.org/go-chi/cache v1.0.0 code.forgejo.org/go-chi/cache v1.0.1
code.forgejo.org/go-chi/captcha v1.0.1 code.forgejo.org/go-chi/captcha v1.0.2
code.forgejo.org/go-chi/session v1.0.1 code.forgejo.org/go-chi/session v1.0.2
code.gitea.io/actions-proto-go v0.4.0 code.gitea.io/actions-proto-go v0.4.0
code.gitea.io/sdk/gitea v0.21.0 code.gitea.io/sdk/gitea v0.21.0
codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570 codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570
connectrpc.com/connect v1.17.0 connectrpc.com/connect v1.18.1
github.com/42wim/httpsig v1.2.3 github.com/42wim/httpsig v1.2.3
github.com/42wim/sshsig v0.0.0-20250502153856-5100632e8920 github.com/42wim/sshsig v0.0.0-20250502153856-5100632e8920
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358
github.com/ProtonMail/go-crypto v1.1.6 github.com/ProtonMail/go-crypto v1.3.0
github.com/PuerkitoBio/goquery v1.10.3 github.com/PuerkitoBio/goquery v1.10.3
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.2 github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.2
github.com/alecthomas/chroma/v2 v2.18.0 github.com/alecthomas/chroma/v2 v2.18.0
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
github.com/blevesearch/bleve/v2 v2.5.1 github.com/blevesearch/bleve/v2 v2.5.2
github.com/buildkite/terminal-to-html/v3 v3.16.8 github.com/buildkite/terminal-to-html/v3 v3.16.8
github.com/caddyserver/certmagic v0.23.0 github.com/caddyserver/certmagic v0.23.0
github.com/chi-middleware/proxy v1.1.1 github.com/chi-middleware/proxy v1.1.1
@ -40,14 +41,14 @@ require (
github.com/gliderlabs/ssh v0.3.8 github.com/gliderlabs/ssh v0.3.8
github.com/go-ap/activitypub v0.0.0-20231114162308-e219254dc5c9 github.com/go-ap/activitypub v0.0.0-20231114162308-e219254dc5c9
github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73 github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73
github.com/go-chi/chi/v5 v5.2.0 github.com/go-chi/chi/v5 v5.2.2
github.com/go-chi/cors v1.2.1 github.com/go-chi/cors v1.2.1
github.com/go-co-op/gocron v1.37.0 github.com/go-co-op/gocron v1.37.0
github.com/go-enry/go-enry/v2 v2.9.2 github.com/go-enry/go-enry/v2 v2.9.2
github.com/go-git/go-git/v5 v5.13.2 github.com/go-git/go-git/v5 v5.13.2
github.com/go-ldap/ldap/v3 v3.4.6 github.com/go-ldap/ldap/v3 v3.4.6
github.com/go-openapi/spec v0.21.0 github.com/go-openapi/spec v0.21.0
github.com/go-sql-driver/mysql v1.9.1 github.com/go-sql-driver/mysql v1.9.3
github.com/go-webauthn/webauthn v0.13.0 github.com/go-webauthn/webauthn v0.13.0
github.com/gobwas/glob v0.2.3 github.com/gobwas/glob v0.2.3
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f
@ -75,7 +76,7 @@ require (
github.com/meilisearch/meilisearch-go v0.31.0 github.com/meilisearch/meilisearch-go v0.31.0
github.com/mholt/archiver/v3 v3.5.1 github.com/mholt/archiver/v3 v3.5.1
github.com/microcosm-cc/bluemonday v1.0.27 github.com/microcosm-cc/bluemonday v1.0.27
github.com/minio/minio-go/v7 v7.0.91 github.com/minio/minio-go/v7 v7.0.94
github.com/msteinert/pam/v2 v2.1.0 github.com/msteinert/pam/v2 v2.1.0
github.com/nektos/act v0.2.52 github.com/nektos/act v0.2.52
github.com/niklasfasching/go-org v1.8.0 github.com/niklasfasching/go-org v1.8.0
@ -86,27 +87,25 @@ require (
github.com/prometheus/client_golang v1.21.1 github.com/prometheus/client_golang v1.21.1
github.com/redis/go-redis/v9 v9.8.0 github.com/redis/go-redis/v9 v9.8.0
github.com/robfig/cron/v3 v3.0.1 github.com/robfig/cron/v3 v3.0.1
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 github.com/santhosh-tekuri/jsonschema/v6 v6.0.2
github.com/sassoftware/go-rpmutils v0.4.0 github.com/sergi/go-diff v1.4.0
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3
github.com/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92
github.com/stretchr/testify v1.10.0 github.com/stretchr/testify v1.10.0
github.com/syndtr/goleveldb v1.0.0 github.com/syndtr/goleveldb v1.0.0
github.com/ulikunitz/xz v0.5.12 github.com/ulikunitz/xz v0.5.12
github.com/urfave/cli/v2 v2.27.6 github.com/urfave/cli/v3 v3.3.3
github.com/valyala/fastjson v1.6.4 github.com/valyala/fastjson v1.6.4
github.com/yohcop/openid-go v1.0.1 github.com/yohcop/openid-go v1.0.1
github.com/yuin/goldmark v1.7.12 github.com/yuin/goldmark v1.7.12
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
gitlab.com/gitlab-org/api/client-go v0.126.0 gitlab.com/gitlab-org/api/client-go v0.130.1
go.uber.org/mock v0.5.1 go.uber.org/mock v0.5.2
golang.org/x/crypto v0.38.0 golang.org/x/crypto v0.39.0
golang.org/x/image v0.27.0 golang.org/x/image v0.27.0
golang.org/x/net v0.40.0 golang.org/x/net v0.41.0
golang.org/x/oauth2 v0.30.0 golang.org/x/oauth2 v0.30.0
golang.org/x/sync v0.14.0 golang.org/x/sync v0.15.0
golang.org/x/sys v0.33.0 golang.org/x/sys v0.33.0
golang.org/x/text v0.25.0 golang.org/x/text v0.26.0
google.golang.org/protobuf v1.36.4 google.golang.org/protobuf v1.36.4
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
gopkg.in/ini.v1 v1.67.0 gopkg.in/ini.v1 v1.67.0
@ -121,7 +120,6 @@ require (
dario.cat/mergo v1.0.0 // indirect dario.cat/mergo v1.0.0 // indirect
filippo.io/edwards25519 v1.1.0 // indirect filippo.io/edwards25519 v1.1.0 // indirect
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 // indirect git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 // indirect
github.com/DataDog/zstd v1.5.5 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/RoaringBitmap/roaring/v2 v2.4.5 // indirect github.com/RoaringBitmap/roaring/v2 v2.4.5 // indirect
github.com/andybalholm/brotli v1.1.1 // indirect github.com/andybalholm/brotli v1.1.1 // indirect
@ -146,14 +144,13 @@ require (
github.com/blevesearch/zapx/v13 v13.4.2 // indirect github.com/blevesearch/zapx/v13 v13.4.2 // indirect
github.com/blevesearch/zapx/v14 v14.4.2 // indirect github.com/blevesearch/zapx/v14 v14.4.2 // indirect
github.com/blevesearch/zapx/v15 v15.4.2 // indirect github.com/blevesearch/zapx/v15 v15.4.2 // indirect
github.com/blevesearch/zapx/v16 v16.2.3 // indirect github.com/blevesearch/zapx/v16 v16.2.4 // indirect
github.com/boombuler/barcode v1.0.1 // indirect github.com/boombuler/barcode v1.0.1 // indirect
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // indirect github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf // indirect
github.com/caddyserver/zerossl v0.1.3 // indirect github.com/caddyserver/zerossl v0.1.3 // indirect
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a // indirect github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cloudflare/circl v1.3.8 // indirect github.com/cloudflare/circl v1.6.1 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
github.com/cyphar/filepath-securejoin v0.3.6 // indirect github.com/cyphar/filepath-securejoin v0.3.6 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/davidmz/go-pageant v1.0.2 // indirect github.com/davidmz/go-pageant v1.0.2 // indirect
@ -210,6 +207,7 @@ require (
github.com/nwaples/rardecode v1.1.3 // indirect github.com/nwaples/rardecode v1.1.3 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/onsi/ginkgo v1.16.5 // indirect github.com/onsi/ginkgo v1.16.5 // indirect
github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c // indirect
github.com/pierrec/lz4/v4 v4.1.21 // indirect github.com/pierrec/lz4/v4 v4.1.21 // indirect
github.com/pjbgf/sha1cd v0.3.2 // indirect github.com/pjbgf/sha1cd v0.3.2 // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
@ -221,15 +219,13 @@ require (
github.com/rivo/uniseg v0.4.7 // indirect github.com/rivo/uniseg v0.4.7 // indirect
github.com/rogpeppe/go-internal v1.13.1 // indirect github.com/rogpeppe/go-internal v1.13.1 // indirect
github.com/rs/xid v1.6.0 // indirect github.com/rs/xid v1.6.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c // indirect
github.com/sirupsen/logrus v1.9.3 // indirect github.com/sirupsen/logrus v1.9.3 // indirect
github.com/skeema/knownhosts v1.3.0 // indirect github.com/skeema/knownhosts v1.3.0 // indirect
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
github.com/tinylib/msgp v1.3.0 // indirect
github.com/x448/float16 v0.8.4 // indirect github.com/x448/float16 v0.8.4 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
github.com/zeebo/assert v1.3.0 // indirect github.com/zeebo/assert v1.3.0 // indirect
github.com/zeebo/blake3 v0.2.4 // indirect github.com/zeebo/blake3 v0.2.4 // indirect
go.etcd.io/bbolt v1.4.0 // indirect go.etcd.io/bbolt v1.4.0 // indirect
@ -237,18 +233,16 @@ require (
go.uber.org/multierr v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect go.uber.org/zap v1.27.0 // indirect
go.uber.org/zap/exp v0.3.0 // indirect go.uber.org/zap/exp v0.3.0 // indirect
golang.org/x/mod v0.24.0 // indirect golang.org/x/mod v0.25.0 // indirect
golang.org/x/time v0.10.0 // indirect golang.org/x/time v0.11.0 // indirect
golang.org/x/tools v0.31.0 // indirect golang.org/x/tools v0.34.0 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect
) )
replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1 replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1
replace github.com/shurcooL/vfsgen => github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0 replace github.com/nektos/act => code.forgejo.org/forgejo/act v1.28.0
replace github.com/nektos/act => code.forgejo.org/forgejo/act v1.26.0
replace github.com/mholt/archiver/v3 => code.forgejo.org/forgejo/archiver/v3 v3.5.1 replace github.com/mholt/archiver/v3 => code.forgejo.org/forgejo/archiver/v3 v3.5.1

128
go.sum
View file

@ -1,13 +1,15 @@
cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I= cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
code.forgejo.org/f3/gof3/v3 v3.10.6 h1:Ru/Iz+pqM8IPi7atUHE7+q7v3O3DRbYgMFqrFTsO1m8= code.forgejo.org/f3/gof3/v3 v3.11.0 h1:f/xToKwqTgxG6PYxvewywjDQyCcyHEEJ6sZqUitFsAE=
code.forgejo.org/f3/gof3/v3 v3.10.6/go.mod h1:K6lQCWQIyN/5rjP/OJL9fMA6fd++satndE20w/I6Kss= code.forgejo.org/f3/gof3/v3 v3.11.0/go.mod h1:4FaRUNSQGBiD1M0DuB0yNv+Z2wMtlOeckgygHSSq4KQ=
code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251 h1:HTZl3CBk3ABNYtFI6TPLvJgGKFIhKT5CBk0sbOtkDKU= code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251 h1:HTZl3CBk3ABNYtFI6TPLvJgGKFIhKT5CBk0sbOtkDKU=
code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251/go.mod h1:PphB88CPbx601QrWPMZATeorACeVmQlyv3u+uUMbSaM= code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251/go.mod h1:PphB88CPbx601QrWPMZATeorACeVmQlyv3u+uUMbSaM=
code.forgejo.org/forgejo/act v1.26.0 h1:6mTmoaw7d/WpYiw/Pw6AaypxFdgJog5OFi/PMEgEbxs= code.forgejo.org/forgejo/act v1.28.0 h1:96njNC7C1YNyjWq5OWvLZMF/nw0PMthzIA8Nwbnn7jo=
code.forgejo.org/forgejo/act v1.26.0/go.mod h1:HFDFrXPrqfM9aH2RCnMiBdo/3ThxDmZjp58InPjGOfo= code.forgejo.org/forgejo/act v1.28.0/go.mod h1:dFuiwAmD5vyrzecysHB2kL/GM3wRpoVPl+WdbCTC8Bs=
code.forgejo.org/forgejo/archiver/v3 v3.5.1 h1:UmmbA7D5550uf71SQjarmrn6yKwOGxtEjb3jaYYtmSE= code.forgejo.org/forgejo/archiver/v3 v3.5.1 h1:UmmbA7D5550uf71SQjarmrn6yKwOGxtEjb3jaYYtmSE=
code.forgejo.org/forgejo/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4= code.forgejo.org/forgejo/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4=
code.forgejo.org/forgejo/go-rpmutils v1.0.0 h1:RZGGeKt70p/WaIEL97pyT6uiiEIoN8/aLmS5Z6WmX0M=
code.forgejo.org/forgejo/go-rpmutils v1.0.0/go.mod h1:cg+VbgLXfrDPza9T+kBsMb3TVmmzPN4XseT6gDGLSUk=
code.forgejo.org/forgejo/go-xsd-duration v0.0.0-20220703122237-02e73435a078 h1:RArF5AsF9LH4nEoJxqRxcP5r8hhRfWcId84G82YbqzA= code.forgejo.org/forgejo/go-xsd-duration v0.0.0-20220703122237-02e73435a078 h1:RArF5AsF9LH4nEoJxqRxcP5r8hhRfWcId84G82YbqzA=
code.forgejo.org/forgejo/go-xsd-duration v0.0.0-20220703122237-02e73435a078/go.mod h1:g/V2Hjas6Z1UHUp4yIx6bATpNzJ7DYtD0FG3+xARWxs= code.forgejo.org/forgejo/go-xsd-duration v0.0.0-20220703122237-02e73435a078/go.mod h1:g/V2Hjas6Z1UHUp4yIx6bATpNzJ7DYtD0FG3+xARWxs=
code.forgejo.org/forgejo/levelqueue v1.0.0 h1:9krYpU6BM+j/1Ntj6m+VCAIu0UNnne1/UfU/XgPpLuE= code.forgejo.org/forgejo/levelqueue v1.0.0 h1:9krYpU6BM+j/1Ntj6m+VCAIu0UNnne1/UfU/XgPpLuE=
@ -16,22 +18,22 @@ code.forgejo.org/forgejo/reply v1.0.2 h1:dMhQCHV6/O3L5CLWNTol+dNzDAuyCK88z4J/lCd
code.forgejo.org/forgejo/reply v1.0.2/go.mod h1:RyZUfzQLc+fuLIGjTSQWDAJWPiL4WtKXB/FifT5fM7U= code.forgejo.org/forgejo/reply v1.0.2/go.mod h1:RyZUfzQLc+fuLIGjTSQWDAJWPiL4WtKXB/FifT5fM7U=
code.forgejo.org/forgejo/ssh v0.0.0-20241211213324-5fc306ca0616 h1:kEZL84+02jY9RxXM4zHBWZ3Fml0B09cmP1LGkDsCfIA= code.forgejo.org/forgejo/ssh v0.0.0-20241211213324-5fc306ca0616 h1:kEZL84+02jY9RxXM4zHBWZ3Fml0B09cmP1LGkDsCfIA=
code.forgejo.org/forgejo/ssh v0.0.0-20241211213324-5fc306ca0616/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8= code.forgejo.org/forgejo/ssh v0.0.0-20241211213324-5fc306ca0616/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8=
code.forgejo.org/go-chi/binding v1.0.0 h1:EIDJtk9brK7WsT7rvS/D4cxX8XlnhY3LMy8ex1jeHu0= code.forgejo.org/go-chi/binding v1.0.1 h1:coKNI+X1NzRN7X85LlrpvBRqk0TXpJ+ja28vusQWEuY=
code.forgejo.org/go-chi/binding v1.0.0/go.mod h1:fWwqaHj0H1/KeCpBqdvKunflq8pYfciEHI5v3UUeE2E= code.forgejo.org/go-chi/binding v1.0.1/go.mod h1:oTFFDg/dkwFbmVuusiULB1OlrIJM95cOGK7Nc3GYcoo=
code.forgejo.org/go-chi/cache v1.0.0 h1:akLfGxNlHcacmtutovNtYFSTMsbdcp5MGjAEsP4pxnE= code.forgejo.org/go-chi/cache v1.0.1 h1:w6IsDcPbeEnEYZn7M2HJe3/3/Ehtcw/72VjcVK7+lBw=
code.forgejo.org/go-chi/cache v1.0.0/go.mod h1:OVlZ/TqDYJ+RUJ+R+J+OLxtlyjo3pbjBeK7LAWAB+Vk= code.forgejo.org/go-chi/cache v1.0.1/go.mod h1:K3aQSyRIN4xiuqV1kanfQ6O4ToDpzDpY3bNOyGjFe3U=
code.forgejo.org/go-chi/captcha v1.0.1 h1:/oe1fvGOpdyyeGijg3oMYNOYLvEovNvp79Y3gLe3qbk= code.forgejo.org/go-chi/captcha v1.0.2 h1:vyHDPXkpjDv8bLO9NqtWzZayzstD/WpJ5xwEkAaqZGQ=
code.forgejo.org/go-chi/captcha v1.0.1/go.mod h1:6EbjSVVa7WoZFENgwK/hLAJZq+HBXtgRsjnIngILC8Y= code.forgejo.org/go-chi/captcha v1.0.2/go.mod h1:lxiPLcJ76UCZHoH31/Wbum4GUi2NgjfFZLrJkKv1lLE=
code.forgejo.org/go-chi/session v1.0.1 h1:RNkcJQZJBqlvJoIFXSth87b3kMFZLDBA18VcitD+Z0Y= code.forgejo.org/go-chi/session v1.0.2 h1:pG+AXre9L9VXJmTaADXkmeEPuRalhmBXyv6tG2Rvjcc=
code.forgejo.org/go-chi/session v1.0.1/go.mod h1:y69sjS984wc7k4xyu77yNE5HKeSlBoQW8VSGdsK7RAs= code.forgejo.org/go-chi/session v1.0.2/go.mod h1:HnEGyBny7WPzCiVLP2vzL5ssma+3gCSl/vLpuVNYrqc=
code.gitea.io/actions-proto-go v0.4.0 h1:OsPBPhodXuQnsspG1sQ4eRE1PeoZyofd7+i73zCwnsU= code.gitea.io/actions-proto-go v0.4.0 h1:OsPBPhodXuQnsspG1sQ4eRE1PeoZyofd7+i73zCwnsU=
code.gitea.io/actions-proto-go v0.4.0/go.mod h1:mn7Wkqz6JbnTOHQpot3yDeHx+O5C9EGhMEE+htvHBas= code.gitea.io/actions-proto-go v0.4.0/go.mod h1:mn7Wkqz6JbnTOHQpot3yDeHx+O5C9EGhMEE+htvHBas=
code.gitea.io/sdk/gitea v0.21.0 h1:69n6oz6kEVHRo1+APQQyizkhrZrLsTLXey9142pfkD4= code.gitea.io/sdk/gitea v0.21.0 h1:69n6oz6kEVHRo1+APQQyizkhrZrLsTLXey9142pfkD4=
code.gitea.io/sdk/gitea v0.21.0/go.mod h1:tnBjVhuKJCn8ibdyyhvUyxrR1Ca2KHEoTWoukNhXQPA= code.gitea.io/sdk/gitea v0.21.0/go.mod h1:tnBjVhuKJCn8ibdyyhvUyxrR1Ca2KHEoTWoukNhXQPA=
codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570 h1:TXbikPqa7YRtfU9vS6QJBg77pUvbEb6StRdZO8t1bEY= codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570 h1:TXbikPqa7YRtfU9vS6QJBg77pUvbEb6StRdZO8t1bEY=
codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570/go.mod h1:IIAjsijsd8q1isWX8MACefDEgTQslQ4stk2AeeTt3kM= codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570/go.mod h1:IIAjsijsd8q1isWX8MACefDEgTQslQ4stk2AeeTt3kM=
connectrpc.com/connect v1.17.0 h1:W0ZqMhtVzn9Zhn2yATuUokDLO5N+gIuBWMOnsQrfmZk= connectrpc.com/connect v1.18.1 h1:PAg7CjSAGvscaf6YZKUefjoih5Z/qYkyaTrBW8xvYPw=
connectrpc.com/connect v1.17.0/go.mod h1:0292hj1rnx8oFrStN7cB4jjVBeqs+Yx5yDIC2prWDO8= connectrpc.com/connect v1.18.1/go.mod h1:0292hj1rnx8oFrStN7cB4jjVBeqs+Yx5yDIC2prWDO8=
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
@ -46,13 +48,11 @@ github.com/6543/go-version v1.3.1 h1:HvOp+Telns7HWJ2Xo/05YXQSB2bE0WmVgbHqwMPZT4U
github.com/6543/go-version v1.3.1/go.mod h1:oqFAHCwtLVUTLdhQmVZWYvaHXTdsbB4SY85at64SQEo= github.com/6543/go-version v1.3.1/go.mod h1:oqFAHCwtLVUTLdhQmVZWYvaHXTdsbB4SY85at64SQEo=
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ=
github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw= github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw=
github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE=
github.com/PuerkitoBio/goquery v1.10.3 h1:pFYcNSqHxBD06Fpj/KsbStFRsgRATgnf3LeXiUkhzPo= github.com/PuerkitoBio/goquery v1.10.3 h1:pFYcNSqHxBD06Fpj/KsbStFRsgRATgnf3LeXiUkhzPo=
github.com/PuerkitoBio/goquery v1.10.3/go.mod h1:tMUX0zDMHXYlAQk6p35XxQMqMweEKB7iK7iLNd4RH4Y= github.com/PuerkitoBio/goquery v1.10.3/go.mod h1:tMUX0zDMHXYlAQk6p35XxQMqMweEKB7iK7iLNd4RH4Y=
github.com/RoaringBitmap/roaring/v2 v2.4.5 h1:uGrrMreGjvAtTBobc0g5IrW1D5ldxDQYe2JW2gggRdg= github.com/RoaringBitmap/roaring/v2 v2.4.5 h1:uGrrMreGjvAtTBobc0g5IrW1D5ldxDQYe2JW2gggRdg=
@ -87,8 +87,8 @@ github.com/bits-and-blooms/bitset v1.22.0 h1:Tquv9S8+SGaS3EhyA+up3FXzmkhxPGjQQCk
github.com/bits-and-blooms/bitset v1.22.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/bits-and-blooms/bitset v1.22.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb h1:m935MPodAbYS46DG4pJSv7WO+VECIWUQ7OJYSoTrMh4= github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb h1:m935MPodAbYS46DG4pJSv7WO+VECIWUQ7OJYSoTrMh4=
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI= github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI=
github.com/blevesearch/bleve/v2 v2.5.1 h1:cc/O++W2Hcjp1SU5ETHeE+QYWv2oV88ldYEPowdmg8M= github.com/blevesearch/bleve/v2 v2.5.2 h1:Ab0r0MODV2C5A6BEL87GqLBySqp/s9xFgceCju6BQk8=
github.com/blevesearch/bleve/v2 v2.5.1/go.mod h1:9g/wnbWKm9AgXrU8Ecqi+IDdqjUHWymwkQRDg+5tafU= github.com/blevesearch/bleve/v2 v2.5.2/go.mod h1:5Dj6dUQxZM6aqYT3eutTD/GpWKGFSsV8f7LDidFbwXo=
github.com/blevesearch/bleve_index_api v1.2.8 h1:Y98Pu5/MdlkRyLM0qDHostYo7i+Vv1cDNhqTeR4Sy6Y= github.com/blevesearch/bleve_index_api v1.2.8 h1:Y98Pu5/MdlkRyLM0qDHostYo7i+Vv1cDNhqTeR4Sy6Y=
github.com/blevesearch/bleve_index_api v1.2.8/go.mod h1:rKQDl4u51uwafZxFrPD1R7xFOwKnzZW7s/LSeK4lgo0= github.com/blevesearch/bleve_index_api v1.2.8/go.mod h1:rKQDl4u51uwafZxFrPD1R7xFOwKnzZW7s/LSeK4lgo0=
github.com/blevesearch/geo v0.2.3 h1:K9/vbGI9ehlXdxjxDRJtoAMt7zGAsMIzc6n8zWcwnhg= github.com/blevesearch/geo v0.2.3 h1:K9/vbGI9ehlXdxjxDRJtoAMt7zGAsMIzc6n8zWcwnhg=
@ -121,13 +121,13 @@ github.com/blevesearch/zapx/v14 v14.4.2 h1:2SGHakVKd+TrtEqpfeq8X+So5PShQ5nW6GNxT
github.com/blevesearch/zapx/v14 v14.4.2/go.mod h1:rz0XNb/OZSMjNorufDGSpFpjoFKhXmppH9Hi7a877D8= github.com/blevesearch/zapx/v14 v14.4.2/go.mod h1:rz0XNb/OZSMjNorufDGSpFpjoFKhXmppH9Hi7a877D8=
github.com/blevesearch/zapx/v15 v15.4.2 h1:sWxpDE0QQOTjyxYbAVjt3+0ieu8NCE0fDRaFxEsp31k= github.com/blevesearch/zapx/v15 v15.4.2 h1:sWxpDE0QQOTjyxYbAVjt3+0ieu8NCE0fDRaFxEsp31k=
github.com/blevesearch/zapx/v15 v15.4.2/go.mod h1:1pssev/59FsuWcgSnTa0OeEpOzmhtmr/0/11H0Z8+Nw= github.com/blevesearch/zapx/v15 v15.4.2/go.mod h1:1pssev/59FsuWcgSnTa0OeEpOzmhtmr/0/11H0Z8+Nw=
github.com/blevesearch/zapx/v16 v16.2.3 h1:7Y0r+a3diEvlazsncexq1qoFOcBd64xwMS7aDm4lo1s= github.com/blevesearch/zapx/v16 v16.2.4 h1:tGgfvleXTAkwsD5mEzgM3zCS/7pgocTCnO1oyAUjlww=
github.com/blevesearch/zapx/v16 v16.2.3/go.mod h1:wVJ+GtURAaRG9KQAMNYyklq0egV+XJlGcXNCE0OFjjA= github.com/blevesearch/zapx/v16 v16.2.4/go.mod h1:Rti/REtuuMmzwsI8/C/qIzRaEoSK/wiFYw5e5ctUKKs=
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs= github.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs=
github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 h1:N7oVaKyGp8bttX0bfZGmcGkjz7DLQXhAn3DNd3T0ous= github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf h1:TqhNAT4zKbTdLa62d2HDBFdvgSbIGB3eJE8HqhgiL9I=
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874/go.mod h1:r5xuitiExdLAJ09PR7vBVENGvp4ZuTBeWTGtxuX3K+c= github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf/go.mod h1:r5xuitiExdLAJ09PR7vBVENGvp4ZuTBeWTGtxuX3K+c=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
@ -150,10 +150,8 @@ github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moA
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
github.com/cloudflare/circl v1.3.8 h1:j+V8jJt09PoeMFIu2uh5JUyEaIHTXVOHslFoLNAKqwI= github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
github.com/cloudflare/circl v1.3.8/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU= github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cyphar/filepath-securejoin v0.3.6 h1:4d9N5ykBnSp5Xn2JkhocYDkOpURL/18CYMpo6xB9uWM= github.com/cyphar/filepath-securejoin v0.3.6 h1:4d9N5ykBnSp5Xn2JkhocYDkOpURL/18CYMpo6xB9uWM=
github.com/cyphar/filepath-securejoin v0.3.6/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= github.com/cyphar/filepath-securejoin v0.3.6/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
@ -215,8 +213,8 @@ github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73/go.mod h1:jyveZeGw5La
github.com/go-asn1-ber/asn1-ber v1.5.5 h1:MNHlNMBDgEKD4TcKr36vQN68BA00aDfjIt3/bD50WnA= github.com/go-asn1-ber/asn1-ber v1.5.5 h1:MNHlNMBDgEKD4TcKr36vQN68BA00aDfjIt3/bD50WnA=
github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-chi/chi/v5 v5.0.1/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/chi/v5 v5.0.1/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/chi/v5 v5.2.0 h1:Aj1EtB0qR2Rdo2dG4O94RIU35w2lvQSj6BRA4+qwFL0= github.com/go-chi/chi/v5 v5.2.2 h1:CMwsvRVTbXVytCk1Wd72Zy1LAsAh9GxMmSNWLHCG618=
github.com/go-chi/chi/v5 v5.2.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/chi/v5 v5.2.2/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
github.com/go-co-op/gocron v1.37.0 h1:ZYDJGtQ4OMhTLKOKMIch+/CY70Brbb1dGdooLEhh7b0= github.com/go-co-op/gocron v1.37.0 h1:ZYDJGtQ4OMhTLKOKMIch+/CY70Brbb1dGdooLEhh7b0=
@ -247,8 +245,8 @@ github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9Z
github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk= github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk=
github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU= github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU=
github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0= github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0=
github.com/go-sql-driver/mysql v1.9.1 h1:FrjNGn/BsJQjVRuSa8CBrM5BWA9BWoXXat3KrtSb/iI= github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
github.com/go-sql-driver/mysql v1.9.1/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U=
github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
@ -384,8 +382,6 @@ github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/libdns/libdns v1.0.0-beta.1 h1:KIf4wLfsrEpXpZ3vmc/poM8zCATXT2klbdPe6hyOBjQ= github.com/libdns/libdns v1.0.0-beta.1 h1:KIf4wLfsrEpXpZ3vmc/poM8zCATXT2klbdPe6hyOBjQ=
github.com/libdns/libdns v1.0.0-beta.1/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ= github.com/libdns/libdns v1.0.0-beta.1/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0 h1:F/3FfGmKdiKFa8kL3YrpZ7pe9H4l4AzA1pbaOUnRvPI=
github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0/go.mod h1:JEfTc3+2DF9Z4PXhLLvXL42zexJyh8rIq3OzUj/0rAk=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
@ -415,8 +411,8 @@ github.com/minio/crc64nvme v1.0.1 h1:DHQPrYPdqK7jQG/Ls5CTBZWeex/2FMS3G5XGkycuFrY
github.com/minio/crc64nvme v1.0.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg= github.com/minio/crc64nvme v1.0.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg=
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
github.com/minio/minio-go/v7 v7.0.91 h1:tWLZnEfo3OZl5PoXQwcwTAPNNrjyWwOh6cbZitW5JQc= github.com/minio/minio-go/v7 v7.0.94 h1:1ZoksIKPyaSt64AVOyaQvhDOgVC3MfZsWM6mZXRUGtM=
github.com/minio/minio-go/v7 v7.0.91/go.mod h1:uvMUcGrpgeSAAI6+sD3818508nUyMULw94j2Nxku/Go= github.com/minio/minio-go/v7 v7.0.94/go.mod h1:71t2CqDt3ThzESgZUlU1rBN54mksGGlkLcFgguDnnAc=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@ -459,6 +455,8 @@ github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3I
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c h1:dAMKvw0MlJT1GshSTtih8C2gDs04w8dReiOGXrGLNoY=
github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
@ -497,17 +495,11 @@ github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU=
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 h1:PKK9DyHxif4LZo+uQSgXNqs0jj5+xZwwfKHgph2lxBw=
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU=
github.com/sassoftware/go-rpmutils v0.4.0 h1:ojND82NYBxgwrV+mX1CWsd5QJvvEZTKddtCdFLPWhpg=
github.com/sassoftware/go-rpmutils v0.4.0/go.mod h1:3goNWi7PGAT3/dlql2lv3+MSN5jNYPjT5mVcQcIsYzI=
github.com/serenize/snaker v0.0.0-20171204205717-a683aaf2d516/go.mod h1:Yow6lPLSAXx2ifx470yD/nUe22Dv5vBvxK/UK9UUTVs= github.com/serenize/snaker v0.0.0-20171204205717-a683aaf2d516/go.mod h1:Yow6lPLSAXx2ifx470yD/nUe22Dv5vBvxK/UK9UUTVs=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c h1:aqg5Vm5dwtvL+YgDpBcK1ITf3o96N/K7/wsRXQnUTEs=
github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c/go.mod h1:owqhoLW1qZoYLZzLnBw+QkPP9WZnjlSWihhxAJC1+/M=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
@ -531,12 +523,14 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
github.com/tinylib/msgp v1.3.0 h1:ULuf7GPooDaIlbyvgAxBV/FI7ynli6LZ1/nVUNu+0ww=
github.com/tinylib/msgp v1.3.0/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0=
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc= github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/urfave/cli/v2 v2.27.6 h1:VdRdS98FNhKZ8/Az8B7MTyGQmpIr36O1EHybx/LaZ4g= github.com/urfave/cli/v3 v3.3.3 h1:byCBaVdIXuLPIDm5CYZRVG6NvT7tv1ECqdU4YzlEa3I=
github.com/urfave/cli/v2 v2.27.6/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= github.com/urfave/cli/v3 v3.3.3/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZtpYpo=
github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ= github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ=
github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
@ -545,8 +539,6 @@ github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
github.com/yohcop/openid-go v1.0.1 h1:DPRd3iPO5F6O5zX2e62XpVAbPT6wV51cuucH0z9g3js= github.com/yohcop/openid-go v1.0.1 h1:DPRd3iPO5F6O5zX2e62XpVAbPT6wV51cuucH0z9g3js=
@ -564,8 +556,8 @@ github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI=
github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE= github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE=
github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
gitlab.com/gitlab-org/api/client-go v0.126.0 h1:VV5TdkF6pMbEdFGvbR2CwEgJwg6qdg1u3bj5eD2tiWk= gitlab.com/gitlab-org/api/client-go v0.130.1 h1:1xF5C5Zq3sFeNg3PzS2z63oqrxifne3n/OnbI7nptRc=
gitlab.com/gitlab-org/api/client-go v0.126.0/go.mod h1:bYC6fPORKSmtuPRyD9Z2rtbAjE7UeNatu2VWHRf4/LE= gitlab.com/gitlab-org/api/client-go v0.130.1/go.mod h1:ZhSxLAWadqP6J9lMh40IAZOlOxBLPRh7yFOXR/bMJWM=
go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk= go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk=
go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk= go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
@ -573,8 +565,8 @@ go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/mock v0.5.1 h1:ASgazW/qBmR+A32MYFDB6E2POoTgOwT509VP0CT/fjs= go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=
go.uber.org/mock v0.5.1/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
@ -591,8 +583,8 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/image v0.27.0 h1:C8gA4oWU/tKkdCfYT6T2u4faJu3MeNS5O8UPWlPF61w= golang.org/x/image v0.27.0 h1:C8gA4oWU/tKkdCfYT6T2u4faJu3MeNS5O8UPWlPF61w=
@ -603,8 +595,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@ -620,8 +612,8 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -633,8 +625,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -684,10 +676,10 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4= golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
@ -695,8 +687,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU= golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ= golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View file

@ -21,7 +21,7 @@ import (
_ "forgejo.org/modules/markup/markdown" _ "forgejo.org/modules/markup/markdown"
_ "forgejo.org/modules/markup/orgmode" _ "forgejo.org/modules/markup/orgmode"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v3"
) )
// these flags will be set by the build flags // these flags will be set by the build flags

38
manifest.scm Normal file
View file

@ -0,0 +1,38 @@
;;; Copyright 2025 The Forgejo Authors. All rights reserved.
;;; SPDX-License-Identifier: MIT
;;;
;;; Commentary:
;;;
;;; This is a GNU Guix manifest that can be used to create a
;;; development environment to build and test Forgejo.
;;;
;;; The following is a usage example to create a containerized
;;; environment, with HOME shared for the Go cache and the network
;;; made available to fetch required Go and Node dependencies.
;;;
#|
guix shell -CNF --share=$HOME -m manifest.scm
export GOTOOLCHAIN=local # to use the Go binary from Guix
export CC=gcc CGO_ENABLED=1
export TAGS="timetzdata sqlite sqlite_unlock_notify"
make clean
make -j$(nproc)
make test -j$(nproc) # run unit tests
make test-sqlite -j$(nproc) # run integration tests
make watch # run an instance/rebuild on changes
|#
(specifications->manifest
(list "bash-minimal"
"coreutils"
"findutils"
"gcc-toolchain"
"git" ;libpcre support is required
"git-lfs"
"gnupg"
"go"
"grep"
"make"
"node"
"nss-certs"
"openssh"
"sed"))

View file

@ -5,6 +5,7 @@ package actions
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"slices" "slices"
"strings" "strings"
@ -54,6 +55,7 @@ type ActionRun struct {
PreviousDuration time.Duration PreviousDuration time.Duration
Created timeutil.TimeStamp `xorm:"created"` Created timeutil.TimeStamp `xorm:"created"`
Updated timeutil.TimeStamp `xorm:"updated"` Updated timeutil.TimeStamp `xorm:"updated"`
NotifyEmail bool
} }
func init() { func init() {
@ -222,29 +224,38 @@ func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWork
var hasWaiting bool var hasWaiting bool
for _, v := range jobs { for _, v := range jobs {
id, job := v.Job() id, job := v.Job()
needs := job.Needs() status := StatusFailure
payload := []byte{}
needs := []string{}
name := run.Title
runsOn := []string{}
if job != nil {
needs = job.Needs()
if err := v.SetJob(id, job.EraseNeeds()); err != nil { if err := v.SetJob(id, job.EraseNeeds()); err != nil {
return err return err
} }
payload, _ := v.Marshal() payload, _ = v.Marshal()
status := StatusWaiting
if len(needs) > 0 || run.NeedApproval { if len(needs) > 0 || run.NeedApproval {
status = StatusBlocked status = StatusBlocked
} else { } else {
status = StatusWaiting
hasWaiting = true hasWaiting = true
} }
job.Name, _ = util.SplitStringAtByteN(job.Name, 255) name, _ = util.SplitStringAtByteN(job.Name, 255)
runsOn = job.RunsOn()
}
runJobs = append(runJobs, &ActionRunJob{ runJobs = append(runJobs, &ActionRunJob{
RunID: run.ID, RunID: run.ID,
RepoID: run.RepoID, RepoID: run.RepoID,
OwnerID: run.OwnerID, OwnerID: run.OwnerID,
CommitSHA: run.CommitSHA, CommitSHA: run.CommitSHA,
IsForkPullRequest: run.IsForkPullRequest, IsForkPullRequest: run.IsForkPullRequest,
Name: job.Name, Name: name,
WorkflowPayload: payload, WorkflowPayload: payload,
JobID: id, JobID: id,
Needs: needs, Needs: needs,
RunsOn: job.RunsOn(), RunsOn: runsOn,
Status: status, Status: status,
}) })
} }
@ -346,7 +357,7 @@ func UpdateRunWithoutNotification(ctx context.Context, run *ActionRun, cols ...s
return err return err
} }
if affected == 0 { if affected == 0 {
return fmt.Errorf("run has changed") return errors.New("run has changed")
// It's impossible that the run is not found, since Gitea never deletes runs. // It's impossible that the run is not found, since Gitea never deletes runs.
} }

View file

@ -54,6 +54,8 @@ type FindRunJobOptions struct {
CommitSHA string CommitSHA string
Statuses []Status Statuses []Status
UpdatedBefore timeutil.TimeStamp UpdatedBefore timeutil.TimeStamp
Events []string // []webhook_module.HookEventType
RunNumber int64
} }
func (opts FindRunJobOptions) ToConds() builder.Cond { func (opts FindRunJobOptions) ToConds() builder.Cond {
@ -76,5 +78,11 @@ func (opts FindRunJobOptions) ToConds() builder.Cond {
if opts.UpdatedBefore > 0 { if opts.UpdatedBefore > 0 {
cond = cond.And(builder.Lt{"updated": opts.UpdatedBefore}) cond = cond.And(builder.Lt{"updated": opts.UpdatedBefore})
} }
if len(opts.Events) > 0 {
cond = cond.And(builder.In("event", opts.Events))
}
if opts.RunNumber > 0 {
cond = cond.And(builder.Eq{"`index`": opts.RunNumber})
}
return cond return cond
} }

View file

@ -16,6 +16,7 @@ import (
repo_model "forgejo.org/models/repo" repo_model "forgejo.org/models/repo"
"forgejo.org/models/shared/types" "forgejo.org/models/shared/types"
user_model "forgejo.org/models/user" user_model "forgejo.org/models/user"
"forgejo.org/modules/log"
"forgejo.org/modules/optional" "forgejo.org/modules/optional"
"forgejo.org/modules/timeutil" "forgejo.org/modules/timeutil"
"forgejo.org/modules/translation" "forgejo.org/modules/translation"
@ -353,3 +354,53 @@ func FixRunnersWithoutBelongingRepo(ctx context.Context) (int64, error) {
} }
return res.RowsAffected() return res.RowsAffected()
} }
func DeleteOfflineRunners(ctx context.Context, olderThan timeutil.TimeStamp, globalOnly bool) error {
log.Info("Doing: DeleteOfflineRunners")
if olderThan.AsTime().After(timeutil.TimeStampNow().AddDuration(-RunnerOfflineTime).AsTime()) {
return fmt.Errorf("invalid `cron.cleanup_offline_runners.older_than`value: must be at least %q", RunnerOfflineTime)
}
cond := builder.Or(
// never online
builder.And(builder.Eq{"last_online": 0}, builder.Lt{"created": olderThan}),
// was online but offline
builder.And(builder.Gt{"last_online": 0}, builder.Lt{"last_online": olderThan}),
)
if globalOnly {
cond = builder.And(cond, builder.Eq{"owner_id": 0}, builder.Eq{"repo_id": 0})
}
if err := db.Iterate(
ctx,
cond,
func(ctx context.Context, r *ActionRunner) error {
if err := DeleteRunner(ctx, r); err != nil {
return fmt.Errorf("DeleteOfflineRunners: %w", err)
}
lastOnline := r.LastOnline.AsTime()
olderThanTime := olderThan.AsTime()
if !lastOnline.IsZero() && lastOnline.Before(olderThanTime) {
log.Info(
"Deleted runner [ID: %d, Name: %s], last online %s ago",
r.ID, r.Name, olderThanTime.Sub(lastOnline).String(),
)
} else {
log.Info(
"Deleted runner [ID: %d, Name: %s], unused since %s ago",
r.ID, r.Name, olderThanTime.Sub(r.Created.AsTime()).String(),
)
}
return nil
},
); err != nil {
return err
}
log.Info("Finished: DeleteOfflineRunners")
return nil
}

View file

@ -6,10 +6,12 @@ import (
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"testing" "testing"
"time"
auth_model "forgejo.org/models/auth" auth_model "forgejo.org/models/auth"
"forgejo.org/models/db" "forgejo.org/models/db"
"forgejo.org/models/unittest" "forgejo.org/models/unittest"
"forgejo.org/modules/timeutil"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -73,3 +75,68 @@ func TestDeleteRunner(t *testing.T) {
idAsBinary[6], idAsBinary[7]) idAsBinary[6], idAsBinary[7])
assert.Equal(t, idAsHexadecimal, after.UUID[19:]) assert.Equal(t, idAsHexadecimal, after.UUID[19:])
} }
func TestDeleteOfflineRunnersRunnerGlobalOnly(t *testing.T) {
baseTime := time.Date(2024, 5, 19, 7, 40, 32, 0, time.UTC)
timeutil.MockSet(baseTime)
defer timeutil.MockUnset()
require.NoError(t, unittest.PrepareTestDatabase())
olderThan := timeutil.TimeStampNow().Add(-timeutil.Hour)
require.NoError(t, DeleteOfflineRunners(db.DefaultContext, olderThan, true))
// create at test base time
unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: 12345678})
// last_online test base time
unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: 10000001})
// created one month ago but a repo
unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: 10000002})
// last online one hour ago
unittest.AssertNotExistsBean(t, &ActionRunner{ID: 10000003})
// last online 10 seconds ago
unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: 10000004})
// created 1 month ago
unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: 10000005})
// created 1 hour ago
unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: 10000006})
// last online 1 hour ago
unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: 10000007})
}
func TestDeleteOfflineRunnersAll(t *testing.T) {
baseTime := time.Date(2024, 5, 19, 7, 40, 32, 0, time.UTC)
timeutil.MockSet(baseTime)
defer timeutil.MockUnset()
require.NoError(t, unittest.PrepareTestDatabase())
olderThan := timeutil.TimeStampNow().Add(-timeutil.Hour)
require.NoError(t, DeleteOfflineRunners(db.DefaultContext, olderThan, false))
// create at test base time
unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: 12345678})
// last_online test base time
unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: 10000001})
// created one month ago
unittest.AssertNotExistsBean(t, &ActionRunner{ID: 10000002})
// last online one hour ago
unittest.AssertNotExistsBean(t, &ActionRunner{ID: 10000003})
// last online 10 seconds ago
unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: 10000004})
// created 1 month ago
unittest.AssertNotExistsBean(t, &ActionRunner{ID: 10000005})
// created 1 hour ago
unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: 10000006})
// last online 1 hour ago
unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: 10000007})
}
func TestDeleteOfflineRunnersErrorOnInvalidOlderThanValue(t *testing.T) {
baseTime := time.Date(2024, 5, 19, 7, 40, 32, 0, time.UTC)
timeutil.MockSet(baseTime)
defer timeutil.MockUnset()
require.Error(t, DeleteOfflineRunners(db.DefaultContext, timeutil.TimeStampNow(), false))
}

View file

@ -34,6 +34,15 @@ var statusNames = map[Status]string{
StatusBlocked: "blocked", StatusBlocked: "blocked",
} }
var nameToStatus = make(map[string]Status, len(statusNames))
func init() {
// Populate name to status lookup map
for status, name := range statusNames {
nameToStatus[name] = status
}
}
// String returns the string name of the Status // String returns the string name of the Status
func (s Status) String() string { func (s Status) String() string {
return statusNames[s] return statusNames[s]
@ -102,3 +111,8 @@ func (s Status) AsResult() runnerv1.Result {
} }
return runnerv1.Result_RESULT_UNSPECIFIED return runnerv1.Result_RESULT_UNSPECIFIED
} }
func StatusFromString(name string) (Status, bool) {
status, exists := nameToStatus[name]
return status, exists
}

View file

@ -7,6 +7,7 @@ package activities
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"net/url" "net/url"
"path" "path"
@ -441,6 +442,12 @@ func (a *Action) GetIssueContent(ctx context.Context) string {
return a.Issue.Content return a.Issue.Content
} }
func GetActivityByID(ctx context.Context, id int64) (*Action, error) {
var act Action
_, err := db.GetEngine(ctx).ID(id).Get(&act)
return &act, err
}
// GetFeedsOptions options for retrieving feeds // GetFeedsOptions options for retrieving feeds
type GetFeedsOptions struct { type GetFeedsOptions struct {
db.ListOptions db.ListOptions
@ -458,7 +465,7 @@ type GetFeedsOptions struct {
// GetFeeds returns actions according to the provided options // GetFeeds returns actions according to the provided options
func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, int64, error) { func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, int64, error) {
if opts.RequestedUser == nil && opts.RequestedTeam == nil && opts.RequestedRepo == nil { if opts.RequestedUser == nil && opts.RequestedTeam == nil && opts.RequestedRepo == nil {
return nil, 0, fmt.Errorf("need at least one of these filters: RequestedUser, RequestedTeam, RequestedRepo") return nil, 0, errors.New("need at least one of these filters: RequestedUser, RequestedTeam, RequestedRepo")
} }
cond, err := activityQueryCondition(ctx, opts) cond, err := activityQueryCondition(ctx, opts)
@ -466,11 +473,8 @@ func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, int64, err
return nil, 0, err return nil, 0, err
} }
sess := db.GetEngine(ctx).Where(cond).
Select("`action`.*"). // this line will avoid select other joined table's columns
Join("INNER", "repository", "`repository`.id = `action`.repo_id")
opts.SetDefaultValues() opts.SetDefaultValues()
sess := db.GetEngine(ctx).Where(cond)
sess = db.SetSessionPagination(sess, &opts) sess = db.SetSessionPagination(sess, &opts)
actions := make([]*Action, 0, opts.PageSize) actions := make([]*Action, 0, opts.PageSize)
@ -597,13 +601,14 @@ func DeleteOldActions(ctx context.Context, olderThan time.Duration) (err error)
} }
// NotifyWatchers creates batch of actions for every watcher. // NotifyWatchers creates batch of actions for every watcher.
func NotifyWatchers(ctx context.Context, actions ...*Action) error { func NotifyWatchers(ctx context.Context, actions ...*Action) ([]Action, error) {
var watchers []*repo_model.Watch var watchers []*repo_model.Watch
var repo *repo_model.Repository var repo *repo_model.Repository
var err error var err error
var permCode []bool var permCode []bool
var permIssue []bool var permIssue []bool
var permPR []bool var permPR []bool
var out []Action
e := db.GetEngine(ctx) e := db.GetEngine(ctx)
@ -614,14 +619,14 @@ func NotifyWatchers(ctx context.Context, actions ...*Action) error {
// Add feeds for user self and all watchers. // Add feeds for user self and all watchers.
watchers, err = repo_model.GetWatchers(ctx, act.RepoID) watchers, err = repo_model.GetWatchers(ctx, act.RepoID)
if err != nil { if err != nil {
return fmt.Errorf("get watchers: %w", err) return nil, fmt.Errorf("get watchers: %w", err)
} }
// Be aware that optimizing this correctly into the `GetWatchers` SQL // Be aware that optimizing this correctly into the `GetWatchers` SQL
// query is for most cases less performant than doing this. // query is for most cases less performant than doing this.
blockedDoerUserIDs, err := user_model.ListBlockedByUsersID(ctx, act.ActUserID) blockedDoerUserIDs, err := user_model.ListBlockedByUsersID(ctx, act.ActUserID)
if err != nil { if err != nil {
return fmt.Errorf("user_model.ListBlockedByUsersID: %w", err) return nil, fmt.Errorf("user_model.ListBlockedByUsersID: %w", err)
} }
if len(blockedDoerUserIDs) > 0 { if len(blockedDoerUserIDs) > 0 {
@ -636,8 +641,9 @@ func NotifyWatchers(ctx context.Context, actions ...*Action) error {
// Add feed for actioner. // Add feed for actioner.
act.UserID = act.ActUserID act.UserID = act.ActUserID
if _, err = e.Insert(act); err != nil { if _, err = e.Insert(act); err != nil {
return fmt.Errorf("insert new actioner: %w", err) return nil, fmt.Errorf("insert new actioner: %w", err)
} }
out = append(out, *act)
if repoChanged { if repoChanged {
act.loadRepo(ctx) act.loadRepo(ctx)
@ -645,7 +651,7 @@ func NotifyWatchers(ctx context.Context, actions ...*Action) error {
// check repo owner exist. // check repo owner exist.
if err := act.Repo.LoadOwner(ctx); err != nil { if err := act.Repo.LoadOwner(ctx); err != nil {
return fmt.Errorf("can't get repo owner: %w", err) return nil, fmt.Errorf("can't get repo owner: %w", err)
} }
} else if act.Repo == nil { } else if act.Repo == nil {
act.Repo = repo act.Repo = repo
@ -656,7 +662,7 @@ func NotifyWatchers(ctx context.Context, actions ...*Action) error {
act.ID = 0 act.ID = 0
act.UserID = act.Repo.Owner.ID act.UserID = act.Repo.Owner.ID
if err = db.Insert(ctx, act); err != nil { if err = db.Insert(ctx, act); err != nil {
return fmt.Errorf("insert new actioner: %w", err) return nil, fmt.Errorf("insert new actioner: %w", err)
} }
} }
@ -709,26 +715,29 @@ func NotifyWatchers(ctx context.Context, actions ...*Action) error {
} }
if err = db.Insert(ctx, act); err != nil { if err = db.Insert(ctx, act); err != nil {
return fmt.Errorf("insert new action: %w", err) return nil, fmt.Errorf("insert new action: %w", err)
} }
} }
} }
return nil return out, nil
} }
// NotifyWatchersActions creates batch of actions for every watcher. // NotifyWatchersActions creates batch of actions for every watcher.
func NotifyWatchersActions(ctx context.Context, acts []*Action) error { func NotifyWatchersActions(ctx context.Context, acts []*Action) ([]Action, error) {
ctx, committer, err := db.TxContext(ctx) ctx, committer, err := db.TxContext(ctx)
if err != nil { if err != nil {
return err return nil, err
} }
defer committer.Close() defer committer.Close()
var out []Action
for _, act := range acts { for _, act := range acts {
if err := NotifyWatchers(ctx, act); err != nil { as, err := NotifyWatchers(ctx, act)
return err if err != nil {
return nil, err
} }
out = append(out, as...)
} }
return committer.Commit() return out, committer.Commit()
} }
// DeleteIssueActions delete all actions related with issueID // DeleteIssueActions delete all actions related with issueID

View file

@ -197,7 +197,8 @@ func TestNotifyWatchers(t *testing.T) {
RepoID: 1, RepoID: 1,
OpType: activities_model.ActionStarRepo, OpType: activities_model.ActionStarRepo,
} }
require.NoError(t, activities_model.NotifyWatchers(db.DefaultContext, action)) _, err := activities_model.NotifyWatchers(db.DefaultContext, action)
require.NoError(t, err)
// One watchers are inactive, thus action is only created for user 8, 1, 4, 11 // One watchers are inactive, thus action is only created for user 8, 1, 4, 11
unittest.AssertExistsAndLoadBean(t, &activities_model.Action{ unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
@ -226,24 +227,6 @@ func TestNotifyWatchers(t *testing.T) {
}) })
} }
func TestGetFeedsCorrupted(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
ID: 8,
RepoID: 1700,
})
actions, count, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
RequestedUser: user,
Actor: user,
IncludePrivate: true,
})
require.NoError(t, err)
assert.Empty(t, actions)
assert.Equal(t, int64(0), count)
}
func TestConsistencyUpdateAction(t *testing.T) { func TestConsistencyUpdateAction(t *testing.T) {
if !setting.Database.Type.IsSQLite3() { if !setting.Database.Type.IsSQLite3() {
t.Skip("Test is only for SQLite database.") t.Skip("Test is only for SQLite database.")

View file

@ -0,0 +1,106 @@
// Copyright 2024 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package activities
import (
"context"
"fmt"
"forgejo.org/models/db"
user_model "forgejo.org/models/user"
"forgejo.org/modules/json"
"forgejo.org/modules/log"
"forgejo.org/modules/timeutil"
"forgejo.org/modules/validation"
ap "github.com/go-ap/activitypub"
)
type FederatedUserActivity struct {
ID int64 `xorm:"pk autoincr"`
UserID int64 `xorm:"NOT NULL"`
ActorID int64
ActorURI string
Actor *user_model.User `xorm:"-"` // transient
NoteContent string `xorm:"TEXT"`
NoteURL string `xorm:"VARCHAR(255)"`
OriginalNote string `xorm:"TEXT"`
Created timeutil.TimeStamp `xorm:"created"`
}
func init() {
db.RegisterModel(new(FederatedUserActivity))
}
func NewFederatedUserActivity(userID, actorID int64, actorURI, noteContent, noteURL string, originalNote ap.Activity) (FederatedUserActivity, error) {
jsonString, err := json.Marshal(originalNote)
if err != nil {
return FederatedUserActivity{}, err
}
result := FederatedUserActivity{
UserID: userID,
ActorID: actorID,
ActorURI: actorURI,
NoteContent: noteContent,
NoteURL: noteURL,
OriginalNote: string(jsonString),
}
if valid, err := validation.IsValid(result); !valid {
return FederatedUserActivity{}, err
}
return result, nil
}
func (federatedUserActivity FederatedUserActivity) Validate() []string {
var result []string
result = append(result, validation.ValidateNotEmpty(federatedUserActivity.UserID, "UserID")...)
result = append(result, validation.ValidateNotEmpty(federatedUserActivity.ActorID, "ActorID")...)
result = append(result, validation.ValidateNotEmpty(federatedUserActivity.ActorURI, "ActorURI")...)
result = append(result, validation.ValidateNotEmpty(federatedUserActivity.NoteContent, "NoteContent")...)
result = append(result, validation.ValidateNotEmpty(federatedUserActivity.NoteURL, "NoteURL")...)
result = append(result, validation.ValidateNotEmpty(federatedUserActivity.OriginalNote, "OriginalNote")...)
return result
}
func CreateUserActivity(ctx context.Context, federatedUserActivity *FederatedUserActivity) error {
if valid, err := validation.IsValid(federatedUserActivity); !valid {
return err
}
_, err := db.GetEngine(ctx).Insert(federatedUserActivity)
return err
}
type GetFollowingFeedsOptions struct {
db.ListOptions
}
func GetFollowingFeeds(ctx context.Context, actorID int64, opts GetFollowingFeedsOptions) ([]*FederatedUserActivity, int64, error) {
log.Debug("user_id = %s", actorID)
sess := db.GetEngine(ctx).Where("user_id = ?", actorID)
opts.SetDefaultValues()
sess = db.SetSessionPagination(sess, &opts)
actions := make([]*FederatedUserActivity, 0, opts.PageSize)
count, err := sess.FindAndCount(&actions)
if err != nil {
return nil, 0, fmt.Errorf("FindAndCount: %w", err)
}
for _, act := range actions {
if err := act.loadActor(ctx); err != nil {
return nil, 0, err
}
}
return actions, count, err
}
func (federatedUserActivity *FederatedUserActivity) loadActor(ctx context.Context) error {
log.Debug("for activity %s", federatedUserActivity)
actorUser, _, err := user_model.GetFederatedUserByUserID(ctx, federatedUserActivity.ActorID)
if err != nil {
return err
}
federatedUserActivity.Actor = actorUser
return nil
}

View file

@ -0,0 +1,24 @@
// Copyright 2024 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package activities
import (
"testing"
"forgejo.org/modules/validation"
)
func Test_FederatedUserActivityValidation(t *testing.T) {
sut := FederatedUserActivity{}
sut.UserID = 13
sut.ActorID = 33
sut.ActorURI = "33"
sut.NoteContent = "Any content!"
sut.NoteURL = "https://example.org/note/17"
sut.OriginalNote = "federatedUserActivityNote-17"
if res, _ := validation.IsValid(sut); !res {
t.Errorf("sut expected to be valid: %v\n", sut.Validate())
}
}

View file

@ -5,6 +5,7 @@ package asymkey
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"strings" "strings"
"time" "time"
@ -209,7 +210,7 @@ func parseGPGKey(ctx context.Context, ownerID int64, e *openpgp.Entity, verified
// deleteGPGKey does the actual key deletion // deleteGPGKey does the actual key deletion
func deleteGPGKey(ctx context.Context, keyID string) (int64, error) { func deleteGPGKey(ctx context.Context, keyID string) (int64, error) {
if keyID == "" { if keyID == "" {
return 0, fmt.Errorf("empty KeyId forbidden") // Should never happen but just to be sure return 0, errors.New("empty KeyId forbidden") // Should never happen but just to be sure
} }
// Delete imported key // Delete imported key
n, err := db.GetEngine(ctx).Where("key_id=?", keyID).Delete(new(GPGKeyImport)) n, err := db.GetEngine(ctx).Where("key_id=?", keyID).Delete(new(GPGKeyImport))

View file

@ -7,6 +7,7 @@ import (
"bytes" "bytes"
"crypto" "crypto"
"encoding/base64" "encoding/base64"
"errors"
"fmt" "fmt"
"hash" "hash"
"io" "io"
@ -75,7 +76,7 @@ func base64DecPubKey(content string) (*packet.PublicKey, error) {
// Check type // Check type
pkey, ok := p.(*packet.PublicKey) pkey, ok := p.(*packet.PublicKey)
if !ok { if !ok {
return nil, fmt.Errorf("key is not a public key") return nil, errors.New("key is not a public key")
} }
return pkey, nil return pkey, nil
} }
@ -122,15 +123,15 @@ func readArmoredSign(r io.Reader) (body io.Reader, err error) {
func extractSignature(s string) (*packet.Signature, error) { func extractSignature(s string) (*packet.Signature, error) {
r, err := readArmoredSign(strings.NewReader(s)) r, err := readArmoredSign(strings.NewReader(s))
if err != nil { if err != nil {
return nil, fmt.Errorf("Failed to read signature armor") return nil, errors.New("Failed to read signature armor")
} }
p, err := packet.Read(r) p, err := packet.Read(r)
if err != nil { if err != nil {
return nil, fmt.Errorf("Failed to read signature packet") return nil, errors.New("Failed to read signature packet")
} }
sig, ok := p.(*packet.Signature) sig, ok := p.(*packet.Signature)
if !ok { if !ok {
return nil, fmt.Errorf("Packet is not a signature") return nil, errors.New("Packet is not a signature")
} }
return sig, nil return sig, nil
} }

View file

@ -6,6 +6,7 @@ package asymkey
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"hash" "hash"
"strings" "strings"
@ -316,7 +317,7 @@ func verifyWithGPGSettings(ctx context.Context, gpgSettings *git.GPGSettings, si
func verifySign(s *packet.Signature, h hash.Hash, k *GPGKey) error { func verifySign(s *packet.Signature, h hash.Hash, k *GPGKey) error {
// Check if key can sign // Check if key can sign
if !k.CanSign { if !k.CanSign {
return fmt.Errorf("key can not sign") return errors.New("key can not sign")
} }
// Decode key // Decode key
pkey, err := base64DecPubKey(k.Content) pkey, err := base64DecPubKey(k.Content)

View file

@ -10,6 +10,7 @@ import (
"encoding/base64" "encoding/base64"
"encoding/binary" "encoding/binary"
"encoding/pem" "encoding/pem"
"errors"
"fmt" "fmt"
"math/big" "math/big"
"os" "os"
@ -93,7 +94,7 @@ func parseKeyString(content string) (string, error) {
block, _ := pem.Decode([]byte(content)) block, _ := pem.Decode([]byte(content))
if block == nil { if block == nil {
return "", fmt.Errorf("failed to parse PEM block containing the public key") return "", errors.New("failed to parse PEM block containing the public key")
} }
if strings.Contains(block.Type, "PRIVATE") { if strings.Contains(block.Type, "PRIVATE") {
return "", ErrKeyIsPrivate return "", ErrKeyIsPrivate
@ -226,7 +227,7 @@ func SSHNativeParsePublicKey(keyLine string) (string, int, error) {
// The ssh library can parse the key, so next we find out what key exactly we have. // The ssh library can parse the key, so next we find out what key exactly we have.
switch pkeyType { switch pkeyType {
case ssh.KeyAlgoDSA: case ssh.KeyAlgoDSA: //nolint:staticcheck
rawPub := struct { rawPub := struct {
Name string Name string
P, Q, G, Y *big.Int P, Q, G, Y *big.Int

View file

@ -69,6 +69,9 @@ func (l *XORMLogBridge) Warn(v ...any) {
// Warnf show warning log // Warnf show warning log
func (l *XORMLogBridge) Warnf(format string, v ...any) { func (l *XORMLogBridge) Warnf(format string, v ...any) {
if format == "Table %s Column %s db default is %s, struct default is %s" || format == "Table %s Column %s db nullable is %v, struct nullable is %v" {
return
}
l.Log(stackLevel, log.WARN, format, v...) l.Log(stackLevel, log.WARN, format, v...)
} }

View file

@ -414,7 +414,7 @@ func IsErrSHAOrCommitIDNotProvided(err error) bool {
} }
func (err ErrSHAOrCommitIDNotProvided) Error() string { func (err ErrSHAOrCommitIDNotProvided) Error() string {
return "a SHA or commit ID must be proved when updating a file" return "a SHA or commit ID must be provided when updating a file"
} }
// ErrInvalidMergeStyle represents an error if merging with disabled merge strategy // ErrInvalidMergeStyle represents an error if merging with disabled merge strategy

View file

@ -0,0 +1,17 @@
-
id: 1001
run_id: 792
runner_id: 1
repo_id: 4
owner_id: 1
commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
storage_path: "27/5/1730330775594233150.chunk"
file_size: 693147180559
file_compressed_size: 693147180559
content_encoding: "application/zip"
artifact_path: "big-file.zip"
artifact_name: "big-file"
status: 4
created_unix: 1730330775
updated_unix: 1730330775
expired_unix: 1738106775

View file

@ -59,14 +59,6 @@
created_unix: 1603011540 # grouped with id:7 created_unix: 1603011540 # grouped with id:7
- id: 8 - id: 8
user_id: 1
op_type: 12 # close issue
act_user_id: 1
repo_id: 1700 # dangling intentional
is_private: false
created_unix: 1603011541
- id: 9
user_id: 34 user_id: 34
op_type: 12 # close issue op_type: 12 # close issue
act_user_id: 34 act_user_id: 34

View file

@ -471,3 +471,64 @@
need_approval: 0 need_approval: 0
approved_by: 0 approved_by: 0
event_payload: '{"head_commit":{"id":"5f22f7d0d95d614d25a5b68592adb345a4b5c7fd"}}' event_payload: '{"head_commit":{"id":"5f22f7d0d95d614d25a5b68592adb345a4b5c7fd"}}'
# GET action run(s) test
-
id: 892
title: "successful push run"
repo_id: 63
owner_id: 2
workflow_id: "success.yaml"
index: 1
trigger_user_id: 2
ref: "refs/heads/main"
commit_sha: "97f29ee599c373c729132a5c46a046978311e0ee"
event: "push"
is_fork_pull_request: 0
status: 1 # success
started: 1683636528
stopped: 1683636626
created: 1683636108
updated: 1683636626
need_approval: 0
approved_by: 0
-
id: 893
title: "failed pull_request run"
repo_id: 63
owner_id: 2
workflow_id: "failed.yaml"
index: 2
trigger_user_id: 2
ref: "refs/heads/bugfix-1"
commit_sha: "35c5cddfc19397501ec8f4f7bb808a7c8f04445f"
event: "pull_request"
is_fork_pull_request: 0
status: 2 # failure
started: 1683636528
stopped: 1683636626
created: 1683636108
updated: 1683636626
need_approval: 0
approved_by: 0
-
id: 894
title: "running workflow_dispatch run"
repo_id: 63
owner_id: 2
workflow_id: "running.yaml"
index: 3
trigger_user_id: 2
ref: "refs/heads/main"
commit_sha: "97f29ee599c373c729132a5c46a046978311e0ee"
event: "workflow_dispatch"
is_fork_pull_request: 0
status: 6 # running
started: 1683636528
created: 1683636108
updated: 1683636626
need_approval: 0
approved_by: 0

View file

@ -18,3 +18,122 @@
created: 1716104432 created: 1716104432
updated: 1716104432 updated: 1716104432
deleted: ~ deleted: ~
- id: 10000001
uuid: 10d3b248-6460-4bf5-b819-1f5b3109e10f
name: global-online
version: v6.3.1+7-gc4c0ca0
owner_id: 0
repo_id: 0
description: ""
base: 0
repo_range: ""
token_hash: 7e9ed71f64e98ce1f70e94c63f3cb6c41a8cb0b90de3e1daf7ec5c35361d60ed44da67c5ac393b7aaf443dcfc766007dc828
token_salt: WUcgZWl7mW
last_online: 1716104422
last_active: 0
agent_labels: '["docker"]'
created: 1716104431
updated: 1716104422
deleted: ~
- id: 10000002
uuid: 1d188484-dd97-4a70-b707-5e87b578ab6b
name: repo-never-used
version: v6.3.1+7-gc4c0ca0
owner_id: 0
repo_id: 1
description: ""
base: 0
repo_range: ""
token_hash: 51e88c17ac8b54dd101dc2e4f530a71643c703adba7170f4b1a28f1cb483b4cfb107798c521e0532ef3c6480b64518a5c6a5
token_salt: 4rh8ncXYIO
last_online: 0
last_active: 0
agent_labels: '["docker"]'
created: 1713512432
updated: 1713512432
deleted: ~
- id: 10000003
uuid: 7a039c6b-b0b2-4cf5-a93d-715d617f99e2
name: global-offline
version: v6.3.1+7-gc4c0ca0
owner_id: 0
repo_id: 0
description: ""
base: 0
repo_range: ""
token_hash: c76960c56bc6069f0d1648991ec626500abe8c15286f5c355d565c3b5ba945d7d6f1272a6c77849e592528179511b94f5d69
token_salt: TFMe2jhOkB
last_online: 1715499632
last_active: 0
agent_labels: '["docker"]'
created: 1715499632
updated: 1715499632
deleted: ~
- id: 10000004
uuid: 93ca7fdd-faca-4df6-a474-8345263ef10b
name: user-online
version: v6.3.1+7-gc4c0ca0
owner_id: 1
repo_id: 0
description: ""
base: 0
repo_range: ""
token_hash: 6ddf7f0f2301d2b3f66418145dc497a6d09fa6586e659afcb5ae2a0c5b639561d795aff8062537db9df73b396842ea826134
token_salt: QcdGuReAp4
last_online: 1716104422
last_active: 0
agent_labels: '["docker"]'
created: 1716104431
updated: 1716104422
deleted: ~
- id: 10000005
uuid: a8534df6-c4be-40f4-9714-903b69d973d9
name: user-never-used
version: v6.3.1+7-gc4c0ca0
owner_id: 1
repo_id: 0
description: desc
base: 0
repo_range: ""
token_hash: 4441de7defcfc3d21baa608dec66a562cf23307abddaabdbb836907ac5f48c8780c354891916c525b79ec7af8e95be7a09b4
token_salt: ONNqIOnj3t
last_online: 0
last_active: 0
agent_labels: '["docker"]'
created: 1713512433
updated: 1713512433
deleted: ~
- id: 10000006
uuid: e1c5bb6c-de68-4335-8955-5192f76708ac
name: orga-fresh-created
version: v6.3.1+7-gc4c0ca0
owner_id: 35
repo_id: 0
description: ""
base: 0
repo_range: ""
token_hash: a61f9ee48c6847d243ace0a8936efe80af9277c7bc46d6da6e03d1d406608b8023ee66600ad24f0effaa8e3338f92ac97ac9
token_salt: fZJKjrFGWA
last_online: 0
last_active: 0
agent_labels: '["docker"]'
created: 1716100832
updated: 1716100832
deleted: ~
- id: 10000007
uuid: ff755f06-948e-479b-8031-5b3e9f123e32
name: orga-offline
version: v6.3.1+7-gc4c0ca0
owner_id: 35
repo_id: 0
description: ""
base: 0
repo_range: ""
token_hash: 9372efb38f9b64efe65065380abe2f24ef34a59d9619f4cdc08f1151e9849f0b6e722aa10538e8730288de6e2f09acdac695
token_salt: TnU7iiIdCb
last_online: 1716100832
last_active: 0
agent_labels: '["docker"]'
created: 1736085520
updated: 1716100832
deleted: ~

View file

@ -113,3 +113,344 @@
review_id: 22 review_id: 22
assignee_id: 5 assignee_id: 5
created_unix: 946684817 created_unix: 946684817
-
id: 13
type: 29 # push
poster_id: 2
issue_id: 19 # in repo_id 58
content: '{"is_force_push":false,"commit_ids":["4ca8bcaf27e28504df7bf996819665986b01c847","96cef4a7b72b3c208340ae6f0cf55a93e9077c93","c5626fc9eff57eb1bb7b796b01d4d0f2f3f792a2"]}'
created_unix: 1688672373
-
id: 14
type: 29 # push
poster_id: 2
issue_id: 19 # in repo_id 58
content: '{"is_force_push":false,"commit_ids":["23576dd018294e476c06e569b6b0f170d0558705"]}'
created_unix: 1688672374
-
id: 15
type: 29 # push
poster_id: 2
issue_id: 19 # in repo_id 58
content: '{"is_force_push":false,"commit_ids":["3e64625bd6eb5bcba69ac97de6c8f507402df861", "c704db5794097441aa2d9dd834d5b7e2f8f08108"]}'
created_unix: 1688672375
-
id: 16
type: 29 # push
poster_id: 2
issue_id: 19 # in repo_id 58
content: '{"is_force_push":false,"commit_ids":["811d46c7e518f4f180afb862c0db5cb8c80529ce", "747ddb3506a4fa04a7747808eb56ae16f9e933dc", "837d5c8125633d7d258f93b998e867eab0145520", "1978192d98bb1b65e11c2cf37da854fbf94bffd6"]}'
created_unix: 1688672376
-
id: 17
type: 29 # push
poster_id: 2
issue_id: 19 # in repo_id 58
content: '{"is_force_push":true,"commit_ids":["1978192d98bb1b65e11c2cf37da854fbf94bffd6", "9b93963cf6de4dc33f915bb67f192d099c301f43"]}'
created_unix: 1749734240
-
id: 2000
type: 8 # milestone
poster_id: 1
issue_id: 1 # in repo_id 1
milestone_id: 1
old_milestone_id: 0
created_unix: 946684820
-
id: 2001
type: 8 # milestone
poster_id: 1
issue_id: 1 # in repo_id 1
milestone_id: 2
old_milestone_id: 1
created_unix: 946684920
-
id: 2002
type: 8 # milestone
poster_id: 1
issue_id: 1 # in repo_id 1
milestone_id: 0
old_milestone_id: 2
created_unix: 946685020
-
id: 2003
type: 8 # milestone
poster_id: 1
issue_id: 1 # in repo_id 1
milestone_id: 10 # not exsting milestone
old_milestone_id: 0
created_unix: 946685080
-
id: 2010
type: 30 # project
poster_id: 1
issue_id: 1 # in repo_id 1
project_id: 1
old_project_id: 0
created_unix: 946685120
-
id: 2011
type: 30 # project
poster_id: 1
issue_id: 1 # in repo_id 1
project_id: 2
old_project_id: 1
created_unix: 946685220
-
id: 2012
type: 30 # project
poster_id: 1
issue_id: 1 # in repo_id 1
project_id: 0
old_project_id: 2
created_unix: 946685320
-
id: 2013
type: 30 # project
poster_id: 1
issue_id: 1 # in repo_id 1
project_id: 10 # not existing project
old_project_id: 0
created_unix: 946685420
-
id: 2020
type: 7 # label
poster_id: 1
issue_id: 1 # in repo_id 1
label_id: 1
content: 1 # add label
created_unix: 946685520
-
id: 2021
type: 7 # label
poster_id: 1
issue_id: 1
label_id: 2
content: 1 # add label
created_unix: 946685620
-
id: 2022
type: 7 # label
poster_id: 2
issue_id: 1 # in repo_id 1
label_id: 1
content: 0 # remove label
created_unix: 946685720
-
id: 2023
type: 7 # label
poster_id: 1
issue_id: 1 # in repo_id 1
label_id: 1
content: 1 # add label
created_unix: 946685720
-
id: 2024
type: 7 # label
poster_id: 1
issue_id: 1 # in repo_id 1
label_id: 2
content: 0 # remove label
created_unix: 946685720
-
id: 2025
type: 7 # label
poster_id: 2
issue_id: 1 # in repo_id 1
label_id: 2
content: 1 # add label
created_unix: 946685820
-
id: 2026
type: 7 # label
poster_id: 1
issue_id: 1 # in repo_id 1
label_id: 1
content: 0 # remove label
created_unix: 946685920
-
id: 2027
type: 7 # label
poster_id: 1
issue_id: 1 # in repo_id 1
label_id: 2
content: 0 # remove label
created_unix: 946685920
- id: 2040
type: 9 # assignee
poster_id: 1
issue_id: 1 # in repo_id 1
assignee_id: 1 # self
removed_assignee: false # add assignee
created_unix: 946688020
- id: 2041
type: 9 # assignee
poster_id: 2
issue_id: 1 # in repo_id 1
assignee_id: 1
removed_assignee: true # remove assignee
created_unix: 946688120
- id: 2042
type: 9 # assignee
poster_id: 1
issue_id: 1 # in repo_id 1
assignee_id: 2
removed_assignee: false # add assignee
created_unix: 946688220
- id: 2043
type: 9 # assignee
poster_id: 2
issue_id: 1 # in repo_id 1
assignee_id: 2 # self
removed_assignee: true # remove assignee
created_unix: 946688320
- id: 2050
type: 23 # lock
poster_id: 1
issue_id: 1 # in repo_id 1
created_unix: 946688420
- id: 2051
type: 24 # unlock
poster_id: 1
issue_id: 1 # in repo_id 1
created_unix: 946688520
- id: 2052
type: 23 # lock
poster_id: 1
issue_id: 1 # in repo_id 1
content: "Too heated"
created_unix: 946688620
- id: 2053
type: 24 # unlock
poster_id: 1
issue_id: 1 # in repo_id 1
created_unix: 946688720
- id: 2060
type: 36 # pin
poster_id: 1
issue_id: 1 # in repo_id 1
created_unix: 946688820
- id: 2061
type: 37 # unpin
poster_id: 1
issue_id: 1 # in repo_id 1
created_unix: 946688920
- id: 2070
type: 2 # close
poster_id: 1
issue_id: 1 # in repo_id 1
created_unix: 946689020
- id: 2071
type: 1 # reopen
poster_id: 2
issue_id: 1 # in repo_id 1
created_unix: 946689220
- id: 2072
type: 2 # close
poster_id: 1
issue_id: 2 # pull in repo_id 1
created_unix: 946689320
- id: 2073
type: 1 # reopen
poster_id: 2
issue_id: 2 # pull in repo_id 1
created_unix: 946689420
- id: 2080
type: 3 # issue reference
poster_id: 1
issue_id: 1 # issue in repo_id 1
ref_repo_id: 1
ref_issue_id: 5 # issue in repo_id 1
created_unix: 946689500
- id: 2081
type: 3 # issue reference
poster_id: 1
issue_id: 1 # issue in repo_id 1
ref_repo_id: 1
ref_issue_id: 2 # pull in repo_id 1
created_unix: 946689600
- id: 2082
type: 3 # issue reference
poster_id: 1
issue_id: 1 # issue in repo_id 1
ref_repo_id: 32
ref_issue_id: 16 # issue in repo_id 32
created_unix: 946689700
- id: 2083
type: 3 # issue reference
poster_id: 1
issue_id: 1 # issue in repo_id 1
ref_repo_id: 10
ref_issue_id: 8 # pull in repo_id 10
created_unix: 946689800
- id: 2090
type: 6 # pull reference
poster_id: 1
issue_id: 2 # pull in repo_id 1
ref_repo_id: 1
ref_issue_id: 1 # issue in repo_id 1
created_unix: 946689900
- id: 2091
type: 6 # pull reference
poster_id: 1
issue_id: 2 # pull in repo_id 1
ref_repo_id: 1
ref_issue_id: 2 # pull in repo_id 1
created_unix: 946690000
- id: 2092
type: 6 # pull reference
poster_id: 1
issue_id: 2 # pull in repo_id 1
ref_repo_id: 32
ref_issue_id: 16 # issue in repo_id 32
created_unix: 946690050
- id: 2093
type: 6 # pull reference
poster_id: 1
issue_id: 2 # pull in repo_id 1
ref_repo_id: 10
ref_issue_id: 8 # pull in repo_id 10
created_unix: 946690100

View file

@ -17,3 +17,13 @@
id: 4 id: 4
user_id: 31 user_id: 31
follow_id: 33 follow_id: 33
-
id: 5
user_id: 4
follow_id: 8
-
id: 6
user_id: 5
follow_id: 8

View file

@ -0,0 +1 @@
[] # empty

View file

@ -795,3 +795,10 @@
type: 10 type: 10
config: "{}" config: "{}"
created_unix: 946684810 created_unix: 946684810
-
id: 115
repo_id: 63
type: 10
config: "{}"
created_unix: 946684810

Some files were not shown because too many files have changed in this diff Show more