Merge branch 'main' into feature/appstore

This commit is contained in:
panshuxiao
2025-11-25 15:19:03 +08:00
repo.diff.parent 1d6ba90c2f d7798195cb
repo.diff.commit 084ad3a542
repo.diff.stats_desc%!(EXTRA int=65, int=1843, int=239)

repo.diff.view_file

@@ -733,6 +733,7 @@ backend-debug: go-check generate-backend $(EXECUTABLE)-debug
$(EXECUTABLE)-debug: $(GO_SOURCES) $(TAGS_PREREQ)
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) build $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '$(LDFLAGS)' -o $@
.PHONY: frontend
frontend: $(WEBPACK_DEST) ## build frontend files
@@ -969,6 +970,13 @@ docker:
# support also build args docker build --build-arg GITEA_VERSION=v1.2.3 --build-arg TAGS="bindata sqlite sqlite_unlock_notify" .
# This endif closes the if at the top of the file
# 添加一个新目标,用于构建 controller-manager
.PHONY: controller-manager
controller-manager: go-check
@echo "Building controller-manager..."
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) build $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o controller-manager modules/k8s/cmd/controller-manager/controller-manager.go
endif
# Disable parallel execution because it would break some targets that don't

repo.diff.view_file

@@ -32,7 +32,7 @@ if [ ! -f ${GITEA_CUSTOM}/conf/app.ini ]; then
fi
# Substitute the environment variables in the template
APP_NAME=${APP_NAME:-"Gitea: Git with a cup of tea"} \
APP_NAME=${APP_NAME:-"DevStar: The Last Mile of Al for R&D"} \
RUN_MODE=${RUN_MODE:-"prod"} \
DOMAIN=${DOMAIN:-"localhost"} \
SSH_DOMAIN=${SSH_DOMAIN:-"localhost"} \

repo.diff.view_file

@@ -26,7 +26,7 @@ if [ ! -f ${GITEA_APP_INI} ]; then
fi
# Substitute the environment variables in the template
APP_NAME=${APP_NAME:-"Gitea: Git with a cup of tea"} \
APP_NAME=${APP_NAME:-"DevStar: The Last Mile of Al for R&D"} \
RUN_MODE=${RUN_MODE:-"prod"} \
RUN_USER=${USER:-"git"} \
SSH_DOMAIN=${SSH_DOMAIN:-"localhost"} \

repo.diff.view_file

@@ -0,0 +1,280 @@
## DevContainerKubernetes + Istio本地运行
仅保留三点Istio 1.27.1 安装、app.ini 手动配置、安装 Devcontainer CRD内嵌 YAML
### 1) 安装 Istio 1.27.1(指定版本)
```bash
ISTIO_VER=1.27.1
curl -L https://istio.io/downloadIstio | ISTIO_VERSION=${ISTIO_VER} sh -
export PATH="$PWD/istio-${ISTIO_VER}/bin:$PATH"
istioctl x precheck
istioctl install -y --set profile=default
kubectl -n istio-system get svc istio-ingressgateway -o wide
```
说明:本项目 WebTerminal 使用 HTTP/80`istio-ingressgateway` 进入,无需立刻配置 HTTPS。
### 2) 本地 app.ini 手动配置非helm安装的 devstar 不会自动写入)
文件:`devstar/custom/conf/app.ini`
```ini
[devstar.devcontainer]
NAMESPACE = default # 创建的devcontainer所在的命名空间
HOST = 192.168.23.138 # 和[server].DOMAIN一致
```
保存后重启后端以加载配置。
### 3) 安装 Devcontainer CRD内嵌 YAML可直接 apply
可直接复制以下清单,通过标准输入安装:
```bash
kubectl apply -f - <<'YAML'
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.16.1
name: devcontainerapps.devcontainer.devstar.cn
spec:
group: devcontainer.devstar.cn
names:
kind: DevcontainerApp
listKind: DevcontainerAppList
plural: devcontainerapps
singular: devcontainerapp
scope: Namespaced
versions:
- name: v1
schema:
openAPIV3Schema:
description: DevcontainerApp is the Schema for the devcontainerapps API
properties:
apiVersion:
description: |-
APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
type: string
kind:
description: |-
Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
type: string
metadata:
type: object
spec:
description: DevcontainerAppSpec defines the desired state of DevcontainerApp
properties:
failedJobsHistoryLimit:
description: |-
The number of failed finished jobs to retain.
This is a pointer to distinguish between explicit zero and not specified.
format: int32
minimum: 0
type: integer
service:
description: ServiceSpec specifies Service for DevContainer
properties:
extraPorts:
description: ExtraPorts 定义额外的端口配置
items:
description: ExtraPortSpec 定义额外端口配置
properties:
containerPort:
description: ContainerPort 是容器内的端口号
maximum: 65535
minimum: 1
type: integer
name:
description: Name 是端口的名称
type: string
servicePort:
description: ServicePort 是服务暴露的端口号
maximum: 65535
minimum: 1
type: integer
required:
- containerPort
- servicePort
type: object
type: array
nodePort:
maximum: 32767
minimum: 30000
type: integer
servicePort:
minimum: 1
type: integer
type: object
startingDeadlineSeconds:
description: |-
Optional deadline in seconds for starting the job if it misses scheduled
time for any reason. Missed jobs executions will be counted as failed ones.
format: int64
minimum: 0
type: integer
statefulset:
description: StatefulSetSpec specifies StatefulSet for DevContainer
properties:
command:
items:
type: string
type: array
containerPort:
minimum: 1
type: integer
gitRepositoryURL:
type: string
image:
type: string
sshPublicKeyList:
description: 至少包含一个 SSH Public Key 才能通过校验规则
items:
type: string
minItems: 1
type: array
required:
- command
- gitRepositoryURL
- image
- sshPublicKeyList
type: object
successfulJobsHistoryLimit:
description: |-
The number of successful finished jobs to retain.
This is a pointer to distinguish between explicit zero and not specified.
format: int32
minimum: 0
type: integer
suspend:
description: |-
This flag tells the controller to suspend subsequent executions, it does
not apply to already started executions. Defaults to false.
type: boolean
required:
- statefulset
type: object
status:
description: DevcontainerAppStatus defines the observed state of DevcontainerApp
properties:
active:
description: A list of pointers to currently running jobs.
items:
description: ObjectReference contains enough information to let
you inspect or modify the referred object.
properties:
apiVersion:
description: API version of the referent.
type: string
fieldPath:
description: |-
If referring to a piece of an object instead of an entire object, this string
should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2].
For example, if the object reference is to a container within a pod, this would take on a value like:
"spec.containers{name}" (where "name" refers to the name of the container that triggered
the event) or if no container name is specified "spec.containers[2]" (container with
index 2 in this pod). This syntax is chosen only to have some well-defined way of
referencing a part of an object.
type: string
kind:
description: |-
Kind of the referent.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
type: string
name:
description: |-
Name of the referent.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
type: string
namespace:
description: |-
Namespace of the referent.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/
type: string
resourceVersion:
description: |-
Specific resourceVersion to which this reference is made, if any.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency
type: string
uid:
description: |-
UID of the referent.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids
type: string
type: object
x-kubernetes-map-type: atomic
type: array
extraPortsAssigned:
description: ExtraPortsAssigned 存储额外端口映射的 NodePort
items:
description: ExtraPortAssigned 定义已分配的额外端口信息
properties:
containerPort:
description: ContainerPort 是容器内的端口号
type: integer
name:
description: Name 是端口的名称
type: string
nodePort:
description: NodePort 是 Kubernetes 分配的 NodePort
type: integer
servicePort:
description: ServicePort 是服务暴露的端口号
type: integer
required:
- containerPort
- nodePort
- servicePort
type: object
type: array
lastScheduleTime:
description: Information when was the last time the job was successfully
scheduled.
format: date-time
type: string
nodePortAssigned:
description: NodePortAssigned 存储 DevcontainerApp CRD调度后集群分配的 NodePort
type: integer
ready:
description: Ready 标识 DevcontainerApp 管理的 Pod 的 Readiness Probe 是否达到就绪状态
type: boolean
type: object
type: object
served: true
storage: true
subresources:
status: {}
YAML
```
验证 CRD
```bash
kubectl get crd devcontainerapps.devcontainer.devstar.cn -o wide
```
### 4) 编译与运行 controller-manager必需
controller-manager 负责监听 `DevcontainerApp` CR并创建/更新/删除底层 K8s 资源StatefulSet、Service 等),并回写 `Status.Ready` 等状态。仅安装 CRD 不会触发任何实际资源变更,必须运行 controller-manager 才会生效。
- 在主目录编译并运行:
```bash
cd /home/psx/devstar-main
make controller-manager
./controller-manager
# 观察日志:应能看到 Reconcile 日志;创建 DevcontainerApp 后会创建 sts/svc
```

100
go.mod
repo.diff.view_file

@@ -96,6 +96,8 @@ require (
github.com/nektos/act v0.2.63
github.com/niklasfasching/go-org v1.8.0
github.com/olivere/elastic/v7 v7.0.32
github.com/onsi/ginkgo/v2 v2.22.0
github.com/onsi/gomega v1.36.1
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.1.1
github.com/pkg/errors v0.9.1
@@ -107,7 +109,7 @@ require (
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1
github.com/sassoftware/go-rpmutils v0.4.0
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3
github.com/spf13/cobra v1.8.1
github.com/spf13/cobra v1.9.1
github.com/spf13/pflag v1.0.6
github.com/stretchr/testify v1.10.0
github.com/syndtr/goleveldb v1.0.0
@@ -129,52 +131,75 @@ require (
golang.org/x/sync v0.15.0
golang.org/x/sys v0.33.0
golang.org/x/text v0.26.0
google.golang.org/grpc v1.72.0
google.golang.org/grpc v1.72.1
google.golang.org/protobuf v1.36.6
gopkg.in/ini.v1 v1.67.0
gopkg.in/yaml.v3 v3.0.1
istio.io/api v1.26.3
istio.io/client-go v1.26.3
k8s.io/api v0.33.3
k8s.io/apimachinery v0.33.3
k8s.io/client-go v0.33.3
k8s.io/component-base v0.33.3
istio.io/api v1.27.2
istio.io/client-go v1.27.2
k8s.io/api v0.34.1
k8s.io/apimachinery v0.34.1
k8s.io/client-go v0.34.1
k8s.io/component-base v0.34.1
k8s.io/klog/v2 v2.130.1
k8s.io/kubectl v0.34.1
mvdan.cc/xurls/v2 v2.6.0
sigs.k8s.io/controller-runtime v0.21.0
sigs.k8s.io/controller-runtime v0.22.3
sigs.k8s.io/yaml v1.6.0
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251
xorm.io/builder v0.3.13
xorm.io/xorm v1.3.9
)
require (
cel.dev/expr v0.24.0 // indirect
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
github.com/distribution/reference v0.5.0 // indirect
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/emicklei/go-restful/v3 v3.12.2 // indirect
github.com/evanphx/json-patch/v5 v5.9.11 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-logr/zapr v1.3.0 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/google/gnostic-models v0.6.9 // indirect
github.com/google/cel-go v0.26.0 // indirect
github.com/google/gnostic-models v0.7.0 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/moby/spdystream v0.5.0 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
golang.org/x/term v0.32.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
k8s.io/apiextensions-apiserver v0.34.1 // indirect
k8s.io/apiserver v0.34.1 // indirect
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 // indirect
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
)
require (
cel.dev/expr v0.20.0 // indirect
cloud.google.com/go/compute/metadata v0.6.0 // indirect
dario.cat/mergo v1.0.1 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
@@ -187,7 +212,6 @@ require (
github.com/andybalholm/brotli v1.1.1 // indirect
github.com/andybalholm/cascadia v1.3.3 // indirect
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
github.com/aws/aws-sdk-go-v2 v1.36.3 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect
@@ -195,7 +219,6 @@ require (
github.com/aymerick/douceur v0.2.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bits-and-blooms/bitset v1.22.0 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/blevesearch/bleve_index_api v1.2.8 // indirect
github.com/blevesearch/geo v0.2.0 // indirect
github.com/blevesearch/go-faiss v1.0.25 // indirect
@@ -217,7 +240,6 @@ require (
github.com/boombuler/barcode v1.0.2 // indirect
github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf // indirect
github.com/caddyserver/zerossl v0.1.3 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/clbanning/mxj/v2 v2.7.0 // indirect
@@ -225,7 +247,7 @@ require (
github.com/couchbase/go-couchbase v0.1.1 // indirect
github.com/couchbase/gomemcached v0.3.3 // indirect
github.com/couchbase/goutils v0.1.2 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/davidmz/go-pageant v1.0.2 // indirect
@@ -233,10 +255,8 @@ require (
github.com/dlclark/regexp2 v1.11.5 // indirect
github.com/docker/docker v24.0.9+incompatible
github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 // indirect
github.com/evanphx/json-patch/v5 v5.9.11 // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fxamacker/cbor/v2 v2.8.0 // indirect
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1 // indirect
github.com/go-ap/errors v0.0.0-20250409143711-5686c11ae650 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect
@@ -244,7 +264,6 @@ require (
github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-ini/ini v1.67.0 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-webauthn/x v0.1.20 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
@@ -254,19 +273,16 @@ require (
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/snappy v1.0.0 // indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/cel-go v0.23.2 // indirect
github.com/google/flatbuffers v25.2.10+incompatible // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/go-tpm v0.9.3 // indirect
github.com/gorilla/css v1.0.1 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/gorilla/securecookie v1.1.2 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
@@ -282,9 +298,8 @@ require (
github.com/minio/crc64nvme v1.0.1 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 // indirect
github.com/mschoch/smat v0.2.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
@@ -315,16 +330,16 @@ require (
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
github.com/zeebo/assert v1.3.0 // indirect
github.com/zeebo/blake3 v0.2.4 // indirect
go.etcd.io/bbolt v1.4.0 // indirect
go.etcd.io/bbolt v1.4.2 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect
go.opentelemetry.io/otel v1.34.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 // indirect
go.opentelemetry.io/otel/metric v1.34.0 // indirect
go.opentelemetry.io/otel v1.35.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 // indirect
go.opentelemetry.io/otel/metric v1.35.0 // indirect
go.opentelemetry.io/otel/sdk v1.34.0 // indirect
go.opentelemetry.io/otel/trace v1.34.0 // indirect
go.opentelemetry.io/proto/otlp v1.4.0 // indirect
go.opentelemetry.io/otel/trace v1.35.0 // indirect
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
@@ -332,14 +347,9 @@ require (
golang.org/x/mod v0.25.0 // indirect
golang.org/x/time v0.11.0 // indirect
golang.org/x/tools v0.33.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250422160041-2d3770c4ea7f // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
k8s.io/apiextensions-apiserver v0.33.0 // indirect
k8s.io/apiserver v0.33.0 // indirect
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 // indirect
)
replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1
@@ -361,3 +371,7 @@ exclude github.com/gofrs/uuid v4.0.0+incompatible
exclude github.com/goccy/go-json v0.4.11
exclude github.com/satori/go.uuid v1.2.0
replace github.com/docker/distribution => github.com/distribution/distribution v2.8.3+incompatible
replace github.com/distribution/reference => github.com/distribution/reference v0.5.0

144
go.sum
repo.diff.view_file

@@ -1,5 +1,5 @@
cel.dev/expr v0.20.0 h1:OunBvVCfvpWlt4dN7zg3FM6TDkzOePe1+foGJ9AXeeI=
cel.dev/expr v0.20.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY=
cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
code.gitea.io/actions-proto-go v0.4.1 h1:l0EYhjsgpUe/1VABo2eK7zcoNX2W44WOnb0MSLrKfls=
@@ -233,9 +233,8 @@ github.com/couchbase/goutils v0.1.2 h1:gWr8B6XNWPIhfalHNog3qQKfGiYyh4K4VhO3P2o9B
github.com/couchbase/goutils v0.1.2/go.mod h1:h89Ek/tiOxxqjz30nPPlwZdQbdB8BwgnuBxeoUe/ViE=
github.com/couchbase/moss v0.1.0/go.mod h1:9MaHIaRuy9pvLPUJxB8sh8OrLfyDczECVL37grCIubs=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
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/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=
github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
@@ -251,6 +250,8 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dimiro1/reply v0.0.0-20200315094148-d0136a4c9e21 h1:PdsjTl0Cg+ZJgOx/CFV5NNgO1ThTreqdgKYiDCMHJwA=
github.com/dimiro1/reply v0.0.0-20200315094148-d0136a4c9e21/go.mod h1:xJvkyD6Y2rZapGvPJLYo9dyx1s5dxBEDPa8T3YTuOk0=
github.com/distribution/distribution v2.8.3+incompatible h1:RlpEXBLq/WPXYvBYMDAmBX/SnhD67qwtvW/DzKc8pAo=
github.com/distribution/distribution v2.8.3+incompatible/go.mod h1:EgLm2NgWtdKgzF9NpMzUKgzmR7AMmb0VQi2B+ZzDRjc=
github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0=
github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/djherbis/buffer v1.1.0/go.mod h1:VwN8VdFkMY0DCALdY8o00d3IZ6Amz/UNVMWcSaJT44o=
@@ -263,8 +264,6 @@ github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55k
github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ=
github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v24.0.9+incompatible h1:HPGzNmwfLZWdxHqK9/II92pyi1EpYKsAqcl4G0Of9v0=
github.com/docker/docker v24.0.9+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
@@ -289,8 +288,8 @@ github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTe
github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 h1:oP4q0fw+fOSWn3DfFi4EXdT+B+gTtzx8GC9xsc26Znk=
github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U=
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=
github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/ethantkoenig/rupture v1.0.1 h1:6aAXghmvtnngMgQzy7SMGdicMvkV86V4n9fT0meE5E4=
@@ -311,8 +310,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/fxamacker/cbor/v2 v2.8.0 h1:fFtUGXUzXPHTIUdne5+zzMPTfffl3RD5qYnkY40vtxU=
github.com/fxamacker/cbor/v2 v2.8.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1 h1:mtDjlmloH7ytdblogrMz1/8Hqua1y8B4ID+bh3rvod0=
github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1/go.mod h1:fenKRzpXDjNpsIBhuhUzvjCKlDjKam0boRAenTE0Q6A=
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
@@ -379,7 +378,6 @@ github.com/go-redsync/redsync/v4 v4.13.0 h1:49X6GJfnbLGaIpBBREM/zA4uIMDXKAh1NDkv
github.com/go-redsync/redsync/v4 v4.13.0/go.mod h1:HMW4Q224GZQz6x1Xc7040Yfgacukdzu7ifTDAKiyErQ=
github.com/go-sql-driver/mysql v1.9.2 h1:4cNKDYQ1I84SXslGddlsrMhc8k4LeDVj6Ad6WRjiHuU=
github.com/go-sql-driver/mysql v1.9.2/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
@@ -432,19 +430,18 @@ github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws
github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE=
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/cel-go v0.23.2 h1:UdEe3CvQh3Nv+E/j9r1Y//WO0K0cSyD7/y0bzyLIMI4=
github.com/google/cel-go v0.23.2/go.mod h1:52Pb6QsDbC5kvgxvZhiL9QX1oZEkcUF/ZqaPx1J5Wwo=
github.com/google/cel-go v0.26.0 h1:DPGjXackMpJWH680oGY4lZhYjIameYmR+/6RBdDGmaI=
github.com/google/cel-go v0.26.0/go.mod h1:A9O8OU9rdvrK5MQyrqfIxo1a0u4g3sF8KB6PUIaryMM=
github.com/google/flatbuffers v24.3.25+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
github.com/google/flatbuffers v25.2.10+incompatible h1:F3vclr7C3HpB1k9mxCGRMXq6FdUalZ6H/pNX4FP1v0Q=
github.com/google/flatbuffers v25.2.10+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw=
github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw=
github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo=
github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
@@ -484,8 +481,10 @@ github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pw
github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ=
github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 h1:TmHmbvxPmaegwhDubVz0lICL0J5Ka2vwTzhoePEXsGE=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0/go.mod h1:qztMSjm835F2bXf+5HKAPIS5qsmQDqZna/PgVt4rWtI=
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@@ -609,13 +608,16 @@ github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
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/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU=
github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI=
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 h1:j2kD3MT1z4PXCiUllUJF9mWUESr9TWKS7iEKsQ/IipM=
@@ -627,6 +629,8 @@ github.com/msteinert/pam v1.2.0 h1:mYfjlvN2KYs2Pb9G6nb/1f/nPfAttT/Jee5Sq9r3bGE=
github.com/msteinert/pam v1.2.0/go.mod h1:d2n0DCUK8rGecChV3JzvmsDjOY4R7AYbsNxAT+ftQl0=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/niklasfasching/go-org v1.8.0 h1:WyGLaajLLp8JbQzkmapZ1y0MOzKuKV47HkZRloi+HGY=
github.com/niklasfasching/go-org v1.8.0/go.mod h1:e2A9zJs7cdONrEGs3gvxCcaAEpwwPNPG7csDpXckMNg=
github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
@@ -731,11 +735,10 @@ github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:s
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
@@ -823,28 +826,28 @@ github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l
gitlab.com/gitlab-org/api/client-go v0.127.0 h1:8xnxcNKGF2gDazEoMs+hOZfOspSSw8D0vAoWhQk9U+U=
gitlab.com/gitlab-org/api/client-go v0.127.0/go.mod h1:bYC6fPORKSmtuPRyD9Z2rtbAjE7UeNatu2VWHRf4/LE=
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
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.2 h1:IrUHp260R8c+zYx/Tm8QZr04CX+qWS5PGfPdevhdm1I=
go.etcd.io/bbolt v1.4.2/go.mod h1:Is8rSHO/b4f3XigBC0lL0+4FwAQv3HXEEIgFMuKHceM=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q=
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 h1:Vh5HayB/0HHfOQA7Ctx69E/Y/DcQSMPpKANYVMQ7fBA=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0/go.mod h1:cpgtDBaqD/6ok/UG0jT15/uKjAY8mRA53diogHBg3UI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 h1:5pojmb1U1AogINhN3SurB+zm/nIcusopeBNp42f45QM=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0/go.mod h1:57gTHJSE5S1tqg+EKsLPlTWhpHMsWlVmer+LA926XiA=
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE=
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk=
go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
go.opentelemetry.io/proto/otlp v1.4.0 h1:TA9WRvW6zMwP+Ssb6fLoUIuirti1gGbP28GcKG1jgeg=
go.opentelemetry.io/proto/otlp v1.4.0/go.mod h1:PPBWZIP98o2ElSqI35IHfu7hIhSwvc5N38Jw8pXuGFY=
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
@@ -856,6 +859,10 @@ go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
go.uber.org/zap/exp v0.3.0 h1:6JYzdifzYkGmTdRR59oYH+Ng7k49H9qVpWwNSsGJj3U=
go.uber.org/zap/exp v0.3.0/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ=
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@@ -1009,8 +1016,8 @@ google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463 h1:
google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463/go.mod h1:U90ffi8eUL9MwPcrJylN5+Mk2v3vuPDptd5yyNUiRR8=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250422160041-2d3770c4ea7f h1:N/PrbTw4kdkqNRzVfWPrBekzLuarFREcbFOiOLkXon4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250422160041-2d3770c4ea7f/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/grpc v1.72.0 h1:S7UkcVa60b5AAQTaO6ZKamFp1zMZSU0fGDK2WZLbBnM=
google.golang.org/grpc v1.72.0/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA=
google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -1049,28 +1056,30 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
istio.io/api v1.26.3 h1:/TiA7bJi24yBQSgpLy5vHhFkobf4DWS1L+CuUxNk4os=
istio.io/api v1.26.3/go.mod h1:DTVGH6CLXj5W8FF9JUD3Tis78iRgT1WeuAnxfTz21Wg=
istio.io/client-go v1.26.3 h1:ryF4+Nyz5wDO4mVCzXcm2W+fqbnekY88Z36hTcv5fnw=
istio.io/client-go v1.26.3/go.mod h1:u2p5L7UvjNswrrlHZ+QMlUOjERK2sXputywzyNhtTMg=
k8s.io/api v0.33.3 h1:SRd5t//hhkI1buzxb288fy2xvjubstenEKL9K51KBI8=
k8s.io/api v0.33.3/go.mod h1:01Y/iLUjNBM3TAvypct7DIj0M0NIZc+PzAHCIo0CYGE=
k8s.io/apiextensions-apiserver v0.33.0 h1:d2qpYL7Mngbsc1taA4IjJPRJ9ilnsXIrndH+r9IimOs=
k8s.io/apiextensions-apiserver v0.33.0/go.mod h1:VeJ8u9dEEN+tbETo+lFkwaaZPg6uFKLGj5vyNEwwSzc=
k8s.io/apimachinery v0.33.3 h1:4ZSrmNa0c/ZpZJhAgRdcsFcZOw1PQU1bALVQ0B3I5LA=
k8s.io/apimachinery v0.33.3/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
k8s.io/apiserver v0.33.0 h1:QqcM6c+qEEjkOODHppFXRiw/cE2zP85704YrQ9YaBbc=
k8s.io/apiserver v0.33.0/go.mod h1:EixYOit0YTxt8zrO2kBU7ixAtxFce9gKGq367nFmqI8=
k8s.io/client-go v0.33.3 h1:M5AfDnKfYmVJif92ngN532gFqakcGi6RvaOF16efrpA=
k8s.io/client-go v0.33.3/go.mod h1:luqKBQggEf3shbxHY4uVENAxrDISLOarxpTKMiUuujg=
k8s.io/component-base v0.33.3 h1:mlAuyJqyPlKZM7FyaoM/LcunZaaY353RXiOd2+B5tGA=
k8s.io/component-base v0.33.3/go.mod h1:ktBVsBzkI3imDuxYXmVxZ2zxJnYTZ4HAsVj9iF09qp4=
istio.io/api v1.27.2 h1:t0m2EAT+LWGA/jSvsyxEhGQoIQYdXD5sJG7tQ9OtQk0=
istio.io/api v1.27.2/go.mod h1:DTVGH6CLXj5W8FF9JUD3Tis78iRgT1WeuAnxfTz21Wg=
istio.io/client-go v1.27.2 h1:4IsF7UAdV5Yg0iq6ONyWZpjFr3z2ahkIbLWyzOHCAwA=
istio.io/client-go v1.27.2/go.mod h1:zgT5R1USl6rwYK1eb2kisPuiji05TQJE7CQHU253iAg=
k8s.io/api v0.34.1 h1:jC+153630BMdlFukegoEL8E/yT7aLyQkIVuwhmwDgJM=
k8s.io/api v0.34.1/go.mod h1:SB80FxFtXn5/gwzCoN6QCtPD7Vbu5w2n1S0J5gFfTYk=
k8s.io/apiextensions-apiserver v0.34.1 h1:NNPBva8FNAPt1iSVwIE0FsdrVriRXMsaWFMqJbII2CI=
k8s.io/apiextensions-apiserver v0.34.1/go.mod h1:hP9Rld3zF5Ay2Of3BeEpLAToP+l4s5UlxiHfqRaRcMc=
k8s.io/apimachinery v0.34.1 h1:dTlxFls/eikpJxmAC7MVE8oOeP1zryV7iRyIjB0gky4=
k8s.io/apimachinery v0.34.1/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw=
k8s.io/apiserver v0.34.1 h1:U3JBGdgANK3dfFcyknWde1G6X1F4bg7PXuvlqt8lITA=
k8s.io/apiserver v0.34.1/go.mod h1:eOOc9nrVqlBI1AFCvVzsob0OxtPZUCPiUJL45JOTBG0=
k8s.io/client-go v0.34.1 h1:ZUPJKgXsnKwVwmKKdPfw4tB58+7/Ik3CrjOEhsiZ7mY=
k8s.io/client-go v0.34.1/go.mod h1:kA8v0FP+tk6sZA0yKLRG67LWjqufAoSHA2xVGKw9Of8=
k8s.io/component-base v0.34.1 h1:v7xFgG+ONhytZNFpIz5/kecwD+sUhVE6HU7qQUiRM4A=
k8s.io/component-base v0.34.1/go.mod h1:mknCpLlTSKHzAQJJnnHVKqjxR7gBeHRv0rPXA7gdtQ0=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4=
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8=
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA=
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts=
k8s.io/kubectl v0.34.1 h1:1qP1oqT5Xc93K+H8J7ecpBjaz511gan89KO9Vbsh/OI=
k8s.io/kubectl v0.34.1/go.mod h1:JRYlhJpGPyk3dEmJ+BuBiOB9/dAvnrALJEiY/C5qa6A=
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y=
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw=
@@ -1097,17 +1106,16 @@ pgregory.net/rapid v0.4.2 h1:lsi9jhvZTYvzVpeG93WWgimPRmiJQfGFRNTEZh1dtY0=
pgregory.net/rapid v0.4.2/go.mod h1:UYpPVyjFHzYBGHIxLFoupi8vwk6rXNzRY9OMvVxFIOU=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 h1:jpcvIRr3GLoUoEKRkHKSmGjxb6lWwrBlJsXc+eUYQHM=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw=
sigs.k8s.io/controller-runtime v0.21.0 h1:CYfjpEuicjUecRk+KAeyYh+ouUBn4llGyDYytIGcJS8=
sigs.k8s.io/controller-runtime v0.21.0/go.mod h1:OSg14+F65eWqIu4DceX7k/+QRAbTTvxeQSNSOQpukWM=
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=
sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/controller-runtime v0.22.3 h1:I7mfqz/a/WdmDCEnXmSPm8/b/yRTy6JsKKENTijTq8Y=
sigs.k8s.io/controller-runtime v0.22.3/go.mod h1:+QX1XUpTXN4mLoblf4tqr5CQcyHPAki2HLXqQMY6vh8=
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v4 v4.6.0 h1:IUA9nvMmnKWcj5jl84xn+T5MnlZKThmUW1TdblaLVAc=
sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251 h1:mUcz5b3FJbP5Cvdq7Khzn6J9OCUQJaBwgBkCR+MOwSs=
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251/go.mod h1:FJGmPh3vz9jSos1L/F91iAgnC/aejc0wIIrF2ZwJxdY=
xorm.io/builder v0.3.13 h1:a3jmiVVL19psGeXx8GIurTp7p0IIgqeDmwhcR6BAOAo=

repo.diff.view_file

@@ -125,6 +125,7 @@ type User struct {
AllowImportLocal bool // Allow migrate repository by local path
AllowCreateOrganization bool `xorm:"DEFAULT true"`
AllowCreateDevcontainer bool `xorm:"DEFAULT false"`
AllowCreateActRunner bool `xorm:"DEFAULT false"`
// true: the user is not allowed to log in Web UI. Git/SSH access could still be allowed (please refer to Git/SSH access related code/documents)
ProhibitLogin bool `xorm:"NOT NULL DEFAULT false"`
@@ -274,6 +275,11 @@ func (u *User) CanCreateDevcontainer() bool {
return u.AllowCreateDevcontainer
}
// CanCreateActrunner returns true if user can create organisation.
func (u *User) CanCreateActrunner() bool {
return u.AllowCreateActRunner
}
// CanEditGitHook returns true if user can edit Git hooks.
func (u *User) CanEditGitHook() bool {
return !setting.DisableGitHooks && (u.IsAdmin || u.AllowGitHook)
@@ -640,6 +646,7 @@ type CreateUserOverwriteOptions struct {
Visibility *structs.VisibleType
AllowCreateOrganization optional.Option[bool]
AllowCreateDevcontainer optional.Option[bool]
AllowCreateActRunner optional.Option[bool]
EmailNotificationsPreference *string
MaxRepoCreation *int
Theme *string
@@ -668,6 +675,7 @@ func createUser(ctx context.Context, u *User, meta *Meta, createdByAdmin bool, o
u.Visibility = setting.Service.DefaultUserVisibilityMode
u.AllowCreateOrganization = setting.Service.DefaultAllowCreateOrganization && !setting.Admin.DisableRegularOrgCreation
u.AllowCreateDevcontainer = setting.Service.DefaultAllowCreateDevcontainer
u.AllowCreateActRunner = setting.Service.DefaultAllowCreateActRunner
u.EmailNotificationsPreference = setting.Admin.DefaultEmailNotification
u.MaxRepoCreation = -1
u.Theme = setting.UI.DefaultTheme

repo.diff.view_file

@@ -59,6 +59,7 @@ func NewActionsUser() *User {
Type: UserTypeBot,
AllowCreateOrganization: true,
AllowCreateDevcontainer: false,
AllowCreateActRunner: false,
Visibility: structs.VisibleTypePublic,
}
}

repo.diff.view_file

@@ -1,10 +1,3 @@
/*
* Copyright (c) Mengning Software. 2025. All rights reserved.
* Authors: DevStar Team, panshuxiao
* Create: 2025-11-19
* Description: Application CRD Go type definitions.
*/
/*
Copyright 2025.

repo.diff.view_file

@@ -0,0 +1,84 @@
/*
Copyright 2024.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package devcontainer
import (
"context"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
devcontainerv1 "code.gitea.io/gitea/modules/k8s/api/devcontainer/v1"
)
var _ = Describe("DevcontainerApp Controller", func() {
Context("When reconciling a resource", func() {
const resourceName = "test-resource"
ctx := context.Background()
typeNamespacedName := types.NamespacedName{
Name: resourceName,
Namespace: "default", // TODO(user):Modify as needed
}
devcontainerapp := &devcontainerv1.DevcontainerApp{}
BeforeEach(func() {
By("creating the custom resource for the Kind DevcontainerApp")
err := k8sClient.Get(ctx, typeNamespacedName, devcontainerapp)
if err != nil && errors.IsNotFound(err) {
resource := &devcontainerv1.DevcontainerApp{
ObjectMeta: metav1.ObjectMeta{
Name: resourceName,
Namespace: "default",
},
// TODO(user): Specify other spec details if needed.
}
Expect(k8sClient.Create(ctx, resource)).To(Succeed())
}
})
AfterEach(func() {
// TODO(user): Cleanup logic after each test, like removing the resource instance.
resource := &devcontainerv1.DevcontainerApp{}
err := k8sClient.Get(ctx, typeNamespacedName, resource)
Expect(err).NotTo(HaveOccurred())
By("Cleanup the specific resource instance DevcontainerApp")
Expect(k8sClient.Delete(ctx, resource)).To(Succeed())
})
It("should successfully reconcile the resource", func() {
By("Reconciling the created resource")
controllerReconciler := &DevcontainerAppReconciler{
Client: k8sClient,
Scheme: k8sClient.Scheme(),
}
_, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
NamespacedName: typeNamespacedName,
})
Expect(err).NotTo(HaveOccurred())
// TODO(user): Add more specific assertions depending on your controller's reconciliation logic.
// Example: If you expect a certain status condition after reconciliation, verify it here.
})
})
})

repo.diff.view_file

@@ -0,0 +1,96 @@
/*
Copyright 2024.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package devcontainer
import (
"context"
"fmt"
"path/filepath"
"runtime"
"testing"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/envtest"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
devcontainerv1 "code.gitea.io/gitea/modules/k8s/api/devcontainer/v1"
// +kubebuilder:scaffold:imports
)
// These tests use Ginkgo (BDD-style Go testing framework). Refer to
// http://onsi.github.io/ginkgo/ to learn more about Ginkgo.
var cfg *rest.Config
var k8sClient client.Client
var testEnv *envtest.Environment
var ctx context.Context
var cancel context.CancelFunc
func TestControllers(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Controller Suite")
}
var _ = BeforeSuite(func() {
logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))
ctx, cancel = context.WithCancel(context.TODO())
By("bootstrapping test environment")
testEnv = &envtest.Environment{
CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")},
ErrorIfCRDPathMissing: true,
// The BinaryAssetsDirectory is only required if you want to run the tests directly
// without call the makefile target test. If not informed it will look for the
// default path defined in controller-runtime which is /usr/local/kubebuilder/.
// Note that you must have the required binaries setup under the bin directory to perform
// the tests directly. When we run make test it will be setup and used automatically.
BinaryAssetsDirectory: filepath.Join("..", "..", "bin", "k8s",
fmt.Sprintf("1.31.0-%s-%s", runtime.GOOS, runtime.GOARCH)),
}
var err error
// cfg is defined in this file globally.
cfg, err = testEnv.Start()
Expect(err).NotTo(HaveOccurred())
Expect(cfg).NotTo(BeNil())
err = devcontainerv1.AddToScheme(scheme.Scheme)
Expect(err).NotTo(HaveOccurred())
// +kubebuilder:scaffold:scheme
k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
Expect(err).NotTo(HaveOccurred())
Expect(k8sClient).NotTo(BeNil())
})
var _ = AfterSuite(func() {
By("tearing down the test environment")
cancel()
err := testEnv.Stop()
Expect(err).NotTo(HaveOccurred())
})

repo.diff.view_file

@@ -0,0 +1,10 @@
/*
* Please refer to the LICENSE file in the root directory of the project.
*/
package options
// Options 包含所有控制器可能需要的选项
type Options struct {
// 可以根据实际需求扩展更多选项
}

repo.diff.view_file

@@ -17,9 +17,7 @@ import (
"code.gitea.io/gitea/modules/setting"
k8s_api_v1 "code.gitea.io/gitea/modules/k8s/api/devcontainer/v1"
devcontainer_errors "code.gitea.io/gitea/modules/k8s/errors"
devcontainer_k8s_agent_modules_errors "code.gitea.io/gitea/modules/k8s/errors"
k8sErrors "code.gitea.io/gitea/modules/k8s/errors"
apimachinery_api_metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
apimachinery_apis_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
apimachinery_apis_v1_unstructured "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -181,7 +179,7 @@ func GetDevcontainer(ctx context.Context, client dynamic_client.Interface, opts
// 0. 检查参数
if ctx == nil || opts == nil || len(opts.Namespace) == 0 || len(opts.Name) == 0 {
return nil, devcontainer_errors.ErrIllegalDevcontainerParameters{
return nil, k8sErrors.ErrIllegalDevcontainerParameters{
FieldList: []string{"ctx", "opts", "opts.Name", "opts.Namespace"},
Message: "cannot be nil",
}
@@ -190,7 +188,7 @@ func GetDevcontainer(ctx context.Context, client dynamic_client.Interface, opts
// 1. 获取 k8s CRD 资源 DevcontainerApp
devcontainerUnstructured, err := client.Resource(groupVersionResource).Namespace(opts.Namespace).Get(ctx, opts.Name, opts.GetOptions)
if err != nil {
return nil, devcontainer_errors.ErrOperateDevcontainer{
return nil, k8sErrors.ErrOperateDevcontainer{
Action: "Get DevcontainerApp thru k8s API Server",
Message: err.Error(),
}
@@ -200,7 +198,7 @@ func GetDevcontainer(ctx context.Context, client dynamic_client.Interface, opts
devcontainerApp := &k8s_api_v1.DevcontainerApp{}
err = apimachinery_runtime_utils.DefaultUnstructuredConverter.FromUnstructured(devcontainerUnstructured.Object, &devcontainerApp)
if err != nil {
return nil, devcontainer_errors.ErrOperateDevcontainer{
return nil, k8sErrors.ErrOperateDevcontainer{
Action: "Convert k8s API Server unstructured response into DevcontainerApp",
Message: err.Error(),
}
@@ -210,7 +208,7 @@ func GetDevcontainer(ctx context.Context, client dynamic_client.Interface, opts
if !IsK8sDevcontainerStatusReady(&devcontainerApp.Status) {
// 3.1 检查 Wait 参数,若用户不需要阻塞式等待,直接返回 “DevContainer 未就绪” 错误
if opts.Wait == false {
return nil, devcontainer_errors.ErrK8sDevcontainerNotReady{
return nil, k8sErrors.ErrK8sDevcontainerNotReady{
Name: opts.Name,
Namespace: opts.Namespace,
Wait: opts.Wait,
@@ -220,7 +218,7 @@ func GetDevcontainer(ctx context.Context, client dynamic_client.Interface, opts
// 3.2 执行阻塞式等待
devcontainerStatusVO, err := waitUntilDevcontainerReadyWithTimeout(ctx, client, opts)
if err != nil {
return nil, devcontainer_errors.ErrOperateDevcontainer{
return nil, k8sErrors.ErrOperateDevcontainer{
Action: "wait for k8s DevContainer to be ready",
Message: err.Error(),
}
@@ -238,21 +236,21 @@ func waitUntilDevcontainerReadyWithTimeout(ctx context.Context, client dynamic_c
// 0. 检查参数
if ctx == nil || client == nil || opts == nil || len(opts.Name) == 0 || len(opts.Namespace) == 0 {
return nil, devcontainer_errors.ErrIllegalDevcontainerParameters{
return nil, k8sErrors.ErrIllegalDevcontainerParameters{
FieldList: []string{"ctx", "client", "opts", "opts.Name", "opts.Namespace"},
Message: "could not be nil",
}
}
// 1. 注册 watcher 监听 DevContainer Status 变化
watcherTimeoutSeconds := setting.DevContainerConfig.TimeoutSeconds
watcherTimeoutSeconds := int64(120)
watcher, err := client.Resource(groupVersionResource).Namespace(opts.Namespace).Watch(ctx, apimachinery_apis_v1.ListOptions{
FieldSelector: fmt.Sprintf("metadata.name=%s", opts.Name),
Watch: true,
TimeoutSeconds: &watcherTimeoutSeconds,
})
if err != nil {
return nil, devcontainer_errors.ErrOperateDevcontainer{
return nil, k8sErrors.ErrOperateDevcontainer{
Action: "register watcher of DevContainer Readiness",
Message: err.Error(),
}
@@ -288,7 +286,7 @@ func waitUntilDevcontainerReadyWithTimeout(ctx context.Context, client dynamic_c
// 2.3 监听 DevcontainerApp ERROR 事件,返回报错信息
apimachineryApiMetav1Status, ok := event.Object.(*apimachinery_api_metav1.Status)
if !ok {
return nil, devcontainer_errors.ErrOperateDevcontainer{
return nil, k8sErrors.ErrOperateDevcontainer{
Action: fmt.Sprintf("wait for Devcontainer '%s' in namespace '%s' to be ready", opts.Name, opts.Namespace),
Message: fmt.Sprintf("An error occurred in k8s CRD DevcontainerApp Watcher: \n"+
" Code: %v (status = %v)\n"+
@@ -303,7 +301,7 @@ func waitUntilDevcontainerReadyWithTimeout(ctx context.Context, client dynamic_c
}
case apimachinery_watch.Deleted:
// 2.4 监听 DevcontainerApp DELETED 事件,返回报错信息
return nil, devcontainer_errors.ErrOperateDevcontainer{
return nil, k8sErrors.ErrOperateDevcontainer{
Action: fmt.Sprintf("Open DevContainer '%s' in namespace '%s'", opts.Name, opts.Namespace),
Message: fmt.Sprintf("'%s' of Kind DevcontainerApp has been Deleted", opts.Name),
}
@@ -311,7 +309,7 @@ func waitUntilDevcontainerReadyWithTimeout(ctx context.Context, client dynamic_c
}
// 3. k8s CRD DevcontainerApp Watcher 超时关闭处理:直接返回超时错误
return nil, devcontainer_errors.ErrOpenDevcontainerTimeout{
return nil, k8sErrors.ErrOpenDevcontainerTimeout{
Name: opts.Name,
Namespace: opts.Namespace,
TimeoutSeconds: setting.DevContainerConfig.TimeoutSeconds,
@@ -357,7 +355,7 @@ func CreateDevcontainer(ctx context.Context, client dynamic_client.Interface, op
jsonData, err := json.Marshal(devcontainerApp)
if err != nil {
log.Error("Failed to marshal DevcontainerApp to JSON: %v", err)
return nil, devcontainer_k8s_agent_modules_errors.ErrOperateDevcontainer{
return nil, k8sErrors.ErrOperateDevcontainer{
Action: "Marshal JSON",
Message: err.Error(),
}
@@ -371,7 +369,7 @@ func CreateDevcontainer(ctx context.Context, client dynamic_client.Interface, op
err = unstructuredObj.UnmarshalJSON(jsonData)
if err != nil {
log.Error("Failed to unmarshal JSON to Unstructured: %v", err)
return nil, devcontainer_k8s_agent_modules_errors.ErrOperateDevcontainer{
return nil, k8sErrors.ErrOperateDevcontainer{
Action: "Unmarshal JSON to Unstructured",
Message: err.Error(),
}
@@ -386,7 +384,7 @@ func CreateDevcontainer(ctx context.Context, client dynamic_client.Interface, op
result, err := client.Resource(groupVersionResource).Namespace(opts.Namespace).Create(ctx, unstructuredObj, opts.CreateOptions)
if err != nil {
log.Error("Failed to create DevcontainerApp: %v", err)
return nil, devcontainer_k8s_agent_modules_errors.ErrOperateDevcontainer{
return nil, k8sErrors.ErrOperateDevcontainer{
Action: "create DevContainer via Dynamic Client",
Message: err.Error(),
}
@@ -398,7 +396,7 @@ func CreateDevcontainer(ctx context.Context, client dynamic_client.Interface, op
resultJSON, err := result.MarshalJSON()
if err != nil {
log.Error("Failed to marshal result to JSON: %v", err)
return nil, devcontainer_k8s_agent_modules_errors.ErrOperateDevcontainer{
return nil, k8sErrors.ErrOperateDevcontainer{
Action: "Marshal result JSON",
Message: err.Error(),
}
@@ -407,7 +405,7 @@ func CreateDevcontainer(ctx context.Context, client dynamic_client.Interface, op
createdDevcontainer := &k8s_api_v1.DevcontainerApp{}
if err := json.Unmarshal(resultJSON, createdDevcontainer); err != nil {
log.Error("Failed to unmarshal result JSON: %v", err)
return nil, devcontainer_k8s_agent_modules_errors.ErrOperateDevcontainer{
return nil, k8sErrors.ErrOperateDevcontainer{
Action: "Unmarshal result JSON",
Message: err.Error(),
}
@@ -418,7 +416,7 @@ func CreateDevcontainer(ctx context.Context, client dynamic_client.Interface, op
func DeleteDevcontainer(ctx context.Context, client dynamic_client.Interface, opts *DeleteDevcontainerOptions) error {
if ctx == nil || opts == nil || len(opts.Namespace) == 0 || len(opts.Name) == 0 {
return devcontainer_errors.ErrIllegalDevcontainerParameters{
return k8sErrors.ErrIllegalDevcontainerParameters{
FieldList: []string{"ctx", "opts", "opts.Name", "opts.Namespace"},
Message: "cannot be nil",
}
@@ -427,7 +425,7 @@ func DeleteDevcontainer(ctx context.Context, client dynamic_client.Interface, op
err := client.Resource(groupVersionResource).Namespace(opts.Namespace).Delete(ctx, opts.Name, opts.DeleteOptions)
if err != nil {
log.Warn("Failed to delete DevcontainerApp '%s' in namespace '%s': %s", opts.Name, opts.Namespace, err.Error())
return devcontainer_errors.ErrOperateDevcontainer{
return k8sErrors.ErrOperateDevcontainer{
Action: fmt.Sprintf("delete devcontainer '%s' in namespace '%s'", opts.Name, opts.Namespace),
Message: err.Error(),
}
@@ -439,7 +437,7 @@ func DeleteDevcontainer(ctx context.Context, client dynamic_client.Interface, op
func ListDevcontainers(ctx context.Context, client dynamic_client.Interface, opts *ListDevcontainersOptions) (*k8s_api_v1.DevcontainerAppList, error) {
if ctx == nil || opts == nil || len(opts.Namespace) == 0 {
return nil, devcontainer_errors.ErrIllegalDevcontainerParameters{
return nil, k8sErrors.ErrIllegalDevcontainerParameters{
FieldList: []string{"ctx", "namespace"},
Message: "cannot be empty",
}
@@ -447,7 +445,7 @@ func ListDevcontainers(ctx context.Context, client dynamic_client.Interface, opt
list, err := client.Resource(groupVersionResource).Namespace(opts.Namespace).List(ctx, opts.ListOptions)
if err != nil {
return nil, devcontainer_errors.ErrOperateDevcontainer{
return nil, k8sErrors.ErrOperateDevcontainer{
Action: fmt.Sprintf("List Devcontainer in namespace '%s'", opts.Namespace),
Message: err.Error(),
}
@@ -455,14 +453,14 @@ func ListDevcontainers(ctx context.Context, client dynamic_client.Interface, opt
// JSON 反序列化为 DevcontainerAppList
jsonData, err := list.MarshalJSON()
if err != nil {
return nil, devcontainer_errors.ErrOperateDevcontainer{
return nil, k8sErrors.ErrOperateDevcontainer{
Action: "verify JSON data of Devcontainer List",
Message: err.Error(),
}
}
devcontainerList := &k8s_api_v1.DevcontainerAppList{}
if err := json.Unmarshal(jsonData, devcontainerList); err != nil {
return nil, devcontainer_errors.ErrOperateDevcontainer{
return nil, k8sErrors.ErrOperateDevcontainer{
Action: "deserialize Devcontainer List data",
Message: err.Error(),
}

repo.diff.view_file

@@ -72,6 +72,7 @@ var Service = struct {
DefaultKeepEmailPrivate bool
DefaultAllowCreateOrganization bool
DefaultAllowCreateDevcontainer bool
DefaultAllowCreateActRunner bool
DefaultUserIsRestricted bool
EnableTimetracking bool
DefaultEnableTimetracking bool
@@ -207,6 +208,7 @@ func loadServiceFrom(rootCfg ConfigProvider) {
Service.DefaultKeepEmailPrivate = sec.Key("DEFAULT_KEEP_EMAIL_PRIVATE").MustBool()
Service.DefaultAllowCreateOrganization = sec.Key("DEFAULT_ALLOW_CREATE_ORGANIZATION").MustBool(true)
Service.DefaultAllowCreateDevcontainer = sec.Key("DEFAULT_ALLOW_CREATE_DEVCONTAINER").MustBool(true)
Service.DefaultAllowCreateActRunner = sec.Key("DEFAULT_ALLOW_CREATE_ACTRUNNER").MustBool(false)
Service.DefaultUserIsRestricted = sec.Key("DEFAULT_USER_IS_RESTRICTED").MustBool(false)
Service.EnableTimetracking = sec.Key("ENABLE_TIMETRACKING").MustBool(true)
if Service.EnableTimetracking {

repo.diff.view_file

@@ -55,6 +55,7 @@ type EditUserOption struct {
ProhibitLogin *bool `json:"prohibit_login"`
AllowCreateOrganization *bool `json:"allow_create_organization"`
AllowCreateDevcontainer *bool `json:"allow_create_devcontainer"`
AllowCreateActRunner *bool `json:"allow_create_actrunner"`
Restricted *bool `json:"restricted"`
Visibility string `json:"visibility" binding:"In(,public,limited,private)"`
}

repo.diff.view_file

@@ -245,8 +245,8 @@ license_desc = Go get <a target="_blank" rel="noopener noreferrer" href="%[1]s">
install=Instalace
installing_desc=Probíhá instalace, čekejte prosím...
title=Výchozí konfigurace
docker_helper=Pokud spouštíte Gitea v Dockeru, přečtěte si <a target="_blank" rel="noopener noreferrer" href="%s">dokumentaci</a>, než budete měnit jakákoliv nastavení.
require_db_desc=Gitea requires MySQL, PostgreSQL, MSSQL, SQLite3 or TiDB (MySQL protocol).
k8s_helper=Pokud spouštíte DevStar v Kubernetesu, přečtěte si <a target="_blank" rel="noopener noreferrer" href="%s">dokumentaci</a>, než budete měnit jakákoliv nastavení.
require_db_desc=DevStar requires MySQL, PostgreSQL, MSSQL, SQLite3 or TiDB (MySQL protocol).
db_title=Nastavení databáze
db_type=Typ databáze
host=Hostitel
@@ -351,7 +351,7 @@ password_algorithm=Hash algoritmus hesla
invalid_password_algorithm=Neplatný algoritmus hash hesla
password_algorithm_helper=Nastavte algoritmus hashování hesla. Algoritmy mají odlišné požadavky a sílu. Algoritmus argon2 je poměrně bezpečný, ale používá spoustu paměti a může být nevhodný pro malé systémy.
enable_update_checker=Povolit kontrolu aktualizací
enable_update_checker_helper=Kontroluje vydání nových verzí pravidelně připojením ke gitea.io.
enable_update_checker_helper=Kontroluje vydání nových verzí pravidelně připojením ke devstar.cn.
env_config_keys=Konfigurace prostředí
env_config_keys_prompt=Následující proměnné prostředí budou také použity pro váš konfigurační soubor:
config_write_file_prompt=Tyto možnosti konfigurace budou zapsány do: %s

repo.diff.view_file

@@ -251,8 +251,8 @@ license_desc = Go get <a target="_blank" rel="noopener noreferrer" href="%[1]s">
install=Installation
installing_desc=Wird jetzt installiert, bitte warten...
title=Erstkonfiguration
docker_helper=Wenn du Gitea in einem Docker-Container nutzt, lies bitte die <a target="_blank" rel="noopener noreferrer" href="%s">Dokumentation</a>, bevor du irgendwelche Einstellungen veränderst.
require_db_desc=Gitea benötigt MySQL, PostgreSQL, MSSQL, SQLite3 oder TiDB (MySQL-Protokoll).
k8s_helper=Wenn du DevStar in einem Kubernetes-Container nutzt, lies bitte die <a target="_blank" rel="noopener noreferrer" href="%s">Dokumentation</a>, bevor du irgendwelche Einstellungen veränderst.
require_db_desc=DevStar benötigt MySQL, PostgreSQL, MSSQL, SQLite3 oder TiDB (MySQL-Protokoll).
db_title=Datenbankeinstellungen
db_type=Datenbanktyp
host=Host
@@ -357,7 +357,7 @@ password_algorithm=Passwort Hashing Algorithmus
invalid_password_algorithm=Ungültiger Passwort-Hash-Algorithmus
password_algorithm_helper=Lege einen Passwort-Hashing-Algorithmus fest. Algorithmen haben unterschiedliche Anforderungen und Stärken. Der argon2-Algorithmus ist ziemlich sicher, aber er verbraucht viel Speicher und kann für kleine Systeme ungeeignet sein.
enable_update_checker=Aktualisierungsprüfung aktivieren
enable_update_checker_helper=Stellt regelmäßig eine Verbindung zu gitea.io her, um nach neuen Versionen zu prüfen.
enable_update_checker_helper=Stellt regelmäßig eine Verbindung zu devstar.cn her, um nach neuen Versionen zu prüfen.
env_config_keys=Umgebungskonfiguration
env_config_keys_prompt=Die folgenden Umgebungsvariablen werden auch auf Ihre Konfigurationsdatei angewendet:
config_write_file_prompt=Diese Konfigurationsoptionen werden in %s geschrieben

repo.diff.view_file

@@ -203,8 +203,8 @@ license_desc = Go get <a target="_blank" rel="noopener noreferrer" href="%[1]s">
[install]
install=Εγκατάσταση
title=Αρχικές Ρυθμίσεις
docker_helper=Αν εκτελέσετε το Gitea μέσα στο Docker, παρακαλώ διαβάστε την <a target="_blank" rel="noopener noreferrer" href="%s">τεκμηρίωση</a> πριν αλλάξετε τις ρυθμίσεις.
require_db_desc=Το Gitea απαιτεί MySQL, PostgreSQL, MSSQL, SQLite3 ή TiDB (με πρωτόκολλο MySQL).
k8s_helper=Αν εκτελέσετε το DevStar μέσα στο Kubernetes, παρακαλώ διαβάστε την <a target="_blank" rel="noopener noreferrer" href="%s">τεκμηρίωση</a> πριν αλλάξετε τις ρυθμίσεις.
require_db_desc=Το DevStar απαιτεί MySQL, PostgreSQL, MSSQL, SQLite3 ή TiDB (με πρωτόκολλο MySQL).
db_title=Ρυθμίσεις Βάσης Δεδομένων
db_type=Τύπος της Βάσης Δεδομένων
host=Διακομιστής
@@ -308,7 +308,7 @@ password_algorithm=Αλγόριθμος Hash Κωδικού Πρόσβασης
invalid_password_algorithm=Μη έγκυρος αλγόριθμος κωδικού πρόσβασης
password_algorithm_helper=Ορίστε τον αλγόριθμο κατακερματισμού για το κωδικό πρόσβασης. Οι αλγόριθμοι διαφέρουν σε απαιτήσεις και αντοχή. Ο αλγόριθμος argon2 είναι αρκετά ασφαλής, αλλά χρησιμοποιεί πολλή μνήμη και μπορεί να είναι ακατάλληλος για μικρά συστήματα.
enable_update_checker=Ενεργοποίηση Ελεγκτή Ενημερώσεων
enable_update_checker_helper=Ελέγχει περιοδικά για νέες εκδόσεις κάνοντας σύνδεση στο gitea.io.
enable_update_checker_helper=Ελέγχει περιοδικά για νέες εκδόσεις κάνοντας σύνδεση στο devstar.cn.
env_config_keys=Ρυθμίσεις Περιβάλλοντος
env_config_keys_prompt=Οι ακόλουθες μεταβλητές περιβάλλοντος θα εφαρμοστούν επίσης στο αρχείο ρυθμίσεων σας:

repo.diff.view_file

@@ -256,8 +256,8 @@ license_desc = Go get <a target="_blank" rel="noopener noreferrer" href="%[1]s">
install = Installation
installing_desc = Installing now, please wait...
title = Initial Configuration
docker_helper = If you run Gitea inside Docker, please read the <a target="_blank" rel="noopener noreferrer" href="%s">documentation</a> before changing any settings.
require_db_desc = Gitea requires MySQL, PostgreSQL, MSSQL, SQLite3 or TiDB (MySQL protocol).
k8s_helper = If you run DevStar inside Kubernetes, please read the <a target="_blank" rel="noopener noreferrer" href="%s">documentation</a> before changing any settings.
require_db_desc = DevStar requires MySQL, PostgreSQL, MSSQL, SQLite3 or TiDB (MySQL protocol).
db_title = Database Settings
db_type = Database Type
host = Host
@@ -363,8 +363,10 @@ default_keep_email_private = Hide Email Addresses by Default
default_keep_email_private_popup = Hide email addresses of new user accounts by default.
default_allow_create_organization = Allow Creation of Organizations by Default
default_allow_create_devcontainer = Allow Creation of DevContainers by Default
default_allow_create_actrunner = Allow Creation of ActionRunners by Default
default_allow_create_organization_popup = Allow new user accounts to create organizations by default.
default_allow_create_devcontainer_popup = Allow new user accounts to create devcontainers by default.
default_allow_create_actrunner_popup = Allow new user accounts to create ActionRunner by default.
default_enable_timetracking = Enable Time Tracking by Default
default_enable_timetracking_popup = Enable time tracking for new repositories by default.
no_reply_address = Hidden Email Domain
@@ -373,7 +375,7 @@ password_algorithm = Password Hash Algorithm
invalid_password_algorithm = Invalid password hash algorithm
password_algorithm_helper = Set the password hashing algorithm. Algorithms have differing requirements and strength. The argon2 algorithm is rather secure but uses a lot of memory and may be inappropriate for small systems.
enable_update_checker = Enable Update Checker
enable_update_checker_helper = Checks for new version releases periodically by connecting to gitea.io.
enable_update_checker_helper = Checks for new version releases periodically by connecting to devstar.cn
env_config_keys = Environment Configuration
env_config_keys_prompt = The following environment variables will also be applied to your configuration file:
config_write_file_prompt = These configuration options will be written into: %s
@@ -3163,6 +3165,7 @@ users.allow_git_hook_tooltip = Git Hooks are executed as the OS user running Git
users.allow_import_local = May Import Local Repositories
users.allow_create_organization = May Create Organizations
users.allow_create_devcontainer= May Create Devcontainers
users.allow_create_actrunner= May Create ActRunners
users.update_profile = Update User Account
users.delete_account = Delete User Account
users.cannot_delete_self = "You cannot delete yourself"
@@ -3881,7 +3884,7 @@ status.blocked = "Blocked"
runners = Runners
runners.runner_manage_panel = Runners Management
runners.new = Create new Runner
runners.new_notice = How to start a runner
runners.new_notice = How to start a runner manually
runners.status = Status
runners.id = ID
runners.name = Name

repo.diff.view_file

@@ -201,8 +201,8 @@ license_desc = Go get <a target="_blank" rel="noopener noreferrer" href="%[1]s">
[install]
install=Instalación
title=Configuración inicial
docker_helper=Si está ejecutando Gitea dentro de un contenedor Docker, por favor lea la <a target="_blank" rel="noopener noreferrer" href="%s">documentación</a> antes de realizar cambios en la configuración.
require_db_desc=Gitea requiere una base de datos MySQL, PostgreSQL, MSSQL, SQLite3 o TiDB (usar el protocolo MySQL).
k8s_helper=Si está ejecutando DevStar dentro de un contenedor Kubernetes, por favor lea la <a target="_blank" rel="noopener noreferrer" href="%s">documentación</a> antes de realizar cambios en la configuración.
require_db_desc=DevStar requiere una base de datos MySQL, PostgreSQL, MSSQL, SQLite3 o TiDB (usar el protocolo MySQL).
db_title=Configuración de base de datos
db_type=Tipo de base de datos
host=Servidor
@@ -306,7 +306,7 @@ password_algorithm=Algoritmo Hash de Contraseña
invalid_password_algorithm=Algoritmo hash de contraseña no válido
password_algorithm_helper=Establece el algoritmo de hashing de contraseña. Los algoritmos tienen diferentes requisitos y fuerza. El algoritmo argon2 es bastante seguro, pero usa mucha memoria y puede ser inapropiado para sistemas pequeños.
enable_update_checker=Activar comprobador de actualizaciones
enable_update_checker_helper=Comprueba el lanzamiento de nuevas versiones periódicamente en gitea.io.
enable_update_checker_helper=Comprueba el lanzamiento de nuevas versiones periódicamente en devstar.cn.
env_config_keys=Configuración del entorno
env_config_keys_prompt=Las siguientes variables de entorno también se aplicarán a su archivo de configuración:

repo.diff.view_file

@@ -134,7 +134,7 @@ license_desc = Go get <a target="_blank" rel="noopener noreferrer" href="%[1]s">
[install]
install=نصب و راه اندازی
title=تنظیمات اولیه
docker_helper=اگر گیتی را با داکر اجرا کرده‌اید، لطفا قبل از هر تغییری <a target="_blank" rel="noopener noreferrer" href="%s">مستندات</a> را مطالعه نمایید.
k8s_helper=اگر گیتی را با داکر اجرا کرده‌اید، لطفا قبل از هر تغییری <a target="_blank" rel="noopener noreferrer" href="%s">مستندات</a> را مطالعه نمایید.
db_title=تنظیمات پایگاه داده
db_type=نوع پایگاه داده
host=میزبان

repo.diff.view_file

@@ -152,8 +152,8 @@ license_desc = Go get <a target="_blank" rel="noopener noreferrer" href="%[1]s">
[install]
install=Asennus
title=Alkuperäiset asetukset
docker_helper=Jos ajat Giteaa Dockerin sisällä, lue <a target="_blank" rel="noopener noreferrer" href="%s">ohjeet</a> ennen minkään asetuksen muuttamista.
require_db_desc=Gitea tarvitsee toimiakseen MySQL, PostgreSQL, MSSQL, SQLite3 tai TiDB (MySQL protokolla) tietokannan.
k8s_helper=Jos ajat DevStara Kubernetesin sisällä, lue <a target="_blank" rel="noopener noreferrer" href="%s">ohjeet</a> ennen minkään asetuksen muuttamista.
require_db_desc=DevStar tarvitsee toimiakseen MySQL, PostgreSQL, MSSQL, SQLite3 tai TiDB (MySQL protokolla) tietokannan.
db_title=Tietokanta asetukset
db_type=Tietokanta tyyppi
host=Isäntä

repo.diff.view_file

@@ -254,8 +254,8 @@ license_desc = Go get <a target="_blank" rel="noopener noreferrer" href="%[1]s">
install=Installation
installing_desc=Installation en cours, veuillez patienter…
title=Configuration initiale
docker_helper=Si vous exécutez Gitea dans Docker, veuillez lire la <a target="_blank" rel="noopener noreferrer" href="%s">documentation</a> avant de modifier les paramètres.
require_db_desc=Gitea nécessite MySQL, PostgreSQL, MSSQL, SQLite3 ou TiDB (avec le protocole MySQL).
k8s_helper=Si vous exécutez DevStar dans Kubernetes, veuillez lire la <a target="_blank" rel="noopener noreferrer" href="%s">documentation</a> avant de modifier les paramètres.
require_db_desc=DevStar nécessite MySQL, PostgreSQL, MSSQL, SQLite3 ou TiDB (avec le protocole MySQL).
db_title=Paramètres de la base de données
db_type=Type de base de données
host=Hôte
@@ -360,7 +360,7 @@ password_algorithm=Algorithme de hachage du mot de passe
invalid_password_algorithm=Algorithme de hachage du mot de passe invalide
password_algorithm_helper=Définissez lalgorithme de hachage du mot de passe. Les algorithmes ont des exigences matérielles et une résistance différentes. Lalgorithme argon2 est bien sécurisé mais utilise beaucoup de mémoire et peut être inapproprié pour les systèmes limités en ressources.
enable_update_checker=Activer la vérification des mises-à-jour
enable_update_checker_helper=Vérifie les mises à jour régulièrement en se connectant à gitea.io.
enable_update_checker_helper=Vérifie les mises à jour régulièrement en se connectant à devstar.cn.
env_config_keys=Configuration de l'environnement
env_config_keys_prompt=Les variables d'environnement suivantes seront également ajoutées à votre fichier de configuration :
config_write_file_prompt=Ces options de configuration seront écrites dans : %s

repo.diff.view_file

@@ -254,7 +254,7 @@ license_desc = Go get <a target="_blank" rel="noopener noreferrer" href="%[1]s">
install=Suiteáil
installing_desc=Suiteáil anois, fan go fóill...
title=Cumraíocht Tosaigh
docker_helper=Má ritheann tú Gitea taobh istigh de Docker, léigh an <a target="_blank" rel="noopener noreferrer" href="%s">doiciméadúchán</a> roimh aon socruithe a athrú.
k8s_helper=Má ritheann tú DevStar taobh istigh de Kubernetes, léigh an <a target="_blank" rel="noopener noreferrer" href="%s">doiciméadúchán</a> roimh aon socruithe a athrú.
require_db_desc=Éilíonn Gitea MySQL, PostgreSQL, MSSQL, SQLite3 nó TiDB (prótacal MySQL).
db_title=Socruithe Bunachar Sonraí
db_type=Cineál Bunachar Sonraí
@@ -360,7 +360,7 @@ password_algorithm=Algartam Hais Pasfhocal
invalid_password_algorithm=Algartam hais pasfhocail neamhbhailí
password_algorithm_helper=Socraigh an algartam hashing pasfhocal. Tá riachtanais agus neart éagsúla ag halgartaim. Tá an algartam argon2 sách slán ach úsáideann sé go leor cuimhne agus d'fhéadfadh sé a bheith míchuí do chórais bheaga.
enable_update_checker=Cumasaigh Seiceoir Nuashonraithe
enable_update_checker_helper=Seiceálacha ar eisiúintí leagan nua go tréimhsiúil trí nascadh le gitea.io.
enable_update_checker_helper=Seiceálacha ar eisiúintí leagan nua go tréimhsiúil trí nascadh le devstar.cn.
env_config_keys=Cumraíocht Comhshaoil
env_config_keys_prompt=Cuirfear na hathróga comhshaoil seo a leanas i bhfeidhm ar do chomhad cumraíochta freisin:
config_write_file_prompt=Scríobhfar na roghanna cumraíochta seo isteach: %s

repo.diff.view_file

@@ -123,7 +123,7 @@ license_desc = Go get <a target="_blank" rel="noopener noreferrer" href="%[1]s">
[install]
install=Telepítés
title=Kezdeti konfiguráció
docker_helper=Ha ön a Gitea-t Docker-ből futtatja, kérem olvassa el a <a target="_blank" rel="noopener noreferrer" href="%s">dokumentációt</a> a beállítások megváltoztatása előtt.
k8s_helper=Ha ön a DevStar-t Kubernetes-ből futtatja, kérem olvassa el a <a target="_blank" rel="noopener noreferrer" href="%s">dokumentációt</a> a beállítások megváltoztatása előtt.
db_title=Adatbázis beállítások
db_type=Adatbázis típusa
host=Kiszolgáló

repo.diff.view_file

@@ -148,8 +148,8 @@ license_desc = Go get <a target="_blank" rel="noopener noreferrer" href="%[1]s">
[install]
install=Uppsetning
title=Upphafleg Uppsetning
docker_helper=Ef þú keyrir Gitea inni í Docker þá viltu vinsamlegast lesa <a target="_blank" rel="noopener noreferrer" href="%s">leiðbeiningaritið</a> áður en þú breytir stillingum.
require_db_desc=Gitea krefst MySQL, PostgreSQL, MSSQL, SQLite3 eða TiDB (MySQL samskiptareglur).
k8s_helper=Ef þú keyrir DevStar inni í Kubernetes þá viltu vinsamlegast lesa <a target="_blank" rel="noopener noreferrer" href="%s">leiðbeiningaritið</a> áður en þú breytir stillingum.
require_db_desc=DevStar krefst MySQL, PostgreSQL, MSSQL, SQLite3 eða TiDB (MySQL samskiptareglur).
db_title=Gagnagrunnsstillingar
db_type=Tegund Gagnagrunns
host=Hýsill

repo.diff.view_file

@@ -154,8 +154,8 @@ license_desc = Go get <a target="_blank" rel="noopener noreferrer" href="%[1]s">
[install]
install=Installazione
title=Configurazione Iniziale
docker_helper=Se stai usando Gitea con Docker, leggi <a target="_blank" rel="noopener noreferrer" href="%s">la documentazione</a> prima di cambiare qualsiasi impostazione.
require_db_desc=Gitea requires MySQL, PostgreSQL, MSSQL, SQLite3 or TiDB (MySQL protocol).
k8s_helper=Se stai usando DevStar con Kubernetes, leggi <a target="_blank" rel="noopener noreferrer" href="%s">la documentazione</a> prima di cambiare qualsiasi impostazione.
require_db_desc=DevStar requires MySQL, PostgreSQL, MSSQL, SQLite3 or TiDB (MySQL protocol).
db_title=Impostazioni Database
db_type=Tipo di database
host=Host

repo.diff.view_file

@@ -251,8 +251,8 @@ license_desc = Go get <a target="_blank" rel="noopener noreferrer" href="%[1]s">
install=インストール
installing_desc=インストール中です、お待ちください...
title=初期設定
docker_helper=GiteaをDocker内で実行する場合は、設定を変更する前に<a target="_blank" rel="noopener noreferrer" href="%s">ドキュメント</a>を読んでください。
require_db_desc=Giteaには、MySQL、PostgreSQL、MSSQL、SQLite3、またはTiDB(MySQL プロトコル) が必要です。
k8s_helper=DevStarをKubernetes内で実行する場合は、設定を変更する前に<a target="_blank" rel="noopener noreferrer" href="%s">ドキュメント</a>を読んでください。
require_db_desc=DevStarには、MySQL、PostgreSQL、MSSQL、SQLite3、またはTiDB(MySQL プロトコル) が必要です。
db_title=データベース設定
db_type=データベースのタイプ
host=ホスト
@@ -357,7 +357,7 @@ password_algorithm=パスワードハッシュアルゴリズム
invalid_password_algorithm=無効なパスワードハッシュアルゴリズム
password_algorithm_helper=パスワードハッシュアルゴリズムを設定します。 アルゴリズムにより動作要件と強度が異なります。 argon2アルゴリズムはかなり安全ですが、多くのメモリを使用するため小さなシステムには適さない場合があります。
enable_update_checker=アップデートチェッカーを有効にする
enable_update_checker_helper=gitea.ioに接続して定期的に新しいバージョンのリリースを確認します。
enable_update_checker_helper=devstar.cnに接続して定期的に新しいバージョンのリリースを確認します。
env_config_keys=環境設定
env_config_keys_prompt=以下の環境変数も設定ファイルに適用されます:
config_write_file_prompt=これらの設定オプションは %s に書き込まれます

repo.diff.view_file

@@ -115,7 +115,7 @@ license_desc = Go get <a target="_blank" rel="noopener noreferrer" href="%[1]s">
[install]
install=설치
title=초기 설정
docker_helper=Gitea를 Docker에서 실행하려면 설정 전에 이 <a target="_blank" rel="noopener noreferrer" href="%s">문서</a>를 읽어보세요.
k8s_helper=DevStar를 Kubernetes에서 실행하려면 설정 전에 이 <a target="_blank" rel="noopener noreferrer" href="%s">문서</a>를 읽어보세요.
db_title=데이터베이스 설정
db_type=데이터베이스 유형
host=호스트

repo.diff.view_file

@@ -206,8 +206,8 @@ license_desc = Go get <a target="_blank" rel="noopener noreferrer" href="%[1]s">
[install]
install=Instalācija
title=Sākotnējā konfigurācija
docker_helper=Ja Gitea ir uzstādīts Docker konteinerī, izlasiet <a target="_blank" rel="noopener noreferrer" href="%s">vadlīninas</a> pirms maināt iestatījumus.
require_db_desc=Gitea nepieciešams MySQL, PostgreSQL, MSSQL, SQLite3 vai TiDB (izmantojot MySQL protokolu).
k8s_helper=Ja DevStar ir uzstādīts Kubernetes konteinerī, izlasiet <a target="_blank" rel="noopener noreferrer" href="%s">vadlīninas</a> pirms maināt iestatījumus.
require_db_desc=DevStar nepieciešams MySQL, PostgreSQL, MSSQL, SQLite3 vai TiDB (izmantojot MySQL protokolu).
db_title=Datu bāzes iestatījumi
db_type=Datu bāzes veids
host=Resursdators
@@ -311,7 +311,7 @@ password_algorithm=Paroles jaucējsummas algoritms
invalid_password_algorithm=Kļūdaina paroles jaucējfunkcija
password_algorithm_helper=Norādiet paroles jaucējalgoritmu. Algoritmi atšķirās pēc prasībām pret resursiem un stipruma. Argon2 algoritms ir drošs, bet tam nepieciešams daudz operatīvās atmiņas, līdz ar ko tas var nebūt piemērots sistēmām ar maz pieejamajiem resursiem.
enable_update_checker=Iespējot jaunu versiju paziņojumus
enable_update_checker_helper=Periodiski pārbaudīt jaunu version pieejamību, izgūstot datus no gitea.io.
enable_update_checker_helper=Periodiski pārbaudīt jaunu version pieejamību, izgūstot datus no devstar.cn.
env_config_keys=Vides konfigurācija
env_config_keys_prompt=Šie vides mainīgie tiks pielietoti arī konfigurācijas failā:

repo.diff.view_file

@@ -153,8 +153,8 @@ license_desc = Go get <a target="_blank" rel="noopener noreferrer" href="%[1]s">
[install]
install=Installatie
title=Initiële configuratie
docker_helper=Als je gitea draait in Docker, Lees eerst de <a target="_blank" rel="noopener noreferrer" href="%s">documentatie</a> voordat je een instelling aanpast.
require_db_desc=Gitea vereist MySQL, PostgreSQL, MSSQL, SQLite3 of TiDB (MySQL protocol).
k8s_helper=Als je gitea draait in Kubernetes, Lees eerst de <a target="_blank" rel="noopener noreferrer" href="%s">documentatie</a> voordat je een instelling aanpast.
require_db_desc=DevStar vereist MySQL, PostgreSQL, MSSQL, SQLite3 of TiDB (MySQL protocol).
db_title=Database-instellingen
db_type=Database-type
host=Server

repo.diff.view_file

@@ -149,8 +149,8 @@ license_desc = Go get <a target="_blank" rel="noopener noreferrer" href="%[1]s">
[install]
install=Instalacja
title=Wstępna konfiguracja
docker_helper=Jeśli używasz Gitea za pomocą Docker'a, przeczytaj <a target="_blank" rel="noopener noreferrer" href="%s">dokumentację</a> przed wprowadzeniem jakichkolwiek zmian.
require_db_desc=Gitea wymaga MySQL, PostgreSQL, MSSQL, SQLite3 lub TiDB (protokół MySQL).
k8s_helper=Jeśli używasz DevStar za pomocą Kubernetes'a, przeczytaj <a target="_blank" rel="noopener noreferrer" href="%s">dokumentację</a> przed wprowadzeniem jakichkolwiek zmian.
require_db_desc=DevStar wymaga MySQL, PostgreSQL, MSSQL, SQLite3 lub TiDB (protokół MySQL).
db_title=Ustawienia bazy danych
db_type=Typ bazy danych
host=Serwer

repo.diff.view_file

@@ -202,8 +202,8 @@ license_desc = Go get <a target="_blank" rel="noopener noreferrer" href="%[1]s">
[install]
install=Instalação
title=Configuração inicial
docker_helper=Se você está rodando o Gitea dentro do Docker, por favor leia a <a target="_blank" rel="noopener noreferrer" href="%s">documentação</a> cuidadosamente antes de alterar qualquer coisa nesta página.
require_db_desc=Gitea requer MySQL, PostgreSQL, MSSQL, SQLite3 ou TiDB (protocolo MySQL).
k8s_helper=Se você está rodando o DevStar dentro do Kubernetes, por favor leia a <a target="_blank" rel="noopener noreferrer" href="%s">documentação</a> cuidadosamente antes de alterar qualquer coisa nesta página.
require_db_desc=DevStar requer MySQL, PostgreSQL, MSSQL, SQLite3 ou TiDB (protocolo MySQL).
db_title=Configurações de banco de dados
db_type=Tipo de banco de dados
host=Servidor
@@ -307,7 +307,7 @@ password_algorithm=Algoritmo Hash de Senha
invalid_password_algorithm=Algoritmo de hash de senha inválido
password_algorithm_helper=Escolha o algoritmo de hash para as senhas. Diferentes algoritmos têm requerimentos e forças diversos. O algoritmo argon2 é bastante seguro, mas usa muita memória e pode ser inapropriado para sistemas com menos recursos.
enable_update_checker=Habilitar Verificador de Atualizações
enable_update_checker_helper=Procura por novas versões periodicamente conectando-se ao gitea.io.
enable_update_checker_helper=Procura por novas versões periodicamente conectando-se ao devstar.cn.
env_config_keys=Configuração do ambiente
env_config_keys_prompt=As seguintes variáveis de ambiente também serão aplicadas ao seu arquivo de configuração:

repo.diff.view_file

@@ -254,8 +254,8 @@ license_desc = Go get <a target="_blank" rel="noopener noreferrer" href="%[1]s">
install=Instalação
installing_desc=Instalando agora, por favor aguarde...
title=Configuração inicial
docker_helper=Se correr o Gitea dentro do Docker, leia a <a target="_blank" rel="noopener noreferrer" href="%s">documentação</a> antes de alterar quaisquer configurações.
require_db_desc=Gitea requer MySQL, PostgreSQL, MSSQL, SQLite3 ou TiDB (protocolo MySQL).
k8s_helper=Se correr o DevStar dentro do Kubernetes, leia a <a target="_blank" rel="noopener noreferrer" href="%s">documentação</a> antes de alterar quaisquer configurações.
require_db_desc=DevStar requer MySQL, PostgreSQL, MSSQL, SQLite3 ou TiDB (protocolo MySQL).
db_title=Configurações da base de dados
db_type=Tipo de base de dados
host=Servidor
@@ -360,7 +360,7 @@ password_algorithm=Algoritmo de Hash da Senha
invalid_password_algorithm=Algoritmo de hash da senha inválido
password_algorithm_helper=Definir o algoritmo de hash da senha. Os algoritmos têm requisitos e resistência distintos. `argon2` é bastante seguro, mas usa muita memória e pode ser inapropriado para sistemas pequenos.
enable_update_checker=Habilitar verificador de novidades
enable_update_checker_helper=Verifica, periodicamente, se foi lançada alguma versão nova, fazendo uma ligação ao gitea.io.
enable_update_checker_helper=Verifica, periodicamente, se foi lançada alguma versão nova, fazendo uma ligação ao devstar.cn.
env_config_keys=Configuração do ambiente
env_config_keys_prompt=As seguintes variáveis de ambiente também serão aplicadas ao seu ficheiro de configuração:
config_write_file_prompt=Estas opções de configuração serão escritas em: %s

repo.diff.view_file

@@ -201,8 +201,8 @@ license_desc = Go get <a target="_blank" rel="noopener noreferrer" href="%[1]s">
[install]
install=Установка
title=Начальная конфигурация
docker_helper=Если вы запускаете Gitea внутри Docker, пожалуйста внимательно прочтите <a target="_blank" rel="noopener noreferrer" href="%s">документацию</a> перед тем, как изменить любые настройки.
require_db_desc=Gitea требует MySQL, PostgreSQL, MSSQL, SQLite3 или TiDB (через протокол MySQL).
k8s_helper=Если вы запускаете DevStar внутри Kubernetes, пожалуйста внимательно прочтите <a target="_blank" rel="noopener noreferrer" href="%s">документацию</a> перед тем, как изменить любые настройки.
require_db_desc=DevStar требует MySQL, PostgreSQL, MSSQL, SQLite3 или TiDB (через протокол MySQL).
db_title=Настройки базы данных
db_type=Тип базы данных
host=Хост
@@ -306,7 +306,7 @@ password_algorithm=Алгоритм хеширования пароля
invalid_password_algorithm=Некорректный алгоритм хеширования пароля
password_algorithm_helper=Задайте алгоритм хеширования паролей. Алгоритмы имеют различные требования и стойкость. Алгоритм argon2 довольно безопасен, но он использует много памяти и может не подходить для слабых систем.
enable_update_checker=Включить проверку обновлений
enable_update_checker_helper=Периодически проверяет наличие новых версий, подключаясь к gitea.io.
enable_update_checker_helper=Периодически проверяет наличие новых версий, подключаясь к devstar.cn.
env_config_keys=Настройка окружения
env_config_keys_prompt=Следующие переменные окружения также будут применены к вашему конфигурационному файлу:

repo.diff.view_file

@@ -134,7 +134,7 @@ license_desc = Go get <a target="_blank" rel="noopener noreferrer" href="%[1]s">
[install]
install=ස්ථාපනය
title=මූලික වින්යාසය
docker_helper=ඔබ Docker තුළ Gitea ධාවනය කරන්නේ නම්, කරුණාකර ඕනෑම සැකසුම් වෙනස් කිරීමට පෙර <a target="_blank" rel="noopener noreferrer" href="%s">ලියකියවිලි</a> කියවන්න.
k8s_helper=ඔබ Kubernetes තුළ DevStar ධාවනය කරන්නේ නම්, කරුණාකර ඕනෑම සැකසුම් වෙනස් කිරීමට පෙර <a target="_blank" rel="noopener noreferrer" href="%s">ලියකියවිලි</a> කියවන්න.
db_title=දත්ත සමුදායේ සැකසුම්
db_type=දත්ත සමුදායේ වර්ගය
host=සත්කාරක

repo.diff.view_file

@@ -200,8 +200,8 @@ license_desc = Go get <a target="_blank" rel="noopener noreferrer" href="%[1]s">
[install]
install=Inštalácia
title=Východzia konfigurácia
docker_helper=Ak spúšťate Gitea v Docker kontajneri, prečítajte si <a target="_blank" rel="noopener noreferrer" href="%s">dokumentáciu</a> pred zmenou akýchkoľvek nastavení.
require_db_desc=Gitea vyžaduje MySQL, PostgreSQL, MSSQL, SQLite3 alebo TiDB (MySQL protokol).
k8s_helper=Ak spúšťate DevStar v Kubernetes kontajneri, prečítajte si <a target="_blank" rel="noopener noreferrer" href="%s">dokumentáciu</a> pred zmenou akýchkoľvek nastavení.
require_db_desc=DevStar vyžaduje MySQL, PostgreSQL, MSSQL, SQLite3 alebo TiDB (MySQL protokol).
db_title=Nastavenie databázy
db_type=Typ databázy
host=Host
@@ -303,7 +303,7 @@ password_algorithm=Hašovací algoritmus hesla
invalid_password_algorithm=Neplatný hash algoritmus hesla
password_algorithm_helper=Nastavte algoritmus hashovania hesla. Algoritmy majú rôzne požiadavky a silu. Algoritmus argon2 je pomerne bezpečný, ale využíva veľa pamäte a môže byť nevhodný pre malé systémy.
enable_update_checker=Povoliť kontrolu aktualizácií
enable_update_checker_helper=Pravidelne kontroluje nové verzie pripojením k gitea.io.
enable_update_checker_helper=Pravidelne kontroluje nové verzie pripojením k devstar.cn.
[home]
uname_holder=Používateľské meno alebo emailová adresa

repo.diff.view_file

@@ -124,7 +124,7 @@ license_desc = Go get <a target="_blank" rel="noopener noreferrer" href="%[1]s">
[install]
install=Installation
title=Ursprunglig konfiguration
docker_helper=Om du kör Gitea i Docker, vänligen läs igenom <a target="_blank" rel="noopener noreferrer" href="%s">dokumentationen</a> innan några inställningar ändras.
k8s_helper=Om du kör DevStar i Kubernetes, vänligen läs igenom <a target="_blank" rel="noopener noreferrer" href="%s">dokumentationen</a> innan några inställningar ändras.
db_title=Databasinställningar
db_type=Databastyp
host=Server

repo.diff.view_file

@@ -251,8 +251,8 @@ license_desc = Go get <a target="_blank" rel="noopener noreferrer" href="%[1]s">
install=Kurulum
installing_desc=Şimdi kuruluyor, lütfen bekleyin...
title=Başlangıç Yapılandırması
docker_helper=Eğer Gitea'yı Docker içerisinde çalıştırıyorsanız, lütfen herhangi bir değişiklik yapmadan önce <a target="_blank" rel="noopener noreferrer" href="%s">belgeleri</a> okuyun.
require_db_desc=Gitea MySQL, PostgreSQL, MSSQL, SQLite3 veya TiDB (MySQL protokolü) gerektirir.
k8s_helper=Eğer DevStar'yı Kubernetes içerisinde çalıştırıyorsanız, lütfen herhangi bir değişiklik yapmadan önce <a target="_blank" rel="noopener noreferrer" href="%s">belgeleri</a> okuyun.
require_db_desc=DevStar MySQL, PostgreSQL, MSSQL, SQLite3 veya TiDB (MySQL protokolü) gerektirir.
db_title=Veritabanı Ayarları
db_type=Veritabanı Türü
host=Sunucu
@@ -357,7 +357,7 @@ password_algorithm=Parola Hash Algoritması
invalid_password_algorithm=Hatalı parola hash algoritması
password_algorithm_helper=Parola hash algoritmasını ayarlayın. Algoritmalar değişen gereksinimlere ve güce sahiptirler. argon2 algoritması iyi özelliklere sahip olmasına rağmen fazla miktarda bellek kullanır ve küçük sistemler için uygun olmayabilir.
enable_update_checker=Güncelleme Denetleyicisini Etkinleştir
enable_update_checker_helper=Düzenli olarak gitea.io'ya bağlanarak yeni yayınlanan sürümleri denetler.
enable_update_checker_helper=Düzenli olarak devstar.cn'ya bağlanarak yeni yayınlanan sürümleri denetler.
env_config_keys=Ortam Yapılandırma
env_config_keys_prompt=Aşağıdaki ortam değişkenleri de yapılandırma dosyanıza eklenecektir:
config_write_file_prompt=Bu yapılandırma seçenekleri şuraya yazılacak: %s

repo.diff.view_file

@@ -246,8 +246,8 @@ license_desc = Go get <a target="_blank" rel="noopener noreferrer" href="%[1]s">
install=Встановлення
installing_desc=Встановлення, будь ласка, зачекайте...
title=Початкова конфігурація
docker_helper=Якщо ви запускаєте Gitea у Docker, будь ласка, прочитайте <a target="_blank" rel="noopener noreferrer" href="%s">документацію</a> перед тим, як змінювати будь-які налаштування.
require_db_desc=Gitea потребує MySQL, PostgreSQL, MSSQL, SQLite3 або TiDB (протокол MySQL).
k8s_helper=Якщо ви запускаєте DevStar у Kubernetes, будь ласка, прочитайте <a target="_blank" rel="noopener noreferrer" href="%s">документацію</a> перед тим, як змінювати будь-які налаштування.
require_db_desc=DevStar потребує MySQL, PostgreSQL, MSSQL, SQLite3 або TiDB (протокол MySQL).
db_title=Налаштування бази даних
db_type=Тип бази даних
host=Хост
@@ -352,7 +352,7 @@ password_algorithm=Алгоритм хешування пароля
invalid_password_algorithm=Недійсний хеш-алгоритм пароля
password_algorithm_helper=Встановіть алгоритм хешування пароля. Алгоритми мають різні вимоги та стійкість. Алгоритм argon2 є досить безпечним, але використовує багато пам'яті і може бути недоречним для малих систем.
enable_update_checker=Увімкнути перевірку оновлень
enable_update_checker_helper=Періодично перевіряти наявність нових версій, підключаючись до gitea.io.
enable_update_checker_helper=Періодично перевіряти наявність нових версій, підключаючись до devstar.cn.
env_config_keys=Конфігурація середовища
env_config_keys_prompt=Наступні змінні середовища також будуть застосовані до вашого файлу конфігурації:
config_write_file_prompt=Ці параметри будуть записані в: %s

repo.diff.view_file

@@ -251,8 +251,8 @@ license_desc=所有的代码都开源在 <a target="_blank" rel="noopener norefe
install=安装页面
installing_desc=正在安装,请稍候...
title=初始配置
docker_helper=如果您正在使用 Docker 容器运行 Gitea,请务必先仔细阅读 <a target="_blank" rel="noopener noreferrer" href="%s">官方文档</a> 后再对本页面进行填写。
require_db_desc=Gitea 需要使用 MySQL、PostgreSQL、MSSQL、SQLite3 或 TiDB (MySQL协议) 等数据库
k8s_helper=如果您正在使用 Kubernetes 运行 DevStar,请务必先仔细阅读 <a target="_blank" rel="noopener noreferrer" href="%s">官方文档</a> 后再对本页面进行填写。
require_db_desc=DevStar 需要使用 MySQL、PostgreSQL、MSSQL、SQLite3 或 TiDB (MySQL协议) 等数据库
db_title=数据库设置
db_type=数据库类型
host=数据库主机
@@ -358,8 +358,10 @@ default_keep_email_private=默认情况下隐藏邮箱地址
default_keep_email_private_popup=默认情况下,隐藏新用户帐户的邮箱地址。
default_allow_create_organization=默认情况下允许创建组织
default_allow_create_devcontainer=默认情况下允许创建容器
default_allow_create_actrunner=默认情况下允许创建工作流运行器
default_allow_create_organization_popup=默认情况下, 允许新用户帐户创建组织。
default_allow_create_devcontainer_popup=默认情况下, 允许新用户帐户创建容器。
default_allow_create_actrunner_popup=默认情况下, 允许新用户帐户创建工作流运行器。
default_enable_timetracking=默认情况下启用时间跟踪
default_enable_timetracking_popup=默认情况下启用新仓库的时间跟踪。
no_reply_address=隐藏邮件域
@@ -368,7 +370,7 @@ password_algorithm=密码哈希算法
invalid_password_algorithm=无效的密码哈希算法
password_algorithm_helper=设置密码散列算法。算法有不同的要求和强度。 argon2 算法相当安全,但使用大量内存,因此可能不适合小型系统。
enable_update_checker=启用更新检查
enable_update_checker_helper=通过连接到 gitea.io 定期检查新版本发布。
enable_update_checker_helper=通过连接到 devstar.cn 定期检查新版本发布。
env_config_keys=环境配置
env_config_keys_prompt=以下环境变量也将应用于您的配置文件:
config_write_file_prompt=这些配置选项将写入以下位置: %s
@@ -3153,6 +3155,7 @@ users.allow_git_hook_tooltip=Git 钩子将会以操作系统用户运行,拥
users.allow_import_local=允许导入本地仓库
users.allow_create_organization=允许创建组织
users.allow_create_devcontainer=允许创建开发容器
users.allow_create_actrunner=允许创建工作流运行器
users.update_profile=更新帐户
users.delete_account=删除帐户
users.cannot_delete_self=您不能删除自己
@@ -3412,6 +3415,7 @@ config.reset_password_code_lives=恢复账户验证码过期时间
config.default_keep_email_private=默认隐藏邮箱地址
config.default_allow_create_organization=默认情况下允许创建组织
config.default_allow_create_devcontainer=默认情况下允许创建 DevContainer
config.default_allow_create_actrunner=默认情况下允许创建 ActRunner
config.enable_timetracking=启用时间跟踪
config.default_enable_timetracking=默认情况下启用时间跟踪
config.default_allow_only_contributors_to_track_time=仅允许成员跟踪时间
@@ -3869,7 +3873,7 @@ status.blocked=阻塞中
runners=运行器
runners.runner_manage_panel=运行器管理
runners.new=创建新运行器
runners.new_notice=如何启动一个运行器
runners.new_notice=如何手动启动一个运行器
runners.status=状态
runners.id=ID
runners.name=名称

repo.diff.view_file

@@ -243,8 +243,8 @@ license_desc=所有的代码都开源在 <a target="_blank" rel="noopener norefe
[install]
install=安裝頁面
title=初始組態
docker_helper=如果您在 Docker 中執行 Gitea,請先閱讀<a target="_blank" rel="noopener noreferrer" href="%s">安裝指南</a>再來調整設定。
require_db_desc=Gitea 需要 MySQL、PostgreSQL、SQLite3、MSSQL、TiDB (MySQL 協定) 等其中一項。
k8s_helper=如果您在 Kubernetes 中執行 DevStar,請先閱讀<a target="_blank" rel="noopener noreferrer" href="%s">安裝指南</a>再來調整設定。
require_db_desc=DevStar 需要 MySQL、PostgreSQL、SQLite3、MSSQL、TiDB (MySQL 協定) 等其中一項。
db_title=資料庫設定
db_type=資料庫類型
host=主機
@@ -349,7 +349,7 @@ password_algorithm=密碼雜湊演算法
invalid_password_algorithm=無效的密碼雜湊演算法
password_algorithm_helper=設定密碼雜湊演算法。演算法有不同的需求與強度。argon2 演算法雖然較安全但會使用大量記憶體,可能不適用於小型系統。
enable_update_checker=啟用更新檢查器
enable_update_checker_helper=定期連線到 gitea.io 檢查更新。
enable_update_checker_helper=定期連線到 devstar.cn 檢查更新。
env_config_keys=環境組態設定
env_config_keys_prompt=下列環境變數也會套用到您的組態檔:
config_write_file_prompt=這些配置選項將被寫入到: %s

repo.diff.view_file

@@ -93,6 +93,13 @@ function install {
sudo docker pull devstar.cn/devstar/webterminal:latest
success "Successfully pulled devstar.cn/devstar/webterminal:latest"
fi
if sudo docker pull mengning997/act_runner:latest; then
sudo docker tag mengning997/act_runner:latest devstar.cn/devstar/act_runner:latest
success "Successfully pulled mengning997/act_runner:latest renamed to devstar.cn/devstar/act_runner:latest"
else
sudo docker pull devstar.cn/devstar/act_runner:latest
success "Successfully pulled devstar.cn/devstar/act_runner:latest"
fi
}
# Function to start
@@ -140,7 +147,10 @@ function stop {
fi
if [ $(docker ps -a --filter "name=^/webterminal-" -q | wc -l) -gt 0 ]; then
sudo docker stop $(docker ps -a --filter "name=^/webterminal-" -q) && sudo docker rm -f $(docker ps -a --filter "name=^/webterminal-" -q)
fi
fi
if [ $(docker ps -a --filter "name=^/runner-" -q | wc -l) -gt 0 ]; then
sudo docker stop $(docker ps -a --filter "name=^/runner-" -q) && sudo docker rm -f $(docker ps -a --filter "name=^/runner-" -q)
fi
}
# Function to logs

repo.diff.view_file

@@ -0,0 +1 @@
{"latest":{"version":"1.25.1"}}

repo.diff.view_file

@@ -246,6 +246,7 @@ func EditUser(ctx *context.APIContext) {
MaxRepoCreation: optional.FromPtr(form.MaxRepoCreation),
AllowCreateOrganization: optional.FromPtr(form.AllowCreateOrganization),
AllowCreateDevcontainer: optional.FromPtr(form.AllowCreateDevcontainer),
AllowCreateActRunner: optional.FromPtr(form.AllowCreateActRunner),
IsRestricted: optional.FromPtr(form.Restricted),
}

repo.diff.view_file

@@ -156,6 +156,7 @@ func Install(ctx *context.Context) {
form.DefaultKeepEmailPrivate = setting.Service.DefaultKeepEmailPrivate
form.DefaultAllowCreateOrganization = setting.Service.DefaultAllowCreateOrganization
form.DefaultAllowCreateDevcontainer = setting.Service.DefaultAllowCreateDevcontainer
form.DefaultAllowCreateActRunner = setting.Service.DefaultAllowCreateActRunner
form.DefaultEnableTimetracking = setting.Service.DefaultEnableTimetracking
form.NoReplyAddress = setting.Service.NoReplyAddress
form.PasswordAlgorithm = hash.ConfigHashAlgorithm(setting.PasswordHashAlgo)
@@ -492,6 +493,7 @@ func SubmitInstall(ctx *context.Context) {
cfg.Section("service").Key("DEFAULT_KEEP_EMAIL_PRIVATE").SetValue(strconv.FormatBool(form.DefaultKeepEmailPrivate))
cfg.Section("service").Key("DEFAULT_ALLOW_CREATE_ORGANIZATION").SetValue(strconv.FormatBool(form.DefaultAllowCreateOrganization))
cfg.Section("service").Key("DEFAULT_ALLOW_CREATE_DEVCONTAINER").SetValue(strconv.FormatBool(form.DefaultAllowCreateDevcontainer))
cfg.Section("service").Key("DEFAULT_ALLOW_CREATE_ACTRUNNER").SetValue(strconv.FormatBool(form.DefaultAllowCreateActRunner))
cfg.Section("service").Key("DEFAULT_ENABLE_TIMETRACKING").SetValue(strconv.FormatBool(form.DefaultEnableTimetracking))
cfg.Section("service").Key("NO_REPLY_ADDRESS").SetValue(form.NoReplyAddress)
cfg.Section("cron.update_checker").Key("ENABLED").SetValue(strconv.FormatBool(form.EnableUpdateChecker))
@@ -630,17 +632,24 @@ func SubmitInstall(ctx *context.Context) {
if form.K8sEnable {
//K8s环境检测
cfg.Section("devstar.devcontainer").Key("HOST").SetValue(form.Domain)
} else {
if !checkDocker(ctx, &form) {
ctx.RenderWithErr("There is no docker environment", tplInstall, &form)
return
}
}
// 注册Global Runners
if setting.Runner.AutoStart {
for i := 0; i < setting.Runner.Count; i++ {
runners_service.RegistGlobalRunner(ctx)
}
}
setting.ClearEnvConfigKeys()
log.Info("First-time run install finished!")
InstallDone(ctx)
go func() {
// Sleep for a while to make sure the user's browser has loaded the post-install page and its assets (images, css, js)
// What if this duration is not long enough? That's impossible -- if the user can't load the simple page in time, how could they install or use Gitea in the future ....
@@ -654,8 +663,6 @@ func SubmitInstall(ctx *context.Context) {
return
}
}
runners_service.RegistGlobalRunner(otherCtx)
// Now get the http.Server from this request and shut it down
// NB: This is not our hammerable graceful shutdown this is http.Server.Shutdown
srv := ctx.Value(http.ServerContextKey).(*http.Server)

repo.diff.view_file

@@ -437,6 +437,7 @@ func EditUserPost(ctx *context.Context) {
MaxRepoCreation: optional.Some(form.MaxRepoCreation),
AllowCreateOrganization: optional.Some(form.AllowCreateOrganization),
AllowCreateDevcontainer: optional.Some(form.AllowCreateDevcontainer),
AllowCreateActRunner: optional.Some(form.AllowCreateActRunner),
IsRestricted: optional.Some(form.Restricted),
Visibility: optional.Some(form.Visibility),
Language: optional.Some(form.Language),
@@ -450,7 +451,6 @@ func EditUserPost(ctx *context.Context) {
}
return
}
log.Trace("Account profile updated by admin (%s): %s", ctx.Doer.Name, u.Name)
if form.Reset2FA {
tf, err := auth.GetTwoFactorByUID(ctx, u.ID)

repo.diff.view_file

@@ -160,6 +160,7 @@ func Runners(ctx *context.Context) {
ctx.Data["RunnerOwnerID"] = opts.OwnerID
ctx.Data["RunnerRepoID"] = opts.RepoID
ctx.Data["SortType"] = opts.Sort
ctx.Data["AllowCreateActRunner"] = ctx.Doer.AllowCreateActRunner
pager := context.NewPagination(int(count), opts.PageSize, opts.Page, 5)
@@ -300,6 +301,14 @@ func RegisterARunner(ctx *context.Context) {
ctx.ServerError("getRunnersCtx", err)
return
}
// 检查用户是否有权创建 runner
if !ctx.Doer.AllowCreateActRunner {
ctx.Flash.Error(ctx.Tr("actions.runners.create_runner_permission_denied"))
ctx.Redirect(rCtx.RedirectLink)
return
}
token, err := actions_model.NewRunnerToken(ctx, rCtx.OwnerID, rCtx.RepoID)
if err != nil {
ctx.ServerError("NewRunnerToken", err)

repo.diff.view_file

@@ -399,6 +399,8 @@ func repoAssignment(ctx *Context, repo *repo_model.Repository) {
ctx.Data["Permission"] = &ctx.Repo.Permission
if ctx.Doer != nil {
ctx.Data["AllowCreateDevcontainer"] = ctx.Doer.AllowCreateDevcontainer
ctx.Data["AllowCreateActRunner"] = ctx.Doer.AllowCreateActRunner
} else {
query := ctx.Req.URL.Query()
userID := query.Get("user")
@@ -416,6 +418,7 @@ func repoAssignment(ctx *Context, repo *repo_model.Repository) {
return
}
ctx.Data["AllowCreateDevcontainer"] = u.AllowCreateDevcontainer
ctx.Data["AllowCreateActRunner"] = u.AllowCreateActRunner
}
if repo.IsMirror {

repo.diff.view_file

@@ -150,7 +150,7 @@ func registerUpdateGiteaChecker() {
RunAtStart: false,
Schedule: "@every 168h",
},
HTTPEndpoint: "https://dl.gitea.com/gitea/version.json",
HTTPEndpoint: "https://devstar.cn/assets/version.json",
}, func(ctx context.Context, _ *user_model.User, config Config) error {
updateCheckerConfig := config.(*UpdateCheckerConfig)
return updatechecker.GiteaUpdateChecker(updateCheckerConfig.HTTPEndpoint)

repo.diff.view_file

@@ -161,7 +161,29 @@ func GetWebTerminalURL(ctx context.Context, userID, repoID int64) (string, error
return "", err
}
if cfg.Section("k8s").Key("ENABLE").Value() == "true" {
//k8s的逻辑
// K8s 模式:使用 Istio Gateway + VirtualService
log.Info("GetWebTerminalURL: 使用 Istio 模式获取 WebTerminal URL for DevContainer: %s", devcontainerName)
// 从配置中读取域名
domain := cfg.Section("server").Key("DOMAIN").Value()
// 从容器名称中提取用户名和仓库名
parts := strings.Split(devcontainerName, "-")
var username, repoName string
if len(parts) >= 2 {
username = parts[0]
repoName = parts[1]
} else {
username = "unknown"
repoName = "unknown"
}
// 构建基于 Istio Gateway 的 URL
path := fmt.Sprintf("/%s/%s/dev-container-webterminal", username, repoName)
webTerminalURL := fmt.Sprintf("http://%s%s", domain, path)
log.Info("GetWebTerminalURL: 生成 Istio WebTerminal URL: %s", webTerminalURL)
return webTerminalURL, nil
}
return "", nil
}
@@ -181,6 +203,7 @@ func GetWebTerminalURL(ctx context.Context, userID, repoID int64) (string, error
10已删除
*/
func GetDevContainerStatus(ctx context.Context, userID, repoID string) (string, error) {
log.Info("GetDevContainerStatus: Starting - userID: %s, repoID: %s", userID, repoID)
var id int
var containerName string
@@ -188,6 +211,7 @@ func GetDevContainerStatus(ctx context.Context, userID, repoID string) (string,
var realTimeStatus uint16
cfg, err := setting.NewConfigProviderFromFile(setting.CustomConf)
if err != nil {
log.Error("GetDevContainerStatus: Failed to load config: %v", err)
return "", err
}
dbEngine := db.GetEngine(ctx)
@@ -197,19 +221,42 @@ func GetDevContainerStatus(ctx context.Context, userID, repoID string) (string,
Where("user_id = ? AND repo_id = ?", userID, repoID).
Get(&status, &id, &containerName)
if err != nil {
log.Error("GetDevContainerStatus: Failed to query database: %v", err)
return "", err
}
log.Info("GetDevContainerStatus: Database query result - id: %d, containerName: %s, status: %d", id, containerName, status)
if id == 0 {
log.Info("GetDevContainerStatus: No devcontainer found, returning -1")
return fmt.Sprintf("%d", -1), nil
}
realTimeStatus = status
log.Info("GetDevContainerStatus: Initial realTimeStatus: %d", realTimeStatus)
switch status {
//正在重启
case 6:
if cfg.Section("k8s").Key("ENABLE").Value() == "true" {
//k8s逻辑
// k8s 逻辑:检查 Pod 是否已恢复运行
log.Info("GetDevContainerStatus: K8s branch for case 6 (restarting), container: %s", containerName)
opts := &OpenDevcontainerAppDispatcherOptions{
Name: containerName,
Wait: false,
}
log.Info("GetDevContainerStatus: Calling AssignDevcontainerGetting2K8sOperator with opts: %+v", opts)
devcontainerApp, err := AssignDevcontainerGetting2K8sOperator(&ctx, opts)
if err != nil {
log.Error("GetDevContainerStatus: AssignDevcontainerGetting2K8sOperator failed: %v", err)
} else if devcontainerApp != nil {
log.Info("GetDevContainerStatus: DevcontainerApp retrieved - Name: %s, Ready: %v", devcontainerApp.Name, devcontainerApp.Status.Ready)
if devcontainerApp.Status.Ready {
realTimeStatus = 4 // 已恢复运行
log.Info("GetDevContainerStatus: Container %s is ready, updating status to 4", containerName)
}
} else {
log.Warn("GetDevContainerStatus: DevcontainerApp is nil for container: %s", containerName)
}
} else {
containerRealTimeStatus, err := GetDevContainerStatusFromDocker(ctx, containerName)
if err != nil {
@@ -222,7 +269,24 @@ func GetDevContainerStatus(ctx context.Context, userID, repoID string) (string,
//正在关闭
case 7:
if cfg.Section("k8s").Key("ENABLE").Value() == "true" {
//k8s逻辑
// k8s 逻辑:检查 Pod 是否已停止
log.Info("GetDevContainerStatus: K8s branch for case 7 (stopping), container: %s", containerName)
opts := &OpenDevcontainerAppDispatcherOptions{
Name: containerName,
Wait: false,
}
log.Info("GetDevContainerStatus: Calling AssignDevcontainerGetting2K8sOperator for stop check with opts: %+v", opts)
devcontainerApp, err := AssignDevcontainerGetting2K8sOperator(&ctx, opts)
if err != nil {
log.Info("GetDevContainerStatus: DevcontainerApp not found or error, considering stopped: %v", err)
realTimeStatus = 8 // 已停止
} else if devcontainerApp == nil || !devcontainerApp.Status.Ready {
log.Info("GetDevContainerStatus: DevcontainerApp is nil or not ready, considering stopped")
realTimeStatus = 8 // 已停止
} else {
log.Info("GetDevContainerStatus: DevcontainerApp still running - Name: %s, Ready: %v", devcontainerApp.Name, devcontainerApp.Status.Ready)
}
// 已在外部通过 StopDevContainer 触发,此处仅检查状态
} else {
containerRealTimeStatus, err := GetDevContainerStatusFromDocker(ctx, containerName)
if err != nil {
@@ -240,7 +304,21 @@ func GetDevContainerStatus(ctx context.Context, userID, repoID string) (string,
break
case 9:
if cfg.Section("k8s").Key("ENABLE").Value() == "true" {
//k8s逻辑
// k8s 逻辑:检查 Pod 是否已删除
log.Info("GetDevContainerStatus: K8s branch for case 9 (deleting), container: %s", containerName)
opts := &OpenDevcontainerAppDispatcherOptions{
Name: containerName,
Wait: false,
}
log.Info("GetDevContainerStatus: Calling AssignDevcontainerGetting2K8sOperator for delete check with opts: %+v", opts)
_, err := AssignDevcontainerGetting2K8sOperator(&ctx, opts)
if err != nil {
log.Info("GetDevContainerStatus: DevcontainerApp not found, considering deleted: %v", err)
realTimeStatus = 10 // 已删除
} else {
log.Info("GetDevContainerStatus: DevcontainerApp still exists, not deleted yet")
}
// 已在外部通过 DeleteDevContainer 触发,此处仅检查状态
} else {
isContainerNotFound, err := IsContainerNotFound(ctx, containerName)
if err != nil {
@@ -259,6 +337,22 @@ func GetDevContainerStatus(ctx context.Context, userID, repoID string) (string,
default:
log.Info("other status")
}
// K8s: 仅在 Ready 后才返回 4否则维持/降为 3
if cfg.Section("k8s").Key("ENABLE").Value() == "true" && (status == 3 || status == 4) {
opts := &OpenDevcontainerAppDispatcherOptions{
Name: containerName,
Wait: false,
}
app, err := AssignDevcontainerGetting2K8sOperator(&ctx, opts)
if err != nil || app == nil {
// 获取不到 CR 或出错时,保守认为未就绪
realTimeStatus = 3
} else if app.Status.Ready {
realTimeStatus = 4
} else {
realTimeStatus = 3
}
}
//状态更新
if realTimeStatus != status {
if realTimeStatus == 10 {
@@ -289,6 +383,7 @@ func GetDevContainerStatus(ctx context.Context, userID, repoID string) (string,
return "", err
}
}
log.Info("GetDevContainerStatus: Final realTimeStatus: %d, returning status string", realTimeStatus)
return fmt.Sprintf("%d", realTimeStatus), nil
}
func CreateDevContainer(ctx context.Context, repo *repo.Repository, doer *user.User, publicKeyList []string, isWebTerminal bool) error {
@@ -330,7 +425,28 @@ func CreateDevContainer(ctx context.Context, repo *repo.Repository, doer *user.U
go func() {
otherCtx := context.Background()
if cfg.Section("k8s").Key("ENABLE").Value() == "true" {
//k8s的逻辑
// K8s 模式:直接调用 K8s Operator 创建 DevContainer
configurationString, err := GetDevcontainerConfigurationString(otherCtx, repo)
if err != nil {
log.Info("CreateDevContainer: 读取 devcontainer 配置失败: %v", err)
return
}
configurationModel, err := UnmarshalDevcontainerConfigContent(configurationString)
if err != nil {
log.Info("CreateDevContainer: 解析 devcontainer 配置失败: %v", err)
return
}
newDTO := &CreateDevcontainerDTO{
Devcontainer: newDevcontainer,
SSHPublicKeyList: publicKeyList,
GitRepositoryURL: strings.TrimSuffix(setting.AppURL, "/") + repo.Link(),
Image: configurationModel.Image,
}
if err := AssignDevcontainerCreation2K8sOperator(&otherCtx, newDTO); err != nil {
log.Error("CreateDevContainer: K8s 创建失败: %v", err)
return
}
} else {
imageName, err := CreateDevContainerByDockerCommand(otherCtx, &newDevcontainer, repo, publicKeyList)
if err != nil {
@@ -367,7 +483,9 @@ func DeleteDevContainer(ctx context.Context, userID, repoID int64) error {
go func() {
otherCtx := context.Background()
if cfg.Section("k8s").Key("ENABLE").Value() == "true" {
//k8s的逻辑
// k8s 模式:调用 K8s Operator 删除 DevContainer 资源
devList := []devcontainer_models.Devcontainer{devContainerInfo}
_ = AssignDevcontainerDeletion2K8sOperator(&otherCtx, &devList)
} else {
err = DeleteDevContainerByDocker(otherCtx, devContainerInfo.Name)
@@ -402,7 +520,15 @@ func RestartDevContainer(ctx context.Context, userID, repoID int64) error {
go func() {
otherCtx := context.Background()
if cfg.Section("k8s").Key("ENABLE").Value() == "true" {
//k8s的逻辑
// k8s 模式:调用 K8s Operator 重启 DevContainer
vo := &DevcontainerVO{
DevContainerName: devContainerInfo.Name,
UserId: userID,
RepoId: repoID,
}
if err := AssignDevcontainerRestart2K8sOperator(&otherCtx, vo); err != nil {
log.Error("RestartDevContainer: K8s 重启失败: %v", err)
}
} else {
err = RestartDevContainerByDocker(otherCtx, devContainerInfo.Name)
if err != nil {
@@ -436,7 +562,15 @@ func StopDevContainer(ctx context.Context, userID, repoID int64) error {
go func() {
otherCtx := context.Background()
if cfg.Section("k8s").Key("ENABLE").Value() == "true" {
//k8s的逻辑
// k8s 模式:调用 K8s Operator 停止 DevContainer
vo := &DevcontainerVO{
DevContainerName: devContainerInfo.Name,
UserId: userID,
RepoId: repoID,
}
if err := AssignDevcontainerStop2K8sOperator(&otherCtx, vo); err != nil {
log.Error("StopDevContainer: K8s 停止失败: %v", err)
}
} else {
err = StopDevContainerByDocker(otherCtx, devContainerInfo.Name)
if err != nil {
@@ -891,7 +1025,20 @@ func Get_IDE_TerminalURL(ctx *gitea_context.Context, doer *user.User, repo *gite
var port string
if cfg.Section("k8s").Key("ENABLE").Value() == "true" {
// K8s 环境:通过 DevcontainerApp 的 NodePort 作为 SSH 端口
apiRequestCtx := ctx.Req.Context()
opts := &OpenDevcontainerAppDispatcherOptions{
Name: devContainerInfo.Name,
Wait: false,
}
devcontainerApp, err := AssignDevcontainerGetting2K8sOperator(&apiRequestCtx, opts)
if err != nil {
return "", err
}
if devcontainerApp == nil || devcontainerApp.Status.NodePortAssigned == 0 {
return "", fmt.Errorf("k8s DevcontainerApp 未就绪或未分配 NodePort: %s", devContainerInfo.Name)
}
port = fmt.Sprintf("%d", devcontainerApp.Status.NodePortAssigned)
} else {
mappedPort, err := docker_module.GetMappedPort(ctx, devContainerInfo.Name, "22")
if err != nil {

repo.diff.view_file

@@ -0,0 +1,891 @@
package devcontainer
import (
"bytes"
"context"
"fmt"
"strings"
"time"
"code.gitea.io/gitea/models/db"
devcontainer_model "code.gitea.io/gitea/models/devcontainer"
devcontainer_dto "code.gitea.io/gitea/modules/k8s"
devcontainer_k8s_agent_module "code.gitea.io/gitea/modules/k8s"
k8s_api_v1 "code.gitea.io/gitea/modules/k8s/api/devcontainer/v1"
"code.gitea.io/gitea/modules/k8s/errors"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
v1 "k8s.io/api/core/v1"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/tools/remotecommand"
"k8s.io/kubectl/pkg/scheme"
// Istio 资源改为 dynamic/unstructured不再直接使用 typed API
)
// 为 K8s Agent 暴露所需的 DTO 类型,便于 K8s 分支创建/查询入口使用
type CreateDevcontainerDTO struct {
devcontainer_model.Devcontainer
SSHPublicKeyList []string
GitRepositoryURL string
Image string
DockerfileContent string
DevcontainerPort uint16
}
type OpenDevcontainerAppDispatcherOptions struct {
Name string `json:"name"`
Wait bool `json:"wait"`
Status uint16
Port uint16
UserPublicKey string
RepoID int64
UserID int64
}
var k8sGroupVersionResource = schema.GroupVersionResource{
Group: "devcontainer.devstar.cn",
Version: "v1",
Resource: "devcontainerapps",
}
type ErrIllegalK8sAgentParams struct {
FieldNameList []string
}
func (err ErrIllegalK8sAgentParams) Error() string {
return fmt.Sprintf("Illegal Params: %v", err.FieldNameList)
}
// AssignDevcontainerGetting2K8sOperator 获取 k8s CRD 资源 DevcontainerApp 最新状态(需要根据用户传入的 wait 参数决定是否要阻塞等待 DevContainer 就绪)
func AssignDevcontainerGetting2K8sOperator(ctx *context.Context, opts *OpenDevcontainerAppDispatcherOptions) (*k8s_api_v1.DevcontainerApp, error) {
log.Info("AssignDevcontainerGetting2K8sOperator: Starting lookup for container: %s, wait=%v",
opts.Name, opts.Wait)
// 0. 检查参数
if ctx == nil || opts == nil || len(opts.Name) == 0 {
return nil, ErrIllegalK8sAgentParams{
FieldNameList: []string{"ctx", "opts", "opts.Name"},
}
}
// 1. 获取 Dynamic Client
ctxVal := *ctx
client, err := devcontainer_k8s_agent_module.GetKubernetesClient(ctxVal, nil, "")
if err != nil {
// 层层返回错误,结束数据库事务
return nil, errors.ErrOperateDevcontainer{
Action: "Connect to k8s API Server",
Message: err.Error(),
}
}
log.Info("AssignDevcontainerGetting2K8sOperator: K8s client created successfully")
// 2. 调用 modules 层 k8s Agent 获取 k8s CRD 资源 DevcontainerApp
optsGetDevcontainer := &devcontainer_dto.GetDevcontainerOptions{
GetOptions: metav1.GetOptions{},
Name: opts.Name,
Namespace: setting.DevContainerConfig.Namespace,
Wait: opts.Wait,
}
log.Info("AssignDevcontainerGetting2K8sOperator: Retrieving DevcontainerApp %s in namespace %s (wait=%v)",
opts.Name, setting.DevContainerConfig.Namespace, opts.Wait)
devcontainerApp, err := devcontainer_k8s_agent_module.GetDevcontainer(ctxVal, client, optsGetDevcontainer)
if err != nil {
log.Error("AssignDevcontainerGetting2K8sOperator: Failed to get DevcontainerApp: %v", err)
return nil, errors.ErrOperateDevcontainer{
Action: fmt.Sprintf("Open Devcontainer '%s' (wait=%v)", opts.Name, opts.Wait),
Message: err.Error(),
}
}
log.Info("AssignDevcontainerGetting2K8sOperator: DevcontainerApp retrieved successfully - Name: %s, NodePort: %d, Ready: %v",
devcontainerApp.Name, devcontainerApp.Status.NodePortAssigned, devcontainerApp.Status.Ready)
// 添加额外端口的日志
if len(devcontainerApp.Status.ExtraPortsAssigned) > 0 {
for i, port := range devcontainerApp.Status.ExtraPortsAssigned {
log.Info("AssignDevcontainerGetting2K8sOperator: Extra port %d - Name: %s, NodePort: %d, ContainerPort: %d",
i, port.Name, port.NodePort, port.ContainerPort)
}
} else {
log.Info("AssignDevcontainerGetting2K8sOperator: No extra ports found for DevcontainerApp %s", devcontainerApp.Name)
}
// 3. 成功获取最新的 DevcontainerApp返回
return devcontainerApp, nil
}
// 补充笔记: modules/ 与 services/ 两个目录中的 k8s Agent 区别是什么?
// - modules/ 与 k8s API Server 交互密切相关
// - services/ 进行了封装,简化用户界面使用
func AssignDevcontainerDeletion2K8sOperator(ctx *context.Context, devcontainersList *[]devcontainer_model.Devcontainer) error {
log.Info("AssignDevcontainerDeletion2K8sOperator: Starting Deletion for containers")
// 1. 获取 Dynamic Client
ctxVal := *ctx
client, err := devcontainer_k8s_agent_module.GetKubernetesClient(ctxVal, nil, "")
if err != nil {
// 层层返回错误,结束数据库事务
return err
}
// 获取标准 Kubernetes 客户端,用于删除 Ingress
stdClient, err := getStandardKubernetesClient()
if err != nil {
log.Warn("AssignDevcontainerDeletion2K8sOperator: 获取标准 K8s 客户端失败: %v", err)
// 继续执行,不阻止主流程
} else {
// 先删除与 DevContainer 相关的 Ingress 资源
for _, devcontainer := range *devcontainersList {
ingressName := fmt.Sprintf("%s-ttyd-ingress", devcontainer.Name)
log.Info("AssignDevcontainerDeletion2K8sOperator: 删除 Ingress %s", ingressName)
err := stdClient.NetworkingV1().Ingresses(setting.DevContainerConfig.Namespace).Delete(*ctx, ingressName, metav1.DeleteOptions{})
if err != nil {
if k8serrors.IsNotFound(err) {
// Ingress 已经不存在,视为正常情况
log.Info("AssignDevcontainerDeletion2K8sOperator: Ingress %s 不存在,跳过删除", ingressName)
} else {
log.Warn("AssignDevcontainerDeletion2K8sOperator: 删除 Ingress %s 失败: %v", ingressName, err)
// 继续执行,不阻止主流程
}
} else {
log.Info("AssignDevcontainerDeletion2K8sOperator: 成功删除 Ingress %s", ingressName)
}
}
}
// 2. 调用 modules 层 k8s Agent执行删除资源
opts := &devcontainer_dto.DeleteDevcontainerOptions{
DeleteOptions: metav1.DeleteOptions{},
Namespace: setting.DevContainerConfig.Namespace,
}
if devcontainersList == nil || len(*devcontainersList) == 0 {
return fmt.Errorf("delete devcontainer in namespace '%s': %s", opts.Namespace, "the DevContainer list is empty")
}
// 3. 遍历列表删除 DevContainer如果删除出错交由 module 层打印日志,交由管理员手动处理
for _, devcontainer := range *devcontainersList {
opts.Name = devcontainer.Name
_ = devcontainer_k8s_agent_module.DeleteDevcontainer(ctxVal, client, opts)
// 删除对应的 VirtualService
if err := deleteDevContainerWebTerminalVirtualService(ctx, devcontainer.Name); err != nil {
log.Warn("AssignDevcontainerDeletion2K8sOperator: 删除 VirtualService 失败 for DevContainer %s: %v", devcontainer.Name, err)
// 不阻止主流程,只记录警告
} else {
log.Info("AssignDevcontainerDeletion2K8sOperator: 成功删除 VirtualService for DevContainer: %s", devcontainer.Name)
}
}
return nil
}
// 补充笔记: modules/ 与 services/ 两个目录中的 k8s Agent 区别是什么?
// - modules/ 与 k8s API Server 交互密切相关
// - services/ 进行了封装,简化用户界面使用
// AssignDevcontainerCreation2K8sOperator 将 DevContainer 资源创建任务派遣至 k8s Operator同时根据结果更新 NodePort
//
// 注意:本方法仍然在数据库事务中,因此不适合执行长时间操作,故需要后期异步判断 DevContainer 是否就绪
func AssignDevcontainerCreation2K8sOperator(ctx *context.Context, newDevContainer *CreateDevcontainerDTO) error {
log.Info("AssignDevcontainerCreation2K8sOperator: Starting creation for container: %s", newDevContainer.Name)
log.Info("AssignDevcontainerCreation2K8sOperator: Container details - Image: %s, RepoURL: %s, SSHKeys: %d",
newDevContainer.Image, newDevContainer.GitRepositoryURL, len(newDevContainer.SSHPublicKeyList))
// 1. 获取 Dynamic Client
ctxVal := *ctx
client, err := devcontainer_k8s_agent_module.GetKubernetesClient(ctxVal, nil, "")
if err != nil {
// 层层返回错误,结束数据库事务
return err
}
// 1.1:插入 devcontainer_output 记录
dbEngine := db.GetEngine(*ctx)
// 更新状态为 1正在拉取镜像
_, err = dbEngine.Table("devcontainer").
Where("user_id = ? AND repo_id = ? ", newDevContainer.UserId, newDevContainer.RepoId).
Update(&devcontainer_model.Devcontainer{DevcontainerStatus: 1})
if err != nil {
log.Info("Failed to update status to 1: %v", err)
}
// 插入拉取镜像记录
if _, err := dbEngine.Table("devcontainer_output").Insert(&devcontainer_model.DevcontainerOutput{
Output: "Pulling image for K8s container: " + newDevContainer.Image,
ListId: 0,
Status: "success", // 设为 success 以满足 created 变量的条件
UserId: newDevContainer.UserId,
RepoId: newDevContainer.RepoId,
Command: "Pull Image",
}); err != nil {
log.Info("Failed to insert Pull Image record: %v", err)
// 不返回错误,继续执行
}
// 更新状态为 2正在创建和启动容器
_, err = dbEngine.Table("devcontainer").
Where("user_id = ? AND repo_id = ? ", newDevContainer.UserId, newDevContainer.RepoId).
Update(&devcontainer_model.Devcontainer{DevcontainerStatus: 2})
if err != nil {
log.Info("Failed to update status to 2: %v", err)
}
// 插入初始化工作区记录 (满足 created = true 的关键条件)
if _, err := dbEngine.Table("devcontainer_output").Insert(&devcontainer_model.DevcontainerOutput{
Output: "Initializing workspace in Kubernetes...",
Status: "success", // 必须为 success
UserId: newDevContainer.UserId,
RepoId: newDevContainer.RepoId,
Command: "Initialize Workspace",
ListId: 1, // ListId > 0 且 Status = success 是 created = true 的条件
}); err != nil {
log.Info("Failed to insert Initialize Workspace record: %v", err)
// 不返回错误,继续执行
}
// 更新状态为 3容器安装必要工具
_, err = dbEngine.Table("devcontainer").
Where("user_id = ? AND repo_id = ? ", newDevContainer.UserId, newDevContainer.RepoId).
Update(&devcontainer_model.Devcontainer{DevcontainerStatus: 3})
if err != nil {
log.Info("Failed to update status to 3: %v", err)
}
// 插入初始化 DevStar 记录
if _, err := dbEngine.Table("devcontainer_output").Insert(&devcontainer_model.DevcontainerOutput{
Output: "Initializing DevStar in Kubernetes...",
Status: "success",
UserId: newDevContainer.UserId,
RepoId: newDevContainer.RepoId,
Command: "Initialize DevStar",
ListId: 2,
}); err != nil {
log.Info("Failed to insert Initialize DevStar record: %v", err)
// 不返回错误,继续执行
}
// 插入 postCreateCommand 记录
if _, err := dbEngine.Table("devcontainer_output").Insert(&devcontainer_model.DevcontainerOutput{
Output: "Running post-create commands in Kubernetes...",
Status: "success",
UserId: newDevContainer.UserId,
RepoId: newDevContainer.RepoId,
Command: "Run postCreateCommand",
ListId: 3,
}); err != nil {
log.Info("Failed to insert Run postCreateCommand record: %v", err)
// 不返回错误,继续执行
}
// 添加 ttyd 端口配置 - WebTerminal 功能
log.Info("AssignDevcontainerCreation2K8sOperator: Adding ttyd port configuration (7681)")
extraPorts := []k8s_api_v1.ExtraPortSpec{
{
Name: "ttyd",
ContainerPort: 7681, // ttyd 默认端口
ServicePort: 7681,
},
}
command := []string{
"/bin/bash",
"-c",
"export DEBIAN_FRONTEND=noninteractive && " +
"apt-get update -y && " +
"apt-get install -y ssh && " +
// 改为条件生成:只有在密钥不存在时才生成
"if [ ! -f /etc/ssh/ssh_host_rsa_key ]; then " +
" echo 'Generating SSH host keys...' && " +
" ssh-keygen -A && " +
" echo 'SSH host keys generated' ; " +
"else " +
" echo 'SSH host keys already exist' ; " +
"fi && " +
"mkdir -p /var/run/sshd && " +
"/usr/sbin/sshd && " +
"if [ -f /ttyd-shared/ttyd ]; then " +
"mkdir -p /data/workspace && " +
"cd /data/workspace && " +
"/ttyd-shared/ttyd -p 7681 -i 0.0.0.0 --writable bash > /tmp/ttyd.log 2>&1 & " +
"fi && " +
"while true; do sleep 60; done",
}
log.Info("AssignDevcontainerCreation2K8sOperator: Command includes ttyd installation and startup")
// 2. 调用 modules 层 k8s Agent执行创建资源
opts := &devcontainer_dto.CreateDevcontainerOptions{
CreateOptions: metav1.CreateOptions{},
Name: newDevContainer.Name,
Namespace: setting.DevContainerConfig.Namespace,
Image: newDevContainer.Image,
/**
* 配置 Kubernetes 主容器启动命令注意事项:
* 1. 确保 Image 中已安装 OpenSSH Server
* 2. 容器启动后必须拉起 OpenSSH 后台服务
* 3. 请勿使用 sleep infinity 或者 tail -f /dev/null 等无限等待命令,
* 可以考虑无限循环 sleep 60s能够防止 k8s 中容器先变成 Completed 然后变为 CrashLoopBackOff
* 也可以防止造成大量僵尸(<defunct>)进程:
* $ ps aux | grep "<defunct>" # 列举僵尸进程
* USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
* pollina+ 2336 0.0 0.0 0 0 ? Z 17:22 0:00 [sshd] <defunct>
* pollina+ 10986 0.0 0.0 0 0 ? Z 16:12 0:00 [sshd] <defunct>
* pollina+ 24722 0.0 0.0 0 0 ? Z 18:36 0:00 [sshd] <defunct>
* pollina+ 26773 0.0 0.0 0 0 ? Z 18:37 0:00 [sshd] <defunct>
* $ ubuntu@node2:~$ ps o ppid 2336 10986 24722 26773 # 查询僵尸进程父进程PID
* PPID
* 21826
* $ ps aux | grep # 列举僵尸进程父进程详情
* USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
* root 21826 0.0 0.0 2520 408 ? Ss 18:36 0:00 sleep infinity
*/
CommandList: command,
ContainerPort: 22,
ServicePort: 22,
SSHPublicKeyList: newDevContainer.SSHPublicKeyList,
GitRepositoryURL: newDevContainer.GitRepositoryURL,
ExtraPorts: extraPorts, // 添加额外端口配置
}
// 2. 创建成功,取回集群中的 DevContainer
log.Info("AssignDevcontainerCreation2K8sOperator: Creating DevcontainerApp %s in namespace %s",
opts.Name, opts.Namespace)
devcontainerInCluster, err := devcontainer_k8s_agent_module.CreateDevcontainer(ctxVal, client, opts)
if err != nil {
log.Error("AssignDevcontainerCreation2K8sOperator: Failed to create DevcontainerApp: %v", err)
return err
}
log.Info("AssignDevcontainerCreation2K8sOperator: DevcontainerApp created successfully - Name: %s",
devcontainerInCluster.Name)
// 不再在创建后立即置为 4保持为 3待 Pod Ready 后由 GetDevContainerStatus 升级为 4
// 3. 处理 NodePort - 检查是否为0尚未分配
nodePort := devcontainerInCluster.Status.NodePortAssigned
if nodePort == 0 {
log.Info("AssignDevcontainerCreation2K8sOperator: NodePort not yet assigned, starting async updater for %s",
devcontainerInCluster.Name)
// 将端口设为0数据库中记录特殊标记
newDevContainer.DevcontainerPort = 0
// 记录容器已创建,但端口待更新
log.Info("DevContainer created in cluster - Name: %s, NodePort: pending assignment",
devcontainerInCluster.Name)
// 启动异步任务来更新端口
go updateNodePortAsync(devcontainerInCluster.Name,
setting.DevContainerConfig.Namespace,
newDevContainer.UserId,
newDevContainer.RepoId)
} else {
log.Info("AssignDevcontainerCreation2K8sOperator: NodePort %d assigned immediately to %s",
nodePort, devcontainerInCluster.Name)
// 端口已分配,直接使用
newDevContainer.DevcontainerPort = nodePort
log.Info("DevContainer created in cluster - Name: %s, NodePort: %d",
devcontainerInCluster.Name, nodePort)
}
log.Info("DevContainer created in cluster - Name: %s, NodePort: %d",
devcontainerInCluster.Name,
devcontainerInCluster.Status.NodePortAssigned)
// 为 ttyd 服务创建 Istio Gateway 和 VirtualService
log.Info("AssignDevcontainerCreation2K8sOperator: 开始创建 Istio 资源 for DevContainer: %s", devcontainerInCluster.Name)
// 1. 确保 Gateway 存在
if err := createDevContainerWebTerminalGateway(ctx); err != nil {
log.Warn("AssignDevcontainerCreation2K8sOperator: 创建 Gateway 失败: %v", err)
// 不阻止主流程,只记录警告
} else {
log.Info("AssignDevcontainerCreation2K8sOperator: Gateway 创建成功")
}
// 2. 创建 VirtualService
if err := createDevContainerWebTerminalVirtualService(ctx, devcontainerInCluster.Name); err != nil {
log.Warn("AssignDevcontainerCreation2K8sOperator: 创建 VirtualService 失败: %v", err)
// 不阻止主流程,只记录警告
} else {
log.Info("AssignDevcontainerCreation2K8sOperator: VirtualService 创建成功")
}
// 4. 层层返回 nil自动提交数据库事务完成 DevContainer 创建
return nil
}
// AssignDevcontainerRestart2K8sOperator 将 DevContainer 重启任务派遣至 K8s 控制器
func AssignDevcontainerRestart2K8sOperator(ctx *context.Context, opts *DevcontainerVO) error {
log.Info("AssignDevcontainerRestart2K8sOperator: Starting restart for container: %s", opts.DevContainerName)
// 1. 获取 Dynamic Client
ctxVal := *ctx
client, err := devcontainer_k8s_agent_module.GetKubernetesClient(ctxVal, nil, "")
if err != nil {
log.Error("Failed to get Kubernetes client: %v", err)
return err
}
// 2. 通过打补丁方式实现重启 - 更新注解以触发控制器重新部署 Pod
// 创建补丁,添加或更新 restartedAt 注解,同时确保 desiredReplicas 为 1
patchData := fmt.Sprintf(`{
"metadata": {
"annotations": {
"devstar.io/restartedAt": "%s",
"devstar.io/desiredReplicas": "1"
}
}
}`, time.Now().Format(time.RFC3339))
log.Info("AssignDevcontainerRestart2K8sOperator: Applying patch to restart container %s",
opts.DevContainerName)
log.Debug("AssignDevcontainerRestart2K8sOperator: Patch data: %s", patchData)
// 应用补丁到 DevcontainerApp CRD
_, err = client.Resource(k8sGroupVersionResource).
Namespace(setting.DevContainerConfig.Namespace).
Patch(ctxVal, opts.DevContainerName, types.MergePatchType, []byte(patchData), metav1.PatchOptions{})
if err != nil {
log.Error("Failed to patch DevcontainerApp for restart: %v", err)
return fmt.Errorf("restart k8s devcontainer '%s' failed: %v", opts.DevContainerName, err)
}
// 记录重启操作日志
log.Info("DevContainer restarted: %s", opts.DevContainerName)
log.Info("AssignDevcontainerRestart2K8sOperator: Restart patch applied successfully for %s",
opts.DevContainerName)
// 将重启操作记录到数据库
dbEngine := db.GetEngine(*ctx)
_, err = dbEngine.Table("devcontainer_output").Insert(&devcontainer_model.DevcontainerOutput{
Output: fmt.Sprintf("Restarting K8s DevContainer %s", opts.DevContainerName),
Status: "success",
UserId: opts.UserId,
RepoId: opts.RepoId,
Command: "Restart DevContainer",
ListId: 0,
})
if err != nil {
log.Warn("Failed to insert restart record: %v", err)
}
return nil
}
// AssignDevcontainerStop2K8sOperator 将 DevContainer 停止任务派遣至 K8s 控制器
func AssignDevcontainerStop2K8sOperator(ctx *context.Context, opts *DevcontainerVO) error {
// 1. 获取 Dynamic Client
ctxVal := *ctx
client, err := devcontainer_k8s_agent_module.GetKubernetesClient(ctxVal, nil, "")
if err != nil {
log.Error("Failed to get Kubernetes client: %v", err)
return err
}
// 2. 通过打补丁方式实现停止 - 添加停止注解
// 创建补丁,添加或更新 stopped 和 desiredReplicas 注解
patchData := fmt.Sprintf(`{
"metadata": {
"annotations": {
"devstar.io/stoppedAt": "%s",
"devstar.io/desiredReplicas": "0"
}
}
}`, time.Now().Format(time.RFC3339))
// 应用补丁到 DevcontainerApp CRD
_, err = client.Resource(k8sGroupVersionResource).
Namespace(setting.DevContainerConfig.Namespace).
Patch(ctxVal, opts.DevContainerName, types.MergePatchType, []byte(patchData), metav1.PatchOptions{})
if err != nil {
log.Error("Failed to patch DevcontainerApp for stop: %v", err)
return fmt.Errorf("stop k8s devcontainer '%s' failed: %v", opts.DevContainerName, err)
}
// 记录停止操作日志
log.Info("DevContainer stopped: %s", opts.DevContainerName)
// 将停止操作记录到数据库
dbEngine := db.GetEngine(*ctx)
_, err = dbEngine.Table("devcontainer_output").Insert(&devcontainer_model.DevcontainerOutput{
Output: fmt.Sprintf("Stopping K8s DevContainer %s", opts.DevContainerName),
Status: "success",
UserId: opts.UserId,
RepoId: opts.RepoId,
Command: "Stop DevContainer",
ListId: 0,
})
if err != nil {
// 只记录错误,不影响主流程返回结果
log.Warn("Failed to insert stop record: %v", err)
}
return nil
}
// 异步更新 NodePort 的辅助函数
func updateNodePortAsync(containerName string, namespace string, userId, repoId int64) {
log.Info("updateNodePortAsync: Starting for container: %s in namespace: %s", containerName, namespace)
log.Info("updateNodePortAsync: Waiting 20 seconds for K8s controller to assign port")
// 等待K8s控制器完成端口分配
time.Sleep(20 * time.Second)
// 创建新的上下文和客户端
ctx := context.Background()
client, err := devcontainer_k8s_agent_module.GetKubernetesClient(ctx, nil, "")
if err != nil {
log.Error("Failed to get K8s client in async updater: %v", err)
return
}
log.Info("updateNodePortAsync: K8s client created successfully")
// 尝试最多10次获取端口
for i := 0; i < 10; i++ {
log.Info("updateNodePortAsync: Attempt %d/10 to retrieve NodePort for %s", i+1, containerName)
getOpts := &devcontainer_k8s_agent_module.GetDevcontainerOptions{
GetOptions: metav1.GetOptions{},
Name: containerName,
Namespace: namespace,
Wait: false,
}
devcontainer, err := devcontainer_k8s_agent_module.GetDevcontainer(ctx, client, getOpts)
if err == nil && devcontainer != nil && devcontainer.Status.NodePortAssigned > 0 {
log.Info("updateNodePortAsync: Success! Found NodePort %d for %s",
devcontainer.Status.NodePortAssigned, containerName)
// 获取到正确的端口,更新数据库
realNodePort := devcontainer.Status.NodePortAssigned
// 记录 ttyd 端口信息到日志
if len(devcontainer.Status.ExtraPortsAssigned) > 0 {
for _, portInfo := range devcontainer.Status.ExtraPortsAssigned {
log.Info("Found extra port for %s: name=%s, nodePort=%d, containerPort=%d",
containerName, portInfo.Name, portInfo.NodePort, portInfo.ContainerPort)
}
}
log.Info("Found real NodePort %d for container %s, updating database record",
realNodePort, containerName)
engine := db.GetEngine(ctx)
_, err := engine.Table("devcontainer").
Where("user_id = ? AND repo_id = ?", userId, repoId).
Update(map[string]interface{}{
"devcontainer_port": realNodePort,
})
if err != nil {
log.Error("Failed to update NodePort in database: %v", err)
} else {
log.Info("Successfully updated NodePort in database to %d", realNodePort)
}
return
}
log.Info("updateNodePortAsync: Port not yet assigned, waiting 5 seconds before next attempt")
time.Sleep(5 * time.Second)
}
log.Warn("updateNodePortAsync: Failed to retrieve real NodePort after multiple attempts")
}
// 获取标准 Kubernetes 客户端
func getStandardKubernetesClient() (*kubernetes.Clientset, error) {
// 使用与 GetKubernetesClient 相同的逻辑获取配置
config, err := clientcmd.BuildConfigFromFlags("", clientcmd.RecommendedHomeFile)
if err != nil {
// 如果集群外配置失败,尝试集群内配置
log.Warn("Failed to obtain Kubernetes config outside of cluster: %v", err)
config, err = rest.InClusterConfig()
if err != nil {
return nil, fmt.Errorf("获取 K8s 配置失败 (集群内外均失败): %v", err)
}
}
// 创建标准客户端
stdClient, err := kubernetes.NewForConfig(config)
if err != nil {
return nil, fmt.Errorf("创建标准 K8s 客户端失败: %v", err)
}
return stdClient, nil
}
// 创建 DevContainer WebTerminal Gateway
func createDevContainerWebTerminalGateway(ctx *context.Context) error {
log.Info("createDevContainerWebTerminalGateway: 开始创建 DevContainer WebTerminal Gateway")
// 获取 Dynamic Client
ctxVal := *ctx
client, err := devcontainer_k8s_agent_module.GetKubernetesClient(ctxVal, nil, "")
if err != nil {
return fmt.Errorf("获取 K8s 客户端失败: %v", err)
}
gatewayName := "devcontainer-webterminal-gateway"
namespace := setting.DevContainerConfig.Namespace
// 检查 Gateway 是否已存在
gwGVR := schema.GroupVersionResource{Group: "networking.istio.io", Version: "v1", Resource: "gateways"}
if _, err := client.Resource(gwGVR).Namespace(namespace).Get(ctxVal, gatewayName, metav1.GetOptions{}); err == nil {
log.Info("createDevContainerWebTerminalGateway: Gateway 已存在: %s", gatewayName)
return nil
} else if !k8serrors.IsNotFound(err) {
return fmt.Errorf("检查 Gateway 失败: %v", err)
}
// 从配置中读取域名
cfg, err := setting.NewConfigProviderFromFile(setting.CustomConf)
if err != nil {
return fmt.Errorf("加载配置文件失败: %v", err)
}
domain := cfg.Section("server").Key("DOMAIN").Value()
// 使用 Unstructured 定义 GatewayHTTP-only
gw := &unstructured.Unstructured{Object: map[string]interface{}{
"apiVersion": "networking.istio.io/v1",
"kind": "Gateway",
"metadata": map[string]interface{}{
"name": gatewayName,
"namespace": namespace,
"labels": map[string]interface{}{
"app.kubernetes.io/name": "devcontainer-webterminal",
"app.kubernetes.io/component": "gateway",
"app.kubernetes.io/managed-by": "devstar",
},
},
"spec": map[string]interface{}{
"selector": map[string]interface{}{"istio": "ingressgateway"},
"servers": []interface{}{
map[string]interface{}{
"port": map[string]interface{}{"number": 80, "name": "http", "protocol": "HTTP"},
"hosts": []interface{}{domain, "*"},
},
},
},
}}
if _, err := client.Resource(gwGVR).Namespace(namespace).Create(ctxVal, gw, metav1.CreateOptions{}); err != nil {
return fmt.Errorf("创建 Gateway 失败: %v", err)
}
log.Info("createDevContainerWebTerminalGateway: 成功创建 Gateway: %s", gatewayName)
return nil
}
// 创建 DevContainer WebTerminal VirtualService
func createDevContainerWebTerminalVirtualService(ctx *context.Context, devcontainerName string) error {
log.Info("createDevContainerWebTerminalVirtualService: 开始创建 VirtualService for DevContainer: %s", devcontainerName)
// 获取 Dynamic Client
ctxVal := *ctx
client, err := devcontainer_k8s_agent_module.GetKubernetesClient(ctxVal, nil, "")
if err != nil {
return fmt.Errorf("获取 K8s 客户端失败: %v", err)
}
vsName := devcontainerName + "-webterminal-vs"
namespace := setting.DevContainerConfig.Namespace
// 从配置中读取域名
cfg, err := setting.NewConfigProviderFromFile(setting.CustomConf)
if err != nil {
return fmt.Errorf("加载配置文件失败: %v", err)
}
domain := cfg.Section("server").Key("DOMAIN").Value()
// VirtualService 的 hosts 只能二选一:有 DOMAIN 用 [DOMAIN],否则用 ["*"]
var vsHosts []interface{}
if domain != "" {
vsHosts = []interface{}{domain}
} else {
vsHosts = []interface{}{"*"}
}
// 从容器名称中提取用户名和仓库名
parts := strings.Split(devcontainerName, "-")
var username, repoName string
if len(parts) >= 2 {
username = parts[0]
repoName = parts[1]
} else {
username = "unknown"
repoName = "unknown"
}
// 构建访问路径
path := fmt.Sprintf("/%s/%s/dev-container-webterminal", username, repoName)
// 使用 Unstructured 定义 VS并增加路径重写到根路径
vsGVR := schema.GroupVersionResource{Group: "networking.istio.io", Version: "v1", Resource: "virtualservices"}
vs := &unstructured.Unstructured{Object: map[string]interface{}{
"apiVersion": "networking.istio.io/v1",
"kind": "VirtualService",
"metadata": map[string]interface{}{
"name": vsName,
"namespace": namespace,
"labels": map[string]interface{}{
"app.kubernetes.io/name": "devcontainer-webterminal",
"app.kubernetes.io/component": "virtualservice",
"app.kubernetes.io/managed-by": "devstar",
"devcontainer-name": devcontainerName,
},
},
"spec": map[string]interface{}{
"hosts": vsHosts,
"gateways": []interface{}{"devcontainer-webterminal-gateway"},
"http": []interface{}{
map[string]interface{}{
"match": []interface{}{map[string]interface{}{"uri": map[string]interface{}{"prefix": path}}},
"rewrite": map[string]interface{}{"uri": "/"},
"route": []interface{}{
map[string]interface{}{
"destination": map[string]interface{}{
"host": devcontainerName + "-svc",
"port": map[string]interface{}{"number": 7681},
},
},
},
"timeout": "3600s",
"retries": map[string]interface{}{
"attempts": 3,
"perTryTimeout": "30s",
"retryOn": "5xx,gateway-error,connect-failure",
},
},
},
},
}}
if _, err := client.Resource(vsGVR).Namespace(namespace).Create(ctxVal, vs, metav1.CreateOptions{}); err != nil {
return fmt.Errorf("创建 VirtualService 失败: %v", err)
}
log.Info("createDevContainerWebTerminalVirtualService: 成功创建 VirtualService: %s, 路径: %s", vsName, path)
return nil
}
// 删除 DevContainer WebTerminal VirtualService
func deleteDevContainerWebTerminalVirtualService(ctx *context.Context, devcontainerName string) error {
log.Info("deleteDevContainerWebTerminalVirtualService: 开始删除 VirtualService for DevContainer: %s", devcontainerName)
// 获取 Dynamic Client
ctxVal := *ctx
client, err := devcontainer_k8s_agent_module.GetKubernetesClient(ctxVal, nil, "")
if err != nil {
return fmt.Errorf("获取 K8s 客户端失败: %v", err)
}
vsName := devcontainerName + "-webterminal-vs"
namespace := setting.DevContainerConfig.Namespace
vsGVR := schema.GroupVersionResource{Group: "networking.istio.io", Version: "v1", Resource: "virtualservices"}
if err := client.Resource(vsGVR).Namespace(namespace).Delete(ctxVal, vsName, metav1.DeleteOptions{}); err != nil {
if k8serrors.IsNotFound(err) {
log.Info("deleteDevContainerWebTerminalVirtualService: VirtualService 不存在,无需删除: %s", vsName)
return nil
}
return fmt.Errorf("删除 VirtualService 失败: %v", err)
}
log.Info("deleteDevContainerWebTerminalVirtualService: 成功删除 VirtualService: %s", vsName)
return nil
}
// executeCommandInK8sPod 在 K8s Pod 中执行命令的辅助函数
func executeCommandInK8sPod(ctx *context.Context, client *kubernetes.Clientset, namespace, devcontainerName, containerName string, command []string) error {
log.Info("executeCommandInK8sPod: 开始为 DevContainer %s 查找对应的 Pod", devcontainerName)
// 1. 首先根据标签选择器查找对应的 Pod
labelSelector := fmt.Sprintf("app=%s", devcontainerName)
pods, err := client.CoreV1().Pods(namespace).List(*ctx, metav1.ListOptions{
LabelSelector: labelSelector,
})
if err != nil {
log.Error("executeCommandInK8sPod: 查找 Pod 失败: %v", err)
return fmt.Errorf("查找 Pod 失败: %v", err)
}
if len(pods.Items) == 0 {
log.Error("executeCommandInK8sPod: 未找到 DevContainer %s 对应的 Pod", devcontainerName)
return fmt.Errorf("未找到 DevContainer %s 对应的 Pod", devcontainerName)
}
// 2. 找到第一个运行中的 Pod
var targetPod *v1.Pod
for i := range pods.Items {
pod := &pods.Items[i]
if pod.Status.Phase == v1.PodRunning {
targetPod = pod
break
}
}
if targetPod == nil {
log.Error("executeCommandInK8sPod: DevContainer %s 没有运行中的 Pod", devcontainerName)
return fmt.Errorf("DevContainer %s 没有运行中的 Pod", devcontainerName)
}
podName := targetPod.Name
log.Info("executeCommandInK8sPod: 找到运行中的 Pod: %s, 在容器 %s 中执行命令",
podName, containerName)
// 3. 执行命令
req := client.CoreV1().RESTClient().Post().
Resource("pods").
Name(podName).
Namespace(namespace).
SubResource("exec").
Param("container", containerName)
req.VersionedParams(&v1.PodExecOptions{
Container: containerName,
Command: command,
Stdin: false,
Stdout: true,
Stderr: true,
TTY: false,
}, scheme.ParameterCodec)
// 获取 executor
config, err := clientcmd.BuildConfigFromFlags("", clientcmd.RecommendedHomeFile)
if err != nil {
// 如果集群外配置失败,尝试集群内配置
config, err = rest.InClusterConfig()
if err != nil {
return fmt.Errorf("获取 K8s 配置失败: %v", err)
}
}
executor, err := remotecommand.NewSPDYExecutor(config, "POST", req.URL())
if err != nil {
return fmt.Errorf("创建命令执行器失败: %v", err)
}
// 执行命令
var stdout, stderr bytes.Buffer
err = executor.StreamWithContext(*ctx, remotecommand.StreamOptions{
Stdout: &stdout,
Stderr: &stderr,
})
if err != nil {
log.Error("executeCommandInK8sPod: 命令执行失败: %v, stderr: %s",
err, stderr.String())
return fmt.Errorf("命令执行失败: %v, stderr: %s", err, stderr.String())
}
log.Info("executeCommandInK8sPod: 命令执行成功, stdout: %s", stdout.String())
return nil
}

repo.diff.view_file

@@ -48,8 +48,9 @@ type AdminEditUserForm struct {
Restricted bool
AllowGitHook bool
AllowImportLocal bool
AllowCreateOrganization bool
AllowCreateDevcontainer bool
AllowCreateOrganization bool `form:"allow_create_organization"`
AllowCreateDevcontainer bool `form:"allow_create_devcontainer"`
AllowCreateActRunner bool `form:"allow_create_actrunner"`
ProhibitLogin bool
Reset2FA bool `form:"reset_2fa"`
Visibility structs.VisibleType

repo.diff.view_file

@@ -60,8 +60,9 @@ type InstallForm struct {
EnableCaptcha bool
RequireSignInView bool
DefaultKeepEmailPrivate bool
DefaultAllowCreateOrganization bool
DefaultAllowCreateDevcontainer bool
DefaultAllowCreateOrganization bool `form:"default_allow_create_organization"`
DefaultAllowCreateDevcontainer bool `form:"default_allow_create_devcontainer"`
DefaultAllowCreateActRunner bool `form:"default_allow_create_actrunner"`
DefaultEnableTimetracking bool
EnableUpdateChecker bool
NoReplyAddress string

repo.diff.view_file

@@ -40,6 +40,7 @@ func checkK8sIsEnable() bool {
func RegistRunner(ctx context.Context, token string) error {
log.Info("开始注册Runner...")
var err error
if checkK8sIsEnable() {
err = registK8sRunner(ctx, token)

repo.diff.view_file

@@ -52,6 +52,7 @@ type UpdateOptions struct {
DiffViewStyle optional.Option[string]
AllowCreateOrganization optional.Option[bool]
AllowCreateDevcontainer optional.Option[bool]
AllowCreateActRunner optional.Option[bool]
IsActive optional.Option[bool]
IsAdmin optional.Option[UpdateOptionField[bool]]
EmailNotificationsPreference optional.Option[string]
@@ -170,6 +171,11 @@ func UpdateUser(ctx context.Context, u *user_model.User, opts *UpdateOptions) er
cols = append(cols, "allow_create_devcontainer")
}
if opts.AllowCreateActRunner.Has() {
u.AllowCreateActRunner = opts.AllowCreateActRunner.Value()
cols = append(cols, "allow_create_act_runner")
}
if opts.RepoAdminChangeTeamAccess.Has() {
u.RepoAdminChangeTeamAccess = opts.RepoAdminChangeTeamAccess.Value()

repo.diff.view_file

@@ -155,6 +155,8 @@
<dd>{{svg (Iif .Service.DefaultAllowCreateOrganization "octicon-check" "octicon-x")}}</dd>
<dt>{{ctx.Locale.Tr "admin.config.default_allow_create_devcontainer"}}</dt>
<dd>{{svg (Iif .Service.DefaultAllowCreateDevcontainer "octicon-check" "octicon-x")}}</dd>
<dt>{{ctx.Locale.Tr "admin.config.default_allow_create_actrunner"}}</dt>
<dd>{{svg (Iif .Service.DefaultAllowCreateActRunner "octicon-check" "octicon-x")}}</dd>
<dt>{{ctx.Locale.Tr "admin.config.enable_timetracking"}}</dt>
<dd>{{svg (Iif .Service.EnableTimetracking "octicon-check" "octicon-x")}}</dd>
{{if .Service.EnableTimetracking}}

repo.diff.view_file

@@ -155,6 +155,13 @@
</div>
</div>
<div class="inline field">
<div class="ui checkbox">
<label><strong>{{ctx.Locale.Tr "admin.users.allow_create_actrunner"}}</strong></label>
<input name="allow_create_actrunner" type="checkbox" {{if .User.AllowCreateActRunner}}checked{{end}}>
</div>
</div>
{{if .TwoFactorEnabled}}
<div class="divider"></div>
<div class="inline field">

repo.diff.view_file

@@ -8,7 +8,7 @@
<div class="ui attached segment">
{{template "base/alert" .}}
<p>{{ctx.Locale.Tr "install.docker_helper" "https://docs.gitea.com/installation/install-with-docker"}}</p>
<p>{{ctx.Locale.Tr "install.k8s_helper" "https://www.mengning.com/src/devstar/install/install-k8s"}}</p>
<form class="ui form" action="{{AppSubUrl}}/" method="post">
<!-- Database Settings -->
@@ -160,7 +160,7 @@
<div>
<!-- k8s -->
<details class="optional field">
<details class="optional field">
<summary class="right-content tw-py-2{{if .Err_K8s}} text red{{end}}">
{{ctx.Locale.Tr "install.k8s_title"}}
</summary>
@@ -178,7 +178,6 @@
<label for="k8s_token">{{ctx.Locale.Tr "install.k8s_token"}}</label>
<input id="k8s_token" name="k8s_token" value="{{.k8s_token}}">
</div>
</div>
</details>
<!-- Email -->
<details class="optional field">
@@ -310,6 +309,12 @@
<input name="default_allow_create_devcontainer" type="checkbox" {{if .default_allow_create_devcontainer}}checked{{end}}>
</div>
</div>
<div class="inline field">
<div class="ui checkbox">
<label data-tooltip-content="{{ctx.Locale.Tr "install.default_allow_create_actrunner_popup"}}">{{ctx.Locale.Tr "install.default_allow_create_actrunner"}}</label>
<input name="default_allow_create_actrunner" type="checkbox" {{if .default_allow_create_actrunner}}checked{{end}}>
</div>
</div>
<div class="inline field">
<div class="ui checkbox">
<label data-tooltip-content="{{ctx.Locale.Tr "install.default_enable_timetracking_popup"}}">{{ctx.Locale.Tr "install.default_enable_timetracking"}}</label>

repo.diff.view_file

@@ -9,9 +9,11 @@
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
</button>
<div class="menu">
{{if or (.AllowCreateActRunner)}}
<div class="item">
<a href="{{$.Link}}/regist_runner">{{ctx.Locale.Tr "actions.runners.regist_runner"}}</a>
</div>
{{end}}
<div class="item">
<a href="https://docs.gitea.com/usage/actions/act-runner">{{ctx.Locale.Tr "actions.runners.new_notice"}}</a>
</div>

repo.diff.view_file

@@ -8,7 +8,7 @@ test.beforeAll(async ({browser}, workerInfo) => {
test('homepage', async ({page}) => {
const response = await page.goto('/');
expect(response?.status()).toBe(200); // Status OK
await expect(page).toHaveTitle(/^Gitea: Git with a cup of tea\s*$/);
await expect(page).toHaveTitle(/^DevStar: The Last Mile of Al for R&D\s*$/);
await expect(page.locator('.logo')).toHaveAttribute('src', '/assets/img/logo.svg');
});

repo.diff.view_file

@@ -1,4 +1,4 @@
APP_NAME = Gitea: Git with a cup of tea
APP_NAME = DevStar: The Last Mile of Al for R&D
RUN_MODE = prod
[database]

repo.diff.view_file

@@ -1,4 +1,4 @@
APP_NAME = Gitea: Git with a cup of tea
APP_NAME = DevStar: The Last Mile of Al for R&D
RUN_MODE = prod
[database]

repo.diff.view_file

@@ -1,4 +1,4 @@
APP_NAME = Gitea: Git with a cup of tea
APP_NAME = DevStar: The Last Mile of Al for R&D
RUN_MODE = prod
[database]

repo.diff.view_file

@@ -1,4 +1,4 @@
APP_NAME = Gitea: Git with a cup of tea
APP_NAME = DevStar: The Last Mile of Al for R&D
RUN_MODE = prod
[database]